ceremony/drills/0.2.0.md
claude-bot-andresmgsl c0df9f436d release: record the 0.2.0 drill — six probes on a fragment-shaped scratch consumer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:37:11 +00:00

6.7 KiB

0.2.0 — drill record

Run 2026-07-24, by claude-bot-andresmgsl, against the release candidate heavy-duty/ceremony@682b9cb8929aa8c50a3101b64ca57fee7b09fef1 (PR #129's head at drill time — the ceremony PR adds only VERSION, the assembled section with its fragment deletions, the self-ref stamps, and this record; no machinery differs between the drilled tree and the released one).

Where: scratch private repo claude-bot-andresmgsl/ceremony-drill-0.2.0, carrying the docs/CONSUMERS.md caller stubs (modulo the pin deviation below) and a fixture consumer tree in the fragment shape this release exists to ship: VERSION 0.5.0-dev, CHANGELOG.md preamble plus a shipped ## 0.4.0 section, changelog.d/ with its README.md marker and two fragments, drills/, and the five guard actions in CI at the drill ref. Archived after this record was written; deletion needs the delete_repo scope this box's token deliberately lacks, so the operator owns the final delete — the repo is the evidence's scaffolding, this file is the evidence. Run ids below are kept for the record's own audit trail.

The deviation: the self-drill deadlock recurs past the first release

The spec (#118) expected the 0.1.0 record's fork deviation to be moot — "0.1.0 exists, so the pinned path runs straight." It does not, for ceremony's own drill: the candidate tree pins CEREMONY_SELF_REF: "0.2.0" — the very stamp this PR makes — and the consumer path checks out heavy-duty/ceremony at exactly that ref, which cannot exist before the merge. What 0.1.0 existing does make moot is the consumer-side deadlock: a governed repo drilling its own release pins ceremony at a real tag and runs straight. Raised on #118 before drilling; proceeded per the deviation triage already ruled for #11, unchanged in shape: no 0.2.0-named ref on the canonical repo; the scratch callers pin the fork ref claude-bot-andresmgsl/ceremony@drill/0.2.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.2.0""682b9cb8929aa8c50a3101b64ca57fee7b09fef1". Every .ceremony-src byte the doors executed still came from heavy-duty/ceremony at the candidate SHA (reachable there via PR #129's pull ref); the fork supplied orchestration YAML differing by the one line the ceremony stamps each release anyway. Drilling against 0.1.0 instead would not have been a drill of the candidate: lib/changelog.sh, which both doors source for notes extraction, is +214 lines since 0.1.0 (fragment mode; #98).

The probes

# probe run result
a merge-door ceremony, fragment shape 30089779736 (attempt 1) exactly one release
b mislabeled ordinary PR 30089842547 green NOTICE no-op
c bare-version PR, no label 30089908740 refused, red, created nothing
d re-run of the completed ceremony 30089779736 (attempt 2) refused, red, created nothing
e tag door, manual matching tag 30090053523 published, no bump
f tag door, mismatched tag 30090082676 refused, red, created nothing

(a) The ceremony, in fragment shape. PR release: 0.5.0 — hand-set release label, VERSION 0.5.0-dev0.5.0, and the stamp made by the candidate's own bin/changelog-assemble 0.5.0: two fragments folded into ## 0.5.0 — 2026-07-24 (newest issue first), deleted in the same commit, changelog.d/ left holding only its README.md marker — no re-arm edit, armed by existing. The five guard actions at the drill ref — changelog-armed, changelog-monotonic, changelog-assembled, drill-recorded, runner-isolated — all green on that PR (run 30089703224): the assembled guard replayed the assembly from the merge base and matched it byte-for-byte, live, on the mechanism this release ships. Merged. The one run: facts ver='0.5.0' base_ver='0.5.0-dev' labeled='yes'ceremony=yes → tag 0.5.0 on the merge commit (9b9e58e), release 0.5.0 published with the assembled section — verbatim, both bullets — as its body, and main re-armed to 0.5.1-dev by the run itself. The GITHUB_TOKEN tag create and bump push fired no second run (anti-recursion held).

(b) Mislabeled ordinary PR. Docs-only PR wearing the release label. Green: NOTICE: the version '0.5.1-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 edits — 0.5.1-dev0.5.1, a section assembled from a fragment — label deliberately absent. Red at decide: the version transitioned ('0.5.1-dev' -> '0.5.1') 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, and the revert push itself ran green as a no-op (run 30090017774), as it should.

(d) Re-run of the completed ceremony. Re-ran (a)'s run. Red at the nothing-may-exist assert: tag '0.5.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.6.0 and its section assembled from a fragment; manual 0.6.0 tag pushed at its head (never merged to main). The tag door published release 0.6.0 with that section as its body; the merge door correctly skipped. Main untouched: VERSION stayed 0.5.1-dev — the fallback does not rewrite main.

(f) Mismatched tag. Tag 9.9.9 pushed at main (tree version 0.5.1-dev). Red: tag '9.9.9' does not match the tree's version '0.5.1-dev' — creating nothing. No release, and the operator-facing remediation text printed as designed.

Failures

None among the probes; every refusal path was checked for droppings — the only releases and workflow-created tags that exist are (a)'s and (e)'s.

One finding outside the probe set, written down plainly: the fixture's initial push — the very first commit of the scratch repo, caller included — ran the merge door red (run 30089682128): lib/facts.sh resolves the base as MERGE_SHA^1 when event.before is all-zeros, and a repo's first commit has no parent, so git rev-parse fails loudly (exit 128) before deciding. Nothing was created, and every subsequent push behaved; but a greenfield consumer bootstrapping caller-first will meet one red run on its first push, which the doctrine's "every legitimate non-ceremony is a green NOTICE no-op" does not intend. facts.sh is unchanged since 0.1.0 — not a candidate regression, not a blocker for this release; escalated as a discussion, not fixed here.