feat(skill): extract review-merge-pr workflow from monolithic SKILL.md

Closes #334

- Add workflows/review-merge-pr.md with reviewer-only gates and steps
- Add schemas/review-merge-final-report.md with mutation ledger fields
- Turn SKILL.md into task-mode router; replace inlined §F/§G with links
- Point review/merge templates at extracted workflow
- Add doc-contract tests for split structure

Part of #333.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-07 03:32:59 -04:00
co-authored by Claude Opus 4.8
parent e4d78a5013
commit 3976f886e4
6 changed files with 447 additions and 104 deletions
@@ -0,0 +1,243 @@
# Review and merge PR workflow
**Task mode:** `review-merge-pr`
Load this file before any PR review or merge mutation. Parent router:
[`../SKILL.md`](../SKILL.md). Final report schema:
[`../schemas/review-merge-final-report.md`](../schemas/review-merge-final-report.md).
## Mode isolation (hard walls)
Review mode **must not**:
- create issues or edit issue bodies
- edit repository files, run formatting, or fix code
- claim issues, create branches, commit, push, or open PRs
- use author MCP namespace (`gitea-tools` / `prgs-author`) for mutations
- improvise raw MCP repair, shell fallbacks, or background task tools
Review mode **must**:
- use reviewer MCP namespace (`gitea-reviewer` / `prgs-reviewer`)
- prove identity, profile, runtime context, and exact capabilities before mutation
- validate only in a session-owned worktree under `branches/`
- keep the main checkout on `master`/`main`/`dev` for orchestration only
If the authenticated user equals the PR author → **stop** (no self-review, no self-merge).
---
## Global worktree rule (reviewer)
The main project checkout is orchestration only. All review validation happens
under `branches/` — typically a detached review worktree from
`scripts/worktree-review <pr-head-branch>`.
Before validation, prove:
1. project root
2. working directory (must be under `branches/`)
3. worktree path
4. worktree clean status
5. pinned PR head SHA == `git rev-parse HEAD`
Never run `git stash`, `git checkout --`, `git reset`, or `git clean` on
another session's dirty files. If the shared worktree is dirty, create a scratch
review worktree instead.
Merge simulation (`git merge --no-commit`, `git merge --abort`) is a
**worktree/index mutation**, not read-only diagnostics (#317).
---
## 1. Pre-flight gates
Run before any PR mutation:
1. `gitea_whoami` — prove reviewer identity (not PR author)
2. `gitea_get_profile` / `gitea_get_runtime_context`
3. `gitea_resolve_task_capability` for `review_pr`, `approve_pr`, `merge_pr`
4. `gitea_check_pr_eligibility` for the intended action on the selected PR
Capability claims must cite exact resolver output (#179); bare "checks passed" is
downgraded.
Stay in the reviewer namespace. Any author-namespace call requires explicit
justification in the final report.
---
## 2. PR inventory and queue selection
### Repo disambiguation
- `Gitea-Tools` / `gitea tool` / `MCP Gitea tool``Scaled-Tech-Consulting/Gitea-Tools`
- `mcp-control-plane``Scaled-Tech-Consulting/mcp-control-plane`
- Ambiguous requests → inventory **both** configured repos
Report must state exactly which repo(s) were checked. A single-repo zero must not
be reported as a global empty queue if the other repo was not inventoried.
### Pagination proof (#330)
PR inventory completeness requires explicit final-page/no-next-page evidence,
total-count evidence, or traversal until an empty/final page. Do not prove
completeness by assuming the default Gitea API page size.
### Trust gate (#196, #198)
Before reporting "no open PRs" or "queue empty":
- run `pr_inventory_trust_gate` (or `review_proofs.assess_reviewer_queue_inventory`)
- only `trusted_empty` allows a clean empty-queue stop
- a bare `[]` from `gitea_list_prs` is never sufficient
Empty-queue reports must include `pr_inventory_trust_gate.status`, reasons,
corroboration, remote/owner/repo/state filter, and inventory MCP profile.
### Queue ordering (#321, #318)
Select the lowest eligible open PR unless a documented blocker requires skipping.
Skipping requires live proof the blocker still applies — not stale handoff text.
### Already-landed gate (#292, #294, #295)
If the PR head is already on the target branch, stop review/approve/merge.
Redirect to reconciliation workflow; do not treat as review-eligible.
---
## 3. Review worktree and pinned head
1. `scripts/worktree-review <pr-head-branch>` → detached under `branches/review-*`
2. Prove checkout (#173):
- pinned PR head SHA from Gitea
- local `git rev-parse HEAD`
- `HEAD ==` pinned head
- diff base == PR base branch
3. If head moved → re-pin and restart review
4. Confirm worktree clean; inspect full diff; confirm scope matches linked issue
Linked issue consistency (#314): fetch/verify the linked issue live. Report the
same issue number in all sections. If not verified:
`Linked issue status: not verified in this session`.
Secret/provenance sweep (#179): state exact command/script/pattern and scope.
---
## 4. Validation
Run tests from the review worktree. Report must include (#311):
- exact command as executed
- working directory
- exact result (pass/fail counts)
- executable path proof for bare commands like `pytest` (`which`, `--version`)
If full suite fails, default action is `REQUEST_CHANGES` (#323). Approval with
failures requires baseline worktree proof under `branches/`, not main checkout.
During validation, use `gitea_dry_run_pr_review` — never live APPROVE/REQUEST_CHANGES
to probe tool paths.
---
## 5. Review mutation (terminal)
After validation completes:
1. Re-fetch PR; verify head SHA unchanged
2. `gitea_mark_final_review_decision` with pinned head
3. `gitea_submit_pr_review(..., final_review_decision_ready=True)` — exactly one live review
4. `gitea_get_pr_review_feedback` — confirm visible verdict (`approval_visible` for APPROVE; PENDING drafts do not count — #244)
Final reports must list exactly one review mutation
(`review_proofs.assess_review_mutation_final_report`).
Hard-stop after terminal review mutation (#332): do not perform additional review
mutations in the same session unless operator-approved correction.
---
## 6. Merge preflight and execution
Merge only when:
- eligible non-author reviewer
- visible APPROVED review at pinned head
- no undismissed REQUEST_CHANGES
- PR open and mergeable
- live head == pinned reviewed head (re-fetch immediately before merge)
Use `gitea_merge_pr` with `confirmation="MERGE PR <n>"` and `expected_head_sha`.
Gated merge refuses moved heads and widened diffs when `expected_changed_files` is set.
---
## 7. Post-merge cleanup
After confirmed merge on remote `master`:
1. Post-merge file-presence verification on `master`
2. Close/release linked issue; remove `status:in-progress`
3. Delete remote branch (when safe)
4. `scripts/worktree-clean --delete-branch <branch>`
5. Fetch/prune; confirm main checkout clean and `0 0` vs remote
Never run cleanup before merge is confirmed on remote `master`.
See also [`../templates/worktree-cleanup.md`](../templates/worktree-cleanup.md).
---
## 8. Fail-closed stops
Stop and report (no mutating action) when:
- identity/capability cannot be proven
- self-author detected
- worktree state unclear
- pinned head mismatch
- inventory incomplete or untrusted
- tests fail without baseline proof
- PR closed-not-merged (`merged=false`) — run reconciliation instead
- infra_stop or MCP transport failure — recovery handoff only
When blocked, emit recovery handoff per
[`../templates/recover-bad-state.md`](../templates/recover-bad-state.md).
---
## 9. Role-boundary proof (#175)
A reviewer queue task must not silently become author implementation. If no
eligible PR exists, stop with the queue report. Do not claim issues, branch,
commit, push, or open PRs unless the operator explicitly retasks as author work.
Use `review_proofs.assess_role_boundary`; downgrade or stop if not clean.
---
## 10. A-bar proofs (#179)
All required before A-grade report or merge:
- `assess_capability_evidence`
- `assess_sweep_evidence`
- `assess_live_state_recheck` (immediately before review submit and merge)
- `assess_role_boundary`
- `assess_review_mutation_final_report`
- `build_final_report`
Missing proof → downgrade or block merge.
---
## Prompt templates
Short triggers still work via templates that reference this workflow:
- [`../templates/review-pr.md`](../templates/review-pr.md)
- [`../templates/merge-pr.md`](../templates/merge-pr.md)