feat: block issue closure on unproven expected pre-existing failure (#529)

Delivers #529 acceptance criterion 6: controller issue closure must be
blocked when the closure report calls a non-zero test-suite exit an
"expected pre-existing"/baseline failure without pre-merge proof.

- controller_closure_baseline_proof.py: assess_controller_closure_baseline_proof
  reuses the #533 pre-merge baseline verifier; empty report -> skipped/allowed,
  clean pass -> allowed, proven baseline -> allowed, unproven baseline -> block.
- gitea_close_issue: optional closure_report param; fails closed (no state PATCH)
  when the gate blocks.
- final_report_validator: new controller_close task kind (alias close_issue)
  wired to the pre-merge baseline proof rule so a closure can be pre-checked.
- Tests: tests/test_controller_closure_baseline_proof.py plus two
  gitea_close_issue gate tests.

Scope: criteria 2/3 (non-zero exit not a clean pass; baseline claims need
proof) are already enforced on master via premerge_baseline_proof (#533) and
the reviewer schema rules. Criteria 1/4/5 (post-merge moot canonical wording
and validation:* process-state labels) remain follow-up work.

Validation: full suite 2365 passed, 6 skipped; the 9 remaining failures
(tests/test_config.py TestAuthIntegration, tests/test_credentials.py
TestGetCredentials, test_issue_540 reviewer-role) are baseline-proven —
identical failures on clean prgs/master 6913ac9 (keychain/env dependent),
unrelated to this change.

Refs #529

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-09 15:39:54 -04:00
co-authored by Claude Opus 4.8
parent 6913ac98f1
commit eddb68818e
5 changed files with 261 additions and 0 deletions
+23
View File
@@ -6070,6 +6070,7 @@ def gitea_close_issue(
host: str | None = None,
org: str | None = None,
repo: str | None = None,
closure_report: str | None = None,
) -> dict:
"""Close an issue by setting its state to 'closed'.
@@ -6079,6 +6080,9 @@ def gitea_close_issue(
host: Override the Gitea host.
org: Override the owner/organization.
repo: Override the repository name.
closure_report: Optional validation/closure summary. When it claims a
non-zero test-suite exit is an "expected pre-existing"/baseline
failure without pre-merge proof, the close fails closed (#529).
Returns:
dict with 'success' boolean and 'message'.
@@ -6088,6 +6092,25 @@ def gitea_close_issue(
if blocked:
return blocked
verify_preflight_purity(remote, task="close_issue")
# #529: block closure that buries an unproven non-zero suite exit as an
# "expected pre-existing failure". Skipped when no closure report is given.
from controller_closure_baseline_proof import (
assess_controller_closure_baseline_proof,
)
closure_gate = assess_controller_closure_baseline_proof(closure_report)
if closure_gate["block"]:
return {
"success": False,
"blocked": True,
"message": (
f"Issue #{issue_number} close blocked (#529): closure report "
"claims an expected pre-existing/baseline failure without "
"pre-merge proof."
),
"reasons": closure_gate["reasons"],
"safe_next_action": closure_gate["safe_next_action"],
}
h, o, r = _resolve(remote, host, org, repo)
auth = _auth(h)
url = f"{repo_api_url(h, o, r)}/issues/{issue_number}"