feat: block reviewer local Gitea fallbacks during normal review (Closes #324)

Add assess_reviewer_fallback_report to fail closed when reviewer final
reports or action logs show profiles.json access or local helper scripts
while MCP tools are available. Recovery mode requires explicit fallback
classification and identity/profile/repo/capability proof.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-07 05:06:37 -04:00
co-authored by Claude Opus 4.8
parent cbae9a3ecd
commit 24788d3b4c
4 changed files with 338 additions and 0 deletions
+6
View File
@@ -118,6 +118,12 @@ def test_start_issue_template_references_work_issue_workflow():
assert "workflows/work-issue.md" in text
def test_reviewer_fallback_verifier_exported():
from review_proofs import assess_reviewer_fallback_report
assert callable(assess_reviewer_fallback_report)
def test_create_issue_final_report_verifier_exported():
from review_proofs import assess_create_issue_final_report
+112
View File
@@ -0,0 +1,112 @@
"""Tests for reviewer local-fallback verifier (#324)."""
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from reviewer_fallback import assess_reviewer_fallback_report # noqa: E402
class TestReviewerFallbackNormalMode(unittest.TestCase):
def test_clean_mcp_report_passes(self):
report = (
"Used gitea_list_prs and gitea_view_pr via MCP reviewer namespace.\n"
"Capability evidence: gitea_resolve_task_capability(review_pr)."
)
result = assess_reviewer_fallback_report(report, mcp_available=True)
self.assertTrue(result["proven"])
self.assertFalse(result["block"])
def test_profiles_json_access_blocks(self):
report = (
"Inspected ~/.config/gitea-tools/profiles.json for reviewer credentials.\n"
"Then used gitea_view_pr."
)
result = assess_reviewer_fallback_report(report, mcp_available=True)
self.assertFalse(result["proven"])
self.assertTrue(result["block"])
self.assertTrue(any("profiles.json" in r for r in result["reasons"]))
def test_local_list_prs_script_blocks_when_mcp_available(self):
report = (
"Ran `python list_prs.py --remote prgs` with GITEA_MCP_CONFIG set.\n"
"MCP tools were also available."
)
result = assess_reviewer_fallback_report(
report,
mcp_available=True,
action_log=[{"command": "GITEA_MCP_PROFILE=prgs-reviewer python list_prs.py"}],
)
self.assertFalse(result["proven"])
self.assertTrue(result["block"])
self.assertTrue(result["script_violations"])
def test_action_log_profile_access_blocks(self):
result = assess_reviewer_fallback_report(
"Review complete via MCP.",
action_log=[{"path": "/Users/me/.config/gitea-tools/profiles.json", "action": "read"}],
mcp_available=True,
)
self.assertFalse(result["proven"])
self.assertTrue(result["block"])
class TestReviewerFallbackRecoveryMode(unittest.TestCase):
def _recovery_report(self, **extra):
base = "\n".join([
"Recovery mode: MCP tools unavailable in this session.",
"Local fallback: ran python view_pr.py after capability proof.",
"Identity proof: sysadmin / prgs-reviewer",
"Profile proof: prgs-reviewer",
"Repo proof: Scaled-Tech-Consulting/Gitea-Tools",
"Capability proof: gitea.pr.review allowed for recovery command",
"Why MCP was unavailable: reviewer MCP namespace failed to start",
])
return base + ("\n" + extra.get("append", "") if extra.get("append") else "")
def test_recovery_fallback_with_proof_passes(self):
result = assess_reviewer_fallback_report(
self._recovery_report(),
action_log=[{"command": "python view_pr.py --remote prgs"}],
mcp_available=False,
recovery_mode=True,
)
self.assertTrue(result["proven"])
self.assertFalse(result["block"])
def test_recovery_fallback_without_proof_blocks(self):
report = (
"Recovery mode engaged.\n"
"Local fallback: python list_prs.py\n"
)
result = assess_reviewer_fallback_report(
report,
mcp_available=False,
recovery_mode=True,
action_log=[{"command": "python list_prs.py"}],
)
self.assertFalse(result["proven"])
self.assertTrue(result["block"])
self.assertTrue(
any("recovery" in r.lower() for r in result["reasons"])
)
def test_mcp_unavailable_allows_recovery_attempt(self):
result = assess_reviewer_fallback_report(
self._recovery_report(),
mcp_available=False,
recovery_mode=True,
)
self.assertTrue(result["proven"])
class TestReviewerFallbackExport(unittest.TestCase):
def test_review_proofs_reexport(self):
from review_proofs import assess_reviewer_fallback_report as exported
self.assertTrue(callable(exported))
if __name__ == "__main__":
unittest.main()