status:pr-open was applied by gitea_create_pr and never removed again. Every
terminal path finished without touching it, so a repository audit found 40
closed issues still advertising an open PR.
Add terminal_pr_label_cleanup.py as the single authoritative rule and route
every sanctioned terminal path through it, so the paths cannot drift:
- merge (gitea_merge_pr)
- close without merge (gitea_edit_pr)
- supersession/abandonment (gitea_reconcile_superseded_by_merged_pr)
- already-landed reconciliation (gitea_reconcile_already_landed_pr)
- controller closure (gitea_close_issue)
- retry/recovery (new gitea_cleanup_terminal_pr_labels)
The rule removes only status:pr-open, preserves every other label, allows an
empty resulting set, is a no-op when the label is absent (so retries are
safe), and confirms the outcome by read-after-write rather than assumption.
Controller closure runs the cleanup before the state change and fails closed
if it cannot be completed and verified; closing first would bake in the stale
label with no later step to catch it. Post-merge cleanup never blocks the
merge, which already happened, and reports failures with a safe next action.
Also:
- gitea_assess_terminal_label_hygiene: read-only terminal validation that
reports residual status:pr-open, exempting issues with a genuinely open PR.
- _put_issue_label_names now accepts Gitea's empty response body when the
requested set is empty, so clearing the last label works.
- test_audit's close_issue fixture keys on the request instead of call order,
since closing now also reads labels for the cleanup and its read-back.
Docs: label-taxonomy terminal-transition section, runbook pointer, and the
review-merge / reconcile-landed final-report terminal-label requirements.
Suite: 4045 passed, 11 failed, 6 skipped. The same 11 failures reproduce on
clean master df31674 (4010 passed, 11 failed) and are pre-existing.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
1.9 KiB
Reconcile-landed controller handoff schema
Task mode: reconcile-landed-pr
End every reconciliation run with a section titled exactly Controller Handoff.
Use this canonical field set. Do not omit fields — use none or
not verified in this session where appropriate.
Reject stale author/reviewer fields: PR number opened, Pinned reviewed head,
Scratch worktree used, Workspace mutations, Mutations: None (when mutations
occurred).
## Controller Handoff
- Task:
- Repo:
- Role:
- Identity:
- Active profile:
- Runtime context:
- Selected PR:
- PR live state:
- Candidate head SHA:
- Target branch:
- Target branch SHA:
- Ancestor proof:
- Linked issue:
- Linked issue live status:
- Eligibility class:
- Capabilities proven:
- Missing capabilities:
- PR comments posted:
- Issue comments posted:
- PRs closed:
- Issues closed:
- File edits by reconciler:
- Worktree/index mutations:
- Git ref mutations:
- MCP/Gitea mutations:
- Reconciliation mutations:
- Terminal label cleanup:
- External-state mutations:
- Read-only diagnostics:
- Blockers:
- Current status:
- Safe next action:
- Safety statement:
- No review/merge confirmation:
Identity format: username / profile (not personal email unless required — #305).
git fetch belongs under Git ref mutations, not read-only diagnostics (#297).
Terminal label cleanup (#780) reports the pr_open_label_cleanup record the
reconciliation tool returned — clean / failed / not applicable (no linked issue), with the labels removed and preserved. Reconciliation is a terminal
transition, so a non-clean record blocks any "reconciled" claim; recover with
gitea_cleanup_terminal_pr_labels (terminal_reason='retry_recovery') and
confirm with gitea_assess_terminal_label_hygiene.
The report must also carry the canonical self-propagating handoff block
(schemas/self-propagating-handoff.md, #626) and that block must be posted to
the Gitea issue or PR thread.