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]>
139 lines
4.9 KiB
Markdown
139 lines
4.9 KiB
Markdown
# 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).
|
|
|
|
```md
|
|
## 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 applicable` or `not run` — never `passed`
|
|
- Review worktree detail fields: `not applicable` or `none` when Review worktree used is `false`
|
|
- Blockers must not be `none` if 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/or `total_count` from `gitea_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 rebase` command 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. |