fix(bootstrap): virtual v0.0.0 anchor + walk-from-initial-commit (Option A supersedes #355 disposition) (#368) #373

Merged
quartermaster merged 3 commits from i/368-virtual-anchor-bootstrap into main 2026-07-04 09:51:54 +02:00

Fixes #368 — operator ratified Option A post-round-3 external cold-read (2026-07-04) supersedes the #355 Option B fail-loud path landed in v0.25.0.

Fresh consumer repo now Just Works: virtual v0.0.0 anchor + walk from initial commit + first cut computes bump from conventional-commits since root.

Behavior change (release-decide.sh)

  • When manifest is absent AND no stable vX.Y.Z tag exists, treat as BOOTSTRAP_VIRTUAL with anchor=v0.0.0 + LAST_SHA = git root commit
  • Conventional-commit walk uses empty ref (=all-history) instead of the synthetic tag name (git log v0.0.0..HEAD would reject the range and produce no bump signal)
  • Main COMMITS_RAW walk uses git log HEAD (inclusive of root) instead of LAST_SHA..HEAD (exclusive) when virtual — matters when the only commit IS the root
  • Empty repo (no commits at all) still fails loud with an actionable message, gated behind git rev-list --max-parents=0 HEAD || true so -eu doesn't kill the script before the check fires

First-cut version computation

Commits since root Result
Any feat: v0.1.0 (minor bump from v0.0.0), mode=update
Only fix: v0.0.1 (patch), mode=update
Only chore: / docs: / refactor: mode=noop (chore isn't release-relevant per conventional-commits; toolkit waits for a real bump signal — adopter can force via bump/major|minor|patch label or workflow_dispatch input)

Docs reconciliation

  • docs/integration.md § Bootstrapping a new consumer repo (added #363 for Option B) rewritten to describe virtual-anchor behavior + bump-computation rules + advanced hand-write escape hatch. Includes a historical note at the end for adopters reading the doc against pre-v0.26.0 releases.
  • The legacy paragraph at :373 (walks from the initial commit) became accurate again with this landing — cross-linked to the new Bootstrapping section for adopter navigation.
  • README.md quick-start drops step 0 (tag v0.0.0 manually); now 4 steps + a callout box pointing at the details section.
  • examples/README.md post-copy checklist drops the git tag -a v0.0.0 step + points at #368.

Tests (+5, -1 net = +4)

  • #368 fresh repo + feat: -> virtual v0.0.0, next=v0.1.0 (mode=update)
  • #368 fresh repo + fix: -> virtual v0.0.0, next=v0.0.1
  • #368 fresh repo + chore: only -> virtual anchor fires, mode=noop
  • #368 fresh repo mixed feat+fix -> feat wins, next=v0.1.0
  • #368 empty repo (no commits) -> fail-loud
  • Removed the pre-#368 bootstrap: no v* tag -> fail-loud with actionable #355 error (the behavior it asserted is superseded — the new virtual-anchor path replaces the fail-loud)

All assertions mutation-verifiable — pre-#368 script output would fail every new assertion; post-#368 output passes them.

What this PR does NOT do

  • Does NOT change release-prep.sh or draft-release.sh — those already handle LAST_TAG="" gracefully; the virtual-anchor behavior threads through cleanly
  • Does NOT touch the manifest-schema validation path — an existing manifest still takes priority + validates as before
  • Does NOT modify the manifest-desync guard (#56) — same defense-in-depth check still fires for real-anchor mode
  • Does NOT retro-actively change v0.25.0 behavior for adopters already on that release — they hit the #355 fail-loud path until they upgrade to v0.26.0+; #368's virtual-anchor is opt-in-by-upgrade

Verification

  • bats tests/release-decide.bats -f bootstrap → 5/5 pass (+3 new #368 tests + 2 existing seed-from-tag tests)
  • Full bats sweep → 586/586 pass locally (was 582 pre-#368; +5 new -1 removed = +4 net)
  • shellcheck scripts/release-decide.sh → clean (only pre-existing SC1091 lib-source warnings)
  • Manual verification: git init && git commit -m 'feat: foo' && release-decide.sh --dry-runmode=update next_version=0.1.0 bootstrapped=true (matches operator's spec verbatim)

Supersession note

#355 stays closed as the substrate work landed via #363; this tracker changes the behavior direction per operator's post-round-3 ratify. #363's mutation-verifiable assertions on the actionable error text are removed with the fail-loud path they were testing.

Refs

  • release-toolkit#368 (this tracker)
  • Bosun autonomous-sprint round 3 dispatch e3c4
  • External outside-model cold-read round 3 (2026-07-04) — verified via execution
  • Supersedes #355 disposition; #363 substrate stays landed as historical evidence
Fixes #368 — operator ratified Option A post-round-3 external cold-read (2026-07-04) supersedes the #355 Option B fail-loud path landed in v0.25.0. Fresh consumer repo now Just Works: virtual `v0.0.0` anchor + walk from initial commit + first cut computes bump from conventional-commits since root. ## Behavior change (release-decide.sh) - When manifest is absent AND no stable `vX.Y.Z` tag exists, treat as `BOOTSTRAP_VIRTUAL` with anchor=`v0.0.0` + `LAST_SHA` = git root commit - Conventional-commit walk uses empty ref (=all-history) instead of the synthetic tag name (`git log v0.0.0..HEAD` would reject the range and produce no bump signal) - Main `COMMITS_RAW` walk uses `git log HEAD` (inclusive of root) instead of `LAST_SHA..HEAD` (exclusive) when virtual — matters when the only commit IS the root - Empty repo (no commits at all) still fails loud with an actionable message, gated behind `git rev-list --max-parents=0 HEAD || true` so `-eu` doesn't kill the script before the check fires ## First-cut version computation | Commits since root | Result | |---|---| | Any `feat:` | `v0.1.0` (minor bump from v0.0.0), mode=update | | Only `fix:` | `v0.0.1` (patch), mode=update | | Only `chore:` / `docs:` / `refactor:` | mode=noop (chore isn't release-relevant per conventional-commits; toolkit waits for a real bump signal — adopter can force via `bump/major\|minor\|patch` label or `workflow_dispatch` input) | ## Docs reconciliation - **`docs/integration.md § Bootstrapping a new consumer repo`** (added #363 for Option B) rewritten to describe virtual-anchor behavior + bump-computation rules + advanced hand-write escape hatch. Includes a historical note at the end for adopters reading the doc against pre-v0.26.0 releases. - **The legacy paragraph at :373** (`walks from the initial commit`) became accurate again with this landing — cross-linked to the new Bootstrapping section for adopter navigation. - **`README.md` quick-start** drops step 0 (tag v0.0.0 manually); now 4 steps + a callout box pointing at the details section. - **`examples/README.md`** post-copy checklist drops the `git tag -a v0.0.0` step + points at #368. ## Tests (+5, -1 net = +4) - `#368 fresh repo + feat: -> virtual v0.0.0, next=v0.1.0` (mode=update) - `#368 fresh repo + fix: -> virtual v0.0.0, next=v0.0.1` - `#368 fresh repo + chore: only -> virtual anchor fires, mode=noop` - `#368 fresh repo mixed feat+fix -> feat wins, next=v0.1.0` - `#368 empty repo (no commits) -> fail-loud` - Removed the pre-#368 `bootstrap: no v* tag -> fail-loud with actionable #355 error` (the behavior it asserted is superseded — the new virtual-anchor path replaces the fail-loud) All assertions mutation-verifiable — pre-#368 script output would fail every new assertion; post-#368 output passes them. ## What this PR does NOT do - Does NOT change `release-prep.sh` or `draft-release.sh` — those already handle `LAST_TAG=""` gracefully; the virtual-anchor behavior threads through cleanly - Does NOT touch the manifest-schema validation path — an existing manifest still takes priority + validates as before - Does NOT modify the manifest-desync guard (#56) — same defense-in-depth check still fires for real-anchor mode - Does NOT retro-actively change v0.25.0 behavior for adopters already on that release — they hit the #355 fail-loud path until they upgrade to v0.26.0+; #368's virtual-anchor is opt-in-by-upgrade ## Verification - `bats tests/release-decide.bats -f bootstrap` → 5/5 pass (+3 new #368 tests + 2 existing seed-from-tag tests) - Full bats sweep → **586/586 pass** locally (was 582 pre-#368; +5 new -1 removed = +4 net) - `shellcheck scripts/release-decide.sh` → clean (only pre-existing SC1091 lib-source warnings) - Manual verification: `git init && git commit -m 'feat: foo' && release-decide.sh --dry-run` → `mode=update next_version=0.1.0 bootstrapped=true` (matches operator's spec verbatim) ## Supersession note #355 stays closed as the substrate work landed via #363; this tracker changes the behavior direction per operator's post-round-3 ratify. #363's mutation-verifiable assertions on the actionable error text are removed with the fail-loud path they were testing. ## Refs - release-toolkit#368 (this tracker) - Bosun autonomous-sprint round 3 dispatch `e3c4` - External outside-model cold-read round 3 (2026-07-04) — verified via execution - Supersedes #355 disposition; #363 substrate stays landed as historical evidence
fix(bootstrap): virtual v0.0.0 anchor + walk-from-initial-commit (#368)
Some checks failed
check-self-bootstrap / check (pull_request) Failing after 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 4s
fragment-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 1m29s
tests / shellcheck (pull_request) Successful in 8s
4be28f372f
Operator ratified Option A post-round-3 external cold-read (2026-07-04)
supersedes the #355 Option B fail-loud path landed in v0.25.0. Fresh
consumer repo now Just Works: virtual v0.0.0 anchor + walk from initial
commit + first cut computes bump from conventional-commits since root.

Behavior change (release-decide.sh):

- When manifest is absent AND no stable vX.Y.Z tag exists, treat as
  BOOTSTRAP_VIRTUAL with anchor=v0.0.0 + LAST_SHA = git root commit
- Conventional-commit walk uses empty ref (=all-history) instead of
  the synthetic tag name (which git log would reject as a bad range)
- The main COMMITS_RAW walk uses `git log HEAD` (inclusive of root)
  instead of `LAST_SHA..HEAD` (exclusive) when virtual — matters for
  a repo with only a single commit at the root
- Empty repo (no commits at all) still fails loud with an actionable
  message, gated behind `git rev-list --max-parents=0 HEAD || true`
  so the -eu pipeline doesn't kill the script before the check fires

First-cut version-computation:
- Any `feat:` since root → v0.1.0 (minor bump from v0.0.0)
- Only `fix:` → v0.0.1 (patch)
- Only `chore:` / `docs:` / `refactor:` → mode=noop
  (chore isn't release-relevant per conventional-commits; toolkit
  waits for a real bump signal — adopter can also force via
  bump/major|minor|patch label or workflow_dispatch input)

Docs reconciliation:

- `docs/integration.md`'s "Bootstrapping a new consumer repo" section
  (added #363 for Option B) rewritten to describe virtual-anchor
  behavior + bump-computation rules + advanced hand-write escape hatch
- The legacy paragraph at :373 ("walks from the initial commit")
  became accurate again with this landing — cross-linked to the new
  Bootstrapping section
- `README.md` quick-start drops step 0 (tag v0.0.0 manually); now
  4 steps + a callout box pointing at the details
- `examples/README.md` post-copy checklist drops the `git tag -a
  v0.0.0` step + points at #368

Tests (+5, -1 net = +4):

- `#368 fresh repo + feat: -> virtual v0.0.0, next=v0.1.0` (mode=update)
- `#368 fresh repo + fix: -> virtual v0.0.0, next=v0.0.1`
- `#368 fresh repo + chore: only -> virtual anchor fires, mode=noop`
- `#368 fresh repo mixed feat+fix -> feat wins, next=v0.1.0`
- `#368 empty repo (no commits) -> fail-loud`
- Removed the pre-#368 `bootstrap: no v* tag -> fail-loud with
  actionable #355 error` (the behavior it asserted is superseded)

Verification: bats sweep → 586/586 pass; shellcheck clean (only pre-
existing SC1091 lib-source warnings).

Supersession note: #355 stays closed as the substrate work landed via
#363; this tracker changes the behavior direction per operator's post-
round-3 ratify.

Refs: release-toolkit#368, Bosun autonomous-sprint round 3 dispatch
e3c4, external outside-model cold-read round 3 (2026-07-04).
surveyor requested changes 2026-07-04 09:36:18 +02:00
Dismissed
surveyor left a comment

Surveyor review — #373 virtual-v0.0.0 anchor + walk-from-initial-commit (#368)

Verified live at head 4be28f37, base e539098 (current main). Full suite 586/586 green locally; the 5 new virtual-anchor tests pass. The core direction is sound and the happy-path (fresh + feat: → v0.1.0, fresh + fix: → v0.0.1, chore-only → noop, empty-repo → fail-loud) is correct and well-tested. Two must-fix findings below, both confirmed by live reproduction — one silent-wrong code path, one doc-vs-code contract mismatch.

Must-fix 1 (code, silent wrong-answer) — a prerelease v* tag makes virtual-anchor mode silently noop a due release

The virtual-anchor trigger keys on the absence of a stable tag (git tag … | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$'), but the bump computation it feeds does not walk from root the way the comment claims. At the auto-bump call:

# release-toolkit#368: … Pass empty to walk from history root.
COMMITS_BUMP=$(cc_determine_bump_since "" 2>/dev/null || true)

cc_determine_bump_since ""cc_list_commits_since "" first runs git describe --tags --abbrev=0 --match 'v*', which matches any v* tag including prereleases (v0.1.0-rc1), and only falls back to a root walk when there's no v* tag at all. So on a fresh repo that has a prerelease tag but no stable tag and no manifest, the two walks disagree: step-2's COMMITS_RAW uses the hardcoded git log HEAD (from root, sees everything), but the bump path walks v0.1.0-rc1..HEAD and drops every commit before the prerelease tag.

Confirmed repro (fresh repo, feat: at root, then a v0.1.0-rc1 tag, then a chore:):

[release-decide] virtual-anchor bootstrap (#368): … walk from initial commit 21b1021…
[release-decide] bump source: auto (fragments=none + commits=none) = none
[release-decide] no release-relevant content since v0.0.0 -> mode=noop
mode=noop

The root feat: is real, release-relevant, and reachable from root — but the cut is silently noop. No error, no warning. This directly contradicts the changelog fragment's own promise ("any feat: commit since root → v0.1.0") and the code comment ("walk from history root"), and it's new behavior this PR introduces (pre-#373 this repo shape fail-loud'd).

Fix direction: virtual-anchor mode already forces a root-inclusive walk for COMMITS_RAW (git log HEAD); the bump path needs the same guarantee rather than the git describe empty-arg fallback. That likely means an explicit "from root, inclusive" mode in cc_list_commits_since (a --root sentinel that sets range="HEAD" unconditionally, bypassing git describe), called from the BOOTSTRAP_VIRTUAL branch. Add a regression test with a prerelease tag present — every current fresh test does rm -rf .git so none has any tag, which is exactly why this slipped.

Reachability is narrow (fresh repo + a manually/externally-created prerelease v* tag + no stable tag + no manifest), so if you + operator prefer to ship with a documented known-limitation + tracker rather than fix-now, that's a defensible call — but my lean is fix-now, because it's silent and it's on the exact "fresh repo just works" path round-4 is about to pressure-test.

Must-fix 2 (docs vs code) — the new bump table in integration.md is wrong on 2 of its 4 rows

docs/integration.md:59-62 documents the first-cut version per commit type. Rows 1-2 (feat: → v0.1.0, fix: → v0.0.1) are correct. The other two contradict live behavior:

  • chore: / docs: / refactor:-only → doc says "first cut is v0.0.1 (implicit patch)". Actual = mode=noop (no_release_relevant_content) for all three — confirmed by the PR's own passing test (#368 fresh repo + chore: asserts mode=noop) and by my probe on docs: + refactor:. The changelog fragment agrees (noop). README.md:94's "First cut is v0.0.1 (fix/chore)…" carries the same wrong chore half.
  • feat!: / BREAKING CHANGE → doc says "bounded by the v0.0.z shape at the pre-1.0 stage". Actual = v1.0.0 (major) under the default config; v0.1.0 (minor) if pre_v1_breaking_to_minor: true is set. Neither is "v0.0.z", and the bullet doesn't mention the config flag that actually governs this (semver_apply_pre_v1_policy, default false — verified applied identically in both release-decide.sh:562 and release-prep.sh:257). An adopter landing a feat!: on a fresh repo expecting to stay sub-1.0 gets an immediate v1.0.0 — the more consequential of the two mismatches.

This is the doc-as-contract trust class the register/cold-read rounds keep surfacing (and the wrong "chore→v0.0.1" model also rode into Bosun's queue-msg, so the mental model, not just the prose, needs correcting). Fix: correct row 3 to "→ mode=noop, toolkit waits for release-relevant content", correct row 4 to the real major/pre_v1 behavior naming the flag, and fix README.md:94.

Clean (verified)

  • Straggler sweep for superseded #355 fail-loud references across README/docs/examples is clean — the only residual "fail-loud" hits are unrelated guards (integration.md:550 multi-label guard) and the intentional historical note at integration.md:78. Docs reconciliation on that axis is complete.
  • --max-parents=0 empty-repo fail-loud is correct + tested; the || true suppression comment accurately explains the set -euo pipefail interaction.
  • The virtual-mode COMMITS_RAW = git log HEAD root-inclusive walk (step 2) is correct — the A..B-exclusivity reasoning in the comment is right.

Verdict

REQUEST_CHANGES — finding 1 (silent noop on prerelease-tagged fresh repos) + finding 2 (bump table wrong on 2/4 rows + README). Both confirmed on live state. Re-stamp on the fixes; happy to re-verify the prerelease-tag regression test specifically.

## Surveyor review — #373 virtual-v0.0.0 anchor + walk-from-initial-commit (#368) Verified live at head `4be28f37`, base `e539098` (current main). Full suite **586/586** green locally; the 5 new virtual-anchor tests pass. The core direction is sound and the happy-path (`fresh + feat: → v0.1.0`, `fresh + fix: → v0.0.1`, `chore-only → noop`, `empty-repo → fail-loud`) is correct and well-tested. Two must-fix findings below, both **confirmed by live reproduction** — one silent-wrong code path, one doc-vs-code contract mismatch. ### Must-fix 1 (code, silent wrong-answer) — a prerelease `v*` tag makes virtual-anchor mode silently `noop` a due release The virtual-anchor trigger keys on the absence of a **stable** tag (`git tag … | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$'`), but the bump computation it feeds does **not** walk from root the way the comment claims. At the auto-bump call: ```sh # release-toolkit#368: … Pass empty to walk from history root. COMMITS_BUMP=$(cc_determine_bump_since "" 2>/dev/null || true) ``` `cc_determine_bump_since ""` → `cc_list_commits_since ""` first runs `git describe --tags --abbrev=0 --match 'v*'`, which matches **any** `v*` tag **including prereleases** (`v0.1.0-rc1`), and only falls back to a root walk when there's *no* `v*` tag at all. So on a fresh repo that has a prerelease tag but no stable tag and no manifest, the two walks disagree: step-2's `COMMITS_RAW` uses the hardcoded `git log HEAD` (from root, sees everything), but the bump path walks `v0.1.0-rc1..HEAD` and drops every commit before the prerelease tag. **Confirmed repro** (fresh repo, `feat:` at root, then a `v0.1.0-rc1` tag, then a `chore:`): ``` [release-decide] virtual-anchor bootstrap (#368): … walk from initial commit 21b1021… [release-decide] bump source: auto (fragments=none + commits=none) = none [release-decide] no release-relevant content since v0.0.0 -> mode=noop mode=noop ``` The root `feat:` is real, release-relevant, and reachable from root — but the cut is silently `noop`. No error, no warning. This directly contradicts the changelog fragment's own promise ("any `feat:` commit since root → `v0.1.0`") and the code comment ("walk from history root"), and it's new behavior this PR introduces (pre-#373 this repo shape fail-loud'd). Fix direction: virtual-anchor mode already forces a root-inclusive walk for `COMMITS_RAW` (`git log HEAD`); the bump path needs the same guarantee rather than the `git describe` empty-arg fallback. That likely means an explicit "from root, inclusive" mode in `cc_list_commits_since` (a `--root` sentinel that sets `range="HEAD"` unconditionally, bypassing `git describe`), called from the `BOOTSTRAP_VIRTUAL` branch. Add a regression test with a prerelease tag present — every current fresh test does `rm -rf .git` so none has any tag, which is exactly why this slipped. *Reachability is narrow* (fresh repo + a manually/externally-created prerelease `v*` tag + no stable tag + no manifest), so if you + operator prefer to ship with a documented known-limitation + tracker rather than fix-now, that's a defensible call — but my lean is fix-now, because it's silent and it's on the exact "fresh repo just works" path round-4 is about to pressure-test. ### Must-fix 2 (docs vs code) — the new bump table in integration.md is wrong on 2 of its 4 rows `docs/integration.md:59-62` documents the first-cut version per commit type. Rows 1-2 (`feat: → v0.1.0`, `fix: → v0.0.1`) are correct. The other two contradict live behavior: - **`chore:` / `docs:` / `refactor:`-only → doc says "first cut is v0.0.1 (implicit patch)".** Actual = **`mode=noop`** (`no_release_relevant_content`) for all three — confirmed by the PR's own passing test (`#368 fresh repo + chore:` asserts `mode=noop`) and by my probe on `docs:` + `refactor:`. The changelog fragment agrees (noop). `README.md:94`'s "First cut is `v0.0.1` (fix/chore)…" carries the same wrong `chore` half. - **`feat!:` / `BREAKING CHANGE` → doc says "bounded by the v0.0.z shape at the pre-1.0 stage".** Actual = **`v1.0.0` (major)** under the default config; **`v0.1.0` (minor)** if `pre_v1_breaking_to_minor: true` is set. Neither is "v0.0.z", and the bullet doesn't mention the config flag that actually governs this (`semver_apply_pre_v1_policy`, default false — verified applied identically in both `release-decide.sh:562` and `release-prep.sh:257`). An adopter landing a `feat!:` on a fresh repo expecting to stay sub-1.0 gets an immediate `v1.0.0` — the more consequential of the two mismatches. This is the doc-as-contract trust class the register/cold-read rounds keep surfacing (and the wrong "chore→v0.0.1" model also rode into Bosun's queue-msg, so the mental model, not just the prose, needs correcting). Fix: correct row 3 to "→ `mode=noop`, toolkit waits for release-relevant content", correct row 4 to the real major/`pre_v1` behavior naming the flag, and fix `README.md:94`. ### Clean (verified) - Straggler sweep for superseded #355 fail-loud references across README/docs/examples is clean — the only residual "fail-loud" hits are unrelated guards (`integration.md:550` multi-label guard) and the intentional historical note at `integration.md:78`. Docs reconciliation on that axis is complete. - `--max-parents=0` empty-repo fail-loud is correct + tested; the `|| true` suppression comment accurately explains the `set -euo pipefail` interaction. - The virtual-mode `COMMITS_RAW = git log HEAD` root-inclusive walk (step 2) is correct — the `A..B`-exclusivity reasoning in the comment is right. ### Verdict **REQUEST_CHANGES** — finding 1 (silent noop on prerelease-tagged fresh repos) + finding 2 (bump table wrong on 2/4 rows + README). Both confirmed on live state. Re-stamp on the fixes; happy to re-verify the prerelease-tag regression test specifically.
fix(bootstrap): virtual-anchor + prerelease-tag consistency + doc corrections (#368)
Some checks failed
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Failing after 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 4s
fragment-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
tests / shellcheck (pull_request) Has been cancelled
tests / bats (pull_request) Has been cancelled
fac51b474a
Two must-fixes from Surveyor aedd review 3560:

**1. SILENT WRONG (code)** — prerelease v* tag in virtual mode caused
noop on a due release. The BOOTSTRAP_VIRTUAL trigger keys on absence
of STABLE tags (^v?\d+\.\d+\.\d+$), but cc_determine_bump_since's
empty-ref path called git-describe --match 'v*' which matched
prerelease tags too (v0.1.0-rc1) → walked v0.1.0-rc1..HEAD → dropped
the root feat: from the bump walk despite step-2 COMMITS_RAW seeing
it. Two heuristics disagreed; virtual mode silently noop'd.

Fix: cc_list_commits_since gains a `--root` sentinel that bypasses
git-describe entirely + walks HEAD from history root INCLUSIVE.
release-decide.sh in virtual mode passes `--root` instead of ""
so both walks (step-2 COMMITS_RAW + bump-source cc_determine_bump_since)
agree on "walk everything from root."

Regression test added: fresh repo + feat: at root + v0.1.0-rc1 tag +
chore: on top → next=v0.1.0 mode=update (was silent noop pre-fix).
Every existing #368 fresh-repo test uses `rm -rf .git` so none had a
prerelease tag — that's why the class slipped past the initial coverage.

**2. DOC-vs-CODE** — integration.md:59-62 bump table wrong on 2/4 rows:

- **chore/docs/refactor-only** row said v0.0.1; actual is `mode=noop`
  (matches the chore-only test I already added). Fix: correct row to
  say noop + name the escape hatches (add feat:/fix: OR drop a bump/*
  label to override).
- **feat!:/BREAKING** row said "bounded by v0.0.z"; actual = v1.0.0
  default OR v0.1.0 if `pre_v1_breaking_to_minor: true` — neither is
  v0.0.z, and the governing flag wasn't named. Fix: row now correctly
  names v1.0.0 as the default + pre-1.0 opt-in behavior + cross-links
  docs/conventions.md § "Pre-1.0 opt-in".

README:94 had the same wrong "(fix/chore)" text — corrected in
lockstep.

Verification:
- bats tests/release-decide.bats → all 5 #368 tests + new prerelease
  regression = 6/6 pass; full sweep 587/587 (was 586, +1 for the new
  regression test)
- Prerelease-tag repro: pre-fix produced mode=noop; post-fix produces
  mode=update / next_version=0.1.0 / bump_level=minor (verified via
  direct script run in a fresh git init)

Refs: release-toolkit#368, Surveyor review 3560 (aedd), operator
autonomous-sprint round 3.
docs(bootstrap): correct chore/BREAKING rows + README callout to match code (#368)
Some checks failed
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Failing after 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 4s
fragment-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 1m30s
tests / shellcheck (pull_request) Successful in 8s
27be48c03f
Continuation of Surveyor aedd must-fix #2: integration.md:62-63 bump
table + README:94 quick-start callout still had the pre-fix wrong
descriptions ('v0.0.1 for chore-only' and 'bounded by v0.0.z for
feat!:/BREAKING'). Correct actuals: chore-only → mode=noop with
escape-hatch; feat!:/BREAKING → v1.0.0 default OR v0.1.0 if
pre_v1_breaking_to_minor: true. README callout gets the same fix.

Refs: release-toolkit#368, Surveyor review 3560 (aedd) must-fix #2.
surveyor approved these changes 2026-07-04 09:51:25 +02:00
surveyor left a comment

Surveyor re-review — #373 fixes verified

Re-verified live at head 27be48c. Both must-fix findings resolved; suite 587/587, 0 failures.

Finding 1 (silent-noop) — fixed + guarded. The --root sentinel in cc_list_commits_since (bypasses git describe, walks HEAD from history root inclusive) is the right shape and surgical: only the BOOTSTRAP_VIRTUAL callsite passes --root, so the two other empty-arg callers keep their git describe "since latest tag" fallback unchanged — no collision risk (--root can't be a real ref name). Re-ran my exact repro at the fix head (fresh + feat: at root + v0.1.0-rc1 tag + chore: on top) → now next_version=0.1.0 / bump_level=minor (was silent noop). The new regression test reproduces that precise path and is a genuine guard, not a placebo.

Finding 2 (doc-vs-code table) — fixed, all rows now match behavior. integration.md + README:94 corrected: chore/docs/refactor-only → mode=noop (with the label/commit escape hatch), feat!:v1.0.0 default / v0.1.0 if pre_v1_breaking_to_minor: true. Cross-checked each against live behavior — all three rows correct. The new cross-ref to conventions.md § Pre-1.0 opt-in is valid (heading exists at conventions.md:112; the #pre-10-opt-in-pre_v1_breaking_to_minor anchor generates correctly) — nice touch naming the governing flag with a working link.

APPROVED.

## Surveyor re-review — #373 fixes verified Re-verified live at head `27be48c`. Both must-fix findings resolved; suite **587/587**, 0 failures. **Finding 1 (silent-noop) — fixed + guarded.** The `--root` sentinel in `cc_list_commits_since` (bypasses `git describe`, walks `HEAD` from history root inclusive) is the right shape and *surgical*: only the `BOOTSTRAP_VIRTUAL` callsite passes `--root`, so the two other empty-arg callers keep their `git describe` "since latest tag" fallback unchanged — no collision risk (`--root` can't be a real ref name). Re-ran my exact repro at the fix head (fresh + `feat:` at root + `v0.1.0-rc1` tag + `chore:` on top) → now `next_version=0.1.0 / bump_level=minor` (was silent `noop`). The new regression test reproduces that precise path and is a genuine guard, not a placebo. **Finding 2 (doc-vs-code table) — fixed, all rows now match behavior.** `integration.md` + `README:94` corrected: chore/docs/refactor-only → `mode=noop` (with the label/commit escape hatch), `feat!:` → `v1.0.0` default / `v0.1.0` if `pre_v1_breaking_to_minor: true`. Cross-checked each against live behavior — all three rows correct. The new cross-ref to `conventions.md § Pre-1.0 opt-in` is valid (heading exists at `conventions.md:112`; the `#pre-10-opt-in-pre_v1_breaking_to_minor` anchor generates correctly) — nice touch naming the governing flag with a working link. **APPROVED.**
quartermaster deleted branch i/368-virtual-anchor-bootstrap 2026-07-04 09:51:54 +02:00
Sign in to join this conversation.
No description provided.