Merge feature/release-candidate-skill into develop
This commit is contained in:
commit
0c0b7481c7
@ -63,6 +63,7 @@ rules:
|
||||
| BDR-039 | 2026-06-29 | Gitea branch protection = Option-1 owner-pushable, not require-PR | accepted |
|
||||
| BDR-040 | 2026-06-29 | doc-syncer MINOR-shape oracle: deterministic floor under LLM's MINOR call | accepted |
|
||||
| BDR-041 | 2026-06-30 | /reconcile = deterministic declared-vs-real engine + thin gated skill (reconciler, not lister) | accepted |
|
||||
| BDR-042 | 2026-06-30 | /release-candidate = thin orchestrator over gitflow release; the tag lives in the skill, not the lib | accepted |
|
||||
|
||||
---
|
||||
|
||||
@ -645,3 +646,12 @@ rules:
|
||||
- **Alternatives rejected**: monolithic teaching/discipline skill (agents reconcile when guided → no teaching value); `grep '[ ]'` lister (reproduces the lie); trust Index (drift, [[LRN-055]]); blocking write-back gate (friction — A/B/C surface chosen).
|
||||
- **Honest limits (graven)**: deferral detection LEXICAL (marked-only; unmarked "à reprendre quand X" missed); contradictions = CANDIDATES (token overlap), surfaced not asserted; cross-repo "not verifiable here"; cross-ref verdicts ("[~] done because chantier X below complete") surfaced, not auto-resolved.
|
||||
- **Reference**: `lib/reconcile.sh`, `lib/tests/run-reconcile.sh` (20/20) + `lib/tests/fixtures/` (neutral-named, [[LRN-077]]), `skills/reconcile/SKILL.md`, CLAUDE.md "Skill routing". Born of the 2026-06-29 manual inventory (its known-good oracle). Built via superpowers:writing-skills. See [[LRN-075]], [[LRN-076]], [[EVAL-011]].
|
||||
|
||||
## BDR-042 — /release-candidate = thin orchestrator over gitflow release; the tag lives in the skill
|
||||
- **Date**: 2026-06-30
|
||||
- **Status**: accepted
|
||||
- **Decision**: `/release-candidate <X.Y.Z>` cuts a release by ORCHESTRATING the existing `lib/gitflow.sh` mechanic, NOT rewriting it. Flow: preconditions (clean tree, identity, develop ahead of main) → `gitflow start release` → prep (version.txt + CHANGELOG, breaking documented) → run-tests gate → HUMAN "WHEN to release" gate → `gitflow finish` (fan-out main+develop+delete, lib L108-111) → **`git tag -a vX.Y.Z main`** → push gated. The `git tag` lives in the SKILL, lib UNTOUCHED — the tag is release-specific (version + message + human call), the lib's fan-out is generic. Scheme `vX.Y.Z`, CONTINUES the version.txt/CHANGELOG lineage (never restart at v1.0.0 — desyncs from a CHANGELOG already at 3.x).
|
||||
- **Why**: gitflow release was wired (start base=develop L49, finish fan-out main+develop L108-111) but had NO tag step (grep-confirmed: zero `git tag` in gitflow.sh). The tag is the only gap; an orchestrator supplies it without touching the tested mechanic.
|
||||
- **Consequence (accepted)**: a release cut by calling `gitflow finish` directly, bypassing the skill, fans out but is NOT tagged → `/release-candidate` is the CANONICAL sole release path. Acceptable for a solo repo; revisit (tag in lib) only if direct-lib releases become a need.
|
||||
- **Alternatives rejected**: tag inside `gitflow_finish` (atomic but modifies the tested generic mechanic for a release-specific concern — lib=mechanic/skill=judgment); restart tags at v1.0.0 (desyncs tag↔CHANGELOG lineage).
|
||||
- **Reference**: `skills/release-candidate/SKILL.md`, `lib/tests/run-release-candidate.sh` (RED no-tag → GREEN 5/5), CLAUDE.md routing. Built via writing-skills TDD. Consumes the gitflow model [[BDR-039]]. See [[LRN-078]], [[LRN-079]], [[EVAL-012]].
|
||||
|
||||
@ -32,6 +32,7 @@ rules:
|
||||
| EVAL-009 | 2026-06-27 | deploy skill subagent-driven build: multi-stage review + pressure-test net-positive | keep |
|
||||
| EVAL-010 | 2026-06-29 | prune-memory hardening: RED-7 deterministic fix + RED-8 accept + 34-row index backfill | keep |
|
||||
| EVAL-011 | 2026-06-30 | /reconcile build: RED contaminated→corrected (unguided control), GREEN behavioral confirmed, dogfooded on itself | keep |
|
||||
| EVAL-012 | 2026-06-30 | /release-candidate build: RED (gitflow fans out, no tag) → GREEN 5/5 (tag), throwaway-repo flow replay | keep |
|
||||
|
||||
---
|
||||
|
||||
@ -128,3 +129,10 @@ rules:
|
||||
- **method**: 2-arm RED — GUIDED baselines (A/B, "use git + justify") SUCCEEDED = contaminated; UNGUIDED tempting baselines (a4872/a0f68) MIRRORED the TODO = real failure ([[LRN-075]]). RED-B = deterministic Index-ignore with TEETH (shim engine to read Index → reds). GREEN behavioral (a8404): same terrain + skill → verified via engine, stale reported done w/ SHAs, applied A/B/C gate, surfaced cross-ref as candidate. Dogfood: ran on the live repo, found its OWN chantier, marked S3 PARTIAL (routage absent per path oracle), not done.
|
||||
- **anomalies**: (1) first baseline LEADING → corrected with an unguided control ([[LRN-075]]). (2) fixture-name false-signal — a0f68 read "pre-reconcile" from the dir name → re-froze fixtures neutral ([[LRN-077]]). (3) harness caught a real bug mid-build: BLK-004 status bleed from BLK-005's header ([[LRN-076]]). (4) META: marked `[x] routage DONE` BEFORE the CLAUDE.md edit succeeded (errored — Read-first) → created the exact declared-vs-real gap `/reconcile` traps, caught by the next verify. The tool's build produced the gap it detects.
|
||||
- **action**: keep. RED watched red before green ([[LRN-074]] discipline), bug caught + fixed, behavioral loop closed.
|
||||
|
||||
## EVAL-012 — /release-candidate build (TDD): RED no-tag → GREEN 5/5, throwaway-repo replay
|
||||
- **Date**: 2026-06-30
|
||||
- **output**: `skills/release-candidate/SKILL.md` (thin orchestrator, 45 l) + `lib/tests/run-release-candidate.sh` (flow replay) + CLAUDE.md routing + CHANGELOG [Unreleased] entries (/reconcile + /release-candidate).
|
||||
- **method**: read-first cartography (gitflow release wired: start L49 base=develop, finish L108-111 fan-out; grep-confirmed NO `git tag` → the gap). TDD on a throwaway repo: RED (`RC_TAG=0`) = start→prep→finish → 4 GREEN (fan-out / merge-back / branch-deleted / CHANGELOG) + 1 RED (tag v4.0.0 absent — gitflow never tags); GREEN (`RC_TAG=1`) = + `git tag -a` → 5/5, tag on main's merge commit. shellcheck clean (caught + fixed an SC2164 mid-build).
|
||||
- **anomalies**: (1) versioning reasoning corrected by the user — number derives from change nature, not justification ([[LRN-078]]); caveman verified `Removed` not breaking from refs, not memory. (2) tag-in-skill consequence (direct-lib release wouldn't tag) made explicit + accepted, not left implicit. (3) layers kept distinct — this built+tested the skill; cutting the real v4.0.0 is a separate later act.
|
||||
- **action**: keep. RED red for the right reason (gap = tag), GREEN closes it, teeth proven.
|
||||
|
||||
@ -254,3 +254,10 @@ rules:
|
||||
- RED 2-arm: guided baselines succeed (contaminated) / unguided mirror the TODO (real failure) → value = determinism+gate, not teaching ([[LRN-075]]). GREEN behavioral confirmed; dogfooded on its own chantier (S3 marked partial honestly). [[EVAL-011]].
|
||||
- Learnings: unguided-control RED ([[LRN-075]]); last-block-wins status + BLK-004 bleed bug ([[LRN-076]]); neutral fixture names = same symptom/distinct cause as [[LRN-074]] ([[LRN-077]]).
|
||||
- Ship: feature/reconcile-skill → develop (gitflow finish). Push to origin gated (ASK).
|
||||
|
||||
## 2026-06-30 (cont.) — /release-candidate skill built (gitflow release orchestrator)
|
||||
- Built `/release-candidate` via writing-skills TDD: thin orchestrator over gitflow release + the version tag the lib lacks (grep-confirmed no `git tag` in gitflow.sh). RED (gitflow fans out, no tag) → GREEN 5/5 on a throwaway repo. [[BDR-042]], [[EVAL-012]].
|
||||
- Decisions: tag in the skill not the lib (release-specific vs generic mechanic); canonical sole release path (direct-lib release wouldn't tag, accepted); vX.Y.Z continues the lineage.
|
||||
- Learnings: semver derives from change nature, caveman = Removed not breaking ([[LRN-078]]); orchestrator-skill TDD = throwaway-repo flow replay ([[LRN-079]]).
|
||||
- CHANGELOG [Unreleased]: added /reconcile + /release-candidate under ### Added (so the eventual v4.0.0 captures them — /reconcile shipped without its entry, rectified here).
|
||||
- Ship: feature/release-candidate-skill → develop (gitflow finish). Push gated (ASK). Real v4.0.0 cut = separate later act (layer 2).
|
||||
|
||||
@ -97,6 +97,8 @@ rules:
|
||||
| LRN-075 | 2026-06-30 | skill-vs-no-skill RED must test the UNGUIDED control: a "use git + justify" baseline makes a capable agent succeed (contaminated RED); real failure shows only on the tempting prompt | building any skill whose value is determinism/gate over a capable agent — strip guidance AND tempt the failure; control succeeds → don't author (or rescope) |
|
||||
| LRN-076 | 2026-06-30 | append-only registry status mutates in place (UPDATE/FINAL blocks): CURRENT status = LAST status line, not Index, not first; range scan inclusive of next header bleeds a sibling's status word | parsing any in-place-mutated status; take last line, bound entry exclusive of next header |
|
||||
| LRN-077 | 2026-06-30 | test fixtures must carry NEUTRAL names — a name that telegraphs the answer lets the subject pass by reading the name, not doing the work | designing any test fixture/path; same symptom as [[LRN-074]] (passes for WRONG reason), distinct cause (leaky fixture vs assumed command) |
|
||||
| LRN-078 | 2026-06-30 | semver number DERIVES from the change nature, not "justify a target"; solo-repo "breaking" = requires a migration of own usage; a removal nothing invokes = Removed not breaking | choosing a release version; classifying MAJOR/MINOR/PATCH; deciding if a removal is breaking |
|
||||
| LRN-079 | 2026-06-30 | orchestrator-skill TDD = replay the prescribed flow on a throwaway repo (gitflow-test style): RED runs the flow minus the new step → the outcome assertion reds on the gap | testing a skill that orchestrates an existing mechanic + one new step |
|
||||
|
||||
---
|
||||
|
||||
@ -860,3 +862,14 @@ rules:
|
||||
- **pattern**: a baseline agent on a worktree named `wt-pre-reconcile` read "pre-reconcile" FROM THE DIR NAME and inferred staleness — reasoning for the WRONG reason (the name), not the right one (verify git). Fixtures + the GREEN test were re-frozen under NEUTRAL names so the engine reaches truth by querying git, never by reading a path hint.
|
||||
- **meta — same symptom, distinct cause as [[LRN-074]]**: 074 = a COMMAND-ASSUMPTION (ugrep parsed `-9..` → false green); 077 = a LEAKY FIXTURE (name telegraphs the answer). Different mechanisms, SAME symptom: the test passes/fails for the wrong reason. Cross-cutting lesson = verify a test passes for the RIGHT reason, not merely that it passes — whether the false signal comes from an assumed command (074) or a leaky fixture (077).
|
||||
- **future application**: name fixtures/paths neutrally; for any green, ask "did it pass because the subject did the work, or because something leaked the answer?"
|
||||
|
||||
## LRN-078 — semver number DERIVES from the change nature; "breaking" = requires a migration
|
||||
- **Date**: 2026-06-30
|
||||
- **pattern**: framing a release as "it's 4.0.0 → find the breaking changes to justify it" is backwards. Semver runs the other way: the number FOLLOWS the nature of the changes. The real question = "is there a breaking change?", not "how do I justify the target". Solo / mono-user repo, no public API ⇒ "breaking" = casse mon propre usage / EXIGE une migration de ma part.
|
||||
- **applied (v4.0.0)**: gitflow universal = a TRUE breaking workflow change (master→main, mandatory branches, hook, 6-repo migration) → MAJOR on its own. caveman removal = VERIFIED nothing invoked it (grep: only the kept memory format-rule + frozen fixtures, settings/hooks clean) → a clean `### Removed` (capability gone, nothing breaks, no migration), NOT breaking. The MAJOR rests on gitflow alone; don't mislabel a removal as breaking.
|
||||
- **future application**: pick MAJOR/MINOR/PATCH from the changes, then the lineage gives the digits. Verify "does X actually break / require migration?" from the refs (grep), not from the size of the change or the desire for a round number.
|
||||
|
||||
## LRN-079 — orchestrator-skill TDD: replay the flow on a throwaway repo, RED = flow minus the new step
|
||||
- **Date**: 2026-06-30
|
||||
- **pattern**: a thin orchestrator skill (composes an existing tested mechanic + ONE new step) is not unit-testable as a function, but its FLOW is testable by replay on a throwaway repo (gitflow-test style). RED = run the prescribed sequence WITHOUT the new step (the existing mechanic alone) and assert the desired outcome → it reds on exactly the gap. GREEN = add the step. For `/release-candidate`: `gitflow start release`→prep→`finish` (no tag) → assert `vX.Y.Z` on main → REDS (gitflow fans out but never tags); add `git tag` → 5/5. Teeth: the single toggled line (`RC_TAG`) flips red↔green so GREEN can't pass by accident.
|
||||
- **future application**: for any orchestrator over a lib mechanic, test the END-TO-END flow on a disposable repo; isolate the NEW step so the RED reds precisely on it (don't re-test the lib's generic part — it has its own tests).
|
||||
|
||||
@ -9,6 +9,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
<!-- DRAFT (doc-syncer): grounded in commits since 3.4.0; review wording + completeness before release. -->
|
||||
|
||||
### Added
|
||||
- `/reconcile` — declared-vs-real reconciler: confronts TODO checkboxes + registry statuses (never the `## Index`) against real git/fs and surfaces the gaps in four categories + contradiction candidates, with a gated TODO write-back. Engine `lib/reconcile.sh` (body enumeration, git/fs oracles, last-block-wins status); thin skill
|
||||
- `/release-candidate` — orchestrator over the gitflow release mechanic that adds the version tag the lib doesn't: finalize `version.txt` + CHANGELOG, fan-out `develop`→`main` + back, tag `vX.Y.Z`, push (gated). Lib stays the generic mechanic; the skill owns the tag
|
||||
- Coupled-capitalize: dev flows (feat / hotfix / bugfix / commit-change, ship-feature, init-project) auto-commit their memory in the same breath, via shared `lib/capitalize-commit.md` + `lib/memory-commit.sh` (surgical — `.claude/memory` + `.claude/tasks` only, never `git add -A`)
|
||||
- Coupled doc-sync: dev flows (feat / bugfix / hotfix, ship-feature, init-project) auto-commit the public docs `doc-syncer` patches, via shared `lib/doc-commit.md` + `lib/doc-commit.sh` (surgical — only the patched files, never `git add -A`, never `.claude/` / `CLAUDE.md`; refuses an out-of-scope path loudly with exit 4). `doc-syncer` surfaces `PATCHED_FILES` (one path per line) as the handoff
|
||||
- `lib/doc-shape.sh` — deterministic MINOR-shape oracle for `doc-syncer` AUTO MODE: re-checks each LLM-classified MINOR patch (added-heading / size / new-file / non-doc envelope, thresholds env-overridable) and escalates a shape-suspect patch to the existing SIGNIFICANT gate instead of silently auto-committing it. A structural floor under the LLM's classification, not a blocking gate (genuine MINOR still auto-commits, zero friction); catches structural/size significance, not semantic
|
||||
|
||||
@ -262,6 +262,7 @@ only the non-obvious cases: gstack fallbacks, disambiguation, cryptic names.
|
||||
- Bug / error / 500 → investigate (bugfix if gstack off)
|
||||
- feat / hotfix / bugfix distinguished by file count → see descriptions
|
||||
- Ship / deploy / PR → ship (ship-feature if gstack off)
|
||||
- Cut a release / tag a version (develop ahead of main) → release-candidate
|
||||
- Docs post-ship → document-release (doc if gstack off); stale-doc audit → doc
|
||||
- Audit of changes since last run → audit-delta
|
||||
- Open-work inventory / "queue empty?" / stale TODO vs real git → reconcile
|
||||
|
||||
61
lib/tests/run-release-candidate.sh
Normal file
61
lib/tests/run-release-candidate.sh
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# run-release-candidate.sh — TDD harness for /release-candidate.
|
||||
#
|
||||
# The skill is an ORCHESTRATOR over the existing gitflow release mechanic + the ONE
|
||||
# piece the lib lacks: the version tag. This harness replays the skill's prescribed
|
||||
# sequence on a throwaway repo (gitflow-test style) and asserts the release outcome.
|
||||
#
|
||||
# RED (RC_TAG=0): run start→prep→finish only (the existing mechanic) → the tag
|
||||
# assertion REDS, proving gitflow fans out main+develop but never tags.
|
||||
# GREEN(RC_TAG=1): the skill's flow adds `git tag` → tag present on main's merge commit.
|
||||
set -uo pipefail
|
||||
|
||||
GREP=/usr/bin/grep # LRN-074: pin grep
|
||||
LIBDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # repo lib/
|
||||
GITFLOW="$LIBDIR/gitflow.sh"
|
||||
RC_TAG="${RC_TAG:-0}" # 0=RED (no tag), 1=GREEN
|
||||
WORK="${RC_WORK:?set RC_WORK to a throwaway dir}"
|
||||
|
||||
pass=0; fail=0
|
||||
ok(){ echo "GREEN ✓ $*"; pass=$((pass+1)); }
|
||||
no(){ echo "RED ✗ $*"; fail=$((fail+1)); }
|
||||
|
||||
# ── seed a throwaway repo: main (v3.5.0) + develop ahead ──────────────────────
|
||||
rm -rf "$WORK"; mkdir -p "$WORK"
|
||||
git -C "$WORK" init -q
|
||||
git -C "$WORK" config user.email t@t; git -C "$WORK" config user.name T
|
||||
printf '3.5.0\n' > "$WORK/version.txt"
|
||||
printf '# Changelog\n\n## [Unreleased]\n\n### Added\n- new skill foo\n\n## [3.4.0] — 2026-04-15\n- prev\n' > "$WORK/CHANGELOG.md"
|
||||
git -C "$WORK" add -A; git -C "$WORK" commit -qm "initial"
|
||||
git -C "$WORK" branch -M main
|
||||
git -C "$WORK" branch develop
|
||||
git -C "$WORK" checkout -q develop
|
||||
printf 'feature work\n' > "$WORK/feat.txt"; git -C "$WORK" add -A; git -C "$WORK" commit -qm "feat on develop"
|
||||
echo "setup: develop +$(git -C "$WORK" rev-list --count main..develop) vs main, version.txt=$(cat "$WORK/version.txt"), tags=$(git -C "$WORK" tag | wc -l)"
|
||||
echo
|
||||
|
||||
# ── the /release-candidate flow (what the skill prescribes) ──────────────────
|
||||
( cd "$WORK" || exit 1
|
||||
bash "$GITFLOW" start release 4.0.0 >/dev/null # base develop → release/4.0.0 (lib L49/L71)
|
||||
printf '4.0.0\n' > version.txt # prep: version bump
|
||||
sed -i 's/## \[Unreleased\]/## [Unreleased]\n\n## [4.0.0] — 2026-06-30/' CHANGELOG.md
|
||||
git commit -qam "chore(release): 4.0.0 — version.txt + CHANGELOG"
|
||||
bash "$GITFLOW" finish >/dev/null # fan-out main+develop+delete (lib L108-111)
|
||||
# TAG = the gap. Lives in the SKILL (lib untouched). RED skips it, GREEN does it.
|
||||
if [ "$RC_TAG" = "1" ]; then git tag -a v4.0.0 main -m "release 4.0.0"; fi
|
||||
)
|
||||
|
||||
# ── assertions: fan-out (existing mechanic) + the tag (the new piece) ─────────
|
||||
echo "=== assertions (RC_TAG=$RC_TAG) ==="
|
||||
if [ "$(git -C "$WORK" show main:version.txt 2>/dev/null)" = "4.0.0" ]; then ok "fan-out: main carries the release (version.txt 4.0.0)"; else no "fan-out: main version.txt != 4.0.0"; fi
|
||||
if [ "$(git -C "$WORK" show develop:version.txt 2>/dev/null)" = "4.0.0" ]; then ok "merge-back: develop carries 4.0.0"; else no "merge-back failed"; fi
|
||||
if git -C "$WORK" show-ref --verify -q refs/heads/release/4.0.0; then no "release/4.0.0 NOT deleted"; else ok "release/4.0.0 branch deleted"; fi
|
||||
if git -C "$WORK" show main:CHANGELOG.md | $GREP -q '## \[4.0.0\]'; then ok "CHANGELOG [4.0.0] on main"; else no "CHANGELOG not finalized"; fi
|
||||
if git -C "$WORK" rev-parse -q --verify refs/tags/v4.0.0 >/dev/null; then
|
||||
if [ "$(git -C "$WORK" rev-list -n1 v4.0.0)" = "$(git -C "$WORK" rev-parse main)" ]; then ok "tag v4.0.0 on main's release-merge commit"; else no "tag v4.0.0 exists but not on main HEAD"; fi
|
||||
else
|
||||
no "tag v4.0.0 ABSENT — gitflow finish fans out but does NOT tag (the gap /release-candidate fills)"
|
||||
fi
|
||||
|
||||
echo; echo "================ $pass GREEN / $fail RED (RC_TAG=$RC_TAG) ================"
|
||||
[ "$fail" -eq 0 ] && exit 0 || exit 1
|
||||
45
skills/release-candidate/SKILL.md
Normal file
45
skills/release-candidate/SKILL.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
name: release-candidate
|
||||
description: Use when develop is ahead of main and you want to cut a versioned release — finalize version.txt + CHANGELOG, merge develop→main via the gitflow fan-out, tag it, and push. Triggers: "cut a release", "release candidate", "tag a version", "ship develop to main". NOT feature/bugfix integration (that is gitflow finish via /ship-feature) nor a hotfix.
|
||||
---
|
||||
|
||||
# /release-candidate — cut a gitflow release (orchestrator)
|
||||
|
||||
## Overview
|
||||
Turns the accumulated work on `develop` into a tagged release on `main`. THIN ORCHESTRATOR over `lib/gitflow.sh`: the lib does the generic fan-out (release branch → main + back to develop + delete the branch); the skill adds what the lib deliberately does not know — the **version number, the CHANGELOG, the human "is it time?" gate, and the git tag**.
|
||||
|
||||
**Division of labour (lib = mechanic, skill = judgment):** the tag lives HERE, not in `gitflow.sh`, because it is release-specific (version + message + human decision) while the lib's fan-out is generic. **Consequence (accepted):** a release cut by calling `gitflow finish` directly, bypassing this skill, fans out but is NOT tagged — `/release-candidate` is the canonical release path.
|
||||
|
||||
## When to use
|
||||
- `develop` is ahead of `main` and you want to publish a version.
|
||||
- "cut a release", "release candidate", "tag a version", "ship develop to main".
|
||||
|
||||
Not for: integrating a feature/bugfix → `gitflow finish` (via /ship-feature). A prod emergency fix off main → `hotfix` (different fan-out).
|
||||
|
||||
## Versioning
|
||||
- Tag scheme `vX.Y.Z` (semver, v-prefix — Gitea/GitHub release convention). **Continues** the `version.txt` + CHANGELOG lineage (the repo's authority); never restart at v1.0.0 (desyncs from a CHANGELOG already at 3.x+).
|
||||
- The number DERIVES from the change nature (semver), not the reverse: a migration-requiring/breaking change → MAJOR; new features → MINOR; fixes → PATCH. Personal repo ⇒ "breaking" = requires a migration of your own usage. Decide the number BEFORE running.
|
||||
|
||||
## Flow
|
||||
**REQUIRED:** `lib/gitflow.sh` (the release mechanic). Clean tree, identity set, `develop` ahead of `main`.
|
||||
|
||||
1. **Preconditions** — clean tree, git identity, `develop` ahead of `main` (else nothing to release).
|
||||
2. `gitflow start release <X.Y.Z>` — forks from develop, lands on `release/<X.Y.Z>`.
|
||||
3. **Prep** on the release branch:
|
||||
- `version.txt` → `<X.Y.Z>`.
|
||||
- CHANGELOG: `## [Unreleased]` → `## [<X.Y.Z>] — <date>`, re-open an empty `[Unreleased]`. A MAJOR must spell out its breaking change (`### Changed`/`### Removed`/BREAKING); review the doc-syncer draft for completeness.
|
||||
- Any release-candidate fixes; commit the prep on the branch.
|
||||
- **Run the test suite** (`lib/tests/*`, gitflow-test) — RC gate; never release red.
|
||||
4. **HUMAN GATE — WHEN to release.** STOP. Proceed only on an explicit human go (mirror /ship-feature's finish gate). Never fire on "tests pass".
|
||||
5. `gitflow finish` — lib fans out: merge `release/*`→`main`, merge-back→`develop`, delete the branch.
|
||||
6. **Tag** (the piece the lib lacks): `git tag -a v<X.Y.Z> main -m "release <X.Y.Z>"` — annotated, on main's release-merge commit, AFTER finish.
|
||||
7. **Push — GATED (ASK).** On explicit go only ([[LRN-069]]): `git push origin main develop && git push origin v<X.Y.Z>`.
|
||||
|
||||
## Common mistakes
|
||||
- Tagging before `gitflow finish` → tag wouldn't sit on main's merge commit. Tag AFTER, on main.
|
||||
- Auto-firing finish because tests pass → finish is a HUMAN gate.
|
||||
- Restarting the tag at v1.0.0 → desyncs from the CHANGELOG lineage. Continue it.
|
||||
- Pushing without the ASK gate → [[LRN-069]].
|
||||
|
||||
## Validation
|
||||
`RC_WORK=$(mktemp -d) RC_TAG=1 bash lib/tests/run-release-candidate.sh` → 5/5 (fan-out + tag on main). `RC_TAG=0` reds the tag assertion — proves the lib alone never tags (the gap this skill fills).
|
||||
Loading…
Reference in New Issue
Block a user