Encoding the release, part 7 of 8.
Part 3 ended with a promissory note: our nulls fake the shapes of GitHub's answers but
never its behaviour, so the real side of the gh wrapper would be proven by a real
release rather than a faked GitHub. This is the afternoon the bill arrived. It cost two
crashes and one near-miss, and it was the cheapest education we have ever bought.
Crash one: the race we had never met. The second run of ekohacks release 0.4.1
sailed through preflight, branched, bumped, pushed, opened PR #198 — and died:
Error: gh pr checks 198 failed:
no checks reported on the 'release/v0.4.1' branch
In the seconds between a PR opening and CI registering its checks, gh pr checks exits
non-zero with "no checks reported". Our code treated any such failure as fatal. No test
caught this because every test ran against our null — and we would never have thought to
fake an error message we did not know existed. A hand-written fake GitHub would have
encoded exactly the same ignorance and then certified it. Testing against a fake you
wrote yourself proves only that you agree with yourself; reality is the only oracle for
the boundary. The fix: "no checks reported" parses as an empty round, not an error.
The near-miss inside the crash. Fixing it exposed something worse. Had checks()
quietly returned an empty list there, the cut policy considered zero checks "all
concluded" — and would have merged the release PR before CI ever started. The
crash, embarrassing as it was, had been standing guard over an insta-merge. Both fixes
landed red-first: cut now waits while no checks are reported, pinned by a test that
walks empty → green → merge.
Crash two: the human did a human thing. With PR #198 finished by hand, the operator did the natural thing — ran the whole command again. It crashed again:
fatal: a branch named 'release/v0.4.1' already exists
Why? The rail has no memory. The first attempt's local branch was still sitting there; the re-run started from the beginning, tried to create it again, and git — correctly — refused to overwrite the record of the earlier attempt. Preflight could not warn anyone, because every one of its checks was genuinely true: it verifies "is this a good place to start a release?", and has no concept of "this release is already half-done". Nothing about the release was broken. The stop protected the state. It just said so in the worst possible language: a stack trace.
That is the deepest lesson of the three. Our stories had promised that every stop condition "stops the rail with a named reason", which makes a raw stack trace not a cosmetic flaw but a specification violation. People re-run failed commands — twice, in our sample of one afternoon — so the failure mode of a stopped rail is not an edge case, it is the primary interface of a bad day. Cut now answers in its own voice:
stopped: branch release/v0.4.1 already exists from an earlier cut
We kept the no-resume design. Clean named stops plus standalone stages-as-recovery is still a better bargain than resume logic that must correctly diagnose every partial state. But we no longer believe the design is done when the happy path is; it is done when every way it stops has a name.
Three lessons, one afternoon, all found within minutes of touching reality — precisely because we had refused to fake it.
Next: The Release Is the Acceptance Test, which is still being written.


