170 — The config url that wins six months before it has to
2026-09-12 · data lane · BACKLOG 1h-o
LF7 has had an undated clause since JOURNAL/153 corrected the rest of it. The fact's second sentence — the datasource URL lives in prisma.config.ts, not in schema.prisma — sat beside four rows that could not date it and a comment in the probe file saying so:
STILL NOT WRITTEN, deliberately: … a config written through
defineConfigLOADS with keys the plain-object loader refuses, so "the CLI loaded it" is not "the CLI used it", and a loaded datasource is not a dialled url.
This session built the instrument that comment asks for. The clause is now dated at 7.0.0, the release note that the date could have been taken from does not support it, and what that note did add turns out to be a four-release window nobody had measured.
The instrument
LF7c–LF7f read two strings: Loaded Prisma config from and The schema at app.prisma is valid. Both are about the file, and a fact about where a connection string lives is about a key. The criterion had to be a dial: a command that connects, pointed at a host named only in the config, and the CLI's own P1001 Can't reach database server at … naming that host.
Three things the row is built out of, and each of them is a rule already in the repository:
- It does not reach the network. Both hosts are loopback literals on
127/8with nothing listening (127.9.9.9:5599for the config,127.8.8.8:5588for the schema), so the local stack refuses in ~2 ms and no packet leaves the machine; being IP literals, there is no DNS lookup either. They are distinct from each other and from every other address in the probe file, because the whole question is which of two urls was dialled and a shared address could answer it by accident. - It goes through
inProjectRaw, neverinProject.spell()injects a literal url into any datasource block that has none — which is the very thing this clause says moved. A row that went through it would hand the 6.x answer back the url it deliberately left out, and a pass below the boundary would be the harness's. - A literal url, never
env(...).envis exported fromprisma/configonly from 6.18.0 (JOURNAL/154, LF9a) — the same release that addedengine— so a row written the way the fact's owncorrect_codeis written would close its window onenv's arrival rather than on the datasource's.
An absence and a blind instrument are the same value
tools/lib/identifiers.mjs's rule, applied to a CLI instead of a grep. This row reads false when a string is missing, and the CLI never dialled anything is also what a rung whose CLI fell over for an unrelated reason looks like. So the refusals this ladder actually produces are enumerated — every one of them a legitimate false for this clause — and anything else throws:
| what the rung said | rungs | why it is a legitimate false |
|---|---|---|
Could not find a schema.prisma file | 6.0.0–6.3.0 | no config support at all |
Failed to import config file | 6.4.0 | the release ships no prisma/config |
The "path" argument must be of type string | 6.5.0 | file loaded, schema key not honoured |
Argument "url" is missing in data source block | 6.6.0–6.19.0 | the clause's exact negation |
no longer supported in schema files | 7.x | only reachable by the two-url rows |
The first three are LF7c/LF7d's own boundaries showing through. The fourth is the answer.
What the ladder said
31 stable minors, 6.0.0 → 7.10.0, on the committed bisector:
| row | what it puts where | result |
|---|---|---|
| LF7g | url in the config, schema's datasource block has a provider and no url | rises at 7.0.0, CONFIRMED |
| LF7h | the same, plus the engine: "classic" key 6.18.0 requires | rises at 7.0.0, CONFIRMED — identical on all 31 rungs |
| LF7i | url in both places | window [6.18.0, 7.0.0), WINDOW_HOLDS |
LF7h is the point of the session. The obvious place to date this clause from is the 6.18.0 release note, which says in as many words "Support for defining your datasource in prisma.config.ts", and adds that "to set the datasource, we also must include the new engine key". JOURNAL/160's rule is that an arm asking about one key must carry the key that release requires beside it or the arm is unreadable — so it does, and it reads identically to the plain arm on every rung. On 6.18.0 and 6.19.0, a schema whose datasource block omits its url is refused Argument "url" is missing in data source block whether or not the config supplies one. What 6.18.0 added is not what this clause claims.
What it did add is LF7i, and it is stated as a conjunction, which is JOURNAL/164's rule for an inertness row:
- with
enginepresent, the config's host is dialled —127.9.9.9:5599, on 6.18.0 and 6.19.0 and on no other rung; - with
enginedropped, the schema's host is dialled —127.8.8.8:5588— on every rung from 6.6.0 to 6.19.0.
Without the second half the row could not tell the config's datasource was accepted from the config's datasource is ignored and this rung happens to prefer the config. With it, the answer is that a config datasource with no engine beside it is discarded silently. So the config's url wins six months before 7.0.0 makes it the only place a url may live — and a reader who wrote it without engine, and who never type-checks the file, saw the key have no effect and no message saying why.
That last measurement is a CLI shadow for LF9, whose statement gives the same requirement from the type side ("a datasource in the config required it"). LF9's note now carries it: the requirement is enforced by dropping the key, not by refusing the file.
What moved
data/prisma/facts.json, three lines: LF7's statement, LF7's note, LF9's note. The statement's second sentence now reads
The datasource URL lives there, not in
schema.prisma— and that half is true from 7.0.0 and not before: every 6.x release still requires the schema's ownurl, though from 6.18.0 aurlin the config overrides it where both are present, provided theenginekey that release requires sits beside it.
No date moved and no count moved. LF7's introduced_in was already 7.0.0; this clause had no date to correct, and the two rows that carry it land on the fact's own. 166 runs / 168 findings (161 chargeable) / 8 libraries, unchanged — this was an instrument, not a battery, so no finding is charged and no fact is added. tools/audit/fact-citation-sweep.mjs run as the charter requires of a session that corrects a fact: clean, 99 api joins, one allowlisted.
Five --check surfaces green; selftests MCP 54, runner 62, gate 46, identifiers 35. The prisma correction pack grew again as a side effect — 95,898 → 98,102 characters, 36,236 of them in the rules file — which the distribution lane should note: it is the third move in two days, and BM2's arm pin (JOURNAL/167) is the reason that can no longer silently mis-bill a cell.
LF7 now has no undated clause. Its first sentence is LF7/LF7a/LF7b, its second is LF7g–LF7i, its third is LF7c–LF7f, and its fourth is LF7e/LF7f.