feat: enforce issue-linked branches + document versioning/tagging policy (#48)

Formalize the branch↔issue relationship and add a release/version-tagging policy.

Branch/issue linkage:
- scripts/worktree-start now validates branch names: implementation branches
  must match (fix|feat|docs|chore)/issue-<number>-<slug>; review branches
  review/pr-<number>-<slug>. Untraceable names are rejected with a clear error
  (exit 2). New --allow-unlinked override for genuine exceptions. --dry-run
  preserved.
- Documented issue → branch → worktree → PR → cleanup traceability in the
  runbook and the portable SKILL, including the claim-comment convention and
  Closes #n / Refs #n PR-body usage.
- Noted that Gitea exposes no native issue→branch API field (only a PR head
  branch), so linkage is enforced via branch name + claim comment + PR body +
  cleanup.

Versioning / tagging policy (docs only; no release automation yet):
- SemVer vMAJOR.MINOR.PATCH (v0.x.y while unstable) with PATCH/MINOR/MAJOR bump
  rules.
- Annotated tags only, from the exact commit on remote master, only after the
  full suite passes, with release notes referencing merged PRs/issues. Never tag
  feature branches, dirty worktrees, unreviewed/self-authored work, or commits
  not on remote master.
- Release runbook in the runbook + SKILL, plus a new
  skills/llm-project-workflow/templates/release-tag.md prompt template.

Tests: worktree-start branch validation — accepts fix/feat/docs/chore/issue-*
and review/pr-*, rejects fix/random-name / my-branch / non-numeric issue,
honors --allow-unlinked, preserves --dry-run. Full suite 291 passed / 0 failures;
bash -n clean; git diff --check clean; no secrets.

Release-tag automation (a scripts/release-tag helper) intentionally deferred to a
later issue to keep this diff narrow and testable.

Closes #48. Refs #38, #39, #46.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 03:41:59 -04:00
parent 00ec883014
commit f18cecc998
5 changed files with 214 additions and 9 deletions
@@ -0,0 +1,41 @@
# Template: cut a release tag
Copy, fill the `<...>` fields, and paste as the task prompt. Tagging is
irreversible-ish and outward-facing — fail closed on any doubt.
```text
Task: cut release <vX.Y.Z> from master.
Rules (llm-project-workflow — versioning & tagging):
- SemVer: vMAJOR.MINOR.PATCH (v0.x.y while unstable).
- PATCH: bug fixes, docs, tests, wrappers, non-breaking workflow polish.
- MINOR: new tools/helpers/config features, backward-compatible behavior.
- MAJOR: breaking config/schema/API or changed MCP contract.
- Tag ONLY from clean, tested remote master. Annotated tags only (git tag -a).
- NEVER tag: feature branches, dirty worktrees, unreviewed/self-authored work,
or commits not present on remote master.
Steps:
1. git fetch <remote> --prune
2. Confirm local master == <remote>/master (git rev-list --left-right --count
<remote>/master...master → 0 0) and the tree is clean.
3. Run the FULL test suite; it must pass. STOP on any failure.
4. Inspect merged issues/PRs since the last tag:
git log --oneline <last-tag>..<remote>/master
5. Choose the bump (PATCH/MINOR/MAJOR) per the rules above; set <vX.Y.Z>.
6. Create an ANNOTATED tag on <remote>/master with release notes that reference
the merged PRs/issues:
git tag -a <vX.Y.Z> <remote>/master -m "<vX.Y.Z>: <summary>
- #<n> <title> (PR #<pr>)
- ..."
7. Push the tag: git push <remote> <vX.Y.Z>
8. Create/update the release notes / changelog entry if the forge supports it.
Fail-closed: STOP if tests fail, the tree/worktree is dirty, master != remote,
the target commit is not on remote master, or the work was self-authored/
unreviewed. Never tag to "fix" a failing state.
Handoff: version, bump rationale, commit tagged, tests result, tag pushed,
release notes link.
```