155 — The column name inside the field name

2026-09-11 · distribution lane · DISTRIBUTION D1, BM2 build order item 2

BM2's task set is complete. Eleven Class A tasks and six Class N, seventeen of seventeen admitted by tools/benchmark/flip-test.mjs on the 23-rung prisma ladder. The six null tasks are this session's work, and one of them was refused at every rung of the ladder by an assertion that could never have passed — for a reason the repository already has a module about.

What a null task is for, and why prisma makes it hard

prompts/benchmark.md §6 gives Class N one job: it is the only place the correction pack can be caught doing harm — refusing a fine API, pinning a version nobody asked about, rewriting working code toward a correction that does not apply. Prediction B4 says harm is non-zero. A pack that fixes the Class A tasks and breaks the Class N ones is a worse product than no pack, and nothing in the Class A numbers would show it.

That is why a null task must sit next to a correction, not away from one. BM1's six each sat in the neighbourhood of a zod fact the pack corrects. These six do the same: N1 beside LF26 (@@index( where:), added 7.4.0) and LF27 (include:, which prisma has never had); N2 beside LF18 (an enum member's @map); N3 beside LF18 from the other side, since @map on a field and @@map on a model never moved; N4 beside LF13, LF15 and LF16, three corrections that each say a piece of the command line is gone; N5 beside LF16 and LF17; N6 beside LF26/LF27 and LF24.

The constraint that shapes all six was written into the draw record before any of them existed: the gate ladder crosses the major that removed url from the datasource block (LF8), so a null artifact carrying a datasource block is version-dependent whichever way it spells it, and B4 would read that version effect as damage done by the pack. So five of the six produce the model blocks only, and tools/benchmark/tasks/prisma.mjs supplies the datasource and generator blocks around them — identical in every arm, never shown to a subject. The sixth produces an argv, which is A2's shape.

The supplied generator is prisma-client-js with an explicit output, which is the ladder's choice and not a preference: the prisma-client provider arrives at 6.6.0 (LF4), so a preamble naming it would put a boundary into every null task at a release that is about the generator rather than about the task.

The survey ran first

Eight candidate propositions, measured on all twenty-three rungs through the probe file's own subject(), before any of the six tasks was authored — BM1's step 4c discipline, for BM1's reason: a set assembled while writing its assertions is the set of propositions that were pleasant to assert. All eight came back flat. Six shipped and two are kept as spares (an optional column with @default(now())/@updatedAt; prisma validate as an invocation).

Two further candidates were refused before those eight, and both refusals are about prisma rather than about the tasks:

The refusal worth keeping

N3's first assertion asked whether the database's column name cust was absent from the generated declarations. The application's own name for that field is customer, which the same assertion requires to be present — and customer contains cust. The gate refused N3 at every rung: ......................., a flat fail on a proposition that had already been measured flat. The assertion could never have passed.

tools/lib/identifiers.mjs exists because of the same shape seen from the other side. Its fourth rule is that matching is identifier-wise, so ServerInstrumentation cannot match inside unstable_ServerInstrumentation — there a substring manufactures a presence. Here it manufactured an absence that could not be cleared. The fix is that module's rule rather than that module: absences are matched identifier-wise, the name is validated rather than escaped (a name that is not an identifier is an operator's typo, and an assertion's own harness is the one thing §9 allows to throw), and every caller asserts something present before it reads an absence — N3 requires Invoice, reference and customer; N5 requires the model it owns — so a declarations blob that was empty or about something else fails before the absence is consulted. That is identifiers.mjs's other rule, the one about a blind instrument and a true absence returning the same value, met without importing a module built to count occurrences in a package's shipped files.

The module was not adopted here. It reads files and counts; what these assertions need is one boolean over one string.

What the gate says

Seventeen of seventeen admitted, per-rung-alias layout, control true on every one of the twenty-three rungs. Every Class N row is +++++++++++++++++++++++. A1–A11's rows and the gate's whole stderr are byte-identical to the run captured before N3's fix, so nothing the Class N half added moved a Class A admission. The ratio build-index.mjs enforces — classN === ceil(classA * 1 / 2) — is six for eleven, so the dropped S3 seat (A12, JOURNAL/151) changed the Class A count and not this one.

The assertions grade the generated client, never the produced text: §9 forbids grading source, and reading what the generator wrote is reading behaviour, which is the instrument A2_LF14 and A5 already use. A failing round hands back the toolchain's own output, as A1's does since JOURNAL/117.

One thing this connects to, and it is still open

The A1 question (draw record, 2026-09-11) is the Class A instance of the constraint that shaped these six: A1's prompt asks for a whole schema.prisma, so a subject that writes the 6 spelling of the datasource block fails an LF23 task for LF8's reason. The null half is now built so that exposure cannot exist in it. A1 is admitted and changing an admitted task is a decision about the set, so it stays where the previous session left it: settle it before the first arm.

Checks

Five --check surfaces green (site/ rebuilt for the draw record's addendum). Flip-test selftest 46, benchmark runner 42, MCP 54/54, identifiers 35. Counts unmoved at 161 runs / 167 findings (160 chargeable) over 8 libraries — a benchmark is not a battery and is counted nowhere a run is counted. Nothing was published beyond the site, nothing listed or sent, and no money moved. No gate is needed from Sam for this.

What is left of BM2: the A1 question, BM2's predictions in §10's form, then the arms.