Coverage for src/ai_jury/incremental.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-05 20:29 +0000

1"""Incremental review mode for updated PRs (issue #9). 

2 

3Re-reviewing a large PR's full diff on every push is slow and costly. Incremental 

4mode reviews only what changed since the jury last ran: it records the 

5reviewed head SHA in a hidden marker on its summary comment, and on the next run 

6compares that SHA to the current head to fetch just the new range. 

7 

8This module holds the PURE, network-free core — embedding/parsing the marker and 

9deciding the review scope — so it is fully unit-testable. The thin GitHub calls 

10(fetch head SHA, fetch comment bodies, fetch the range diff) live in 

11``github.py`` and the CLI; this module never touches the network. 

12""" 

13from __future__ import annotations 

14 

15import re 

16 

17# Hidden marker embedded in the jury summary comment recording the SHA that 

18# was reviewed, so a later run can compute the incremental range. 

19_MARKER_RE = re.compile(r"<!--\s*arc-reviewed-sha:([0-9a-fA-F]{7,40})\s*-->") 

20 

21MODE_FULL = "full" 

22MODE_INCREMENTAL = "incremental" 

23 

24 

25def reviewed_sha_marker(sha: str) -> str: 

26 """Return the hidden HTML-comment marker recording the reviewed head SHA.""" 

27 return f"<!-- arc-reviewed-sha:{sha} -->" 

28 

29 

30def parse_reviewed_sha(comment_bodies) -> str | None: 

31 """Return the most recent reviewed SHA across jury comment bodies, or None. 

32 

33 Scans every body for the marker and returns the LAST match found (later 

34 comments override earlier ones), so a fresh re-review marker wins. 

35 """ 

36 last: str | None = None 

37 for body in comment_bodies or []: 

38 for m in _MARKER_RE.finditer(body or ""): 

39 last = m.group(1) 

40 return last 

41 

42 

43def decide_review(prev_sha: str | None, head_sha: str | None) -> tuple[str, str]: 

44 """Decide review scope from the previous reviewed SHA and the current head. 

45 

46 Returns ``(mode, reason)`` where ``mode`` is ``"full"`` or ``"incremental"``. 

47 Falls back to a full review whenever incremental is not safely possible: no 

48 prior marker, unknown head, or an unchanged head (nothing new to review). 

49 """ 

50 if not prev_sha: 

51 return MODE_FULL, "no prior jury marker found — full review" 

52 if not head_sha: 

53 return MODE_FULL, "current head SHA unavailable — full review" 

54 if prev_sha == head_sha: 

55 return MODE_FULL, "head unchanged since last review — full review" 

56 return ( 

57 MODE_INCREMENTAL, 

58 f"incremental: reviewing {prev_sha[:7]}..{head_sha[:7]}", 

59 ) 

60 

61 

62def compare_range(prev_sha: str, head_sha: str) -> str: 

63 """Return the ``base...head`` range spec for the GitHub compare API.""" 

64 return f"{prev_sha}...{head_sha}" 

65 

66 

67def scope_note(mode: str, reason: str) -> str: 

68 """Render the user-facing review-scope line for the report (issue #9).""" 

69 label = "Incremental" if mode == MODE_INCREMENTAL else "Full" 

70 return f"**Review scope:** {label}{reason}"