--- 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. --- ## 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. 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. 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-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` or `Fixes #123` when the PR should close the issue (do NOT use `Implements #123` or `Refs #123` to 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: ```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 - **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-author` and `gitea-reviewer`), each pointing to its respective `GITEA_MCP_PROFILE`. - Tool calls become namespace-scoped: `mcp__gitea-author__*` and `mcp__gitea-reviewer__*`. - **Trust Model:** Separate tokens remain separate. Profile gates enforce allowed operations, `whoami` is 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. - **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.** 13. **Normal issue work must not directly push to `master`.** PR content should be merged through the forge PR merge mechanism. 14. Direct push to `master` is 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 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. 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: 1. Confirm remote `master` actually contains the merge commit (A PR is not done just because `master` moved. A PR is done only when: Gitea reports the PR merged or reconciliation documents equivalent content on `master`; remote `master` contains the expected content; 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 `). 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 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 `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 (`merged=false`):** do not merge. Run reconciliation: compare PR content to remote `master` and decide: - **fully landed:** comment that content is present on `master`, remove `status: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. - **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. - [`reconcile-closed-not-merged-pr.md`](templates/reconcile-closed-not-merged-pr.md) — reconcile a closed-not-merged PR. - [`worktree-cleanup.md`](templates/worktree-cleanup.md) — clean up after merge. - [`release-tag.md`](templates/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): ```md ## Controller Handoff - Task: - Repo/state: - Issues/PRs: - Changed: - Validation: - Blockers: - Review: - Next: - Safety: ``` The `Safety:` line is never omitted; it is usually: ```text 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): ```md ## 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 ```md ## 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 | |-------------|---------|--------------| | `` | 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 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`. 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`](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.