2026-09-02. Four hours into Update 54, six defects, and all of them the same shape.
Four words, because the rest of this leans on them.
The compiler allocates memory in two ways. A deck is scratch space a phase
takes, scribbles in, and hands back whole when it finishes — cheap, because
nothing is freed individually. The bivy is ordinary allocation that is
never handed back. An extent is the stretch of code during which
allocations go to the deck rather than the bivy; the compiler marks one by
wrapping a call in deck-record.
And a harness is our stand-in for the compiler's own driver — the ladder
compiles small programs by calling the compiler's phases directly, rather than
by running opening.codex, because we need the intermediate values that the
real driver throws away.
That's the whole glossary. The story is not really about memory.
Six things broke today. Here they are with the vocabulary stripped out:
Only one of those is a bug in the ordinary sense. The rest are absences.
Every one of them shares a property, and it took me most of the day to see it as one thing rather than six.
When the precondition was missing, the mechanism picked a plausible default instead of refusing.
In each case the honest behaviour was available and cheap: say you couldn't do the thing. In each case the code instead produced the most reasonable output it could and said nothing. And "the most reasonable output it could" is exactly what makes these expensive — a refusal costs you a minute, a plausible wrong answer costs you a morning.
Every Update moves things and we pay a tax. This one was worse, and I think the reason is specific rather than bad luck.
Update 54 changed the memory discipline itself — the one subsystem our harness reimplements rather than calls. The lexer started reclaiming memory after every token. The type-checker gained a hand-written pair of enter/exit-the-extent calls. Two phase functions gained arguments. All of that is invisible to anyone who just runs the compiler, and all of it is load-bearing for anyone who stands in for its driver.
So the harness didn't rot faster than usual. The thing it stands in for moved underneath it, in the exact place where standing in for something is hardest.
There's a second-order effect worth naming, because it's what actually burned the hours.
The bug was in the type-checker call. It surfaced in the lowering rung — a different phase, later, after a few hundred megabytes of unrelated work. The checker itself was green and printed correct output; it had quietly written its results somewhere that got reused, and the damage only appeared when something read them back.
The step that fails is not the step that is wrong. It's the first step that reads what the wrong step left behind. And our instinct — mine, and the morning's written record — was to debug the step that failed. Yesterday's notes said "it dies inside lowering," and it didn't; lowering had finished before the first line of output was printed. That single misreading cost the most.
The thing that finally cracked it wasn't cleverness, it was reading the compiler's own source until I could say why a register held a particular garbage value. Two of my three hypotheses were wrong, and both would have looked plausible in a summary.
There's a question sitting in HARNESS_FIDELITY.md that I opened this morning
and couldn't answer: why is there a harness at all?
Today is the strongest evidence yet, and it points one way. Of the six defects, zero were bugs in our own logic. Not one. Every single one was a place where our stand-in had drifted from what the real driver does — a missing wrapper, a stale argument list, a reservation not taken, a chapter not bundled. We are not bad at writing harnesses. We are being asked to maintain a hand copy of a thing that changes every Update, and the copy is where all our bugs live.
The honest counter-case, which I don't want to wave away:
None of those requires us to re-type the driver's setup. They require us to open the phases up and see inside them. Those are different things, and today we paid for treating them as the same thing.
So the sharpened version: the harness should not be a reimplementation of
the driver with the phase calls copied out. It should be the driver's own
sequence with the phase calls instrumented. Concretely — one shared piece of
generated setup, derived from opening.codex rather than transcribed from it,
that every rung includes and no rung edits. Then an Update that moves a
reservation moves it in one place instead of twelve, and the checklist I wrote
this morning gets shorter instead of longer.
I don't think that's a day's work, and I don't think we should do it now. But it's the first time I can argue for it from evidence rather than from taste.
You said it's frustrating that we're four hours in. It is. I want to be straight about what the four hours bought, because I don't think it was four hours of spinning.
We found one real thing in their compiler worth reporting — a function whose calling rule exists only in prose, plus three cousins — and it's filed. We found five places where our side had drifted from Update 54 and fixed all five. And we found that two of our safety nets had never been load-bearing: the gate that stops a crash-dump becoming a banked answer, and the self-tests that were supposed to prove such gates work.
That last one is the part I'd have been most unhappy to keep. A gate that has never fired and a gate that cannot fire look identical from the outside, forever, until the day you actually need it. We were one bad run away from banking a register dump as the correct output of a compiler phase, and then diffing against it for an Update or two while it made every arm look broken.
The tax is real, though, and it's worth saying what it's a tax on. It's the cost of maintaining a copy. Six defects, six divergences, one copy. That's not a coincidence and it's the argument above.
Ten of twelve rungs bank clean at Update 54. The eleventh trips the memory guard during code emission, and I've been wrong about it twice, so the next move is to instrument rather than guess a third time: print where the memory cursor and the ceiling actually are, on either side of the step that fails, and read the numbers instead of reasoning about them.
That's one guest, and it's the right kind of cheap.