Add mcp_native_cleanup_proof verifier that blocks raw git branch deletion and raw API comment deletion scripts as cleanup proof, requires authorized reconciler MCP tools in cleanup mutation ledgers, and separates merge from cleanup mutations in controller handoffs. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
138 lines
5.3 KiB
Python
138 lines
5.3 KiB
Python
"""Tests for MCP-native post-merge cleanup proof (#517)."""
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from final_report_validator import assess_final_report_validator # noqa: E402
|
|
from mcp_native_cleanup_proof import ( # noqa: E402
|
|
assess_authorized_reconciler_cleanup_path,
|
|
assess_mcp_native_cleanup_proof,
|
|
assess_merger_cleanup_handoff_guidance,
|
|
assess_raw_branch_delete_report,
|
|
assess_raw_comment_delete_report,
|
|
)
|
|
|
|
|
|
def _authorized_cleanup_report(**overrides):
|
|
fields = {
|
|
"Merge mutations": "gitea_merge_pr on PR #100",
|
|
"Cleanup mutations": (
|
|
"gitea_reconcile_merged_cleanups deleted remote branch; "
|
|
"gitea_cleanup_post_merge_moot_lease released lease"
|
|
),
|
|
"Reconciler capability": "prgs-reconciler / gitea.branch.delete resolved",
|
|
"Next actor": "reconciler session complete",
|
|
}
|
|
fields.update(overrides)
|
|
lines = ["## Controller Handoff", ""]
|
|
lines.extend(f"- {key}: {value}" for key, value in fields.items())
|
|
return "\n".join(lines)
|
|
|
|
|
|
class TestRawBranchDeleteBlocked(unittest.TestCase):
|
|
def test_git_branch_d_blocked(self):
|
|
report = "Cleanup mutations: git branch -d feat/issue-1-x"
|
|
result = assess_raw_branch_delete_report(report)
|
|
self.assertTrue(result["block"])
|
|
self.assertIn("git branch -d", result["commands"][0])
|
|
|
|
def test_git_push_delete_blocked(self):
|
|
report = "Ran git push origin --delete feat/x for cleanup"
|
|
result = assess_raw_branch_delete_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_authorized_mcp_path_passes(self):
|
|
report = _authorized_cleanup_report()
|
|
result = assess_raw_branch_delete_report(report)
|
|
self.assertFalse(result["block"])
|
|
|
|
|
|
class TestRawCommentDeleteBlocked(unittest.TestCase):
|
|
def test_curl_delete_comment_blocked(self):
|
|
report = (
|
|
"Cleanup: curl -X DELETE https://gitea.example/api/v1/repos/o/r/"
|
|
"issues/9/comments/42"
|
|
)
|
|
result = assess_raw_comment_delete_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_delete_issue_comment_helper_blocked(self):
|
|
report = "Used delete_issue_comment script to purge lease comments"
|
|
result = assess_raw_comment_delete_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_moot_lease_append_only_passes(self):
|
|
report = _authorized_cleanup_report()
|
|
result = assess_raw_comment_delete_report(report)
|
|
self.assertFalse(result["block"])
|
|
|
|
|
|
class TestAuthorizedReconcilerPath(unittest.TestCase):
|
|
def test_cleanup_without_mcp_tool_blocked(self):
|
|
report = "\n".join([
|
|
"## Controller Handoff",
|
|
"- Cleanup mutations: removed branch manually after merge",
|
|
"- Reconciler capability: prgs-reconciler",
|
|
])
|
|
result = assess_authorized_reconciler_cleanup_path(report)
|
|
self.assertTrue(result["block"])
|
|
self.assertTrue(any("authorized MCP" in r for r in result["reasons"]))
|
|
|
|
def test_authorized_cleanup_succeeds(self):
|
|
result = assess_authorized_reconciler_cleanup_path(_authorized_cleanup_report())
|
|
self.assertFalse(result["block"])
|
|
|
|
|
|
class TestMergerHandoffGuidance(unittest.TestCase):
|
|
def test_merger_cleanup_without_handoff_blocked(self):
|
|
report = "Merger deleted remote branch and removed worktree after merge"
|
|
result = assess_merger_cleanup_handoff_guidance(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_merger_cleanup_with_reconciler_handoff_passes(self):
|
|
report = (
|
|
"Merger deleted remote branch; next actor: reconciler for worktree audit"
|
|
)
|
|
result = assess_merger_cleanup_handoff_guidance(report)
|
|
self.assertFalse(result["block"])
|
|
|
|
|
|
class TestCompositeVerifier(unittest.TestCase):
|
|
def test_full_authorized_report_passes(self):
|
|
result = assess_mcp_native_cleanup_proof(_authorized_cleanup_report())
|
|
self.assertFalse(result["block"], result["reasons"])
|
|
|
|
def test_raw_bypasses_fail_composite(self):
|
|
report = "\n".join([
|
|
"## Controller Handoff",
|
|
"- Merge mutations: gitea_merge_pr",
|
|
"- Cleanup mutations: git branch -D feat/x; curl -X DELETE .../comments/1",
|
|
])
|
|
result = assess_mcp_native_cleanup_proof(report)
|
|
self.assertTrue(result["block"])
|
|
self.assertGreaterEqual(len(result["reasons"]), 2)
|
|
|
|
|
|
class TestFinalReportValidatorIntegration(unittest.TestCase):
|
|
def test_validator_blocks_raw_branch_delete_in_review_report(self):
|
|
report = "\n".join([
|
|
"## Controller Handoff",
|
|
"- Cleanup mutations: git branch -d feat/issue-517-test",
|
|
])
|
|
result = assess_final_report_validator(report, "review_pr")
|
|
rules = {f["rule_id"] for f in result["findings"]}
|
|
self.assertIn("shared.mcp_native_cleanup_proof", rules)
|
|
|
|
def test_validator_passes_authorized_cleanup_report(self):
|
|
result = assess_final_report_validator(_authorized_cleanup_report(), "review_pr")
|
|
blocked = [f for f in result["findings"] if f.get("severity") == "block"]
|
|
mcp_blocks = [
|
|
f for f in blocked if f.get("rule_id") == "shared.mcp_native_cleanup_proof"
|
|
]
|
|
self.assertEqual(mcp_blocks, [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() |