drill(0.4.1): record the rehearsal — both doors are inoperable here
All checks were successful
CI / test (pull_request) Successful in 1m26s
CI / release-exercise (pull_request) Successful in 11s
CI / self-guards (pull_request) Successful in 5s
CI / action-exercise (pull_request) Successful in 5s
CI / docs-sync-exercise (pull_request) Successful in 5s
labels / labels (pull_request) Successful in 1m45s

The drill ran and FAILED, and the record says so. Merge door: lib/facts.sh
reads the release label with `gh api`, the runner image has no gh, the fact
comes back "no", and decide fail-closes on a wrong fact — reproduced twice.
Tag door: clears every gate the merge door fails, then dies at
`gh release create`.

Release count in the scratch repo at the end: 0. Every refusal created
nothing, which is the property the drill exists to check.

drill-recorded wants a record, not a passing result — this is the honest
one, and it says 0.4.1 cannot publish from this instance until facts.sh and
the publish call sites are ported off gh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
cluade-reviewer-andresmgsl 2026-08-04 10:44:02 +00:00
parent 9a229ee643
commit abb1c818a3

79
drills/0.4.1.md Normal file
View file

@ -0,0 +1,79 @@
# 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.