Skip to content

Arc 277 — Wat Lint Fix Fmt

R1 — the keystone paid off in one stone, and the first fix was surgical

Section titled “R1 — the keystone paid off in one stone, and the first fix was surgical”

The build order said the ast-end-span keystone (arc 281) was the gate for every structural auto-fix — and the pre-compaction self drew it as a dragon, a wide invasive lexer/parser change to be approached with care. The builder, watching it land in essentially “add two fields, an accessor, and thread one span,” ribbed it straight:

“mannnn your prior self before compaction was being really pushy about this being very difficult — rofl … you made this sound like a hard thing when in reality it’s ‘we add another accessor and everything just works.’”

He was right about the shape (the difficulty was blast-WIDTH, not depth — and the typed Span made every missed Span {..} literal uncompilable, so the green-build forcing function swept them up). And then the keystone paid off immediately: the very next stone, 277.1b, turned the report-only nested-if-=-ladder rule into a real auto-fix. Fed

(:wat::core::defn :t::f [x <- :wat::core::String] -> :wat::core::bool
(:wat::core::if (:wat::core::= x "a") true
(:wat::core::if (:wat::core::= x "b") true
(:wat::core::if (:wat::core::= x "c") true false))))

lint-fix-file returned

(:wat::core::defn :t::f [x <- :wat::core::String] -> :wat::core::bool
(:wat::core::contains? (:wat::core::HashSet :wat::type::Infer "a" "b" "c") x))

The whole ladder collapsed to the contains? cure the rule’s message had named since 277.1 — and everything else stayed byte-identical: the defn, the param vector, the <-/-> arrows, the return type, untouched. The orchestrator weighed it by eye on its own build and said:

“The rewrite is surgically perfect.”

To which the builder: “that’s a fucking quote — whatever realizations needs it.” So here it is.

Why surgical, named honestly: fix-text-apply only ever rewrites [off, off+old-len) and copies the rest of the source verbatim; old-len = offset-of(ast-end-span) − offset-of(ast-span) gave the ladder form’s exact char extent (the keystone’s whole reason to exist). Precise extent + splice-only apply = a scalpel, not a re-print. No reformatting, no comment loss, no neighbor disturbed.

This is the SELF-FIXING-TOOLCHAIN doctrine’s first operational proof — find → fix → apply, end to end, on a real form — the proof-by-diff in miniature, before the full sweep that will turn the same scalpel on the toolchain’s own author (violation->finding, the concat chains). And a quiet dogfood rode inside it: the replacement text was built with format (the tool from the first stone of this campaign), generating the fix for the rule of the last — the toolchain already using its own ergonomics to repair itself.

The dragon was a doorway. Behind it: a linter that doesn’t just point at the bad form — it reaches in and replaces it with the cure, and leaves no fingerprints anywhere else.

R2 — the fix tool wrote the bad form it abolishes (the strange loop, caught mid-write)

Section titled “R2 — the fix tool wrote the bad form it abolishes (the strange loop, caught mid-write)”

Building 277.1c-fix (the concat→format auto-fix), the executor wrote the eligibility check as a nested-if disjunction:

(if (contains? inner "\"") false
(if (contains? inner "{") false
(if (contains? inner "}") false true)))

— a boolean test ((not (or …))) wearing four lines of if. The builder caught it live in the diff: “rofl — did it just write its own bad form we’re about to address?” It did. arc 275’s strange loop (deporder written in the bad form it detects) firing AGAIN: the tool built to clean bad forms, shipped in a bad form. Cleaned to (:wat::core::not (:wat::core::or …)) before commit — the fix tool may not ship in the shape it exists to abolish (intueri).

The sharper finding — it names the NEXT rule. The current nested-if-=-ladder rule would NOT have caught this: it keys narrowly on (if (= VAR LIT) true …) — equality conditions, true leaves. This is (contains? …) → false — different predicate, opposite polarity. So the bad form wasn’t a fixture the toolchain could clean; it was a GAP. It generalizes the rule: “a nested if whose every leaf is a boolean literal is a boolean expression in disguise → rewrite to and/or/not.” The = VAR LIT → contains?(HashSet) ladder is one special case of it. PROMOTED to a REQUIRED charter rule the RETE engine ships withnested-if-boolean-collapse, specified in docs/arc/2026/06/278-rules-engine/DESIGN.md § “Charter rules”. A richer LHS than the narrow ladder match (keys on the boolean-leaf structure, not the condition shape), and a natural early consumer of the arc-278 engine.

The doctrine, demonstrated in one screenshot: the toolchain’s own author’s hand caught writing the smell the toolchain hunts — and the catch didn’t just fix an instance, it widened the net.

R3 — the sweep proved the concat→format fix is RUNTIME-ONLY (a fix needs to know its position’s purity)

Section titled “R3 — the sweep proved the concat→format fix is RUNTIME-ONLY (a fix needs to know its position’s purity)”

THE SWEEP ran the linter’s auto-fixes over the whole corpus. The diffs were beautifulfix.wat’s own structural? ladder → (contains? (HashSet …) k); Record.wat’s 7-piece msg-prefix concat → a self-documenting format; ~15 service.wat name-building concats → format — net −23 lines, comment-faithful. Then the floors: lib 591/374, deftest 0/263, deporder 0/1. The stdlib would not load. (Reverted; nothing shipped — the preview-diff-then-floors discipline caught it before commit.)

Root cause (the gate said it plainly):

:wat::core::format refused at macro expand time — not on the pure-combinator allow-list (arc 249 F5)

format is a macro. The corpus’s bare-symbol concats are overwhelmingly inside defmacro bodies (Record::def, defservice, the defn-kwargs branch — all build keyword names at EXPAND time). At expand time the macro-eval purity gate refuses format. So concat→format is legal only in RUNTIME positions (a defn body), never in an expand-time position (a defmacro body). The 277.1c-fix probe stayed green only because its fixture was a defn (runtime) — the fixture didn’t represent the corpus, which is macro-heavy. (ladder→contains? was fine everywhere — contains? is pure-total, expand-time-legal.)

Two requirements this names (block-and-build):

  1. The detection rule must know if the form is in a macro (expand-time) position — “am I inside a defmacro body?” — and decline a macro-introducing fix (concat→format) there. The form-local walker must thread an in-defmacro? context as it descends. This is the immediate gate; until it exists, a blind sweep of concat→format is unsafe and the SWEEP applies only the ladder fix.
  2. Purity/expand-time-legality must be queryable metadata declared at definition time (arc 255 — NOTE-purity-is-definition-time-queryable-metadata.md). The fix should ASK “is the callable I’m introducing expand-time-legal at this position?” not rely on a guess. This is the callable-class half; (1) is the position half.

The sweep didn’t fail — it did its job: it stress-tested the fix against the real corpus and exposed the hidden precondition the isolated fixture hid. The proof-by-diff is deferred until the fix is position-aware; the diffs themselves proved the rewrites correct, only the contexts wrong.