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

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

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.