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]>
4.9 KiB
Review-merge controller handoff schema
Task mode: review-merge-pr
End every review/merge 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.
Do not use legacy fields: 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:
- Linked issue:
- Eligibility class:
- Queue ordering policy:
- Inventory pagination proof:
- Earlier PRs skipped:
- Candidate head SHA:
- Reviewed head SHA:
- Target branch:
- Target branch SHA:
- Already-landed gate:
- Author-safety result:
- Prior request-changes state:
- Review worktree used:
- Review worktree path:
- Review worktree inside branches:
- Review worktree HEAD state:
- Review worktree dirty before validation:
- Review worktree dirty after validation:
- Baseline worktree used:
- Baseline worktree path:
- Files reviewed:
- Validation:
- Official validation integrity status:
- Terminal review mutation:
- Review decision:
- Merge preflight:
- Merge result:
- Linked issue status:
- Main checkout branch:
- Main checkout dirty state:
- Main checkout updated:
- File edits by reviewer:
- Worktree/index mutations:
- Git ref mutations:
- MCP/Gitea mutations:
- Review mutations:
- Merge mutations:
- Cleanup mutations:
- External-state mutations:
- Read-only diagnostics:
- Blockers:
- Current status:
- Safe next action:
- Safety statement:
- Workflow-load helper result:
The Workflow-load helper result field must carry structured output from
gitea_load_review_workflow (workflow_hash, final_report_schema_hash,
boundary_status). Narrative claims that workflow files were viewed locally are
not sufficient (#403).
Already-landed handoff overrides
When eligibility class is ALREADY_LANDED_RECONCILE_REQUIRED:
- Reviewed head SHA:
none - Review worktree used:
false - Review worktree path:
none - Review decision:
none - Merge result:
none
Identity format: username / profile (not personal email unless required — #305).
Queue-status-only runs (no selected PR)
When the run inventories the queue but selects no PR for review:
- Selected PR:
none - Already-landed gate, Author-safety result, Merge preflight:
not applicableornot run— neverpassed - Review worktree detail fields:
not applicableornonewhen Review worktree used isfalse - Blockers must not be
noneif the narrative says all open PRs are conflicted, blocked, or unverified - Inventory pagination proof must cite final-page metadata, not default page-size assumptions
Verifier: review_proofs.assess_queue_status_report().
Narrative final report and controller handoff must agree on eligibility class, candidate/reviewed head SHA, mutation state, worktree usage, review decision, terminal review mutation, merge result, and linked issue status.
Terminal label state (#780)
A run that takes a PR to a terminal state — merged, closed without merge,
superseded, or reconciled as already landed — must report what happened to the
linked issue's status:pr-open label, quoting the pr_open_label_cleanup
record the terminal tool returned:
- Terminal label cleanup:
clean/failed/not applicable (no linked issue) - Labels removed and preserved per issue, with the read-after-write read-back
Never claim the transition is complete while that record is not clean. A
failed cleanup does not undo the merge; the safe next action is
gitea_cleanup_terminal_pr_labels with terminal_reason='retry_recovery',
confirmed by gitea_assess_terminal_label_hygiene.
Proof-backed claims (#395)
Proof-sensitive claims must cite explicit command/tool evidence in the report or structured MCP metadata — not narrative alone:
- Inventory complete:
has_more=false,is_final_page=true,inventory_complete=true, and/ortotal_countfromgitea_list_prs. - Skipped earlier PRs: merge-simulation command output, conflict file list,
or live
gitea_get_pr_review_feedback/ mergeability fields. - Baseline validation: baseline worktree path, baseline target SHA, dirty-before/after, exact command, exact result.
- Master integration: exact
git merge/git rebasecommand and exit status. - Cleanup: final
git worktree list(or remove commands) proving session worktrees were removed.
When a claim relies on prior-session blocker state or MCP metadata only, label
the proof source explicitly (command, MCP metadata, prior blocker,
not checked). Do not use live proof without that classification.
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 PR thread. A reviewer hands off to merger; a merger transitions to
merged-awaiting-controller rather than declaring the work accepted.