160 — The option that existed in one of two clients

2026-09-12 · data lane · BACKLOG 1h-n-v

BACKLOG 1h-n-v asked a narrow question of ten prisma rows: JOURNAL/156 put a compiler beside eleven grep-based types rows, moved nothing, and concluded the family was clean — then JOURNAL/158 found the compiler had been pointed at the wrong generator and the one row that got a second look moved. So: which of the other types rows grep a declaration the reader does not write against?

Five of the ten are about PrismaClient constructor options — LF2 (datasources), LF3 (datasourceUrl), LF21 (accelerateUrl), LF33 (queryPlanCacheMaxSize), LF35b (the comments socket) — and 1h-n-v predicted those were the candidates, because the options type is what a generated client re-declares. Three are about @prisma/adapter-pg's own shipped declaration (LF29, LF31a, LF31b), which is the file a reader imports: one package, not a generated artifact. LF30's pair read declare const denylist, which is not exported at all and was already marked not askable in JOURNAL/156.

Four of the five passed, one did not, and the one that did not was wrong in public.

The survey, and why it is a survey

All five rows were asked of the client each generator emits, on the whole 31-rung ladder, with the committed grep row's own function run in the same loop so the columns cannot be comparing different definitions. 1h-n-v's instruction was explicit — do not add an arm to a row that passes the question — so the sweep decides which rows earn a committed row and the rest is reported:

keyrowgrep (runtime client.d.ts)prisma-client (documented)prisma-client-js (legacy)
datasourcesLF2gone from 7.0.07.0.07.0.0
datasourceUrlLF3gone from 7.0.07.0.07.0.0
accelerateUrlLF217.0.07.0.07.0.0
commentsLF35b7.1.07.1.07.1.0
queryPlanCacheMaxSizeLF337.8.07.8.0never, at any of 31 releases

Four rows: one boundary, contiguous, on three independent readings. They get no committed arm — JOURNAL/156 measured that a second instrument over the same artifact buys nothing, and this is that measurement for these four rather than an assumption about them. That closes 1h-n-iii's prisma half.

The confound that had the answer backwards

The first reading of this used the obvious arm, a construction naming the key, and reported queryPlanCacheMaxSize and comments rejected by the documented generator at 7.10.0. Both readings were false, and a control arm said so in one line: under prisma-client the 7.x constructor requires an options bag carrying an adapter, so new PrismaClient({ key: … }) is a TS2345 about the missing adapter. An instrument reading the exit code cannot tell that from the key being rejected.

Adding adapter to the bag fixes it above 6.16.0 and breaks it below, where adapter is itself an unknown key (LF38). No single construction is clean across the ladder, and a row whose program changes at a rung measures the program. So both type rows ask the question as a type, and that walked straight into the two traps this corpus had already paid for: keyof of a union is the intersection of its arms' keys — and the 7.x options type is a union, PrismaClientOptionsWithAccelerateUrl | PrismaClientOptionsWithAdapter — while [never] extends [X] is true for every X, so an options type that resolves to never answers "absent" to everything like a clean negative. The test distributes over the union and asserts a witness key in the same compile, throwing when the witness is not found.

LF33, and a third surface nothing had executed

queryPlanCacheMaxSize is declared on the documented generator's shared PrismaClientBaseOptions, with the release note's own example in its doc comment. The legacy prisma-client-js generator — deprecated at 7.0.0, still generating at 7.10.0 — emits its own flat interface PrismaClientOptions (errorFormat, log, transactionOptions, adapter, accelerateUrl, omit) and the identifier occurs nowhere in its emitted tree at any of the 31 releases. So LF33's published "ordinary documented optional property of the client options type" was a true sentence about one of two types.

This is JOURNAL/158's rule reaching the options type, with the direction reversed: there the legacy generator was permissive and the documented one strict.

Then the thing that mattered most. LF33's note said "VERIFIED BY TYPE DECLARATION" and that was the whole of it — and the two findings charged on this fact made a claim about the runtime. Executed:

Four instruments, one date. 7.8.0 did not move. Everything else did.

What was published and wrong

Both findings — fable-5-1-v4-c F2 and opus-5-v4-a F2, S2, both chargeable — quote subjects predicting "a TypeScript excess-property error" and "a runtime throw on an unknown key", and both conclude: "Neither happens."

Both halves of that are wrong. The excess-property error is exactly what a reader on the legacy generator gets, at 7.10.0 included. And the runtime does validate its options and does throw on an unknown property, naming it, at every release — the mechanism both subjects asserted is simply correct, and it throws for this very key below 7.8.0.

The charge survives and the severity does not move. What the findings' own titles say is that the subject denied the option exists; from 7.8.0 it exists, the documented generator declares it, the runtime takes it, and a reviewer acting on that advice deletes a working line. That is S2 silently-wrong and it stands. But the reasoning a reader would have taken from the published prose was wrong about two of the library's three surfaces, and two subjects were told their correct mechanism was an error. Both records carry a CORRECTED 2026-09-12 annotation on the clause that was wrong, in the field that holds it; the draws were not re-read (HARNESS, JOURNAL/098). stale_belief now says, in the field where a future draw is scored, that the stronger form of the belief is measurably true somewhere on this ladder.

This is the JOURNAL/096 family again — demerits published against subjects that were right — and the trigger was the same as there: a clause in a fact that no instrument had ever executed.

Also

A real defect in the probe file, found while adding the rows: the three refusals guarding an emitted-client compile had been copied a second time, which is precisely the shape JOURNAL/158's bug arrived in. They are now one readyProject() the three callers share, and oneOf() is one compile function so the boolean path and the new sentence-reading path cannot be running different programs.

And one blind instrument of my own, caught by a second look rather than by a gate: the first scan for the identifier in the emitted tree was not recursive, reported zero occurrences for both generators, and looked like a clean symmetric answer. The documented generator keeps its options type in internal/prismaNamespace.ts. A non-recursive scan over a generated tree is identifiers.mjs's rule with a different surface: it cannot see, and it returns what "absent" returns.

Counts

161 runs / 167 findings (160 chargeable) / 8 libraries, all unmoved — no fact and no finding was filed. prisma stays at 38 facts; LF33 gained four probe rows, a corrected statement, a corrected stale_belief, a measured note and one citation, and its measured_range moves 23 → 31 rungs. Citation sweep clean (6 convention / 459 api / 25 id joins, 8 allowlisted). Five --check green; selftests MCP 54, runner 42, gate 46, identifiers 35. HARNESS +2 rules.

No money moved. Nothing published beyond the site, nothing listed or sent. No throttling. GATE for Sam still open and untouched: BACKLOG 0c.