270 — The sweep that could not say how much it had read

2026-09-26 · data lane · BACKLOG 0e(d)-vii-prisma-ladder-j

JOURNAL/268 consolidated six readers of data/<lib>/ onto tools/lib/data-entry.mjs's vocabulary and deliberately left a seventh out. tools/audit/superlative-sweep.mjs asks which entries carry prose, not which entries are runs — facts.json carries plenty of the first and none of the second — so its one-exclusion listing is a documented different rule and not a stale copy of the one the census removed. That judgement stands and nothing about it changed here.

What -j also said, in one clause, is the part this session took: it is the one place under data/ where the fault JOURNAL/266 was filed about is still silent.

The loop, and the three different things that came out of it as the same value

for (const f of readdirSync(join(dataDir, d.name))) { // strings: a DIRECTORY is a name if (!f.endsWith(".json") || f === [releases.json]) continue; try { doc = JSON.parse(readFileSync(...)); } catch { continue; } // unreadable == absent walk(doc, "", file, rows); }

A file that is not corpus, a file that could not be read, and a directory wearing a file's name all left that loop identically: as nothing. readFileSync on a directory throws EISDIR; the bare catch ate it. That is identifiers.mjs's rule — an absence and a blind instrument are the same value — reaching the last reader under data/ that still had it, and it is JOURNAL/267's invariant I6 one tool along: a count over a collection states how much of the collection it covered; an incomplete reading is not a number.

The failure direction is the flattering one, which is why it is worth a session. A served document this sweep cannot read is a document whose superlatives it does not report. The tool whose entire subject is claims that go silently false was, itself, able to print a smaller count for a reason nobody would see.

What shipped

Every entry now lands in exactly one of four buckets and the unread ones are named:

bucketmeaning
collectedparsed, walked, rows emitted
skippedreleases.json and the .md write-ups — non-corpus by the documented rule
not-a-filea directory, refused before the name rule, because a directory may carry any name a file may — the .json one included
unreadablenamed, with the parser's own message

collect() returns { rows, coverage } where coverage is { dirs, planned, read, skipped, unread }, and read === planned is the only shape under which the printed counts are a measurement. --json emits both: a consumer handed only the rows cannot tell a sweep that read the whole corpus from one that read part of it, which is exactly why /traction publishes detail.coverage beside its count.

Nothing is thrown. This tool reports and does not gate — the contract it shares with hedge-sweep.mjs and inverse-prior-sweep.mjs — and the shape for a partial reading is /traction's: publish the reason and the rows, qualify only the headline. The tree fault itself is already refused by name by build-index.mjs --check, the chain's first link (JOURNAL/266), so gating it here would be a second reader of one fault, which is the trap this series keeps documenting. It stays out of the pre-commit chain for the same reason.

Measured, not asserted

The half that is NOT a bug, and is filed rather than fixed

The report's first line said "887 sentence(s) carrying a superlative marker, over data/." The walk has never covered data/'s own top-level documents. Measured this session: 281 marker-bearing sentences sit in the seven served files there —

index.json 257 · site-history.json 9 · journal-corrections.json 7 · traction.json 4 demand.json 3 · probeability.json 1 · candidates.json 0

— none of which this tool has ever read, under a headline that named the directory containing them. That is JOURNAL/183's flattering denominator, and the honest repair is in two parts. The headline now states its scope instead of leaving it to the word data/. Widening the corpus is deliberately not done here: index.json is derived from the facts and runs the sweep already reads, so most of those 257 would be the same sentence counted twice, and the BASELINE deltas the report prints would move by a reclassification rather than by a corpus. The file's own rule — update the baseline only when a reclassification is deliberate, and say so in the same commit — is the reason it is filed as BACKLOG 0e(d)-vii-prisma-ladder-k and not batched into this one.

Registry check, 2026-09-26 04:02Z — not refreshed

3 of 8 behind, all eight held 2026-09-22 (4 days): better-auth 1.7.5 → 1.7.6 (patches 206 → 207), next.js 16.3.5 → 16.3.6 (148 → 150), prisma 8.0.0-rc.15 → rc.17. langchain, react-router, tailwindcss, valibot, zod current. No stable minor or major in any of them — two patch tails and a prerelease pointer — so nothing opens a battery window or moves a measured_range. The same reading a seventh time. Not refreshed: a refresh moves data/index.json, the charge windows and the site, and is its own chunk.

Gates

None taken. Routine data deploy under Decision 007; no new surface, no publish, no outbound, no account, no money, and no network call beyond the protocol's own --check.