release: 0.1.0 #49

Merged
claude-bot-andresmgsl merged 2 commits from build/11-ceremony into main 2026-07-23 00:12:15 +00:00
4 changed files with 117 additions and 1 deletions

View file

@ -6,6 +6,8 @@ so entries say what changed, cite the issue, and stop.
## Unreleased
## 0.1.0 — 2026-07-22
- `lib/version.sh` — one version abstraction, `file` and `package-json` backends (#3).
- `lib/changelog.sh` + `bin/changelog-section` — the one canonical changelog-section extractor (#4).
- `actions/changelog-armed` — the version-keyed arming guard (#5).

View file

@ -1 +1 @@
0.1.0-dev
0.1.0

103
drills/0.1.0.md Normal file
View file

@ -0,0 +1,103 @@
# 0.1.0 — drill record
Run 2026-07-22, by `claude-bot-andresmgsl`, against the release candidate
`heavy-duty/ceremony@dbf857a94a7c4b068962efbccc996b41c5e4ab89` (main at the
time — the ceremony PR adds only `VERSION`, the changelog stamp, this record,
and a doctrine caveat; no machinery differs between the drilled tree and the
released one).
Where: scratch private repo `claude-bot-andresmgsl/ceremony-drill-0.1.0`,
carrying the `docs/CONSUMERS.md` caller stubs verbatim (modulo the pin
deviation below) and a fixture consumer tree in the armed state (`VERSION`
`0.1.0-dev`, `## Unreleased` on top, `drills/`). Deleted after this record
was written, per `drills/README.md`: the repo is the evidence's scaffolding,
this file is the evidence. Run ids below are kept for the record's own
audit trail; they died with the repo.
## The deviation: the first release cannot take the pure consumer path
`CEREMONY_SELF_REF` is `"0.1.0"` — the ref this drill exists to rehearse
creating. Demonstrated live before working around it: the release caller
pinned `@0.1.0` per the stubs died at workflow resolution before any job
started (run 29966400928) — the deadlock #44's bypass solves for ceremony
itself, hit from the consumer side, where no bypass can exist.
Ruled on #11 by triage: **no `0.1.0` branch on `heavy-duty/ceremony`** — a
drill must not leave a hazard in the canonical ref namespace (a branch
shadowing the tag every consumer pins) to prove the namespace works. The
drill instead pinned the scratch callers to the fork ref
`claude-bot-andresmgsl/ceremony@drill/0.1.0`: the candidate tree with
exactly one line changed per pin carrier (`release.yml` and `labels.yml`,
both — `self-ref-check.sh` requires the carriers to agree),
`CEREMONY_SELF_REF: "0.1.0"``"dbf857a94a7c4b068962efbccc996b41c5e4ab89"`.
Every `.ceremony-src` byte the doors executed — `lib/`, `actions/`, decide —
still came from `heavy-duty/ceremony` at the candidate SHA; the fork
supplied orchestration YAML differing by the one line the ceremony stamps
each release anyway. From `0.2.0` on, a real tag exists behind every
candidate and the pure pinned path drills as written.
## The probes
| # | probe | run | result |
|---|---|---|---|
| a | merge-door ceremony | 29966647167 (attempt 1) | ✅ exactly one release |
| b | mislabeled ordinary PR | 29966530098 | ✅ green NOTICE no-op |
| c | bare-version PR, no label | 29966562846 | ✅ refused, red, created nothing |
| d | re-run of the completed ceremony | 29966647167 (attempt 2) | ✅ refused, red, created nothing |
| e | tag door, manual matching tag | 29966718335 | ✅ published, no bump |
| f | tag door, mismatched tag | 29966744675 | ✅ refused, red, created nothing |
| g | labels bootstrap from nothing | 29966488038 | ✅ taxonomy created |
**(a) The ceremony.** PR `release: 0.1.0` — hand-set `release` label,
`VERSION` `0.1.0-dev``0.1.0`, changelog stamped `## 0.1.0 — 2026-07-22`
with an empty `## Unreleased` re-armed above it. Merged. The one run:
facts `ver='0.1.0' base_ver='0.1.0-dev' labeled='yes'``ceremony=yes`
tag `0.1.0` created on the merge commit (verified equal to the PR's merge
SHA), release `0.1.0` published with the fixture changelog section —
verbatim — as its body, and main re-armed to `0.1.1-dev` by the run itself.
The GITHUB_TOKEN tag create and bump push fired **no** second run
(anti-recursion held: the run list gained nothing after the merge run).
**(b) Mislabeled ordinary PR.** README-only PR wearing the `release` label.
Green: `NOTICE: the version '0.1.0-dev' is -dev and unchanged by this PR —
release-flow work under the release label, not a ceremony. Nothing to
publish.` No tag, no release.
**(c) Bare-version PR without the label.** The ceremony's two edits, label
deliberately absent. Red at decide: `the version transitioned ('0.1.0-dev'
-> '0.1.0') but no merged, release-labeled PR is behind this commit — a
release is a labeled ceremony PR, not a bare push — creating nothing.` No
tag, no release; main reverted to the armed state afterwards (the revert
push itself ran green as a no-op, as it should).
**(d) Re-run of the completed ceremony.** Re-ran (a)'s run. Red at the
nothing-may-exist assert: `tag '0.1.0' already exists — this release
already happened, or a manual tag won the race; refusing to re-release,
creating nothing.` Release count still exactly one.
**(e) Tag door.** Branch with `VERSION` `0.2.0` and its stamped section;
manual `0.2.0` tag pushed at its head (never merged to main). The tag door
published release `0.2.0` with that section as its body; the merge door
correctly skipped. Main untouched: `VERSION` stayed `0.1.1-dev` — the
fallback does not rewrite main.
**(f) Mismatched tag.** Tag `9.9.9` pushed at main (tree version
`0.1.1-dev`). Red: `tag '9.9.9' does not match the tree's version
'0.1.1-dev' — creating nothing.` No release, and the operator-facing
remediation text printed as designed.
**(g) Labels bootstrap from nothing** (moved into the drill by triage on
#11 — ceremony's own taxonomy already exists, so the scratch repo is the
only honest place left to exercise the path every fresh consumer hits
first). `workflow_dispatch` of the scratch labels caller: the reconcile job
took the consumer path (`.ceremony-src` fetched at the candidate SHA),
`bootstrap=yes`, and created the full taxonomy — state, blocker,
work-queue, `release` — plus the consumer-owned `scope:fixture` row from
`labels.conf`, on a repo that had only GitHub's defaults. Subsequent
PR-event runs reconciled without re-upserting, as documented.
## Failures
None. Six probes plus (g) behaved exactly as specified, and every refusal
path was checked for droppings — no tag and no release exists that a
refusal created.

View file

@ -15,6 +15,17 @@ drill is where they run live *before* a version rests on them.
2. Install the docs/CONSUMERS.md caller stubs, pinned to the release
candidate ref. A branch ref works: refs are static identifiers — the
family's own drill doctrine.
**Except for the first release** (learned drilling 0.1.0, #11): the
stubs' consumer path fetches ceremony at `CEREMONY_SELF_REF` — the very
ref the first drill exists to rehearse creating — so the pure pinned
path cannot run before some `X.Y.Z` ref exists, and a branch named like
the tag must NOT be created on the canonical repo to paper over it (it
would shadow the tag for every consumer until someone remembers to
delete it). The first drill instead pins the callers to a fork ref
carrying the candidate tree with `CEREMONY_SELF_REF` rewritten to the
candidate SHA in every pin carrier, and records that one-line deviation
in its record. From the second release on, this paragraph is moot.
3. Give it a fixture `VERSION` / `CHANGELOG.md` / `drills/` in the armed
state (`X.Y.Z-dev`, `## Unreleased` on top).
4. Exercise both doors, one probe at a time: