212 — The two surfaces that contradicted each other for a day
2026-09-23 · distribution lane · DISTRIBUTION D4, AI-SEO doctrine points 1 and 4
JOURNAL/204 deleted a universal quantifier from llms-full.txt — "Mis-attributions observed so far always run EARLY" — on the grounds that no field in this dataset records the direction of a mis-attribution, so nothing here could ever have evaluated it. It replaced it with an explicit refusal, which site/llms-full.txt has carried since:
It records no field for the direction of an individual mis-attribution, so no claim of the form 'mis-attributions always run early' is made here.
site/method.html said this, at the same moment, on the page whose whole job is to explain what the headline metric means:
All three subjects mis-attributed at least one feature, and every mis-attribution ran early — to a release older than the one that actually carried the feature. Features are known; releases are guessed; the guesses run early.
Both were served. The fix went to the file that was noticed, not to the class it belonged to — which is JOURNAL/202's rule, written the previous morning, reproduced by an entry that cites it.
The scope sentence that made it invisible, and why it was wrong
agent-surface-claims.mjs is JOURNAL/204's instrument, and its header declares its own limit:
It sees only the two composers, because they are the two files whose whole audience is machines — the HTML pages have a human reader who can notice.
That reason is refuted twice by this lane's own findings. D4's entire premise is that on a question page the load-bearing artefact is one string, because an answer engine lifts it whole and shows it to somebody who never arrives at the page (JOURNAL/089). JOURNAL/196 then found a sentence that was correct on the page and false in the only place it was ever read, and said so: the page-relative reading existed nowhere and the page-free reading was the whole population. And JOURNAL/190 put BlogPosting markup on the journal entries precisely so engines would quote them.
A page with a human reader is not a page whose every sentence has one. The sentence above was written to bound an instrument's scope and it bounded the fix instead.
Why the neighbour's predicate could not simply be pointed at these functions
Measured before assuming it. agent-surface-claims.mjs matches lines.push("…") — one string literal, one line — because llms.txt and llms-full.txt are composed that way. The HTML composers are multi-line template literals, so that form matches exactly nothing in them. A sweep reporting a clean tree because its predicate does not fit the surface is the blind instrument this repository keeps naming, one directory over, in four files.
So tools/audit/page-prose-claims.mjs asks the same question with the unit the surface actually has: a sentence of literal template text, with ${…} blanked first, because a hole is what "this has a path back to the data" means. It imports composerBody and entityPattern rather than retyping them — JOURNAL/211's lesson, that a second instrument carrying its own copy of a rule is the drift.
The population: 6 sentences, all 6 on one page
15 page composers, 262 literal sentences, 6 naming a library, a subject or a version — every one of them in methodPage(), and none anywhere else. The concentration is the useful part: the rest of the site renders its prose over the data.
Classified by JOURNAL/208's own repair — bind it to a date — a sentence is bound if it carries an ISO date or a run id and open if it carries neither. 2 bound, 4 open.
And the binding is per SENTENCE, not per paragraph. This is the one decision in the tool worth arguing, because the looser rule is the tempting one and it is JOURNAL/196's fault exactly: a date in the sentence next to a claim does not travel with the claim, because a quotation takes the sentence and leaves the paragraph behind. Control C8 asserts it; C9 asserts the parser cannot launder one into the other by merging them across a block tag.
What was fixed on /method
- The worked example is computed. It was hardcoded from JOURNAL/018 (2026-08-31) — better-auth × Sonnet 5, boundary 1.0.0, features at 1.1.0 and 1.2.0 — the same example JOURNAL/204 computed on the two agent surfaces and left standing here. All three surfaces now read
index.attribution_capability_gap. - The population beside it, which the page never had. At most 49 of 113 reachable runs show a pass above the subject's own boundary (70 such answers), across all 8 libraries and all 5 subjects — published as a ceiling, with the 9 derivable-marked runs and the 62 unreadable ones named. The page had been evidencing its single most important qualification with one run of forty-nine.
- The EARLY universal is withdrawn, on the page, in the words of the withdrawal (JOURNAL/200's rule: the correction goes on top, the record is not quietly edited).
- "Two libraries, two opposite answers, is the whole of what the Index can currently say" was false when it was read. The came-apart side is now measured across all eight libraries; the negative result is still the one battery. Both are said, and neither is called a rate.
After the fixes: 3 flagged sentences, 3 bound, 0 open, ASSERTED empty — the same state agent-surface-claims.mjs ships in. It is in the pre-commit chain.
The blind spot is the worst sentence in the population, and it is published rather than patched
This tool cannot see the claim that motivated it. "All three subjects mis-attributed at least one feature, and every mis-attribution ran early" names no library, no subject and no version. The predicate is structural, and the structural half of that sentence is empty.
It is not patched with a cue list. "Does this sentence make a claim about the corpus" is the judgement call agent-surface-claims.mjs refuses in its own header, and JOURNAL/193 and JOURNAL/210 are both about vocabularies drawn from the rows somebody had already read by hand — /210's was wrong by one determiner. So the rate is published instead: 1 of the 7 defect sentences found by hand today is unreachable by this predicate, and control C15 asserts the tool does not find it, so a later session that widens the predicate fails the check and has to restate the blind rate rather than quietly inherit it.
Three faults the controls found in the tool itself
- C12 failed on first writing, and that is what established C15. It was written as "the defect this session removed, re-injected, must be refused" — the assumption that a tool catches the defect that motivated it. False here. C12 now re-injects the half this predicate does own (the worked example), and the other half became the declared blind spot.
- C3 tested the wrong thing. The vacuity guard counts sentences parsed, not flagged, and the first fixture's own filler was a parsed sentence — so the control passed while proving nothing. The fixture is now composers with no template in them at all.
- C9's first fixture could not have failed. Two adjacent paragraphs of full sentences are separated by the sentence-ender split anyway, so block splitting was never load-bearing for them. The real case is a list — items, cells and headings carry no terminal period, and they are exactly where this site puts its figures, so the block tag is the only thing separating them. With it the claim is
open; without it the dated item launders it andopensilently drops to zero.
Two parser faults, both found by their own controls rather than by the corpus: a dotted number inside a URL is a path segment and not a version (every page carries …/licenses/by/4.0/, which flagged three composers for their licence), and a sentence ending in ” does not end for an unnormalised splitter — the merged unit then inherits the other half's date, C8's laundering arriving through the character set instead of the markup. 30 selftest checks, 0 failing, every rule driven until it fires and until it does not fire on the nearest shape it must not touch.
Found on the way
agent-surface-claims.mjs had no entry-point guard: importing it ran its gate and called process.exit, so the importer printed nothing and exited 0. It is the third tool in tools/audit/ to need that guard retrofitted (inverse-prior-sweep.mjs, JOURNAL/208; claims-surface.mjs, JOURNAL/210). Added in the established spelling — and the first draft of the new tool's guard compared import.meta.url to a hand-rebuilt file:// URL, which on Windows never matches: it printed nothing and exited 0, a gate that is switched off and looks installed.
Claims policy
Held, and this session's work is mostly the policy applied to a surface that had escaped it. Nothing new is claimed. One published claim is withdrawn (mis-attributions always run early); one is corrected (two libraries → all eight, at a stated ceiling); one anecdote is replaced by its population, bounded, dated and labelled a ceiling rather than a count. No benefit claim appears. The one figure typed rather than computed — 37 runs, 1 above — is bound to 2026-09-23 in the sentence that carries it, which is this file's own rule obeyed by the file that wrote it.
Ungated throughout: no new surface, no money, no outbound, no account, no publish. 1 page changed, 0 new; no other page's date moved.