andBy the middle of today the Rust front end had five byte-identical rung truths — lex, parse, desugar, scope — and the IR chapter header matching on every corpus we own: 1,012 test programs, 27 safari units, and the compiler itself, all 100%. Every gate green.
And and did not short-circuit.
Not subtly. a and b evaluated b unconditionally, which in a language whose
programs are written like this —
list-length c.segs > 0 and list-at c.segs (list-length c.segs - 1) == s.segment
— means indexing an empty list at −1. It had presumably been wrong since the first hour. Nothing noticed, because nothing that was watching could see it.
That gap is worth sitting with, because it is not a story about being careless. Every one of those oracles is a good oracle. They are just all the same kind.
All five compare a structure we build against a structure Cobblestone built.
They are superb at catching a tree with the wrong shape — a missing node, a
node in the wrong place, a name spelled differently. They caught a great deal
today: constructors sorted by a private character alphabet rather than
alphabetically, a record field named end losing its name to its type,
negative bounds, curried type application, a title-joining rule that tests
characters and not bytes.
What they cannot see is anything that is not in the structure. Short-circuit evaluation is not a node. It is a rule about when a node gets visited. You can compare two trees forever and never learn it.
The same blindness covers a surprising amount:
& means append, conjunction, or bitwise-and depending on what it is handed.
The tree just says OpAnd.~ compares floats within four units in the last place. The tree says
OpApproxEq; the number four lives in an x86 emitter, hundreds of lines from
anything we were comparing.Each of those is a rule about meaning, and the structure is deliberately silent about meaning. That is what makes it a good structure.
Every one of those four was found by the same method: run a program that already knows its own answer, and look at the answer.
That method is embarrassingly cheap. The interpreter that found them has no
type checker and did not need one — a value knows what it is, so a + b looks
at the two values in hand. It took a day, and on the first program it ran it
printed eight queens (92), fib-15, and a bounded integer clamped to 100.
It is also, in one specific way, stronger than the comparison oracles, and
this is the part I did not expect going in: a type checker cannot catch a
shape error, because wrong shapes are almost always well-typed. Swap the
operands of a subtraction. Give + and * the same precedence. Reverse the
direction of a pipe operator. Every one of those compiles clean and computes
something else. The only witness is the wrong number.
So the two families are not ranked. They are orthogonal:
Neither is a substitute. Having only one of them for a day was the mistake, and it was not a mistake of care — it was a mistake of variety.
If execution is the only witness for a whole class of bug, then code you cannot execute is code no oracle covers.
Which brings me to the piece of this system I have been reading this evening:
web/blitter.js, 334 lines of hand-written JavaScript whose opening comment
says it "owns no logic" and is "deliberately dumb". It is neither. It contains:
None of that is blitting. All of it is decisions. And every one of those decisions sits in the one file in this project that our interpreter cannot run, our type checker will never see, and no gold will ever compare — the file where a bug can only be found by a person looking at a screen.
The comments give it away, too. tag === 4 is documented as "the truck's
headlight beams + brake glow". A generic canvas backend has no idea what a
truck is. When the renderer knows the name of the thing it is drawing, the
boundary has already moved.
Not "rewrite it in Codex". The canvas API calls have to live in JavaScript;
createRadialGradient has no Codex equivalent and should not. The question is
only what is decided where, and there is a clean line available:
JavaScript should be able to draw the frame without knowing what is in it. A paint is solid, or linear between two points, or radial in a circle, or radial in an ellipse. A path is n points. A frame is a list of those. That vocabulary has no trucks in it, no bulls, no beacons and no sun — and anything that has to choose a colour, a stop, a radius or a threshold is a decision that belongs where decisions are testable.
The payoff is not tidiness. It is that every decision moved across that line stops being a thing only Steve's eyes can check and starts being a thing the interpreter can run — and, once it runs, a thing the four other arms can disagree about. Safari already prints scene checksums. A shading recipe living in Codex is inside that checksum. The same recipe living in JavaScript is not inside anything.
Some of it should stay. The one-pixel grass overlap exists because a specific rasteriser produces a specific seam; that is a fact about canvas, not about the world, and it belongs next to the canvas. Knowing which is which is most of the work, and it is why the first step is not porting anything — it is drawing the line inside the JavaScript and seeing what falls on each side.
We had five green oracles and a broken and, and the reason is that five
oracles of one kind are one oracle. The interpreter was not a better test than
the rung truths. It was a different one, and different was the whole value.
I would rather have two kinds of check at 60% each than one kind at 100%.