Files
sysadmin 4e43347b2d feat: add scripts/release-tag automation helper (#50)
Automate the documented release-tag checklist (#48) without bypassing safety
gates.

scripts/release-tag:
- Requires a SemVer tag (vMAJOR.MINOR.PATCH); validates before any git/network.
- Fetch/prune first, then refuses: dirty worktree, non-master branch, local
  master != remote master, HEAD not on remote master, and an existing local or
  remote tag of the same name.
- Runs the full suite by default; --skip-tests is an explicit opt-out that warns.
- Creates an ANNOTATED tag (git tag -a), never lightweight.
- Safe by default: no push unless --push; --dry-run prints planned actions and
  changes nothing. Supports --notes-file <path> for the annotation message.
- Prints: commit, tag, tests_run, tag_created, tag_pushed.
- Env injection points for testing/CI: RELEASE_TAG_REMOTE, RELEASE_TAG_TEST_CMD.

tests/test_release_tag.py (14 cases): valid SemVer dry-run; invalid version;
dirty worktree; non-master; master/remote mismatch; existing tag; missing
notes-file; annotated-not-lightweight; no-push-without-flag; push-only-with-flag;
notes-file message; --skip-tests warns; default runs tests (fail blocks tag,
pass tags). Each test builds a throwaway repo with a LOCAL bare remote (cloned,
not pushed) and stubs the test command — no network, no real tags, no pushing
from the project repo.

Docs: reference scripts/release-tag from the runbook, SKILL, and the release-tag
template (script preferred; manual steps are the fallback).

Full suite 305 passed / 0 failures; bash -n clean; git diff --check clean; no
secrets.

Closes #50. Refs #48.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 05:37:41 -04:00

13 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.

  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-<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 #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:

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 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

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 <remote> --prune.
  3. Confirm local master equals remote master (git rev-list --left-right --count <remote>/master...master0 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 <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.

G. Merge / cleanup workflow

Only an eligible (non-author) reviewer merges. 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 <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 <remote>/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 <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>.

J. Prompt snippets

Ready-to-copy templates live in templates/:

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–§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):

  1. git fetch <remote> --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 <last-tag>..<remote>/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.