Make the nine-line compact Controller Handoff the default end-of-task format; reserve the long Controller Handoff Summary for high-risk/complex tasks (merge/tag/release, failed validation, blocked gates, secrets/prod, complicated owner decisions, cross-repo state, or explicit owner request). Compact form is for controller-LLM readability, safety confirmations are never omitted, and PR bodies still carry full review detail. Updates SKILL.md §K, llm-workflow-runbooks.md, and the start-issue / review-pr templates. Documentation only. Refs #101. Closes #108. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
18 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.
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.
B. Isolated worktree rule
Never implement or review in the main checkout. The main checkout is for
orchestration and status only (issue creation, git status, creating worktrees).
- 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 #123when the PR should close the issue,Refs #123when related but not closing, - 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
- 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
- Verify the orchestration checkout (right repo, clean tree).
- 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).
- 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). - Commit with an issue-linked message.
- Push the branch.
- Open a PR to
master. - 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
- Use a separate review worktree (
scripts/worktree-review <branch>), detached. - Verify your authenticated identity.
- Verify the PR author — you must not be the author.
- Verify the worktree is clean.
- Inspect the full diff; confirm scope matches the linked issue; flag unrelated files.
- Run the tests.
- Do not merge if checks fail. Do not merge if the reviewer is the author.
G. Merge / cleanup workflow
Only an eligible (non-author) reviewer merges. Before merging: always verify the authenticated identity and the PR author; respect runtime profile gates; run independent validation (do not trust the author's reported results); 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:
- Confirm remote
masteractually contains the merge commit (A PR is not done just becausemastermoved. A PR is done only when: Gitea reports the PR merged or reconciliation documents equivalent content onmaster; remotemastercontains the expected content; linked issues are closed;status:in-progressis removed). - Close/release the issue.
- Whenever an issue is closed, check for
status:in-progress: remove it, or report why it could not be removed. - 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. - Remove the local branch.
- Remove the branch worktree folder (
scripts/worktree-clean --delete-branch <branch>). Branches/worktrees are cleaned only after the above is verified. - Fetch/prune.
- Confirm the main checkout is clean and current (
0 0vs remote). - Final merge/reconciliation reports must include both: PR metadata (state, merged flag, merge commit/hash) and Git content (remote master hash, expected content present or not).
Never run cleanup before the merge is confirmed on remote master.
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.
- 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 — 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 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):
## Controller Handoff
- Task:
- Repo/state:
- Issues/PRs:
- Changed:
- Validation:
- Blockers:
- Review:
- Next:
- Safety:
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.