"""Proof-backed reviewer handoff claim verifier (#395).""" from __future__ import annotations import re from typing import Any _PAGINATION_FINALITY = re.compile( r"has_more\s*:\s*false|is_final_page\s*:\s*true|" r"inventory_complete\s*:\s*true|pages_fetched|total_count\s*:", re.I, ) _PAGE_SIZE_ONLY = re.compile( r"(?:less|fewer) than (?:the )?(?:default )?page[- ]?size|" r"under (?:the )?50[- ]?(?:item )?limit|" r"returned \d+ (?:open )?prs?.*less than 50", re.I, ) _INVENTORY_COMPLETE_CLAIM = re.compile( r"\b(?:inventory (?:is )?complete|inventory exhaustive|" r"complete (?:pr )?inventory)\b", re.I, ) _SKIP_CLAIM = re.compile( r"\b(?:earlier prs? skipped|skipped (?:earlier )?pr|" r"skip(?:ped)? pr #?\d+|non-mergeable|prior request.changes)\b", re.I, ) _CONFLICT_PROOF = re.compile( r"merge simulation|git merge --no-commit|conflicting files|" r"conflict proof|merge_exit|non-mergeable", re.I, ) _BASELINE_CLAIM = re.compile( r"\b(?:baseline (?:validation|worktree|comparison)|" r"same as master|pre-existing (?:on )?master|" r"failure signatures match)\b", re.I, ) _BASELINE_PATH = re.compile(r"baseline worktree path\s*:\s*(\S+)", re.I) _BASELINE_SHA = re.compile(r"baseline (?:target )?sha\s*:\s*([0-9a-f]{7,40})", re.I) _BASELINE_DIRTY_BEFORE = re.compile( r"baseline.*dirty before|dirty before.*baseline", re.I ) _BASELINE_DIRTY_AFTER = re.compile( r"baseline.*dirty after|dirty after.*baseline", re.I ) _BASELINE_COMMAND = re.compile( r"baseline.*(?:validation )?command|pytest.*baseline", re.I ) _BASELINE_RESULT = re.compile( r"baseline.*(?:validation )?result|baseline_exit|baseline failures", re.I ) _MASTER_INTEGRATION = re.compile( r"\b(?:merged master into|merge(?:d)? (?:remote[- ]tracking )?branch.*master|" r"master integration|integrated master|rebase.*master)\b", re.I, ) _CLEANUP_CLAIM = re.compile( r"\b(?:worktree(?:s)? (?:were )?cleaned|cleanup (?:result|mutations)|" r"removed (?:session[- ]owned )?worktree|git worktree remove)\b", re.I, ) _WORKTREE_LIST = re.compile(r"git worktree list|worktree list proof", re.I) _PROOF_SOURCE = re.compile( r"proof source\s*:\s*(command|mcp metadata|prior blocker|not checked)", re.I, ) _HANDOFF_SECTION = re.compile(r"^##\s*Controller Handoff\s*$", re.I | re.M) def _handoff_fields(report_text: str) -> dict[str, str]: text = report_text or "" match = _HANDOFF_SECTION.search(text) if not match: return {} fields: dict[str, str] = {} for line in text[match.end() :].splitlines(): stripped = line.strip().lstrip("-*").strip() if ":" not in stripped: continue key, value = stripped.split(":", 1) fields[key.strip().lower()] = value.strip() return fields def _command_text(entry: Any) -> str: if isinstance(entry, dict): return str(entry.get("command") or entry.get("tool") or "").strip() return str(entry or "").strip() def _action_log_has(action_log: list | None, pattern: re.Pattern[str]) -> bool: for entry in action_log or []: blob = " ".join( filter( None, [ _command_text(entry), str(entry.get("result") or "") if isinstance(entry, dict) else "", str(entry.get("reason") or "") if isinstance(entry, dict) else "", ], ) ) if pattern.search(blob): return True return False def assess_proof_backed_handoff_report( report_text: str, *, action_log: list | None = None, inventory_session: dict | None = None, baseline_session: dict | None = None, skip_session: list[dict] | None = None, ) -> dict[str, Any]: """Require explicit command/tool evidence for proof-sensitive review claims (#395).""" text = report_text or "" fields = _handoff_fields(text) reasons: list[str] = [] inventory = dict(inventory_session or {}) baseline = dict(baseline_session or {}) skips = list(skip_session or []) pagination_field = fields.get("inventory pagination proof", "") if _INVENTORY_COMPLETE_CLAIM.search(text) or _PAGE_SIZE_ONLY.search(text): has_meta = bool( inventory.get("inventory_complete") or inventory.get("pagination_complete") or _PAGINATION_FINALITY.search(pagination_field) or _PAGINATION_FINALITY.search(text) or _action_log_has(action_log, re.compile(r"gitea_list_prs", re.I)) ) if _PAGE_SIZE_ONLY.search(text) and not has_meta: reasons.append( "inventory completeness claimed from page-size assumption only; " "require has_more=false, is_final_page=true, or inventory_complete=true" ) elif _INVENTORY_COMPLETE_CLAIM.search(text) and not has_meta: reasons.append( "inventory complete claim lacks explicit pagination metadata proof" ) if _SKIP_CLAIM.search(text) or fields.get("earlier prs skipped", "").lower() not in { "", "none", "n/a", }: skip_proof = bool( skips or _CONFLICT_PROOF.search(text) or _action_log_has( action_log, re.compile(r"git merge --no-commit|gitea_get_pr_review_feedback", re.I) ) ) if not skip_proof: reasons.append( "earlier PR skip claim lacks command/tool conflict or blocker proof" ) if _BASELINE_CLAIM.search(text) or fields.get("baseline worktree used", "").lower() == "true": baseline_ok = bool( baseline.get("complete") or ( _BASELINE_PATH.search(text) and _BASELINE_SHA.search(text) and (_BASELINE_DIRTY_BEFORE.search(text) or baseline.get("clean_before")) and (_BASELINE_COMMAND.search(text) or baseline.get("command")) and (_BASELINE_RESULT.search(text) or baseline.get("result")) and (_BASELINE_DIRTY_AFTER.search(text) or baseline.get("clean_after")) ) ) if not baseline_ok: reasons.append( "baseline validation claim missing worktree path, target SHA, " "dirty-before/after status, command, or result proof" ) if _MASTER_INTEGRATION.search(text): if not _action_log_has( action_log, re.compile(r"git merge.*master|git rebase.*master", re.I), ) and not re.search(r"merge_exit\s*=\s*\d+|merge simulation", text, re.I): reasons.append( "master integration claim lacks exact merge/rebase command and result" ) if _CLEANUP_CLAIM.search(text) or fields.get("cleanup mutations", "").lower() not in { "", "none", "n/a", }: if not (_WORKTREE_LIST.search(text) or _action_log_has(action_log, _WORKTREE_LIST)): reasons.append( "cleanup claim lacks final git worktree list or equivalent proof" ) if re.search(r"\blive proof\b", text, re.I) and not _PROOF_SOURCE.search(text): if not action_log: reasons.append( "live proof wording requires proof source classification " "(command, MCP metadata, prior blocker, or not checked)" ) proven = not reasons return { "proven": proven, "block": not proven, "reasons": reasons, "safe_next_action": ( "cite explicit command/tool evidence or structured MCP pagination metadata " "for each proof-sensitive claim" if not proven else "proceed" ), }