Enforce single terminal review decision per PR review run (Issue #211)
Reviewer agents could post probe APPROVE/REQUEST_CHANGES reviews while testing lock paths, polluting PR audit trails. Add a review decision lock seeded by gitea_resolve_task_capability(review_pr) that requires gitea_mark_final_review_decision and final_review_decision_ready=True before gitea_submit_pr_review performs a live mutation. Add gitea_dry_run_pr_review for read-only submission validation, gitea_authorize_review_correction for operator-approved fixes, and assess_review_mutation_final_report for final-report proof. One live review mutation per run unless correction is explicitly authorized.
This commit is contained in:
@@ -582,6 +582,54 @@ def assess_role_boundary(proof=None, *, task_role=None, namespaces_used=None,
|
||||
}
|
||||
|
||||
|
||||
def assess_review_mutation_final_report(report_text, review_decision_lock):
|
||||
"""Require final reports to list exactly one live review mutation.
|
||||
|
||||
Two mutations are allowed only when an operator-approved correction flow
|
||||
was invoked and explained in the report.
|
||||
"""
|
||||
lock = review_decision_lock or {}
|
||||
text = report_text or ""
|
||||
lower = text.lower()
|
||||
mutations = list(lock.get("live_mutations") or [])
|
||||
missing = []
|
||||
|
||||
count = len(mutations)
|
||||
if count == 0:
|
||||
if any(term in lower for term in ("submitted 'approve'", "submitted 'request_changes'", "live review mutation")):
|
||||
missing.append("review mutation count")
|
||||
elif count == 1:
|
||||
m = mutations[0]
|
||||
action = m.get("action")
|
||||
pr_number = m.get("pr_number")
|
||||
if action and action.lower() not in lower:
|
||||
missing.append("review mutation action")
|
||||
if pr_number is not None and f"#{pr_number}" not in lower and f"pr {pr_number}" not in lower:
|
||||
missing.append("review mutation PR number")
|
||||
elif count > 1:
|
||||
if not lock.get("correction_authorized") and "correction" not in lower:
|
||||
missing.append("correction flow explanation")
|
||||
if "review mutations:" not in lower and "review mutation" not in lower:
|
||||
missing.append("review mutation listing")
|
||||
|
||||
if missing:
|
||||
return {
|
||||
"complete": False,
|
||||
"downgraded": True,
|
||||
"missing_fields": missing,
|
||||
"reasons": [
|
||||
f"final report missing review-mutation field: {field}"
|
||||
for field in missing
|
||||
],
|
||||
}
|
||||
return {
|
||||
"complete": True,
|
||||
"downgraded": False,
|
||||
"missing_fields": [],
|
||||
"reasons": [],
|
||||
}
|
||||
|
||||
|
||||
def build_final_report(checkout_proof, inventory, validation, contamination,
|
||||
identity_eligible, merge_performed,
|
||||
issue_status_verified,
|
||||
|
||||
Reference in New Issue
Block a user