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:
@@ -45,7 +45,37 @@ orchestration and status only (issue creation, `git status`, creating worktrees)
|
||||
- Branch folders are removed only after the PR is merged/closed **and** cleanup
|
||||
is explicitly part of the task.
|
||||
|
||||
Preferred helpers (if present in the project):
|
||||
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:
|
||||
|
||||
```bash
|
||||
scripts/worktree-start fix/issue-123-example # → branches/fix-issue-123-example
|
||||
@@ -174,6 +204,7 @@ Ready-to-copy templates live in [`templates/`](templates/):
|
||||
- [`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
|
||||
|
||||
@@ -188,3 +219,34 @@ Replace these project-specific names when copying the skill elsewhere:
|
||||
| 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 <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.
|
||||
|
||||
@@ -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.
|
||||
```
|
||||
Reference in New Issue
Block a user