feat(author): harden reporting and claim capability per #183
- Add 'mark_issue' to TASK_MAP in resolve_task_capability for exact mutation proof (closes gap on unknown treated as allowed). - Add assess_controller_handoff and integrate into build_final_report (downgrades missing handoff). - Add tests for handoff and update existing. - Update SKILL.md and review-pr.md to mandate exact 'Controller Handoff' section, exact sweep evidence, PR head SHA in reports. - Author profile used throughout; no review/merge. Refs #183
This commit is contained in:
@@ -21,6 +21,7 @@ import unittest
|
||||
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))
|
||||
|
||||
from review_proofs import ( # noqa: E402
|
||||
assess_controller_handoff,
|
||||
assess_inventory_completeness,
|
||||
assess_self_review_contamination,
|
||||
assess_validation_report,
|
||||
@@ -314,6 +315,21 @@ class TestValidationReporting(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(result["verdict"], "strong")
|
||||
|
||||
def test_stdout_capture_fix_without_summary_is_weak(self):
|
||||
result = _good_validation(
|
||||
is_stdout_capture_fix=True,
|
||||
normal_pytest_summary=False
|
||||
)
|
||||
self.assertEqual(result["verdict"], "weak")
|
||||
self.assertTrue(any("stdout-capture" in r for r in result["reasons"]))
|
||||
|
||||
def test_stdout_capture_fix_with_summary_is_strong(self):
|
||||
result = _good_validation(
|
||||
is_stdout_capture_fix=True,
|
||||
normal_pytest_summary=True
|
||||
)
|
||||
self.assertEqual(result["verdict"], "strong")
|
||||
|
||||
|
||||
class TestSelfReviewContamination(unittest.TestCase):
|
||||
"""Required behavior 5: contamination claims need evidence."""
|
||||
@@ -380,6 +396,7 @@ class TestFinalReport(unittest.TestCase):
|
||||
"identity_eligible": True,
|
||||
"merge_performed": False,
|
||||
"issue_status_verified": True,
|
||||
"controller_handoff": "Controller Handoff\n- Task: test",
|
||||
}
|
||||
kwargs.update(overrides)
|
||||
return build_final_report(**kwargs)
|
||||
@@ -464,6 +481,24 @@ class TestFinalReport(unittest.TestCase):
|
||||
self.assertNotEqual(report["grade"], "A")
|
||||
self.assertFalse(report["merge_allowed"])
|
||||
|
||||
def test_failed_capability_proof_downgrades(self):
|
||||
cap_proof = {
|
||||
"proven": False,
|
||||
"reasons": ["task mark_issue not allowed"]
|
||||
}
|
||||
report = self._report(capability_proof=cap_proof)
|
||||
self.assertNotEqual(report["grade"], "A")
|
||||
self.assertTrue(any("capability" in r for r in report["downgrade_reasons"]))
|
||||
|
||||
def test_failed_sweep_proof_downgrades(self):
|
||||
sweep_proof = {
|
||||
"proven": False,
|
||||
"reasons": ["method missing"]
|
||||
}
|
||||
report = self._report(sweep_proof=sweep_proof)
|
||||
self.assertNotEqual(report["grade"], "A")
|
||||
self.assertTrue(any("sweep" in r for r in report["downgrade_reasons"]))
|
||||
|
||||
|
||||
class TestStdoutIsolation(unittest.TestCase):
|
||||
"""Regression test for #178: tests must not close or corrupt stdout/stderr
|
||||
@@ -576,5 +611,105 @@ class TestRepoNameDisambiguation(unittest.TestCase):
|
||||
self.assertTrue(result["can_claim_exhaustive"])
|
||||
|
||||
|
||||
class TestControllerHandoff(unittest.TestCase):
|
||||
"""#183: every final report must contain the Controller Handoff section."""
|
||||
|
||||
def test_report_with_handoff_passes(self):
|
||||
report = "some details\n\nController Handoff\n- Task: foo"
|
||||
result = assess_controller_handoff(report)
|
||||
self.assertTrue(result["present"])
|
||||
|
||||
def test_report_without_handoff_downgrades(self):
|
||||
report = "long details without the section"
|
||||
result = assess_controller_handoff(report)
|
||||
self.assertFalse(result["present"])
|
||||
self.assertTrue(any("Controller Handoff" in r for r in result["reasons"]))
|
||||
|
||||
|
||||
class TestAuthorReporting(unittest.TestCase):
|
||||
"""Harness assertions for author reporting and capability/sweep proofs (#183)."""
|
||||
|
||||
def test_good_capability_proof_passes(self):
|
||||
resolved = {
|
||||
"mark_issue": {
|
||||
"requested_task": "mark_issue",
|
||||
"required_operation_permission": "gitea.issue.write",
|
||||
"allowed_in_current_session": True,
|
||||
}
|
||||
}
|
||||
result = assess_capability_proof(resolved)
|
||||
self.assertTrue(result["proven"])
|
||||
|
||||
def test_missing_capability_proof_fails_closed(self):
|
||||
result = assess_capability_proof({})
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertTrue(any("fail closed" in r for r in result["reasons"]))
|
||||
|
||||
def test_unresolved_capability_proof_fails_closed(self):
|
||||
# unknown task resolving to None/unknown requested task
|
||||
resolved = {
|
||||
"mark_issue": {
|
||||
"requested_task": "unknown_task",
|
||||
"required_operation_permission": None,
|
||||
"allowed_in_current_session": None,
|
||||
}
|
||||
}
|
||||
result = assess_capability_proof(resolved)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertTrue(any("could not be resolved" in r for r in result["reasons"]))
|
||||
|
||||
def test_good_secret_sweep_passes(self):
|
||||
sweep = {
|
||||
"method": "git diff | grep -iE 'token|secret'",
|
||||
"scope": "staged diff relative to master",
|
||||
"clean": True,
|
||||
}
|
||||
result = assess_secret_sweep(sweep)
|
||||
self.assertTrue(result["proven"])
|
||||
self.assertEqual(result["verdict"], "strong")
|
||||
|
||||
def test_vague_secret_sweep_without_method_is_weak(self):
|
||||
sweep = {
|
||||
"method": "",
|
||||
"scope": "staged diff",
|
||||
"clean": True,
|
||||
}
|
||||
result = assess_secret_sweep(sweep)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertEqual(result["verdict"], "weak")
|
||||
self.assertTrue(any("method" in r or "scan" in r for r in result["reasons"]))
|
||||
|
||||
def test_unconfirmed_secret_sweep_is_invalid(self):
|
||||
sweep = {
|
||||
"method": "git diff | grep",
|
||||
"scope": "staged diff",
|
||||
"clean": False,
|
||||
}
|
||||
result = assess_secret_sweep(sweep)
|
||||
self.assertFalse(result["proven"])
|
||||
self.assertEqual(result["verdict"], "weak")
|
||||
|
||||
def test_good_author_pr_report_passes(self):
|
||||
pr = {
|
||||
"pr_number": 185,
|
||||
"branch": "feat/issue-184-repo-name-disambiguation",
|
||||
"head_sha": "e2bccbafeeb93124ba068bfb06058d5aa7467cae",
|
||||
}
|
||||
result = assess_author_pr_report(pr)
|
||||
self.assertTrue(result["complete"])
|
||||
|
||||
def test_incomplete_author_pr_report_fails(self):
|
||||
pr = {
|
||||
"pr_number": 0,
|
||||
"branch": "",
|
||||
"head_sha": "e2bccba",
|
||||
}
|
||||
result = assess_author_pr_report(pr)
|
||||
self.assertFalse(result["complete"])
|
||||
self.assertTrue(any("number" in r for r in result["reasons"]))
|
||||
self.assertTrue(any("branch" in r for r in result["reasons"]))
|
||||
self.assertTrue(any("SHA" in r for r in result["reasons"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user