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:
@@ -17,6 +17,30 @@ The core promise: **an LLM never does unsafe or untracked work.** Every change
|
|||||||
is tracked by an issue, isolated in its own worktree, reviewed by a different
|
is tracked by an issue, isolated in its own worktree, reviewed by a different
|
||||||
identity, and cleaned up only after a real merge.
|
identity, and cleaned up only after a real merge.
|
||||||
|
|
||||||
|
## Task mode router (#333)
|
||||||
|
|
||||||
|
**Identify task mode before any mutation.** Load the matching workflow file and
|
||||||
|
final-report schema; do not apply rules from other modes.
|
||||||
|
|
||||||
|
| Task mode | Workflow | Final report schema |
|
||||||
|
|-----------|----------|---------------------|
|
||||||
|
| `review-merge-pr` | [`workflows/review-merge-pr.md`](workflows/review-merge-pr.md) | [`schemas/review-merge-final-report.md`](schemas/review-merge-final-report.md) |
|
||||||
|
| `create-issue` | `workflows/create-issue.md` (planned — #333) | `schemas/create-issue-final-report.md` (planned) |
|
||||||
|
| `work-issue` | `workflows/work-issue.md` (planned — #333) | `schemas/work-issue-final-report.md` (planned) |
|
||||||
|
|
||||||
|
Mode isolation:
|
||||||
|
|
||||||
|
- **review-merge-pr** — reviewer namespace only; no issue creation, no code edits, no approve/merge replay after terminal review
|
||||||
|
- **create-issue** — author namespace; duplicate search + `create_issue` capability; no PR review/merge
|
||||||
|
- **work-issue** — author namespace; implement, validate, commit, PR; no approve/merge
|
||||||
|
|
||||||
|
Shared gate references (all modes): `gates/identity-capability-rules.md`,
|
||||||
|
`gates/mutation-ledger-rules.md`, `gates/proof-wording-rules.md` (planned — #333).
|
||||||
|
|
||||||
|
Until `create-issue.md` and `work-issue.md` land, author rules remain in this
|
||||||
|
file (§A, §E) and review rules live in
|
||||||
|
[`workflows/review-merge-pr.md`](workflows/review-merge-pr.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
@@ -283,110 +307,16 @@ Worktree folder = branch with `/` replaced by `-`
|
|||||||
|
|
||||||
## F. Review workflow
|
## F. Review workflow
|
||||||
|
|
||||||
1. Use a separate review worktree (`scripts/worktree-review <branch>`), detached.
|
Moved to [`workflows/review-merge-pr.md`](workflows/review-merge-pr.md) (#334).
|
||||||
2. Verify your authenticated identity.
|
Load that file for PR inventory, validation, review mutation, merge preflight,
|
||||||
3. Verify the PR author — **you must not be the author.** Self-review
|
cleanup, and reviewer hard walls. Final report:
|
||||||
contamination must be *evidence-backed* (#173): state the authenticated
|
[`schemas/review-merge-final-report.md`](schemas/review-merge-final-report.md).
|
||||||
reviewer identity, the PR author identity, whether this session
|
|
||||||
authored/touched the PR branch, and the evidence source for any
|
|
||||||
"same-session author" claim. If the evidence is missing, report the
|
|
||||||
status as **unknown** — never declare contamination by assumption — and
|
|
||||||
choose another PR or stop (`review_proofs.assess_self_review_contamination`).
|
|
||||||
4. Verify the worktree is clean.
|
|
||||||
5. **Checkout proof (#173):** before reviewing or validating, prove and
|
|
||||||
state: the selected PR head SHA from Gitea (pinned), the local checkout
|
|
||||||
SHA (`git rev-parse HEAD`), that `HEAD ==` the pinned PR head SHA, and
|
|
||||||
that the diff base is the PR base branch. If `HEAD` does not match the
|
|
||||||
pinned head, **stop before review/merge**
|
|
||||||
(`review_proofs.verify_pinned_head_checkout`).
|
|
||||||
6. **Inventory proof (#173 + repo disambiguation hardening):** a blind queue
|
|
||||||
review must prove listing completeness before claiming "only PRs found".
|
|
||||||
Use repo-name disambiguation:
|
|
||||||
- "Gitea-Tools" / "gitea tool" / "MCP Gitea tool" / "gitea MCP tool" /
|
|
||||||
"gitea-tools repo" resolve **only** to `Scaled-Tech-Consulting/Gitea-Tools`.
|
|
||||||
- "mcp-control-plane" resolves only to `Scaled-Tech-Consulting/mcp-control-plane`.
|
|
||||||
- Ambiguous ("open PRs", no explicit repo, "MCP Gitea tooling") → inventory
|
|
||||||
**both** configured repos.
|
|
||||||
Report must state exactly which repo(s) were checked. If only one checked:
|
|
||||||
"Only <repo> was checked. Other configured repos were not checked. This is
|
|
||||||
not a complete queue inventory." Never let a single-repo zero hide PRs in
|
|
||||||
the other.
|
|
||||||
Both configured repos must be reported with state filter, pagination proof,
|
|
||||||
and open-PR count (`review_proofs.assess_inventory_completeness` and
|
|
||||||
`resolve_repos_from_user_reference`). Before inventory, reconcile the
|
|
||||||
operator-supplied PR backlog against the target repo
|
|
||||||
(`review_proofs.reconcile_queue_target`); never report `trusted_empty`
|
|
||||||
for one repo while ignoring contradictory supplied PR numbers in another.
|
|
||||||
7. **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, create branches, commit, push, or open
|
|
||||||
PRs unless the operator explicitly retasks the run as author work. Mixed
|
|
||||||
reviewer+author namespace use must be reported with a justification, and
|
|
||||||
scratch-only notes are not durable evidence unless posted or committed
|
|
||||||
intentionally (`review_proofs.assess_role_boundary`).
|
|
||||||
8. Inspect the full diff; confirm scope matches the linked issue; flag unrelated files.
|
|
||||||
9. Run the tests. Validation reporting must include the exact command and
|
|
||||||
exact results: pass/fail, counts of tests passed/skipped/failed, any
|
|
||||||
ignored paths and why they are safe to ignore, and whether the command
|
|
||||||
differs from the repository's canonical validation command. Only claim a
|
|
||||||
validation result after the command has completed and its output has
|
|
||||||
been read (`review_proofs.assess_validation_report`).
|
|
||||||
During validation, review work is **read-only**: use
|
|
||||||
`gitea_dry_run_pr_review` to prove submission mechanics — never post live
|
|
||||||
APPROVE, REQUEST_CHANGES, or review comments to probe tool paths. After
|
|
||||||
validation completes, call `gitea_mark_final_review_decision`, then submit
|
|
||||||
exactly one live review via
|
|
||||||
`gitea_submit_pr_review(..., final_review_decision_ready=True)`.
|
|
||||||
After submitting, re-read `gitea_get_pr_review_feedback` and confirm the
|
|
||||||
verdict is visible (`approval_visible` true for APPROVE; PENDING drafts do
|
|
||||||
not count — #244). Do not merge until a visible APPROVED review exists.
|
|
||||||
Final reports must list exactly one review mutation
|
|
||||||
(`review_proofs.assess_review_mutation_final_report`) unless an
|
|
||||||
operator-approved correction flow was invoked and explained.
|
|
||||||
10. **Do not merge if checks fail. Do not merge if the reviewer is the author.**
|
|
||||||
11. **#179 A-bar proofs** (all fail closed when missing —
|
|
||||||
`review_proofs.assess_capability_evidence`, `assess_sweep_evidence`,
|
|
||||||
`assess_live_state_recheck`, `assess_role_boundary`):
|
|
||||||
- Capability claims must cite exact `gitea_resolve_task_capability`
|
|
||||||
output (or runtime context); a bare "capability checks passed" is
|
|
||||||
downgraded.
|
|
||||||
- The secret/provenance sweep must state the exact command/script/
|
|
||||||
pattern/named method and the scope scanned.
|
|
||||||
- Immediately before submitting a review verdict (and again before any
|
|
||||||
merge), re-read live PR state and prove: still open, live head ==
|
|
||||||
pinned head, base unchanged, no unresolved blocking review state.
|
|
||||||
- Reviewer runs stay in the reviewer namespace; any author-namespace
|
|
||||||
call requires an explicit justification in the report.
|
|
||||||
12. The final report must distinguish (`review_proofs.build_final_report`):
|
|
||||||
identity eligible; PR author different from reviewer; session
|
|
||||||
contamination absent (with evidence); validation performed on the pinned
|
|
||||||
head; capability evidence; sweep verdict; live-state recheck; role
|
|
||||||
boundary; merge performed; issue status verified. If any proof is
|
|
||||||
missing, stop or downgrade the result instead of merging confidently.
|
|
||||||
|
|
||||||
## G. Merge / cleanup workflow
|
## G. Merge / cleanup workflow
|
||||||
|
|
||||||
Only an eligible (non-author) reviewer merges. Before merging: always verify
|
Moved to [`workflows/review-merge-pr.md`](workflows/review-merge-pr.md) §6–§7
|
||||||
the authenticated identity **and** the PR author; cite exact capability
|
and [`templates/worktree-cleanup.md`](templates/worktree-cleanup.md). Load the
|
||||||
evidence for merge_pr (#179); respect runtime profile gates; run independent
|
workflow file before any merge mutation.
|
||||||
validation (do not trust the author's reported results); perform the **final
|
|
||||||
live-state recheck** (#179 — PR still open, live head == pinned head, base
|
|
||||||
unchanged, no unresolved blocking review state) immediately before the merge
|
|
||||||
mutation; and merge with a **pinned head SHA** and, where supported, the
|
|
||||||
**expected changed-file set**, so a moved head or widened diff refuses the
|
|
||||||
merge. After a real merge:
|
|
||||||
|
|
||||||
1. Confirm remote `master` actually contains the merge commit or expected squashed changes via post-merge file-presence verification (A PR is not done just because `master` moved or is marked "closed". Verify that expected files added/modified in the PR are actually present on `master` using `git pull`, `git log --oneline -- <file>`, or `git merge-base --is-ancestor`; linked issues are closed; `status:in-progress` is removed).
|
|
||||||
2. Close/release the issue.
|
|
||||||
3. Whenever an issue is closed, check for `status:in-progress`: remove it, or report why it could not be removed.
|
|
||||||
4. Do not delete the remote source branch until: PR `merged=true`, or reconciliation confirms content is safely landed, or the issue owner explicitly abandons the work.
|
|
||||||
5. Remove the local branch.
|
|
||||||
6. Remove the branch worktree folder (`scripts/worktree-clean --delete-branch <branch>`). Branches/worktrees are cleaned only after the above is verified.
|
|
||||||
7. Fetch/prune.
|
|
||||||
8. Confirm the main checkout is clean and current (`0 0` vs remote).
|
|
||||||
9. Final merge/reconciliation reports must include: PR metadata (state, merged flag, merge commit/hash), Git content (remote master hash, expected content present or not), and the exact post-merge verification method used & results.
|
|
||||||
|
|
||||||
Never run cleanup before the merge is confirmed on remote `master`.
|
|
||||||
|
|
||||||
## H. Fail-closed cases
|
## H. Fail-closed cases
|
||||||
|
|
||||||
@@ -479,9 +409,11 @@ Compact format (default, canonical field set per issue #182):
|
|||||||
|
|
||||||
Role-specific fields (append to the compact block):
|
Role-specific fields (append to the compact block):
|
||||||
|
|
||||||
- review/merge tasks: `Selected PR:`, `Reviewer eligibility:`,
|
- review/merge tasks: use
|
||||||
`Pinned reviewed head:`, `Review decision:`, `Merge result:`,
|
[`schemas/review-merge-final-report.md`](schemas/review-merge-final-report.md)
|
||||||
`Linked issue status:`, `Cleanup status:`
|
(includes `Selected PR:`, `Reviewer eligibility:`, `Pinned reviewed head:`,
|
||||||
|
`Review decision:`, `Merge result:`, `Linked issue status:`, `Cleanup status:`,
|
||||||
|
and precise mutation categories — not `Workspace mutations`)
|
||||||
- issue-filing tasks (#191): `Issue created or updated:`, `Related issues:`;
|
- issue-filing tasks (#191): `Issue created or updated:`, `Related issues:`;
|
||||||
body must cite exact issue number/title, duplicate-search summary (issues
|
body must cite exact issue number/title, duplicate-search summary (issues
|
||||||
searched, closest matches, why update rejected / new issue justified), full
|
searched, closest matches, why update rejected / new issue justified), full
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# Review-merge final report schema
|
||||||
|
|
||||||
|
**Task mode:** `review-merge-pr`
|
||||||
|
|
||||||
|
Every review or merge run must end with a section titled exactly
|
||||||
|
`Controller Handoff` validated by `review_proofs.assess_controller_handoff`.
|
||||||
|
|
||||||
|
Use this schema — not author or issue-filing fields (`PR number opened`,
|
||||||
|
`Claim/comment status`, `Workspace mutations`).
|
||||||
|
|
||||||
|
## Required header
|
||||||
|
|
||||||
|
```md
|
||||||
|
## Controller Handoff
|
||||||
|
|
||||||
|
- Task: review-merge-pr
|
||||||
|
- Repo:
|
||||||
|
- Role/profile:
|
||||||
|
- Identity:
|
||||||
|
- Mode: review-merge-pr
|
||||||
|
```
|
||||||
|
|
||||||
|
Identity format: `username / profile` (e.g. `sysadmin / prgs-reviewer`). Do not
|
||||||
|
include personal email unless required for disambiguation (#305).
|
||||||
|
|
||||||
|
## Selection and eligibility
|
||||||
|
|
||||||
|
```md
|
||||||
|
- Selected PR:
|
||||||
|
- PR live state:
|
||||||
|
- Pinned reviewed head:
|
||||||
|
- Reviewer eligibility:
|
||||||
|
- Repositories checked:
|
||||||
|
- Open PR counts:
|
||||||
|
- Inventory completeness:
|
||||||
|
- Inventory pagination proof:
|
||||||
|
- Selected PR or reason none selected:
|
||||||
|
- Linked issue:
|
||||||
|
- Linked issue live status:
|
||||||
|
```
|
||||||
|
|
||||||
|
Linked issue number must match across all sections (#314). If not verified live:
|
||||||
|
`Linked issue status: not verified in this session`.
|
||||||
|
|
||||||
|
## Validation proof (#311, #323)
|
||||||
|
|
||||||
|
```md
|
||||||
|
- Validation command:
|
||||||
|
- Validation working directory:
|
||||||
|
- Validation executable proof:
|
||||||
|
- Validation result:
|
||||||
|
- Baseline SHA: (required if full suite failed on PR head)
|
||||||
|
- Baseline validation command/result: (required if approving despite full-suite failure)
|
||||||
|
- Matching failure list:
|
||||||
|
```
|
||||||
|
|
||||||
|
Reject vague claims like "same as master" without baseline worktree proof.
|
||||||
|
|
||||||
|
## Review and merge outcome
|
||||||
|
|
||||||
|
```md
|
||||||
|
- Review decision:
|
||||||
|
- Review mutations:
|
||||||
|
- Merge result:
|
||||||
|
- Merge commit:
|
||||||
|
- Cleanup status:
|
||||||
|
```
|
||||||
|
|
||||||
|
`Review mutations` must list exactly one live terminal review unless
|
||||||
|
operator-approved correction is documented.
|
||||||
|
|
||||||
|
## Mutation ledger (#319, #320)
|
||||||
|
|
||||||
|
Use precise categories — **not** `Workspace mutations` or bare `Mutations: None`.
|
||||||
|
|
||||||
|
```md
|
||||||
|
- File edits by reviewer: none
|
||||||
|
- Worktree/index mutations:
|
||||||
|
- Git ref mutations:
|
||||||
|
- MCP/Gitea mutations:
|
||||||
|
- Review mutations:
|
||||||
|
- Merge mutations:
|
||||||
|
- Cleanup mutations:
|
||||||
|
- External-state mutations:
|
||||||
|
- Read-only diagnostics:
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- `git fetch` → Git ref mutations
|
||||||
|
- `git worktree add/remove` → Worktree/index mutations
|
||||||
|
- merge simulation → Worktree/index mutations (#317)
|
||||||
|
- MCP review/merge calls → MCP/Gitea mutations + Review/Merge mutations
|
||||||
|
|
||||||
|
## Proof wording (#330)
|
||||||
|
|
||||||
|
Do not claim "live proof" for blockers unless revalidated this session. Prior
|
||||||
|
feedback at unchanged head: say `prior blocker reused` or `prior request-changes
|
||||||
|
still applies`.
|
||||||
|
|
||||||
|
Reject unsupported phrases: `inventory complete`, `same as master`, `live proof`,
|
||||||
|
`file edits none` — unless matching evidence is present.
|
||||||
|
|
||||||
|
## Blockers and safety
|
||||||
|
|
||||||
|
```md
|
||||||
|
- Blockers:
|
||||||
|
- Safe next action:
|
||||||
|
- Safety: no self-review; no self-merge; no author mutations; no code edits
|
||||||
|
```
|
||||||
|
|
||||||
|
Long format (§K in SKILL.md) is required when merge occurred, validation failed,
|
||||||
|
permissions blocked, or multiple repos were involved.
|
||||||
@@ -5,6 +5,10 @@ Copy, fill the `<...>` fields, and paste as the task prompt.
|
|||||||
```text
|
```text
|
||||||
Task: merge PR #<pr> for issue #<n> if it is eligible and checks pass.
|
Task: merge PR #<pr> for issue #<n> if it is eligible and checks pass.
|
||||||
|
|
||||||
|
Load the canonical workflow first:
|
||||||
|
`skills/llm-project-workflow/workflows/review-merge-pr.md` (task mode: review-merge-pr).
|
||||||
|
Final report schema: `schemas/review-merge-final-report.md`.
|
||||||
|
|
||||||
Rules (llm-project-workflow):
|
Rules (llm-project-workflow):
|
||||||
- Only an eligible, NON-author reviewer merges. If authenticated user == PR
|
- Only an eligible, NON-author reviewer merges. If authenticated user == PR
|
||||||
author → STOP.
|
author → STOP.
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ Repo name disambiguation (Gitea-Tools blind review hardening):
|
|||||||
commit is not valid corroboration. Author-bound sessions must not present
|
commit is not valid corroboration. Author-bound sessions must not present
|
||||||
reviewer queue inventory as a reviewer decision.
|
reviewer queue inventory as a reviewer decision.
|
||||||
|
|
||||||
|
Load the canonical workflow first:
|
||||||
|
`skills/llm-project-workflow/workflows/review-merge-pr.md` (task mode: review-merge-pr).
|
||||||
|
Final report schema: `schemas/review-merge-final-report.md`.
|
||||||
|
|
||||||
Rules (llm-project-workflow):
|
Rules (llm-project-workflow):
|
||||||
- Review in a SEPARATE detached review worktree, never the author's folder.
|
- Review in a SEPARATE detached review worktree, never the author's folder.
|
||||||
- Worktree safety (#233): before checkout, diff, validation, review, or merge,
|
- Worktree safety (#233): before checkout, diff, validation, review, or merge,
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
"""Doc-contract checks for task-specific LLM workflow split (#333, #334)."""
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
SKILL_DIR = REPO_ROOT / "skills" / "llm-project-workflow"
|
||||||
|
|
||||||
|
|
||||||
|
def test_review_merge_workflow_file_exists():
|
||||||
|
path = SKILL_DIR / "workflows" / "review-merge-pr.md"
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
assert "Task mode:" in text
|
||||||
|
assert "review-merge-pr" in text
|
||||||
|
assert "gitea-reviewer" in text
|
||||||
|
assert "scripts/worktree-review" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_review_merge_final_report_schema_exists():
|
||||||
|
path = SKILL_DIR / "schemas" / "review-merge-final-report.md"
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
assert "Controller Handoff" in text
|
||||||
|
assert "Worktree/index mutations" in text
|
||||||
|
assert "Workspace mutations" in text # documented as rejected
|
||||||
|
|
||||||
|
|
||||||
|
def test_skill_router_points_to_review_merge_workflow():
|
||||||
|
skill = (SKILL_DIR / "SKILL.md").read_text(encoding="utf-8")
|
||||||
|
assert "## Task mode router" in skill
|
||||||
|
assert "workflows/review-merge-pr.md" in skill
|
||||||
|
assert "schemas/review-merge-final-report.md" in skill
|
||||||
|
assert "Identify task mode before any mutation" in skill
|
||||||
|
|
||||||
|
|
||||||
|
def test_skill_still_declares_controller_handoff_contract():
|
||||||
|
skill = (SKILL_DIR / "SKILL.md").read_text(encoding="utf-8")
|
||||||
|
assert "## Controller Handoff" in skill
|
||||||
|
assert "assess_controller_handoff" in skill
|
||||||
|
assert "## Global LLM Worktree Rule" in skill
|
||||||
|
|
||||||
|
|
||||||
|
def test_review_pr_template_references_extracted_workflow():
|
||||||
|
text = (SKILL_DIR / "templates" / "review-pr.md").read_text(encoding="utf-8")
|
||||||
|
assert "workflows/review-merge-pr.md" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_pr_template_references_extracted_workflow():
|
||||||
|
text = (SKILL_DIR / "templates" / "merge-pr.md").read_text(encoding="utf-8")
|
||||||
|
assert "workflows/review-merge-pr.md" in text
|
||||||
Reference in New Issue
Block a user