# 0.4.1 — drill record Run 2026-08-04 by `cluade-reviewer-andresmgsl` against release PR !190 head `9a229ee643291c27dbd404fa97e4dcc7e6ed3b17`. **This drill FAILED. Both doors of the release workflow are inoperable on `forgejo.heavyduty.builders`.** The record is written down as the guard requires, and the failure is the finding: 0.4.1 cannot publish from this instance until `lib/facts.sh` and the publish steps are ported off `gh`. Where: disposable private repo `heavy-duty/ceremony-drill-0.4.1`, armed at `0.4.1-dev` with the `docs/CONSUMERS.md` release caller, a fragment-mode fixture (`changelog.d/README.md` + one fragment), and a non-blank drill record. Archived at the end; the operator's delete is pending, and per `drills/README.md` cleanup gates nothing. ## Deviations * **Candidate ref.** The pure consumer path cannot resolve `CEREMONY_SELF_REF: "0.4.1"` before the candidate creates that tag, and `release.yml`'s self-checkout is hardcoded to `heavy-duty/ceremony`. No `0.4.1` branch was created there. The caller used `cluade-reviewer-andresmgsl/ceremony@drill-0.4.1` (`f148255`), whose parent is `9a229ee` and whose only extra commit rewrites both `CEREMONY_SELF_REF` carriers to that same canonical candidate SHA. * **Log access.** The drill repo was flipped public for ~8 minutes to read job logs — Forgejo's web log route (`/{owner}/{repo}/actions/runs/{run}/ jobs/{job}/logs`) 404s for a token-authenticated private repo, and the `/api/v1` log routes 404 outright. Restored to private, then archived. Recorded because it is a real deviation from "scratch **private** repo". ## Probes | # | probe | run | result | |---|---|---|---| | 1 | merge-door ceremony | 12 (and 7, at `0.4.1`) | ❌ **FAILED** — `decide` refused; no tag, no release, main left bare | | 2 | `-dev` push is a green NOTICE no-op | 9, and the fixture push | ✅ green no-op, nothing created | | 3 | bare version without the `release` label refuses | — | ⚠️ not run as an independent probe; probe 1 refused through exactly this path, because the label fact read `no` | | 4 | re-run of a completed ceremony refuses | — | ⚠️ **not reachable** — no ceremony ever completed to re-run | | 5 | tag-door release from a matching tag | 14 | ❌ **FAILED** — `gh: command not found` at *publish the release*; no release | | 6 | mismatched tag refuses | 16 | ✅ `tag '9.9.9' does not match the tree's version '0.4.2' — creating nothing` | Release count in the scratch repo at the end: **0**. Every refusal path created nothing, which is the one property the drill demands of a refusal. ## Finding 1 — the merge door cannot read its own label (`lib/facts.sh`) Probe 1, reproduced twice (`0.4.1`, then `0.4.2` after re-arming): ``` facts: ver=0.4.2 base_ver=0.4.2-dev released= labeled=no decide: the version transitioned ('0.4.2-dev' -> '0.4.2') but no merged, release-labeled PR is behind this commit — a release is a labeled ceremony PR, not a bare push — creating nothing. ``` The PR *was* labeled `release` and *was* merged. `lib/facts.sh` derives that fact with `gh api "repos/$GITHUB_REPOSITORY/commits/$MERGE_SHA/pulls"`, and the Forgejo runner image ships no `gh`. Its own comment says *"A failed API call reads as 'no label', which row 5 refuses: fail-closed."* — so the guard behaved exactly as designed on a fact that was wrong. `#188` ported the reconcilers and `labels-scope` onto `lib/forge.sh`; `lib/facts.sh` was outside that scope and is still gh-only. ## Finding 2 — the tag door cannot publish (`gh release create`) Probe 5: the tag matched, the version assert passed, and the job then died at *publish the release* with `gh: command not found` (exitcode 127). The tag door has no decide and no label check, so it clears every gate the merge door fails — and then falls at the same missing binary. ## Consequence Merging !190 will run this identical path on `heavy-duty/ceremony` and refuse, creating nothing. **There is no door through which 0.4.1 can publish on this instance today.** The fix is the same shape as #188's, one layer out: `lib/facts.sh` and `release.yml`'s `gh release view` / `gh release create` call sites onto `forge_*`. That is new work, not a re-review of !190.