# 0.6.0 — drill record **IN PROGRESS — this record is being written as the rehearsal runs.** It is committed early so the candidate carries evidence at every head rather than appearing at the end; the release PR is a draft until it is complete, and no probe row below is written before its run exists. Run 2026-08-05 by `cndgrr` against the 0.6.0 release PR (Refs #249), candidate branch `build/249-release-0-6-0`. ## Scope ruling — a full rehearsal is owed, and doors-unchanged is refused This record's shape was measured, not chosen. `drills/README.md` allows the doors-unchanged shape only when all three of its conditions hold at the candidate head; the first one does not. The baseline is the last **rehearsed** tag, never the previous tag: `drills/0.4.1.md` and `drills/0.5.0.md` are both doors-unchanged records, so the anchor is **`0.4.0`**, whose record is a full disposable-repo rehearsal, whose release is published, and after which `main` was re-armed to `0.4.1-dev` (`84bb1a4`). Condition 3 holds. The release path is exactly the output of `.github/scripts/release-path.sh` at this head — `.github/workflows/release.yml`, `bin/`, `lib/version.sh`, `lib/decide.sh`, `lib/facts.sh`, `lib/changelog.sh`. Condition 2 holds. Condition 1 fails. Measured at this candidate: ```console $ git diff 0.4.0..HEAD -- $(.github/scripts/release-path.sh) .github/workflows/release.yml | 2 +- lib/changelog.sh | 83 ++++++++++++++++++++++++++++++++++++++--- ``` `release.yml`'s two lines are the `CEREMONY_SELF_REF` pin, which the condition exempts. **`lib/changelog.sh` is not exempt and is not empty**: it carries `72fa3e0` (the terminal issue-citation rule joining the fragment guard, #262) and `75a5b68` (one fragment, one diagnosis, #262). That file is on the release path because the merge door sources it to assemble and read the release section — this is a door byte, not a neighbouring library, and the last-rehearsed anchor exists precisely so an accumulated change like this forces a new rehearsal rather than chaining a third doors-unchanged assertion off the second. So this release owes the disposable-repo rehearsal, and this record is it. ## Where Disposable **private** repo `cndgrr/ceremony-drill-0.6.0`, created 2026-08-05T00:02:58Z. It carries the `docs/CONSUMERS.md` release caller verbatim (`version-source: file`) over a fragment-mode fixture armed at `0.6.0-dev`: a preamble-only `CHANGELOG.md`, `changelog.d/README.md` plus one fragment, and a non-blank `drills/0.6.0.md`. The `release` label was created there before the first ceremony PR, per the guide's prerequisite. _Disposal state is written below when the rehearsal ends, as this record's author observes it — never as an intention._ ## Candidate-ref deviation The pure consumer path cannot resolve this candidate's `CEREMONY_SELF_REF: "0.6.0"`: that tag is the one this release has not created yet. No `0.6.0` branch was created on `heavy-duty/ceremony`. The scratch caller instead pins `cndgrr/ceremony/.github/workflows/release.yml@drill/0.6.0`. That fork ref's parent is the canonical candidate SHA `fb8f8282a9e7b317d4d028f8e8da50501a882d14`, and its one additional commit (`775b4d1f6485ebdde924979ac2dce536643c6071`) rewrites all three `CEREMONY_SELF_REF` carriers — `release.yml`, `labels.yml`, `labels-sweep.yml` — to that same SHA. All runtime machinery in every probe below was therefore fetched from the 0.6.0 candidate tree. Commits pushed to the candidate after `fb8f828` are this record only; the release path (`.github/scripts/release-path.sh`) is byte-identical at the canonical SHA and at the final head. ## Probes One row per probe, written from its run. Runs are in `cndgrr/ceremony-drill-0.6.0`. | # | probe | run | result | |---|---|---|---| | 1 | merge-door ceremony | 30992108742 (attempt 1) | ✅ exactly one `0.6.0` release; tag equals the merge commit; main re-armed to `0.6.1-dev` | | 2 | mislabeled ordinary PR | 30991634654 | ✅ green NOTICE no-op; no tag, no release | | 3 | bare-version PR without `release` | 30991832001 | ✅ refused at decide; no tag, no release | | 4 | re-run completed ceremony | 30992108742 (attempt 2) | ✅ refused at the nothing-exists assert; the release count stayed one | | 5 | manual matching tag | — | — | | 6 | mismatched tag | — | — | ### Probe 1 — the merge-door ceremony PR #4 (`probe1-ceremony`) bumped `0.6.0-dev` to bare `0.6.0` and stamped `## 0.6.0 — 2026-08-05`, assembled from the three fixture fragments by the candidate's own `bin/changelog-assemble` and committed with the deletions in one commit. The `release` label was applied and confirmed before the merge. Facts and verdict: ```text VER: 0.6.0 BASE_VER: 0.6.0-dev RELEASED: LABELED: yes ceremony=yes ``` Observed after the run: - **Exactly one** release: `GET /releases` returned `0.6.0` alone, not a draft, not a pre-release, zero assets (no artifact hook in the fixture — the hook step skipped). - `GET /tags` returned `0.6.0` alone, pointing at `64d02539f4a20286afc08b9997f0f8a7d1dbfccd`, which is PR #4's merge commit — the tag names the tree that was reviewed. - The release body was byte-for-byte the assembled section's bullets: ```text - A second ordinary fragment, written by probe 2 of the 0.6.0 drill (#249). - An ordinary behavior change, landing under the release label (#249). - Fragment mode is exercised by the ceremony 0.6.0 drill (#249). ``` - Main re-armed itself: commit `2d0e19a` ("bump main to 0.6.1-dev — a dev install must not impersonate 0.6.0"), pushed by the job's own token. Main reads `0.6.1-dev` and `changelog.d/` holds only `README.md`. - **The anti-recursion property held.** Neither the tag create nor the bump push started a workflow run — the run list after the ceremony ends at 30992108742. That is what makes the merge door the release's only chance to publish, and it is the reason probe 4 below is the door's own guard rather than a second run's. ### Probe 4 — a re-run of the completed ceremony Re-running 30992108742 as attempt 2 re-decided `ceremony=yes` — the facts at that merge commit have not changed — and then died at the assert: ```text tag '0.6.0' already exists — this release already happened, or a manual tag won the race; refusing to re-release, creating nothing. ``` Tag, publish and bump all skipped. `GET /releases` still returned exactly one `0.6.0`. The refusal is loud (the job is red) and creates nothing, which is the required shape: the assert is what covers a manual tag racing the merge, not only an operator's stray re-run. ### Probe 3 — a bare-version PR without the `release` label PR #3 (`probe3-bare`) bumped `VERSION` to bare `0.6.0` and carried no label; the label list was read as empty before merging. The merge run refused at decide, row 5 of the table: ```text VER: 0.6.0 BASE_VER: 0.6.0-dev RELEASED: LABELED: no the version transitioned ('0.6.0-dev' -> '0.6.0') but no merged, release-labeled PR is behind this commit — a release is a labeled ceremony PR, not a bare push — creating nothing. ``` Notes, the assert, tag, hook, publish and bump all skipped; tags and releases were both still empty afterwards. The merge was then undone and main re-armed to `0.6.0-dev` before the ceremony probe ran (see Setup). ### Probe 2 — a mislabeled ordinary PR PR #2 (`probe2b-mislabeled`) added one changelog fragment and touched no version. The `release` label was applied through `POST /repos/{owner}/{repo}/issues/2/labels` and confirmed present before the merge. The merge run decided row 1 of the table and published nothing: ```text VER: 0.6.0-dev BASE_VER: 0.6.0-dev RELEASED: LABELED: NOTICE: the version '0.6.0-dev' is -dev and unchanged by this PR — release-flow work under the release label, not a ceremony. Nothing to publish. ceremony=no ``` `RELEASED` and `LABELED` are empty on purpose — the `-dev` rows never consult them, which is precisely why the label alone cannot ship anything. Notes, the nothing-exists assert, tag, artifact hook, publish and bump all skipped; `GET /tags` and `GET /releases` were both empty afterwards. An earlier merge (PR #1, run 30991571096) was intended as this probe but landed **unlabeled**: `gh pr edit --add-label` failed against this repo's projects-classic GraphQL surface, and the merge went ahead before the failure was read. That run is a green no-op too, but it is not evidence for this probe — an unlabeled ordinary merge proves less than a labeled one — so the probe was re-run as PR #2 with the label applied through the REST endpoint and verified before merging. Recorded here because the run exists in the repo's history and a reader will find it.