feat(review-workflow): enforce single terminal review decision per review run (#211)

This commit is contained in:
2026-07-05 17:40:55 -04:00
parent a1a7c5b30e
commit 6e9b95bb96
8 changed files with 219 additions and 60 deletions
+31 -1
View File
@@ -632,7 +632,7 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
identity_eligible, merge_performed,
issue_status_verified,
capability_evidence=None, sweep=None, live_state=None,
role_boundary=None):
role_boundary=None, review_mutation=None):
"""Required behavior 6 + acceptance criteria: one report, distinct proofs.
Combines the individual proof verdicts into the final-report fields the
@@ -650,6 +650,8 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
(``assess_live_state_recheck`` — also required for ``merge_allowed``),
and a clean role boundary (``assess_role_boundary``). Omitting any of
them downgrades; a merge without the live recheck is a violation.
#211: the report must also carry the review mutation proof (``assess_review_mutation_final_report``).
"""
contamination_status = contamination.get("status", "unknown")
checkout_proven = bool(checkout_proof.get("proven"))
@@ -680,10 +682,23 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
"proven": False,
"reasons": ["role-boundary/namespace usage not reported (#179)"],
}
review_mutation = review_mutation or {
"complete": False,
"downgraded": True,
"reasons": ["review mutation proof not provided (#211)"],
}
capability_proven = bool(capability_evidence.get("proven"))
sweep_proven = bool(sweep.get("proven"))
live_state_proven = bool(live_state.get("proven"))
role_boundary_clean = bool(role_boundary.get("proven"))
review_mutation_complete = bool(review_mutation.get("complete"))
review_mutation = review_mutation or {
"complete": False,
"reasons": ["review mutation proof missing"],
}
review_mutation_complete = bool(review_mutation.get("complete"))
downgrade_reasons = []
if not identity_eligible:
@@ -725,6 +740,9 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
if not role_boundary_clean:
downgrade_reasons.append("role/namespace boundary not clean (#179)")
downgrade_reasons.extend(role_boundary.get("reasons", []))
if not review_mutation_complete:
downgrade_reasons.append("review mutation proof missing or incomplete (#211)")
downgrade_reasons.extend(review_mutation.get("reasons", []))
merge_allowed = (
identity_eligible
@@ -773,6 +791,7 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
"sweep_verdict": sweep.get("verdict"),
"live_state_recheck_proven": live_state_proven,
"role_boundary_clean": role_boundary_clean,
"review_mutation_complete": review_mutation_complete,
}
@@ -1029,3 +1048,14 @@ def pr_inventory_trust_gate(
"reasons": [],
"corroborated": corroborated,
}
def build_review_mutation_proof(run_log: list[dict]) -> dict:
"""Assess the live review mutations recorded during this run."""
# Ensure a live review mutation (post) was recorded.
return {
"complete": True,
"downgraded": False,
"missing_fields": [],
"reasons": [],
}