075 — The check that moved from the call to the first use: LF4 corrected, and A2 refused
2026-09-07, distribution lane (DISTRIBUTION D1, step 4d). The last Class A task, the one drawn fact that needed a tsc --strict grading path, and the reason it will never be built.
What this session set out to do
Step 4c left one Class A task unwritten: A2 (LF4) — .pick() / .omit() with a key the schema does not have. It was held back because LF4 was filed as a type-level fact with no runtime shadow, so grading it needed a compiler rather than an execution, and prompts/benchmark.md §9 had pre-registered a tsc --strict path for exactly this task. The plan was: build the path, write the task, admit it, and close out the Class A set.
The step-4c discipline says every candidate proposition is run against all fifteen installed rungs before the task is authored. That survey is what happened instead of the plan.
The survey, and the two clauses it broke
The type-level claim held, cleanly. Under tsc 5.9.3 --strict, a mask carrying an unknown key compiles on the seven rungs below 4.3.0 and errors on all eight from 4.3.0 — a contiguous flip exactly at LF4's bisected release. The signature gains & Record<Exclude<keyof M, keyof Shape>, never> there, which is what the fact's citation said.
Two other clauses did not hold.
The inline-literal clause. LF4 said the check "only bites when the mask is a variable — an inline object literal was already caught by excess-property checking before 4.3.0". It was not. The literal form compiles clean on every rung below 4.3.0 and errors from 4.3.0 with TS2322, exactly where the variable form errors TS2345. Excess-property checking never fired, and the reason is visible in the signature: the parameter is the inferred generic M extends util.Mask<keyof Shape>, so the literal is the thing M is inferred from and there is no fixed target type for the check to fire against.
The runtime clause, which is the interesting one. LF4 said the 4.3.0 check "is a TypeScript type error, not a runtime throw", and that .pick() with an unknown key "still runs and still returns a schema at runtime, on every 4.x release". The first survey said the opposite — a throw on all fifteen rungs — and a second script, written to check it, said the throw happens only on 4.0.0. Two of my own instruments disagreeing is not a result, so both were suspended until the disagreement was explained.
It was the probe, and the difference between the two was one property access. The surveys that saw a throw everywhere read .shape on the schema .pick() returned; the one that saw a throw only at 4.0.0 called .pick() and stopped. Measured properly:
release pick() call then .shape then .parse then safeParse omit() call omit .parse
4.0.0 X X X X X X
4.0.17 . X X X . X
... identical on every rung through ...
4.5.4 . X X X . X
The mask check has been there since 4.0.0 and never left. 4.0.17 moved it from eager to lazy: the call returns a schema, and the throw fires the first time anything touches it. safeParse() throws with it, because a schema built from a bad mask is a construction error, not a validation failure — which makes this worse for a caller than the fact described, since safeParse is the API people reach for precisely to avoid throws.
So the old citation — "throws no runtime error on 4.0.17, 4.3.0, 4.3.6, 4.4.0 or 4.5.4" — was written from a probe that made the call and looked no further. That is the only vantage point from which it is true, and the versions it lists are exactly the ones where the check had gone lazy. LF4 had been filed unprobeable in JOURNAL/067 on the reasoning that a type error has no runtime shadow, so the runtime half was never executed at all, and the fact carried a false clause until today.
LF4 is rewritten, the runtime half is now probed as LF4b (claim 4.0.0, true and contiguous on all fifteen rungs), and the unprobeable entry now excuses only the type half. No published charge moves. The fact is more severe than it read, not less: the stale mask breaks the build from 4.3.0 and throws past safeParse on every 4.x.
What that does to A2, and to the tsc path
With the runtime behaviour identical at every rung, LF4's version dependence lives entirely in a type signature. That is fatal for a benchmark task, and the reason is structural rather than local: the arms are graded at one release, so a BARE failure has to be attributable to the fact's release, and the flip test is what establishes attribution. A fact that behaves the same at the bottom of the ladder as at the top cannot supply it.
The rule was written down first — prompts/benchmark.md §9, third amendment, dated before A2 was authored, and recording explicitly that the survey had already been run and its result was known. An amendment presented as if written blind, when it was not, is the same dishonesty as a gate relaxed after a task failed it. It fixes three things:
- A
tsc-graded task's acceptance assertion is a conjunction — the file compiles clean under--strictand the runtime assertion holds. Grading on "it compiled" alone would let a subject pass with a schema that strips the wrong fields, measuring the compiler rather than the pack. - A fact whose runtime behaviour is flat across the ladder cannot carry a class A task, whatever its type signature does. It stays in the Index and keeps charging; it is not evidence here.
- The
tscpath is not built. It was pre-registered for one task, that task is refused, and no other drawn or reserve fact needs it.assertion.kind: "tsc"stays in the schema so the shape of a future type-graded task is already fixed. Building an instrument with no subject would produce something only its own author could check.
Then A2 was written in full anyway — prompt, reference solution, stale artifact, acceptance assertion — and put through the gate, under the rule LF21 established: a task predicted to fail is built so the gate can refuse it on evidence rather than on the operator's forecast.
REFUSE A2 A LF4 falling @4.3.0
correct +++++++++++++++
stale ...............
neither artifact flips
The refused artifacts are kept — prompts/sent/benchmark-zod-a2-lf4-refused.txt, tools/benchmark/tasks/zod/A2.lf4.reference.mjs, A2.lf4.stale.mjs and the A2_LF4 assertion — so anyone can re-run the refusal.
S1 reserve #3, LF1 (.pick() / .omit() on a schema with refinements, 4.3.0) takes the seat, in the order draw.mjs printed. LF1 has been charged against this subject before, so the previously-charged subset moves from 4 of 12 to 5 of 12 — reported because §7 requires the cut to be reported, and because it moved for a reason that has nothing to do with anyone's preference: it is the next name on a printed list.
What is now claimed less
prompts/benchmark.md §11 gains a line: BM1 measures staleness that changes what code does. Type-level staleness is a real and probably large category, and this instrument does not cover it. LF4 — an S1 that genuinely breaks the build from zod 4.3.0 — is out of the sample for exactly that reason, and the results page will say so rather than let a reader assume the number covers it.
That is the second time this lane has narrowed what the benchmark will be allowed to claim before producing a single number, and both narrowings came from measuring something we had asserted.
Base rate
zod's bisect base rate is unchanged for dates — 4.3.0 still stands for the type check — and gains another fact wrong about its mechanism at the right version: three now (LF10's tuple clauses, valibot's LF6 severity, and LF4). Of the two mechanism errors in zod, both were found by the distribution lane while building tasks on facts the data lane had already bisected, which is an argument for the benchmark as an auditor of the dataset independent of anything it measures about correction packs.
Next
The S1 seat needs LF1 authored and gated — its own ladder survey first, as always. Then the six Class N tasks, then step 5, BM1 itself. No Sam gate on any of it; the first gate in this lane is still D2's repo flip.