docs: add portable llm-project-workflow skill + templates (#46)

Extract the project's operating rules into a reusable, project-agnostic skill
so any repo can adopt the same safe LLM workflow.

- skills/llm-project-workflow/SKILL.md: issue-first; isolated branch worktrees
  (main checkout = orchestration only); distinct author/reviewer identities and
  profile safety (secrets by reference only; stop if authenticated user == PR
  author); branch naming; start/review/merge/cleanup workflows; fail-closed
  cases; recovery patterns; and an "Adapting to a project" table for the
  forge-specific names.
- templates/: copy/paste prompts for start-issue, review-pr, merge-pr,
  recover-bad-state, worktree-cleanup.
- Link the skill from README.md and docs/llm-workflow-runbooks.md (the runbook
  is framed as the Gitea-specific application of the portable skill).

Docs-only; no code, no secrets, safe placeholder examples only. No change to
MCP runtime, Gitea API, credential storage, or worktree helpers.

Checks: full suite 287 passed / 0 failures; git diff --check clean; secret scan
of skills/ clean.

Closes #46. Refs #38, #39.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 03:26:25 -04:00
parent 92b449f080
commit 00ec883014
8 changed files with 280 additions and 273 deletions
+144 -179
View File
@@ -1,225 +1,190 @@
---
name: llm-project-workflow
description: >-
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
Use this skill when an LLM is asked to create issues, implement repository
changes, review pull requests, merge pull requests, or recover from unsafe git
state in any project.
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](#adapting-to-a-project).
This skill is intentionally portable. Replace the remote name, default branch,
profile names, and repository URL conventions with the target project's local
standards, but keep the safety rules intact.
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.
## Core Rule
---
No repository change happens without a tracking issue first.
## A. Issue-first rule
Repository changes include creating files, editing files, deleting files,
changing executable bits, changing documentation, adding scripts, changing
config, committing, pushing, and opening pull requests.
**No repository change without a tracking issue.** This includes creating,
editing, deleting, or `chmod`-ing files; docs; scripts; commits; pushes; and PRs.
If no issue exists, create one before touching the repository. If issue creation
or issue claiming fails, stop immediately and report the blocker.
1. Before any change, confirm a tracking issue exists.
2. If none exists, create one first (title + problem + scope + acceptance).
3. Claim it (assign yourself or apply the `status:in-progress` label) and comment
that work is starting, including the planned branch name.
4. **If the issue cannot be created or claimed, stop.** Do not touch files.
## Worktree Isolation
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.
Never implement or review directly in the main checkout.
## B. Isolated worktree rule
The main checkout is for orchestration only: status checks, issue creation,
issue claiming, fetching, and creating branch worktrees. Each issue gets a
separate branch worktree under an ignored `branches/` directory. Review work
gets its own separate review worktree.
**Never implement or review in the main checkout.** The main checkout is for
orchestration and status only (issue creation, `git status`, creating worktrees).
Dirty work in one branch folder must not block unrelated work in another branch
folder. No LLM may edit or clean another issue's worktree unless explicitly
assigned to that issue and cleanup is part of the task.
- 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.
## Branch Naming
Preferred helpers (if present in the project):
Use issue- or PR-scoped names:
```bash
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
```
- `fix/issue-123-short-description`
- `feat/issue-123-short-description`
- `docs/issue-123-short-description`
- `review/pr-456-scope-check`
Use a filesystem-safe folder under `branches/` by replacing slashes with
hyphens, for example `branches/fix-issue-123-short-description`.
## Worktree Creation
Generic pattern:
Manual equivalent:
```bash
git fetch <remote> --prune
git worktree add -b <branch> branches/<safe-branch-folder> <remote>/master
cd branches/<safe-branch-folder>
```
For Gitea-Tools specifically:
```bash
./scripts/worktree-start fix/issue-123-example
git worktree add -b fix/issue-123-example branches/fix-issue-123-example <remote>/master
cd branches/fix-issue-123-example
```
If the default branch is not `master`, use the target project's default branch.
If `local master` is ahead of the remote unexpectedly, stop before creating the
worktree and ask for explicit recovery instructions.
`venv/` and similar are not copied into new worktrees — run checks with a known
interpreter path, or create a venv inside the branch folder.
## Identity And Profile Safety
## C. Identity and profile safety
Use canonical execution profiles where available. The profile is the role; the
LLM is not permanently an author, reviewer, or merger.
- 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 file
- `GITEA_MCP_PROFILE` — the profile to activate
- **If the authenticated user equals the PR author, stop** — no self-review, no
self-merge.
Author and reviewer identities must be distinct. Before review, approval, merge,
or branch cleanup, verify both the authenticated user and the PR author. If the
authenticated user is the PR author, stop immediately.
## D. Branch naming
Never place raw tokens or passwords in LLM configs, prompts, commits, PR
comments, logs, or tool outputs. Prefer profile references such as:
```text
fix/issue-123-short-description
feat/issue-123-short-description
docs/issue-123-short-description
review/pr-456-scope-check
```
- `GITEA_MCP_CONFIG`
- `GITEA_MCP_PROFILE`
Worktree folder = branch with `/` replaced by `-`
(`branches/fix-issue-123-short-description`).
If identity cannot be determined, fail closed. Do not approve, merge, close, or
clean up with an unknown identity.
## E. Start-work workflow
## Implementation Workflow
1. Verify the orchestration checkout (right repo, clean tree).
2. Fetch/prune: `git fetch <remote> --prune`.
3. Confirm local `master` equals remote `master` (`git rev-list --left-right --count <remote>/master...master``0 0`).
4. Create/claim the issue (§A).
5. Create the isolated worktree (§B) from latest remote `master`.
6. Implement the narrow scope only — no unrelated refactors or formatting churn.
7. Add/update focused tests when behavior changes.
8. Run the checks (tests, compile/lint, `git diff --check`, secret scan).
9. Commit with an issue-linked message.
10. Push the branch.
11. Open a PR to `master`.
12. **If you are the author, stop before review/merge.**
1. Verify the request has a tracking issue. If not, create one.
2. Claim the issue using the project's in-progress convention, such as
`status:in-progress`.
3. Fetch/prune and confirm the local default branch matches the remote default
branch.
4. Create an issue-specific branch worktree from the latest remote default
branch.
5. Implement only the issue scope inside that worktree.
6. Run focused checks and the project's required full checks.
7. Inspect the diff for scope, secrets, and unrelated files.
8. Commit.
9. Push.
10. Open a PR to the default branch.
11. Stop before review or merge if you authored the PR.
## F. Review workflow
## Review Workflow
1. Use a separate review worktree (`scripts/worktree-review <branch>`), detached.
2. Verify your authenticated identity.
3. Verify the PR author — **you must not be the author.**
4. Verify the worktree is clean.
5. Inspect the full diff; confirm scope matches the linked issue; flag unrelated files.
6. Run the tests.
7. **Do not merge if checks fail. Do not merge if the reviewer is the author.**
1. Use a separate review worktree.
2. Verify authenticated identity.
3. Verify PR author.
4. Stop if authenticated identity matches the PR author.
5. Verify the PR is open and targets the expected base branch.
6. Verify the PR head matches the reviewed commit or includes it.
7. Verify the review worktree is clean.
8. Inspect changed files and diff against the linked issue scope.
9. Run required tests and checks.
10. Inspect for secrets, credential paths, raw environment dumps, unrelated work,
and disallowed behavior.
11. If checks fail, leave exact blockers and do not approve or merge.
12. If checks pass and the reviewer is eligible, approve or leave the requested
review comment according to the task.
## G. Merge / cleanup workflow
## Merge And Cleanup Workflow
Only an eligible (non-author) reviewer merges. After a real merge:
Only merge when assigned merge duty and reviewer-eligible.
1. Confirm remote `master` actually contains the merge commit.
2. Close/release the issue; remove `status:in-progress` if used.
3. Delete the remote branch.
4. Remove the local branch.
5. Remove the branch worktree folder (`scripts/worktree-clean --delete-branch <branch>`).
6. Fetch/prune.
7. Confirm the main checkout is clean and current (`0 0` vs remote).
After an eligible merge:
Never run cleanup before the merge is confirmed on remote `master`.
1. Confirm the PR is still open and targets the expected base branch.
2. Confirm the PR head is still the reviewed commit or includes it.
3. Confirm the authenticated user is not the PR author.
4. Merge through the project's gated merge workflow.
5. Confirm remote `master` or the target default branch contains the merge.
6. Close or release the linked issue according to project workflow.
7. Remove `status:in-progress` or the equivalent in-progress label when
applicable.
8. Delete the merged remote branch when policy allows.
9. Remove the local PR branch.
10. Remove the local branch worktree folder only after confirming it is merged
or no longer needed.
11. Fetch/prune.
12. Confirm the main checkout is clean and current.
## H. Fail-closed cases
Do not delete branches or worktrees containing unmerged work.
**Stop and report — take no mutating action — if:**
## Fail-Closed Cases
- 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.
- Local `master` is 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.
Stop immediately if any of these are true:
When in doubt, stop and surface the discrepancy; do not guess or work around a gate.
- no issue exists and one cannot be created
- the issue cannot be claimed
- a worktree is dirty and belongs to the current task, but its state is unclear
- dirty work belongs to another issue or another LLM
- branch or PR state conflicts with the prompt
- the PR is closed but not merged
- local `master` is ahead of the remote unexpectedly
- authenticated identity cannot be verified
- authenticated user is the PR author for review, approval, or merge
- the active profile is unknown or not allowed for the requested operation
- secrets, tokens, passwords, auth headers, or credential paths are found in the
diff
- tests or required checks fail
- branch cleanup would delete unmerged work
- production, deploy, rollback, migration, restart, CI trigger, or unrelated
service behavior appears outside the issue scope
## I. Recovery patterns
Fail closed means no mutation, no approval, no merge, and no cleanup that could
destroy work.
- **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 `master` ahead of remote unexpectedly:** do not push `master`. Confirm
the commits are preserved on a feature branch (local + remote) first, then
`git reset --hard <remote>/master` to realign. Never discard commits that are
not safely pushed elsewhere.
- **PR closed but not merged:** the work is not in mainline. Re-push the branch,
reopen (or open a replacement) PR, and let an eligible reviewer merge. Do not
assume "closed" means "merged" — verify remote `master` contains the commits.
- **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 blind `git 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>` and `git log <remote>/<branch>`.
## Recovery Patterns
## J. Prompt snippets
Dirty worktree from another issue:
Ready-to-copy templates live in [`templates/`](templates/):
- Do not edit, reset, delete, or clean it.
- Create or use a separate branch worktree for the assigned issue.
- Report the unrelated dirty files only if they affect the task.
- [`start-issue.md`](templates/start-issue.md) — start a new issue.
- [`review-pr.md`](templates/review-pr.md) — review a PR.
- [`merge-pr.md`](templates/merge-pr.md) — merge a PR (eligible reviewer only).
- [`recover-bad-state.md`](templates/recover-bad-state.md) — recover from bad state.
- [`worktree-cleanup.md`](templates/worktree-cleanup.md) — clean up after merge.
Dirty worktree from the current issue:
## Adapting to a project
- Inspect status and diff.
- Preserve useful work in a commit, patch, or explicit handoff only when asked.
- Do not reset or delete ambiguous work without explicit approval.
Replace these project-specific names when copying the skill elsewhere:
Local default branch ahead of remote:
| 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` |
- Stop.
- Report the local and remote commit hashes.
- Ask whether to push, preserve, or reset. Do not choose destructively.
PR closed but not merged:
- Do not merge.
- Release the issue from in-progress only if the task explicitly asks and the
project workflow allows it.
- Leave a clear comment explaining the PR is closed without merge.
Branch deleted before merge:
- Verify whether the PR was merged.
- If merged, cleanup can continue.
- If not merged, stop and report that the source branch is missing.
Unauthorized untracked file created:
- Stop immediately.
- Report the exact file path.
- Do not commit it.
- Remove it only when explicitly instructed or when the recovery request asks for
removal.
- Recreate the change only after an issue exists and is claimed.
Need to preserve commits before reset:
- Prefer non-destructive preservation: tag, branch, patch file, or pushed backup
branch.
- Do not use destructive reset or checkout commands unless the user explicitly
requests that operation.
## Prompt Templates
Ready-to-copy templates live in `templates/`:
- `start-issue.md`
- `review-pr.md`
- `merge-pr.md`
- `recover-dirty-worktree.md`
Adapt the placeholders to the project before use.
The rules in §A–§I are project-agnostic and should not change.
@@ -1,43 +1,28 @@
# Merge PR Prompt
# Template: merge a PR (eligible reviewer only)
You are the merge operator for PR `<pr-number>` in `<repo-name>`.
Copy, fill the `<...>` fields, and paste as the task prompt.
Hard gate:
```text
Task: merge PR #<pr> for issue #<n> if it is eligible and checks pass.
- Verify authenticated identity before reviewing or merging.
- Verify PR author.
- If authenticated user matches the PR author, stop immediately.
- Merge only if assigned merge duty and reviewer-eligible.
Rules (llm-project-workflow):
- Only an eligible, NON-author reviewer merges. If authenticated user == PR
author → STOP.
- Do not merge unless the PR is open, mergeable, and its checks/review pass.
- No force-merge, no bypassing branch protections.
Workflow:
Steps:
1. Verify authenticated identity + active profile.
2. Confirm PR #<pr>: author (not you), state open, mergeable, review approved.
3. If any gate fails → STOP and report.
4. Merge with explicit confirmation (e.g. confirmation="MERGE PR <pr>"),
optionally pinning the reviewed head SHA / changed-file set.
5. Confirm remote master now contains the merge commit.
1. Confirm PR `<pr-number>` is open and targets `<default-branch>`.
2. Confirm the PR head is still `<reviewed-commit>` or includes it.
3. Confirm changed files still match the reviewed file list.
4. Confirm previous validation still applies or rerun required checks.
5. Merge through the gated project merge workflow.
6. Confirm remote `<default-branch>` contains the merge.
7. Close or release linked issue `<issue-number>` according to project policy.
8. Remove `status:in-progress` if applicable.
9. Delete the merged remote branch if policy allows.
10. Remove local PR branch and branch worktree only after merge is confirmed.
11. Fetch/prune.
12. Confirm main checkout is clean/current.
Then run the cleanup template (worktree-cleanup.md):
- close/release issue #<n>, remove status:in-progress
- delete remote branch, remove local branch + worktree folder
- fetch/prune; confirm main checkout is clean and current (0 0).
Stop if:
- authenticated user is the PR author
- PR head changed after review
- PR is closed but not merged
- tests/checks fail
- cleanup would delete unmerged work
Report:
- authenticated user
- PR author
- eligibility result
- merge commit/hash
- issue final state
- branch/worktree cleanup result
- final PR URL
Handoff: reviewer identity, merge result + commit, cleanup done, issue closed.
```
@@ -0,0 +1,32 @@
# Template: recover from bad state
Copy, fill the `<...>` fields, paste as the task prompt. Recovery is read-then-
act: gather facts first, never discard unmerged work.
```text
Task: recover repo state for <situation>. Do not lose unmerged work.
Rules (llm-project-workflow):
- Fail closed: if state is unclear or a step would delete unmerged work, STOP.
- Never push master. Never discard commits not safely pushed to <remote>.
Diagnose first:
1. git fetch <remote> --prune
2. git status --short; git worktree list
3. git rev-list --left-right --count <remote>/master...master # ahead/behind
4. For any PR involved: confirm state (open/closed/merged) AND whether
<remote>/master actually contains its commits ("closed" != "merged").
Act per case:
- Dirty worktree from another issue: leave it; start yours in a new worktree.
- Local master ahead of remote: confirm the extra commits live on a branch
pushed to <remote>, THEN git reset --hard <remote>/master. Verify with
`git branch --contains <sha>` first.
- PR closed but not merged: re-push the branch, reopen/replace the PR, let an
eligible reviewer merge. Do not merge your own.
- Branch deleted before merge: recover commits from a local branch/reflog (or
git fsck --lost-found), re-push, reopen the PR.
- Unauthorized untracked file: do not commit it; leave pre-existing artifacts.
Handoff: what was wrong, evidence, action taken, current state, what remains.
```
@@ -1,34 +1,26 @@
# Review PR Prompt
# Template: review a PR
You are independently reviewing PR `<pr-number>` in `<repo-name>`.
Copy, fill the `<...>` fields, and paste as the task prompt.
Hard gate:
```text
Task: review PR #<pr> for issue #<n>.
- Verify authenticated identity.
- Verify PR author.
- If authenticated user matches the PR author, stop immediately.
- Do not approve or merge unless reviewer-eligible.
Rules (llm-project-workflow):
- Review in a SEPARATE detached review worktree, never the author's folder.
- You must NOT be the PR author. If the authenticated user == PR author, stop.
- Do not merge if any check fails.
Workflow:
Steps:
1. Verify your authenticated identity (whoami) and the active profile.
2. Fetch the PR facts: PR author, head SHA, state (must be open), base branch.
3. If authenticated user == PR author → STOP (no self-review).
4. scripts/worktree-review <pr-head-branch> # detached, branches/review-*
cd branches/review-<pr-head-branch-slug>
5. Confirm the worktree is clean. Inspect the FULL diff; confirm scope matches
issue #<n>; flag any unrelated files, secrets, or formatting churn.
6. Run the test suite; note results.
7. Post the review verdict: approve only if scope is clean and checks pass;
otherwise request changes with specifics. Never merge from this review step.
1. Create or use a separate review worktree.
2. Fetch/prune.
3. Confirm PR `<pr-number>` is open and targets `<default-branch>`.
4. Confirm the PR head is `<expected-commit>` or includes it.
5. Confirm changed files match the issue scope.
6. Inspect the diff for correctness, secrets, unrelated behavior, and forbidden
changes.
7. Run `<required-checks>`.
8. If anything fails, leave exact blockers and do not approve.
9. If everything passes, approve only if reviewer-eligible.
Report:
- authenticated user
- PR author
- eligibility result
- commit reviewed
- files changed
- checks run and results
- blockers or risks
- final PR URL
Handoff: reviewer identity, PR author, scope verdict, checks + results, decision.
```
@@ -1,34 +1,29 @@
# Start Issue Prompt
# Template: start a new issue
You are implementing issue `<issue-number>` in `<repo-name>`.
Copy, fill the `<...>` fields, and paste as the task prompt.
Rules:
```text
Task: implement <issue title / one-line goal>.
- No repository changes unless the issue exists and is claimed.
- Use an isolated branch worktree under `branches/`.
- Branch from latest `<remote>/<default-branch>`.
- Keep the change limited to issue `<issue-number>`.
Rules (llm-project-workflow):
- No repo changes without a tracking issue. If none exists, create one first;
if it can't be created, stop.
- Work only in an isolated branch worktree under branches/. The main checkout
is orchestration/status only.
- Do not self-review or self-merge.
Workflow:
Steps:
1. Verify the orchestration checkout is the right repo and clean.
2. git fetch <remote> --prune; confirm local master == <remote>/master (0 0).
3. Create the issue "<title>" (problem, scope, acceptance) and claim it
(status:in-progress + a "starting work" comment naming the branch).
4. scripts/worktree-start <type>/issue-<n>-<slug> # type = fix|feat|docs
cd branches/<type>-issue-<n>-<slug>
5. Implement the narrow scope only; add/update focused tests if behavior changes.
6. Checks: run the test suite, compile/lint changed files, git diff --check,
and scan the diff for secrets.
7. Commit (issue-linked message), push the branch, open a PR to master.
8. Stop before review/merge — you are the author.
1. Verify the issue exists and is open.
2. Claim it with the project in-progress label.
3. Fetch/prune and confirm local `<default-branch>` matches
`<remote>/<default-branch>`.
4. Create branch `<branch-name>` in `branches/<safe-folder-name>`.
5. Implement only the scoped change.
6. Run `<required-checks>`.
7. Inspect for secrets and unrelated files.
8. Commit, push, and open a PR.
9. Stop before review or merge.
Final handoff:
- issue number/title
- branch name
- isolated worktree path
- files changed
- checks run and results
- PR URL
- reviewer/merge instructions
Handoff: issue #, branch, worktree path, files changed, checks + results, PR URL.
```
@@ -0,0 +1,28 @@
# Template: clean up after merge
Copy, fill the `<...>` fields, and paste as the task prompt. Only run AFTER a
real merge is confirmed on remote master.
```text
Task: clean up branch/worktree for PR #<pr> / issue #<n> after merge.
Rules (llm-project-workflow):
- Do NOT clean up until the merge is confirmed on <remote>/master.
- Cleanup would-delete-unmerged-work → STOP. Never --force-remove a dirty tree.
Steps:
1. git fetch <remote> --prune
2. Confirm <remote>/master contains the merge of PR #<pr>
(git log <remote>/master | grep the merge, or git branch -r --contains <sha>).
If not merged → STOP; run the recovery template instead.
3. Close issue #<n> if not auto-closed; remove the status:in-progress label.
4. scripts/worktree-clean --delete-branch <type>/issue-<n>-<slug>
(removes branches/<type>-issue-<n>-<slug>; refuses if dirty; git branch -d is
safe-delete only — fails on unmerged.)
5. Delete the remote branch if the merge did not already remove it.
6. From the main checkout: git fetch <remote> --prune; git checkout master;
git reset --hard <remote>/master ONLY if local master safely matches remote.
7. Confirm main checkout clean and current (git status; 0 0 vs <remote>/master).
Handoff: merge confirmed, issue closed, branch+worktree removed, checkout clean.
```