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:
@@ -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)
|
||||
Reference in New Issue
Block a user