Skip to content

The Watch That Found Nothing

Backfill: this covers 2026-08-17 through 2026-08-27 and was written on 2026-09-08 from REALIZATIONS.md (R67 and R68), the theater hunt’s strike list, the wat-grep audit, and the commit bodies — all still in the tree. The window carries 103 commits into docs/arc/2026/06/278-rules-engine/ on main. One thread is not on main, and this post says so where it arrives: the differential fuzzer of August 25–27 landed on grok-rete, gen-tests and claude-compute, had not merged at the cutoff, and is The Exemplar’s material rather than this front’s. Floor counts and byte counts below are quoted from commit bodies; they were not re-run. The window opens on a compiled program nobody had thought to weigh.

By mid-August arc 278’s rules engine was finished in the ways engines are usually called finished. 30725034f (08-17) compiled where down to one Expr DAG, closing the last shape the compiler had been interpreting. The perf grid stood at 30/30. The eight-pass sequence that followed — native fire 160 → 77 → 63 → 52 → 49 ms across roughly forty commits of bind pools, token pools, fact-as-index and laned counters — is real work and is not this post; it is the background against which the window’s actual question got asked. That question was not “is it fast.” It was “what is actually in here,” and every instrument the project pointed at it came back with a different kind of nothing.

R67 begins with a measurement that was wrong in a way nobody noticed until it was said out loud. The apparatus dumped a live Session and reported the compiled export as larger than the source it came from. The builder did not accept the number:

“but… the output is larger than the source?… why?… why don’t we hvae like one edn tag… #wat.rete/Export (…) … how could the export form not be categorically smaller?”

Four questions followed, one after another, and each one deleted a field. R67 records the sequence and what each deletion took with it:

We did not open a DESIGN-STONE and draw #wat.rete/Export. Each question deleted a field the Session was still carrying. “S3 for years” deleted the working set. “Why larger?” deleted the warehouse encoding. “One tag” deleted the tag-per-Expr museum. “All just rules” deleted the Rule/Query split as a wire distinction. “Facts / memories / source not in it” was the residual, spoken before the record existed.

The third of those is worth having in his own words, because it is the one that collapsed two concepts into one:

“so this is just rules and queries?.. and queries are just one sided rules?… so… its all just rules?… that’s the wire format?…”

What landed in a4c8a38c7 is the difference between a warehouse and a program. A Session carries source forms, empty memories, and facts — everything needed to rebuild the network from scratch. An Export carries types, graph, ops and sinks, with no WatAST at all. The commit body states the arrangement flatly:

Source Session is a warehouse (forms, empty memories, facts). Export is the residual: types, graph, ops, sinks. No WatAST. Native fire only. Oracle cannot consume it. … Interior is [:bind 0 0] / [:slot 1], not a PersistentVector tag per op. ABI is FNV-1a of TypeEnv + RETE_OPS; import refuses a miss.

Hello-world compiles to 614 bytes — one rule, one answer, { "?fact" #hw/Hit {:c 10} }, and the wire never sees a Temp. The gate probe_arc278_export measures the one-rule cool world at 638 B export against 1246 B session. The ABI is a FNV-1a hash of the type environment and the op table, and import refuses on a miss rather than decoding a shape it half-recognizes.

Two properties of that residual are not conveniences. The first is that the $oracle — the wat implementation of the same engine, whose job is to disagree with the native path — cannot read an Export at all. The record treats that as honesty rather than a loss: the residual names which machine it is for, and an artifact that pretended to be portable across both would be lying about one of them. The second is the named hole, written into the same commit: stratified import lands an empty rules AST, and that is recorded, not smoothed.

The deferred-computation idea underneath this is older than the window — R5’s “store the thunk, not the answer” was already told in The Slow Engine Is the Spec. What August 18 added is the deletion. A program is what survives after you remove everything you could regenerate, and the way to find that boundary turned out not to be a design document. The builder’s reaction, on hearing the list of things that were not in it:

“‘facts not in it.’ ‘memories are not…’ ‘source forms…’ … wow… that… that looks like.. that looks like one of our magic tricks”

and then the order: “make the compiled program from our source - this is one of our greatest achievements.”

The hunt that opened five days later had a mandate and a wall. The mandate:

“whatever pattern matching you are doing… do it everywhere… there is not much left… you should be able to find them all… the hunt is on.. build a list… then we attack”

and the frame it was run under:

“the physics are our welcoming hand — there’s cruft between us and physics, we are on a crusade to annihilate this cruft and find the physics boundary.”

The wall is stated in the first line of 426873e77: a mark pair costs about 100–155 ns against sub-operations that run 100–300 ns, so the instrument was the same order of magnitude as the thing it measured. Timing could not adjudicate what was left. The pass read the source instead.

What reading found was not a hot spot. fire_fixpoint_delta_armed is 1,774 lines — 87% of its file, twelve levels of brace nesting, sixteen mutable locals at the top level, eight passes braided into one body. Against the project’s four questions it fails Obvious and Simple outright. The commit then makes the finding structural rather than stylistic:

IT IS ALSO THE ROOT OF THEATER THE HUNT COULD NOT REMOVE. Every clone found and not cut is justified in the source by the borrow checker … Not five inefficiencies — ONE arrangement producing five workarounds: a single &mut FireSession held across 1774 lines, so the compiler cannot see that a pass’s reads and writes touch disjoint fields.

Five candidate cuts, five source comments explaining why each clone was necessary, and every one of those comments true. The borrow that produced them is one borrow. That is the project’s own extirpation discipline stated as an engineering rule: eliminate the situation that produces the patch, not the patch.

Two verdicts, both negative (2026-08-23 → 08-24)

Section titled “Two verdicts, both negative (2026-08-23 → 08-24)”

The strike list that came out of the hunt closed two entries without a fix, and the two closed differently.

T8 was cleared by measurement. The entry claimed a capacity-less Vec that grows and reallocates on a hot path, with the obvious remedy of reserving up front. An instrument counted calls across two workloads instead of arguing about it: on strat-neg [6 2000], 12 calls, 6 allocating, 0 multi-parent; on accum [200 200], 10 calls, 5 allocating, 0 multi-parent. Growth requires a multi-parent call. Both fixtures measure zero of them, so the Vec cannot grow, so — in the strike list’s own words — “the proposed fix would have done literally nothing.” The entry was marked cleared, and the probe was kept as a tripwire asserting zero multi-parent calls. The premise is now watched rather than believed, which is the part that survives; a rejected optimization normally leaves nothing behind, and this one left the measurement that killed it.

T7 was reclassified, and the reclassification is the finding. It had sat on the list marked COLD — meaning no performance axis measures this path. 1dceb94e9:

T7 sat on the theater list marked COLD, meaning “no perf axis measures this.” It read as “we have looked at this.” Nobody had.

PROVED, not assumed. A panic! was armed in BOTH arms of compiled_cond’s exec_ops and the binary rebuilt. The ENTIRE where-family differential passed. A direct run of where-boolean.wat — whose rows include demorgan-nor-a/b, literally not(or ...) — ran all 15 rows without firing it. Nothing reached those arms: not the perf grid, not the correctness corpus, not the oracle, not Clara.

A panic armed in both arms of a live branch and the whole test population walked past it. Fifteen rows of boolean where tests, two of them De Morgan cases spelled not(or ...), and none of them reached the code that ought to have been the direct implementation. The diagnosis is the transferable part:

WHY IT HID: there are THREE different ors in this engine, identical at the surface, and the corpus had two of them. … The source’s own doc said the arms were “not exercised by anything in the live grid corpus” — true, and far narrower than the truth.

Three constructs that read identically in source compile to three different engines, and the corpus covered two. The source comment was accurate and it was the reason nobody looked further: it named a bound on one corpus, and it was read as a bound on all of them. “Cold” says a measurement declined to cover this. Unexercised says nothing has ever run it. They are different words, and the strike list had been carrying one under the other.

This is the third face of a class the campaign had already met. In late July (8242a4a29, b9f19ea5, 07-27/28) the record named a suite that passed 4105/4105 for weeks while the protocol it appeared to certify had never once run — not a gate that swallows a failure, but a gate structurally incapable of noticing one. T7 is that shape moved from the test surface into the engine.

wat-grep never lies (2026-08-24 → 08-25)

Section titled “wat-grep never lies (2026-08-24 → 08-25)”

Then the builder pointed the same suspicion at the instrument the campaign had been counting with:

“is wat-grep defective?… if yes… i want that more than anything else right now.”

wat-grep is the corpus census tool — wat reading wat, parsing .wat files into a fact base and running rules over it. The audit’s first finding, 8137598b2:

⛔ F1 — A FILE THAT READS BUT DOES NOT PARSE IS SILENTLY EMPTY, AND THE CAUSE IS DISCARDED. wat/grep.wat:218 binds the parse error to __cause and returns an empty fact base. … so wat-grep cannot distinguish “no matches” from “could not read”, and EVERY CENSUS RUN THROUGH IT — MINE INCLUDED — HAS AN UNKNOWABLE DENOMINATOR. The comment above that line calls it “the no-hidden-failures law”. It is the opposite.

A binding named __cause, holding the reason the file could not be read, discarded one line later, under a comment naming the law it breaks. Every count the campaign had produced through that tool — including the ones in the audit’s own working notes — was a numerator over an unknown population.

The ruling was three words long in substance:

“fix all three - wat-grep must never lie again.”

c80aa5860 turns a parse failure into a first-class fact that a rule can join against, plus an unconditional stderr report and a non-zero exit. It paid immediately:

★ AND F1 IMMEDIATELY FOUND TWO TRACKED FILES EVERY PRIOR CENSUS HAD DROPPED … both #wat.parse/Lex. Silently skipped by every wat-grep run ever made, including the ones that produced 1461, 1411 and 239 in this session’s own notes and the wat-scripts/grep/README table. “An unknown and unknowable denominator” was not rhetoric; it was two files.

The same stone carried an acceptance row that predicted a number and got a different one. The prediction was that the Named-vs-Written delta would come to 1411. Measured: 11,534 — keyword 1411 exactly as predicted, symbol 0, string 10,123. The gap is a boundary bug in the tool’s own accounting: ast-name on a string literal returns the unquoted content while Span covers the quotes, so Written structurally refuses every string literal. That refusal subsumes, by construction, a hazard that had previously been guarded by hand across 1,564 files. The prediction was wrong by a factor of eight and the stone is better for it, because the acceptance row was written before the run and the disagreement had to be explained rather than absorbed.

The fixed point, set in advance (2026-08-19 → 08-21)

Section titled “The fixed point, set in advance (2026-08-19 → 08-21)”

The watch thread ran in parallel with all of this and closed two days before the hunt opened. Vigilia is the grimoire’s parallel cast — every defensive ward pointed at one sub-tree at once — and it had not been run against the substrate’s own body in a long time. The builder said why it was time:

“we haven’t used vigilia on wat… in a very long time… we’ve been grinding… constantly.. hacking the lang into existence… so much cruft was built up as we continued to make it work… then… we made it work… then we made it fast… once it was fast we began to polish… we’ve set the tone for what the rest of the lang must become… rete is our proving ground for so much…”

The stop rule was set before the recasts that would satisfy it, which is the only way a stop rule means anything:

“we break the loop when two vigilias run back to back produce no findings - that’s the fixed point”

Recasts 9 through 11 still found stone: a TypeEnv conjunct left unpinned, a comment claiming present-tense hot-path volume after the code it described had moved, and a nested type the new pin had itself introduced. Recasts 12 and 13 came back empty at HEAD 8839bb16, floor 4911, clippy silent. Two consecutive empty casts. The loop broke where it had said it would.

R68 records the method as the builder described it — “this felt like masonry… we were chisling away the stone to reveal the statue beneath… rete’s code now… its hard to find words to express what we’ve done here…” — and then declines to treat the result as a coronation of the rules engine. Its own line: “The statue is local. The tone is not.” The claim being made was about what the rest of the language now has to look like, not about rete being done.

Four days later, on a branch (2026-08-25 → 08-27)

Section titled “Four days later, on a branch (2026-08-25 → 08-27)”

grok-rete diverged from main on 2026-08-24. Everything in this section landed there and on gen-tests and claude-compute, and none of it had merged to main at this post’s cutoff. It belongs to The Exemplar, which narrates it as its own front’s material. It is named here for one reason, and the reason is not the defects.

A property-based generator library written in wat — wat-gen — was pointed at the rules engine during that stretch. Its first widened run against the engine reported 22 mismatches out of 504 cases (a39c28e10), all at the accumulate shape, decomposing into two families, both reproduced minimally, both silent. A third defect followed the next day (03e34f0f3). b2939f12b closed them on August 26 as one root: a query’s non-monotonic condition was being evaluated inside the fixpoint instead of once against the closed world, and all 72 divergences were :not and accumulate and nothing else — non-monotonic being exactly the class a later round can invalidate.

The part that concerns this front is why the existing corpus could not see any of it. The commit says so directly: the accumulate axes compare derived facts, and production_delta deduplicates those by value, so a rule deriving one distinct fact reads identically whether its token passed through once or four times. The differential compared the answers. The defects were in the passes. Comparing beta rows instead is the reason the fuzzer exists and is the reason it found something.

So the window contains an honest empty result and a real defect population four days apart, and they do not contradict each other. The vigilia converged because it had asked its questions and they were answered. The fuzzer found seventy-two because it asked a question nothing had asked. Convergence is a property of the instrument and the corpus it runs over, dated to the day it converged. It is not a property of the artifact.

Five instruments were pointed at one subsystem in eleven days. A dump reported the export as larger than its source, and four questions turned that into a 614-byte residual. A profiler could not resolve operations of its own magnitude, so a reader found a single mutable borrow held across 1,774 lines wearing five separate justifications. A strike list closed two entries with no code change, one of them by proving that nothing in the project had ever executed a live branch. A census tool was found returning empty results for files it could not parse, under a comment naming the law against exactly that. A watch ran to a stop rule it had published in advance and stopped there.

None of those found what the fuzzer found, and none of them were broken. Every one of them answered the question it was built to ask, and the record’s job in this window was to keep straight which question that was. A green result carries the shape of its instrument, and the only defense is to write down what the instrument could not have seen at the moment you quote it. The campaign wrote it down five times in eleven days, and four days later the note was what made the seventy-two legible instead of a contradiction.

  • Convergence measures the instrument, not the artifact. A formal watch ran to a fixed point defined before the runs that satisfied it — two consecutive casts, zero findings, floor 4911 — and four days later a differently-shaped instrument found three live defects and 72 divergences in the same subsystem. Neither result is wrong. A clean result is a statement about a question and a corpus on a date, and it is only safe to quote alongside the sentence saying what it could not have covered.
  • “Cold” and “unexercised” are different words. A path marked “no perf axis measures this” was read as “we have looked at this.” A panic armed in both arms and the binary rebuilt proved that nothing reached them — not the perf grid, not the correctness corpus, not the same-language oracle, not the third-party reference engine. The cause generalizes: three surface-identical or constructs compiled to three different engines and the corpus held two, and the source comment naming the gap bounded one corpus while being read as bounding all of them.
  • A negative verdict, measured, is a deliverable — and the measurement is the part to keep. A proposed reserve-up-front optimization was cleared by counting calls across two workloads: zero multi-parent calls in both, therefore no growth, therefore the fix “would have done literally nothing.” The probe stayed in the tree as a tripwire asserting the zero. Deleting a rejected optimization leaves nothing to stop it being re-proposed; keeping the instrument that killed it does.
  • Five workarounds can be one arrangement, and each will be individually correct. Every clone the hunt could not cut was justified in the source by the borrow checker, and every justification was true. The subject was a single &mut held across 1,774 lines, which prevents the compiler from seeing that eight braided passes touch disjoint fields. The rule is to check whether the type over-claims before removing anything that looks like a workaround.
  • A tool that returns empty on failure has an unknowable denominator, and the annotation above it is where the premise hides. wat/grep.wat:218 bound the parse error to __cause, discarded it, and returned an empty fact base under a comment calling it “the no-hidden-failures law.” Turning the failure into a joinable fact plus a non-zero exit immediately named two tracked files every prior census had dropped. This is the front’s thesis at its narrowest: the substrate’s own tooling breaking the substrate’s own law inside code annotated with that law’s name.
  • The residual of a program is what survives the deletion of everything regenerable, and it is reached by deletion rather than by design. Source forms, working memories and facts are the warehouse; types, graph, ops and sinks are the program — 614 bytes for hello-world, 638 B against a 1246 B session on the gate. Two honesty properties come with it: the interpreter that produced the residual cannot read it back, because the residual names which machine it is for, and the one shape that does not survive the round trip is recorded as a named hole rather than elided.