--- 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 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). 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. --- ## 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. 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. 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. ## 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-description` - `feat/issue-123-short-description` - `docs/issue-123-short-description` - `chore/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--…` (or a `review/pr--…` 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 #123` when the PR should close the issue, `Refs #123` when 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: ```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 ``` Manual equivalent: ```bash git fetch --prune git worktree add -b fix/issue-123-example branches/fix-issue-123-example /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 file - `GITEA_MCP_PROFILE` — the profile to activate - **If the authenticated user equals the PR author, stop** — no self-review, no self-merge. ## D. Branch naming ```text 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 1. Verify the orchestration checkout (right repo, clean tree). 2. Fetch/prune: `git fetch --prune`. 3. Confirm local `master` equals remote `master` (`git rev-list --left-right --count /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.** ## F. Review workflow 1. Use a separate review worktree (`scripts/worktree-review `), 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.** ## G. Merge / cleanup workflow Only an eligible (non-author) reviewer merges. After a real merge: 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 `). 6. Fetch/prune. 7. Confirm the main checkout is clean and current (`0 0` vs remote). 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. - 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. 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 `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 /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 `, 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 ` and `git log /`. ## J. Prompt snippets Ready-to-copy templates live in [`templates/`](templates/): - [`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. - [`release-tag.md`](templates/release-tag.md) — create a release tag. ## Adapting to a project Replace these project-specific names when copying the skill elsewhere: | Placeholder | Meaning | Example here | |-------------|---------|--------------| | `` | 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–§I 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 remote `master`), - 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`. Release process (see [`templates/release-tag.md`](templates/release-tag.md)): 1. `git fetch --prune`. 2. Verify local `master` equals remote `master` (`0 0`) and the tree is clean. 3. Run the full test suite; stop on any failure. 4. Inspect merged issues/PRs since the last tag (`git log --oneline ../master`). 5. Choose the version bump. 6. Create the annotated tag on remote `master` with release notes. 7. Push the tag. 8. 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.