Merge pull request 'feat: require infra_stop repair handoff for blocked reviewers (Closes #289)' (#366) from feat/issue-289-infra-stop-handoff into master
This commit was merged in pull request #366.
This commit is contained in:
@@ -4752,6 +4752,13 @@ def assess_validation_worktree_edit_report(report_text, **kwargs):
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_infra_stop_handoff_report(report_text, **kwargs):
|
||||
"""#289: repair handoff purity when infra_stop blocks reviewer capability."""
|
||||
from reviewer_infra_stop_handoff import assess_infra_stop_handoff_report as _assess
|
||||
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_mutation_categories_report(report_text, **kwargs):
|
||||
"""#319: require precise mutation categories in reviewer controller handoffs."""
|
||||
from reviewer_mutation_categories import assess_mutation_categories_report as _assess
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
"""Infra-stop repair handoff verifier for blocked reviewer workflows (#289)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from capability_stop_terminal import (
|
||||
TERMINAL_REPORT_HEADING,
|
||||
assess_capability_stop_report,
|
||||
)
|
||||
|
||||
_REPAIR_HANDOFF_RE = re.compile(
|
||||
r"repair handoff|control-checkout repair mode",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_CONTROL_REPAIR_RE = re.compile(r"control-checkout repair mode", re.IGNORECASE)
|
||||
_PR_QUEUE_ADVANCE_RE = re.compile(
|
||||
r"(?:selected pr|pr #\d+ (?:to review|selected)|eligible pr|"
|
||||
r"next (?:eligible )?pr(?: to review)?|oldest eligible pr|pinned (?:review )?head)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_REVIEW_MUTATION_RE = re.compile(
|
||||
r"(?:gitea_review_pr|gitea_merge_pr|request_changes|submitted\s+approve|"
|
||||
r"merge result|review decision\s*:\s*approve)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_BACKGROUND_TOOL_RE = re.compile(r"\b(?:schedule|manage_task)\b", re.IGNORECASE)
|
||||
_PR_REVIEW_REPORT_RE = re.compile(
|
||||
r"(?:review summary|merge recommendation|validation result\s*:\s*pass|"
|
||||
r"queue status report with selected pr)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
_DIAGNOSTIC_FIELDS = {
|
||||
"mcp process root": re.compile(r"mcp process root\s*:", re.IGNORECASE),
|
||||
"inspected git root": re.compile(r"inspected git root\s*:", re.IGNORECASE),
|
||||
"conflict marker path": re.compile(
|
||||
r"(?:conflict marker path|exact conflict marker path)\s*:",
|
||||
re.IGNORECASE,
|
||||
),
|
||||
"merge/rebase control path": re.compile(
|
||||
r"(?:merge/rebase control path|exact merge/rebase control path)\s*:",
|
||||
re.IGNORECASE,
|
||||
),
|
||||
"safe next repair action": re.compile(
|
||||
r"safe next (?:repair )?action\s*:",
|
||||
re.IGNORECASE,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def assess_infra_stop_handoff_report(
|
||||
report_text: str,
|
||||
*,
|
||||
stop_session: dict | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Validate repair handoff purity when infra_stop blocks reviewer capability (#289)."""
|
||||
text = report_text or ""
|
||||
session = dict(stop_session or {})
|
||||
reasons: list[str] = []
|
||||
|
||||
infra_stop = bool(
|
||||
session.get("infra_stop")
|
||||
or session.get("infra_stop_blocked")
|
||||
or session.get("route_result") == "infra_stop"
|
||||
)
|
||||
if not infra_stop:
|
||||
return {
|
||||
"proven": True,
|
||||
"block": False,
|
||||
"reasons": [],
|
||||
"infra_stop": False,
|
||||
"safe_next_action": "proceed",
|
||||
}
|
||||
|
||||
lower = text.lower()
|
||||
repair_handoff = bool(
|
||||
_REPAIR_HANDOFF_RE.search(text)
|
||||
or TERMINAL_REPORT_HEADING.lower() in lower
|
||||
)
|
||||
if not repair_handoff:
|
||||
reasons.append(
|
||||
"infra_stop requires a repair handoff, not a PR review report"
|
||||
)
|
||||
|
||||
if _PR_REVIEW_REPORT_RE.search(text) and not _REPAIR_HANDOFF_RE.search(text):
|
||||
reasons.append(
|
||||
"final output must be a repair handoff instead of stale PR review state"
|
||||
)
|
||||
|
||||
if _PR_QUEUE_ADVANCE_RE.search(text):
|
||||
reasons.append(
|
||||
"infra_stop blocks PR queue advancement; do not select or advance next PR"
|
||||
)
|
||||
|
||||
if _REVIEW_MUTATION_RE.search(text):
|
||||
reasons.append(
|
||||
"review, approval, request-changes, merge, or comment mutations "
|
||||
"forbidden while infra_stop is active"
|
||||
)
|
||||
|
||||
if session.get("pinned_head_sha") or re.search(
|
||||
r"pinned (?:review )?head sha\s*:", text, re.IGNORECASE
|
||||
):
|
||||
if session.get("capability_cleared") is not True:
|
||||
reasons.append(
|
||||
"cannot pin PR head SHA while review capability never cleared"
|
||||
)
|
||||
|
||||
if session.get("main_checkout_diagnostics") and not _CONTROL_REPAIR_RE.search(text):
|
||||
reasons.append(
|
||||
"main-checkout diagnostics must be labeled CONTROL-CHECKOUT REPAIR MODE"
|
||||
)
|
||||
|
||||
if _BACKGROUND_TOOL_RE.search(text):
|
||||
reasons.append(
|
||||
"background schedule/manage_task tools must not be used during "
|
||||
"blocked infra_stop recovery"
|
||||
)
|
||||
|
||||
assessment = session.get("infra_stop_assessment") or {}
|
||||
if assessment.get("infra_stop") or infra_stop:
|
||||
missing = [
|
||||
label
|
||||
for label, pattern in _DIAGNOSTIC_FIELDS.items()
|
||||
if not pattern.search(text)
|
||||
]
|
||||
if missing and session.get("require_infra_diagnostics", True):
|
||||
reasons.append(
|
||||
"infra_stop repair handoff missing diagnostic fields: "
|
||||
+ ", ".join(missing)
|
||||
)
|
||||
conflict_file = assessment.get("conflict_file")
|
||||
if conflict_file and conflict_file.lower() not in lower:
|
||||
reasons.append(
|
||||
f"infra_stop assessment conflict file {conflict_file!r} "
|
||||
"not reflected in repair handoff"
|
||||
)
|
||||
|
||||
capability = assess_capability_stop_report(
|
||||
text,
|
||||
trust_gate_status=session.get("trust_gate_status"),
|
||||
capability_denied=True,
|
||||
)
|
||||
if not capability.get("pure"):
|
||||
reasons.extend(capability.get("reasons") or [])
|
||||
|
||||
proven = not reasons
|
||||
return {
|
||||
"proven": proven,
|
||||
"block": not proven,
|
||||
"reasons": list(dict.fromkeys(reasons)),
|
||||
"infra_stop": True,
|
||||
"repair_handoff": repair_handoff,
|
||||
"safe_next_action": (
|
||||
"stop PR queue work; emit CONTROL-CHECKOUT REPAIR MODE handoff "
|
||||
"with infra diagnostics and safe next repair action"
|
||||
if reasons
|
||||
else "proceed"
|
||||
),
|
||||
}
|
||||
@@ -166,6 +166,12 @@ def test_validation_worktree_edit_verifier_exported():
|
||||
assert callable(assess_validation_worktree_edit_report)
|
||||
|
||||
|
||||
def test_infra_stop_handoff_verifier_exported():
|
||||
from review_proofs import assess_infra_stop_handoff_report
|
||||
|
||||
assert callable(assess_infra_stop_handoff_report)
|
||||
|
||||
|
||||
def test_mutation_categories_verifier_exported():
|
||||
from review_proofs import assess_mutation_categories_report
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
"""Tests for infra-stop repair handoff verifier (#289)."""
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from capability_stop_terminal import TERMINAL_REPORT_HEADING
|
||||
from reviewer_infra_stop_handoff import assess_infra_stop_handoff_report # noqa: E402
|
||||
|
||||
|
||||
def _repair_handoff() -> str:
|
||||
return "\n".join([
|
||||
TERMINAL_REPORT_HEADING,
|
||||
"Repair handoff for infra_stop blocked reviewer workflow.",
|
||||
"CONTROL-CHECKOUT REPAIR MODE",
|
||||
"MCP process root: /Users/dev/Gitea-Tools",
|
||||
"Inspected git root: /Users/dev/Gitea-Tools",
|
||||
"Conflict marker path: gitea_mcp_server.py",
|
||||
"Merge/rebase control path: none",
|
||||
"Safe next repair action: resolve conflict markers and restart MCP",
|
||||
"infra_stop: true",
|
||||
])
|
||||
|
||||
|
||||
class TestInfraStopHandoff(unittest.TestCase):
|
||||
def test_repair_handoff_passes(self):
|
||||
result = assess_infra_stop_handoff_report(
|
||||
_repair_handoff(),
|
||||
stop_session={
|
||||
"infra_stop": True,
|
||||
"infra_stop_assessment": {
|
||||
"infra_stop": True,
|
||||
"conflict_file": "gitea_mcp_server.py",
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertTrue(result["proven"], result["reasons"])
|
||||
|
||||
def test_next_pr_selection_blocks(self):
|
||||
report = "\n".join([
|
||||
TERMINAL_REPORT_HEADING,
|
||||
"Next eligible PR to review: PR #276",
|
||||
"Pinned review head SHA: abc123",
|
||||
])
|
||||
result = assess_infra_stop_handoff_report(
|
||||
report,
|
||||
stop_session={"infra_stop": True, "require_infra_diagnostics": False},
|
||||
)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertTrue(any("queue" in r.lower() for r in result["reasons"]))
|
||||
|
||||
def test_missing_repair_handoff_blocks(self):
|
||||
result = assess_infra_stop_handoff_report(
|
||||
"Validation result: pass\nReview summary for PR #276",
|
||||
stop_session={"infra_stop": True},
|
||||
)
|
||||
self.assertFalse(result["proven"])
|
||||
|
||||
def test_main_checkout_diagnostics_without_label_blocks(self):
|
||||
report = "\n".join([
|
||||
TERMINAL_REPORT_HEADING,
|
||||
"Repair handoff",
|
||||
"Ran git status in main checkout for MCP diagnostics.",
|
||||
])
|
||||
result = assess_infra_stop_handoff_report(
|
||||
report,
|
||||
stop_session={
|
||||
"infra_stop": True,
|
||||
"main_checkout_diagnostics": True,
|
||||
"require_infra_diagnostics": False,
|
||||
},
|
||||
)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertTrue(any("control-checkout" in r.lower() for r in result["reasons"]))
|
||||
|
||||
def test_background_scheduling_blocks(self):
|
||||
report = "\n".join([
|
||||
TERMINAL_REPORT_HEADING,
|
||||
"Repair handoff",
|
||||
"Used schedule/manage_task while waiting for MCP recovery.",
|
||||
])
|
||||
result = assess_infra_stop_handoff_report(
|
||||
report,
|
||||
stop_session={"infra_stop": True, "require_infra_diagnostics": False},
|
||||
)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertTrue(any("schedule" in r.lower() for r in result["reasons"]))
|
||||
|
||||
def test_non_infra_stop_skips(self):
|
||||
result = assess_infra_stop_handoff_report(
|
||||
"Selected PR #1 for review",
|
||||
stop_session={"infra_stop": False},
|
||||
)
|
||||
self.assertTrue(result["proven"])
|
||||
|
||||
|
||||
class TestExport(unittest.TestCase):
|
||||
def test_review_proofs_reexport(self):
|
||||
from review_proofs import assess_infra_stop_handoff_report as exported
|
||||
|
||||
self.assertTrue(callable(exported))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user