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]>
27 KiB
name, description
| name | description |
|---|---|
| llm-project-workflow | Portable, safe operating workflow for LLMs working on any Git/forge project: issue-first, isolated branch worktrees, no self-review/self-merge, distinct author/reviewer profiles, cleanup after merge, and fail-closed behavior. Use at the start of any implementation, review, or merge task on a repo. |
LLM Project Workflow
A reusable workflow any LLM can follow to work on any repository safely. Copy
this skills/llm-project-workflow/ directory into another project unchanged;
adapt only the forge-specific names in Adapting to a project.
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 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 |
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_issuecapability; 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.
Definitions
- Merged: Gitea PR metadata says
merged=true. - Landed: Equivalent content is present on remote
master, but PR metadata may not say merged. - Closed-not-merged: PR state is closed and
merged=false. - Reconciled: A human/LLM verified whether closed-not-merged content landed, partially landed, or was lost, and repaired issue/label/tracker state.
A. Issue-first rule
No repository change without a tracking issue. This includes creating,
editing, deleting, or chmod-ing files; docs; scripts; commits; pushes; and PRs.
- Before any change, confirm a tracking issue exists.
- If none exists, create one first (title + problem + scope + acceptance).
- Claim it (assign yourself or apply the
status:in-progresslabel) and comment that work is starting, including the planned branch name. - If the issue cannot be created or claimed, stop. Do not touch files.
Reading the repo, running read-only status/git log, and creating/claiming the
issue itself are allowed from the orchestration checkout without a prior issue.
Additional issue-first rules:
- Do not implement code without an issue unless explicitly authorized.
- Design-only work uses a discussion/RFC issue — create one or comment on the existing one. Design debates belong on the issue, where other LLMs comment directly. Discussion-only tasks must not create branches or PRs; their comments should include recommendations, risks, open questions, and a Controller Handoff (§K; compact format unless high-risk).
- If the repo/tracker home for the work is unclear, stop and ask for an owner decision. Do not create a new repository or a new tracker unless explicitly approved by the owner.
Work Selection Rule for LLMs
Before starting any issue or PR work, acquire or verify a work lease.
Do not begin coding, reviewing, fixing, branching, committing, pushing, commenting, or creating a PR until you prove the target is not already being worked.
Required checks:
- List open PRs.
- Search for PRs linked to the target issue.
- Search local and remote branches for the issue number.
- Search registered worktrees for the issue branch.
- Check dirty worktrees.
- Check active leases or recent handoffs.
- Check whether the issue was already completed by a merged PR.
If another active LLM/session owns the lease, stop.
Allowed responses:
- continue as the lease owner,
- review the existing PR if reviewer capability allows,
- produce a handoff,
- request takeover after lease expiry,
- stop with "work already claimed."
Never create a parallel branch or PR for the same issue unless the old branch is proven abandoned and the takeover is recorded.
For Gitea-Tools: gitea_lock_issue is the fail-closed lease gate before author
mutations; status:in-progress and claim comments are supporting lease signals.
Use review_proofs.classify_issue_for_selection when reporting fresh issue
selection (#188).
Global LLM Worktree Rule
The main project checkout is a stable control checkout. It must stay on the
configured stable branch: master, main, or dev.
All LLM task work must happen inside the project's branches/ directory.
Before any mutation, prove:
- current project root
- current working directory
- current branch
- stable branch for the main checkout
- session-owned worktree path under
branches/
If cwd is not inside branches/, stop. Do not edit, create, delete, format,
test-write, commit, merge, rebase, checkout task branches, resolve conflicts,
or run cleanup.
There are no exceptions for small fixes, docs, tests, cleanup, PR review fixes, conflict resolution, or emergencies.
The main checkout may only be used for read-only inspection, fetching,
stable-branch update after merged PRs, creating branches/ worktrees, or
explicit control-checkout repair.
Shell Spawn Hard-Stop Rule
A shell tool result of exit_code: -1 with empty stdout/stderr means the
executor failed to spawn — it is not a command failure, and retrying the same
call cannot succeed.
- On the first spawn failure, run one trivial probe (
echo okorpwd). If the probe also fails, mark shell unavailable for the session. - After two consecutive spawn failures, hard-stop all further shell tool use for the session. Never retry the same failing spawn.
- Stop and emit a recovery report instead of improvising fallbacks. The
report must tell the operator to: restart the session, kill hung
background terminals (a hung test runner is a known contributor), and
prefer MCP-native paths (for example
gitea_commit_files) for any remaining mutations.
Retry spirals are a real failure mode (issue #258: 100+ tool calls on a trivial encode-and-commit task). The hard-stop is fail-closed: no shell means stop-and-report, not workarounds.
B. Isolated worktree rule
Never implement or review in the main checkout (Global LLM Worktree Rule).
The main checkout is for orchestration and status only (issue creation,
git status, creating worktrees) and must remain on the stable branch.
- Each issue gets its own branch worktree under an ignored
branches/directory. - Review work uses a separate review worktree, never the author's folder.
- Dirty work in one branch folder must not block starting another issue.
- No LLM may edit another issue's worktree unless explicitly assigned to it.
- Branch folders are removed only after the PR is merged/closed and cleanup is explicitly part of the task.
Every implementation branch must include its issue number so it is traceable end to end: issue → branch → worktree folder → PR → cleanup.
Allowed implementation patterns:
fix/issue-123-short-descriptionfeat/issue-123-short-descriptiondocs/issue-123-short-descriptionchore/issue-123-short-description
Review-only branches:
review/pr-456-short-description
Use a filesystem-safe folder under branches/ by replacing slashes with
hyphens, for example branches/fix-issue-123-short-description.
scripts/worktree-start enforces this: it rejects an implementation branch
that does not match (fix|feat|docs|chore)/issue-<number>-… (or a
review/pr-<number>-… branch), unless --allow-unlinked is passed. Traceability
is maintained by:
- the branch name (contains the issue number),
- a claim comment on the issue, e.g.
Claimed. Branch: fix/issue-123-short-description. Worktree: branches/fix-issue-123-short-description., - the PR body —
Closes #123orFixes #123when the PR should close the issue (do NOT useImplements #123orRefs #123to close, as Gitea will not auto-close), - cleanup after merge — remove the remote branch, local branch, and the issue
worktree folder, and drop
status:in-progress.
For projects using Gitea-Tools helpers:
scripts/worktree-start fix/issue-123-example # → branches/fix-issue-123-example
scripts/worktree-review fix/issue-123-example # → branches/review-fix-issue-123-example (detached)
scripts/worktree-clean --delete-branch fix/issue-123-example
Manual equivalent:
git fetch <remote> --prune
git worktree add -b fix/issue-123-example branches/fix-issue-123-example <remote>/master
cd branches/fix-issue-123-example
venv/ and similar are not copied into new worktrees — run checks with a known
interpreter path, or create a venv inside the branch folder.
C. Identity and profile safety
- Use canonical execution profiles where available; the profile is the role, not the LLM. A task selects a profile; a profile is not permanently assigned.
- Author and reviewer identities must be distinct.
- Never place raw tokens/passwords in an LLM/MCP client config. Reference secrets by keychain id or environment variable name only. Prefer a single canonical config file selected by two env vars, e.g.:
GITEA_MCP_CONFIG— path to the canonical profiles fileGITEA_MCP_PROFILE— the profile to activate
- Dual-Profile MCP Launcher Pattern (Recommended): To avoid relaunch bottlenecks and PR-author deadlocks, register multiple instances of the same MCP server in the client's configuration simultaneously (e.g.,
gitea-authorandgitea-reviewer), each pointing to its respectiveGITEA_MCP_PROFILE.- Tool calls become namespace-scoped:
mcp__gitea-author__*andmcp__gitea-reviewer__*. - Trust Model: Separate tokens remain separate. Profile gates enforce allowed operations,
whoamiis still checked, and self-review/self-merge prevention remains mandatory. This pattern is for convenience and does not bypass security gates. - Deadlock Warning: Reviewer/merge identities must not be used to create PRs, as this makes the reviewer the PR author in Gitea and blocks independent review. PRs should normally be created by the author/work identity, keeping the reviewer identity available for reviews.
- Fallback: If a dual-server launcher is not available in the client, relaunch or restart the client with the correct profile environment variable before claiming work.
- Tool calls become namespace-scoped:
- If the authenticated user equals the PR author, stop — no self-review, no self-merge.
D. Branch naming
fix/issue-123-short-description
feat/issue-123-short-description
docs/issue-123-short-description
review/pr-456-scope-check
Worktree folder = branch with / replaced by -
(branches/fix-issue-123-short-description).
E. Start-work workflow
- Acquire or verify a work lease (Work Selection Rule) — complete all seven
checks before any claim, branch, or PR work.
0b. Global LLM Worktree Rule — prove project root,
cwd, branch, main-checkout stable branch, and session-ownedbranches/worktree path. Ifcwdis not underbranches/, stop before any mutation (no exceptions). - Verify the orchestration checkout (right repo, clean tree, on stable branch).
- Fetch/prune:
git fetch <remote> --prune. - Confirm local
masterequals remotemaster(git rev-list --left-right --count <remote>/master...master→0 0). - Create/claim the issue (§A).
4b. Issue lock from your scratch clone (#249): when using
gitea_lock_issue, passworktree_pathpointing at your own clean scratch clone (or setGITEA_AUTHOR_WORKTREE). The lock gate validates that path — clean tree onmaster/main, no tracked edits yet — not the shared MCP/orchestration checkout. Another session's dirty feature branch in the shared dev worktree must not block your lock. Never stash, reset, or checkout files in the shared worktree to satisfy the gate. Pass the sameworktree_pathtogitea_create_prso the PR gate matches the lock record. - Create the isolated worktree (§B) from latest remote
master. - Implement the narrow scope only — no unrelated refactors or formatting churn.
- Add/update focused tests when behavior changes.
- Run the checks (tests, compile/lint,
git diff --check, secret scan). Record the branch name andHEADSHA at validation time — the drift check in step 9 compares against exactly this state. - Branch proof before commit (#177): prove and state, immediately
before staging/committing (
author_proofs.verify_branch_for_commit,author_proofs.detect_branch_drift):- current branch (
git branch --show-current) equals the intended feature branch from the issue claim - current branch is not
master,main,develop,development, ordev - branch and
HEADhave not changed since validation (step 8) — in a shared checkout another session may switch branches mid-session; treat that as expected and stop before committing when detected If any check fails, stop and reconcile; do not commit.
- current branch (
- Commit with an issue-linked message.
- Branch proof before push (#177): prove that the local branch, the
push target branch, and the intended issue branch all match, and that
none of them is a protected branch
(
author_proofs.verify_push_target). If a commit accidentally landed on a protected branch, do not push: report the accident and the exact repair steps (author_proofs.assess_protected_branch_commit) — never silently continue after a repair. - Push the branch.
- Open a PR to
master. The final report must include the branch proofs from steps 9 and 11 (author_proofs.build_commit_push_report). - If you are the author, stop before review/merge.
- Normal issue work must not directly push to
master. PR content should be merged through the forge PR merge mechanism. - Direct push to
masteris allowed only as a documented recovery exception. If used, the final report must include:- why the PR merge path could not be used
- exact commits pushed
- PR metadata state
- issue labels/state repaired
- whether the PR is closed-not-merged
F. Review workflow
Moved to workflows/review-merge-pr.md (#334).
Load that file for PR inventory, validation, review mutation, merge preflight,
cleanup, and reviewer hard walls. Final report:
schemas/review-merge-final-report.md.
G. Merge / cleanup workflow
Moved to workflows/review-merge-pr.md §6–§7
and templates/worktree-cleanup.md. Load the
workflow file before any merge mutation.
H. Fail-closed cases
Stop and report — take no mutating action — if:
- No issue exists and one cannot be created.
- Worktree state is unclear or unexpected.
- Branch/PR state conflicts with the prompt (e.g. prompt says "merged" but it is not).
- A PR is closed but not merged (closed with
merged=false). In this case:- stop normal review/merge
- do not delete branches/worktrees
- do not start dependent work
- run reconciliation
- Local
masteris ahead of remote unexpectedly. - The authenticated user is the PR author (for review/merge).
- Secrets/tokens appear in the diff.
- Tests fail.
- A cleanup step would delete unmerged work.
When in doubt, stop and surface the discrepancy; do not guess or work around a gate.
I. Recovery patterns
- Dirty worktree from another issue: do not touch it. Start your issue in its
own new worktree; unrelated dirty work must not block you. For Gitea-Tools
author flows, lock the issue from your scratch clone (
worktree_pathongitea_lock_issue) — do not manipulate the shared dev checkout. - Local
masterahead of remote unexpectedly: do not pushmaster. Confirm the commits are preserved on a feature branch (local + remote) first, thengit reset --hard <remote>/masterto realign. Never discard commits that are not safely pushed elsewhere. - PR closed but not merged (
merged=false): do not merge. Run reconciliation: compare PR content to remotemasterand decide:- fully landed: comment that content is present on
master, removestatus:in-progress, keep/close issue as appropriate, clean up only after content equivalence is confirmed. - partially landed: do not clean up, reopen issue if needed, create corrective issue/PR for missing pieces.
- not landed: reopen issue if needed, reopen PR or create replacement PR, do not clean up source branch/worktree.
- fully landed: comment that content is present on
- Branch deleted before merge: if the commits still exist locally (a branch or
reflog), re-push them and reopen the PR; otherwise recover via
git fsck --lost-found. Preserve first, then proceed. - Unauthorized/untracked file created: do not commit it. Leave pre-existing
untracked artifacts (e.g. editor/agent dirs, reports) alone; stage only the
files your issue names (
git add <files>, never blindgit add -A). - Preserve commits before a reset: confirm the target commits are reachable
from a branch that is pushed to the remote, then reset. Verify with
git branch --contains <sha>andgit log <remote>/<branch>.
J. Prompt snippets
Ready-to-copy templates live in templates/:
start-issue.md— start a new issue.review-pr.md— review a PR.merge-pr.md— merge a PR (eligible reviewer only).recover-bad-state.md— recover from bad state.reconcile-closed-not-merged-pr.md— reconcile a closed-not-merged PR.worktree-cleanup.md— clean up after merge.release-tag.md— create a release tag.
K. Controller Handoff (required, every task)
Every LLM task must end with a Controller Handoff (exact title) — whether the
task was implementation, review, merge, issue triage, documentation,
discussion-only, or blocked planning. It lets a controller LLM understand the
current state immediately, without rereading the conversation.
The section title must be exactly "Controller Handoff" (or "Controller Handoff Summary" for long form). Reports without it are downgraded (see review_proofs.assess_controller_handoff).
The compact format is the default. It is written for controller-LLM readability, not as a full human status report. PR bodies still carry the full review detail — the handoff never replaces PR documentation.
Compact format (default, canonical field set per issue #182):
## Controller Handoff
- Task:
- Repo:
- Role:
- Identity:
- Issue/PR:
- Branch/SHA:
- Files changed:
- Validation:
- Mutations:
- Current status:
- Blockers:
- Next:
- Safety:
Role-specific fields (append to the compact block):
- review/merge tasks: use
schemas/review-merge-final-report.md(includesSelected PR:,Reviewer eligibility:,Pinned reviewed head:,Review decision:,Merge result:,Linked issue status:,Cleanup status:, and precise mutation categories — notWorkspace mutations) - issue-filing tasks (#191):
Issue created or updated:,Related issues:; body must cite exact issue number/title, duplicate-search summary (issues searched, closest matches, why update rejected / new issue justified), full 40-char SHAs when citing commits, exact mutation capability per change, andOnly mutation(s):when a single mutation was performed (review_proofs.assess_issue_filing_final_report). - author tasks:
Selected issue:,Claim/comment status:,PR number opened:,No review/merge:(explicit confirmation) - continuation tasks (#188):
Continuation mode:,Existing PR:,PR author:,Branch:,Old PR head:,New PR head:,Session authored PR:,Why continuation allowed:— issues with open PRs are excluded from fresh selection unless operator explicitly requests continuation (review_proofs.classify_issue_for_selection,assess_issue_selection_final_report) - queue/inventory tasks:
Repositories checked:,Open PR counts:,Selected PR or reason none selected:,Inventory completeness:
The section title must be exactly Controller Handoff.
review_proofs.assess_controller_handoff() validates this section; reports
missing it (or missing required fields) are downgraded. The handoff never
replaces the full report — it is the compact continuation summary at the end,
and the full report must still carry exact validation results and mutation
confirmation.
The Safety: line is never omitted; it is usually:
no self-review; no self-merge; no tags; no secrets; no prod
Rules (both formats):
- Never omit the handoff, and never omit the safety confirmations.
- Never bury blockers in earlier text only — they must appear here.
- If you opened a PR, state clearly that review is needed.
- If you reviewed but could not merge, name the exact gate that blocked it.
- If you only commented on a discussion issue, say no code review is needed but owner/design feedback may be needed.
- If release state was touched, state exactly which tag/commit changed and why.
- If blocked (permissions, missing repo, missing second reviewer identity, stale dependency, unclear tracker home): stop and report clearly; never bypass classifiers, profile gates, missing permissions, or live-consent requirements; give the owner concrete options.
Use the long format below instead of the compact one only when the task was high-risk or complex — i.e. when any of these happened:
- a merge, tag, or release
- failed validation
- permissions/profile gates blocked work
- secrets or production access were involved
- a complicated owner decision
- multiple repos or cross-issue state
- the owner explicitly asks for the full format
Long format (high-risk/complex tasks only):
## Controller Handoff Summary
### Work performed
Briefly state what was done.
### Current state
Include:
- current repo
- current branch or master commit
- issue number(s)
- PR number(s), if any
- whether work is complete, blocked, ready for review, or discussion-only
### Files changed
List files changed, or say `None`.
### Validation
List commands run and results, or say `Not applicable — discussion only`.
### Issues encountered
List errors, confusing state, permission/profile problems, stale branches,
failing tests, missing labels, or blocked decisions.
### Review needed?
Say one of:
- `No review needed — discussion/comment only`
- `Review needed — PR is open`
- `Independent non-author review needed`
- `Owner decision needed`
- `Blocked`
### Next recommended action
State exactly what should happen next.
### Safety confirmations
Confirm:
- no self-review
- no self-merge
- no release/tag changes unless explicitly requested
- no secrets committed
- no production access used unless explicitly authorized
Example blocked handoff
## Example blocked handoff
### Work performed
Audited phase-2 MCP Control Plane planning. Found target repo
`mcp-control-plane` does not exist. Prepared issue pack but did not file it.
### Current state
- Repo: `Scaled-Tech-Consulting/Gitea-Tools`, unmodified
- Target repo: `mcp-control-plane`, missing
- Issues: none open in Gitea-Tools
- PRs: none open
- Status: blocked pending owner decision
### Files changed
None.
### Validation
Tracker/repo audit only. No code validation required.
### Issues encountered
Repo creation was denied by permission/classifier because it would be scope
escalation without live consent.
### Review needed?
Owner decision needed.
### Next recommended action
Owner must choose:
1. create `Scaled-Tech-Consulting/mcp-control-plane`
2. authorize repo creation while present
3. file phase-2 issues in Gitea-Tools instead
### Safety confirmations
- no self-review
- no self-merge
- no release/tag changes
- no secrets committed
- no production access used
Adapting to a project
Replace these project-specific names when copying the skill elsewhere:
| Placeholder | Meaning | Example here |
|---|---|---|
<remote> |
Git remote for the forge | prgs |
| default branch | Integration branch | master |
| profile env vars | Canonical config + profile selectors | GITEA_MCP_CONFIG, GITEA_MCP_PROFILE |
branches/ |
Ignored worktree directory | branches/ |
| helper scripts | Worktree helpers | scripts/worktree-start / -review / -clean |
The rules in §A–§K are project-agnostic and should not change.
Versioning And Tagging
Releases follow SemVer: vMAJOR.MINOR.PATCH (use v0.x.y while
unstable). Choose the bump by the largest change since the last tag:
- PATCH — bug fixes, docs, tests, wrappers, non-breaking workflow polish.
- MINOR — new tools/helpers/config features; backward-compatible behavior.
- MAJOR — breaking config/schema/API behavior or a changed MCP contract.
Tags must:
- be created only from
master(the exact commit on remotemaster), - be created only after the full test suite passes,
- be annotated tags (
git tag -a), never lightweight, - include release notes / a changelog summary referencing the merged PRs/issues.
Never tag feature branches, dirty worktrees, unreviewed or self-authored
work, or commits not present on remote master.
Additional tag rules:
- Do not create, move, delete, or push tags unless explicitly instructed.
- Tag only after the intended PR is merged, and tag only the verified final master merge commit (never the PR branch head unless the merge commit is exactly that commit).
- Always report the tag target commit in the final report / handoff.
Release process (see templates/release-tag.md):
git fetch <remote> --prune.- Verify local
masterequals remotemaster(0 0) and the tree is clean. - Run the full test suite; stop on any failure.
- Inspect merged issues/PRs since the last tag
(
git log --oneline <last-tag>..<remote>/master). - Choose the version bump.
- Create the annotated tag on remote
masterwith release notes. - Push the tag.
- Create/update release notes if the forge supports it.
Where present, scripts/release-tag automates this with all gates built in
(SemVer, fetch/prune, on-master, clean tree, local==remote master, HEAD on
remote master, no duplicate tag, tests, annotated-only). Safe by default: no
push without --push; --dry-run changes nothing; --skip-tests must be
explicit and warns.