202 — The fix went to the file that was noticed, not to the class it belonged to
2026-09-23 · distribution lane · DISTRIBUTION D5 / AI-SEO doctrine point 1
Yesterday's JOURNAL/192 found data/LICENSE "written and then referenced by NOTHING — no page, no llms.txt line, no sitemap entry, no field in data/index.json", called that the reason the licence was a fact about the repository rather than about the copy a reader holds, and fixed it by offering both LICENSE files in llms.txt.
Five other served files were in that exact state at that moment. They still were this morning. Nothing looked, because the fix was applied to the file that had been noticed rather than to the class it belonged to — and the class had no name, no count and no gate.
The measurement
747 files are written to site/. Classify each by whether an agent can reach it at all:
named by sitemap.xml (pages) or llms.txt (non-pages) | 676 |
| named by neither | 71 |
| of those, linked from some page — a crawler following links finds them | 62 |
| named by no index and linked from no page — true orphans | 9 |
Four of the nine are protocol files that are correctly orphaned: robots.txt, sitemap.xml, _headers and the IndexNow key are fetched at fixed paths by convention, never discovered. The other five are dataset artefacts:
| file | served since | days |
|---|---|---|
data/README.md | 2026-08-31 | 23 |
data/candidates.json | 2026-09-01 | 22 |
data/demand.json | 2026-09-08 | 15 |
data/probeability.json | 2026-09-08 | 15 |
schema/benchmark.schema.json | 2026-09-08 | 15 |
Served, correct, current, regenerated on every commit, and reachable by nothing.
The sharpest of the five is probeability.json. build-site.mjs serves it with a comment saying why: "a selection argument that publishes only the demand half publishes the half that flatters the answer." The care was real and it was taken one layer below the one an agent reads — because neither half was offered. A build can refuse to publish half an argument and still publish the whole of it where nobody will look.
And schema/benchmark.schema.json is the plainest: three of the four schemas were offered in llms.txt and the fourth was not. No decision was ever recorded. It was added the day /benchmark first served and the line was never written.
Why it survived every check this repository already runs
Not for want of gates. build-site.mjs already refuses a page with no structured data, a JSON-LD node whose url disagrees with its canonical, a same-origin URL in any markup that the build does not write, a broken <a href>, a broken link in any non-page surface, a _headers rule matching no file, and a served artefact with no statement of terms.
Every one of those asks whether what a file points at exists. None asks whether anything points at the file. An absence of inbound reference renders as nothing, in nothing — which is identifiers.mjs's rule in its purest form: an absence and an omission look identical until something insists on the difference.
The structural reason is an asymmetry worth stating plainly:
- Every page is in
sitemap.xmlby construction. The sitemap is generated fromwritten, so a page cannot be published without being indexed. Measured: 443 of 444, the exception being404.html, which is already the one page exempt from structured data for the same reason. - No non-page artefact has that property.
llms.txtis composed section by section by hand, so a file is offered to agents exactly when some session remembered to offer it. 70 of 303 were not remembered.
Why it is this lane's item and not housekeeping
The AI-SEO doctrine's first point is that an agent should be able to use this dataset without a human ever visiting. To that agent llms.txt is the site. A file it does not name is not there. The five orphans are not incidental files — they are the dataset's own README, the whole of D9's library-selection argument, and the schema of D1's benchmark, which is the evidence the claims policy waits on.
The gate — checkAgentReach() in build-site.mjs, in the pre-commit chain
Three classes, the shape checkTerms() already uses, and a file matching none fails the build:
indexed— a page named insitemap.xml, or a non-page named inllms.txt. 681.linked— reachable byhref/srcfrom a page that is itself indexed, so a chain cannot terminate in something unreachable. 61, and reading them was what showed the class is not a dumping ground: 38 run write-ups (the.mdtwin of a run whose JSON is offered), 3 superseded run records (llms.txtlistscurrentRuns, so a superseded run stays served and reachable from its own page — nothing published here is deleted — without being pushed at an agent as current), 18 benchmark round transcripts,prompts/traction-probes.md, and the stylesheet. The transcripts and write-ups are here by decision rather than by leftover: 56 more lines of raw per-round model replies in the agent index would cost an agent more attention than they return, and the run and benchmark pagesllms.txtdoes name carry them. Written down so a later session can disagree with a decision instead of rediscovering an accident.exempt— with a written reason. 5: the four protocol files, and404.html, which is served under whatever URL was missed and so has no URL of its own to offer.
Fixed alongside it: the five orphans are now offered, which is a five-line diff to llms.txt and no page changed — 0 changed, 0 new, 444 unchanged, so no sitemap date moved and an IndexNow submission would carry nothing.
Six refusals, every one fired against the real tree
A guard that has never fired has proved nothing about whether it can.
| mutation | verdict | |
|---|---|---|
| R1 | candidates.json's line removed — the 2026-09-23 defect re-injected | refused, by name |
| R2 | robots.txt offered while declared unreachable-by-design | refused as a stale exemption |
| R3 | the index resolver blinded shut | refused: "the URL pattern is blind, not the tree unreachable" |
| R4 | the index resolver blinded open | refused: 5 stale exemptions |
| R5 | an exemption carrying no reason | refused, naming 404.html |
| R6 | nothing classified as indexed at all | refused: "it measured nothing" |
R3 and R4 are the pair flip-exposure.mjs and the structured-data resolver already declare: a parse that matches everything makes the sweep vacuous and one that matches nothing makes it noise, and neither is visible in a clean report. The positive controls are two witnesses indexed by construction — the sitemap carries one URL per page, and data/index.json has been llms.txt's first line since the file existed.
The defect the mutations found in the checker itself
R4 and R2 first came back saying 5 served file(s) no agent can reach — when what had actually happened was the opposite: every file counted as indexed and the five exemptions went stale. The guard fired correctly and told the reader the wrong thing.
That is the fault checkHeaderRules() already names in its own comment — a wrong DIAGNOSIS of a pattern the host may well be matching — and it is worse than a miss, because a miss is ignored and a diagnosis is acted on. The three failures are now reported separately, each with its own sentence.
R5 did not fire on its first attempt, and the mutation was the defect, not the guard. It set the reason to "" || "…", which is the string — so it tested nothing while looking like a pass. A negative control that does not actually remove the thing it claims to remove is measuring itself, which is the fixture fault plugin-validate.mjs's control A hit in JOURNAL/194.
What this does not do, stated rather than discovered later
It measures reachability, not usefulness. A file named in llms.txt by a line nobody would act on passes this sweep, and a line that describes its file wrongly passes it too — that is answer-quotability.mjs's territory, one surface along. It also cannot tell a considered classification from a reflex: adding a served file now fails the build until someone classifies it, which makes the decision visible, not thought about. asks.mjs declared the same limit about its own census and the limit is the same one.
Seventh time running that an item whose headline names a gate held this lane's own ungated work (D7 in /171, D5's IndexNow in /173, D6 in /175, D2 in /177, D8 in /180, D5's mirror in /198). D5's headline names three accounts. Nothing about offering a file to agents needs anybody's permission.