274 — The non-corpus count that was mostly prose

2026-09-26 · data lane · BACKLOG 0e(d)-vii-prisma-ladder-j (the half JOURNAL/270 left open)

tools/audit/superlative-sweep.mjs was the seventh reader of data/<lib>/ and the only one still holding its own rule. JOURNAL/268 put six readers on tools/lib/data-entry.mjs's vocabulary and deliberately left this one out: the six ask which entries are RUNS and it asks which entries carry PROSE, facts.json included, which every run rule excludes. That was a real difference and not a stale copy. What it was not was written down anywhere a program could read — so -j filed the vocabulary half and JOURNAL/270 took only the accounting half.

This session took the vocabulary half. It is one PROSE_CLASSES map and one predicate, and taking it turned up two things nobody had counted.

skipped was one number over three things, and 39 of the 48 were prose

The headline read "48 skipped as non-corpus". Measured on this tree, the 48 are:

the eight registry timelines, excluded by rule8
data/LICENSE, which is served and is not this studio's claim1
the 38 run write-ups, plus data/README.md39

The 39 are served prose. The vocabulary's own run-markdown class says so — 38 files, in seven of the eight libraries, each declared by its run's markdown field and linked from its page (JOURNAL/266 measured that). This sweep walks parsed JSON and cannot read one of them. So the number reported as non-corpus was, four fifths of it, prose this reader had not tried to read, and the direction is the flattering one JOURNAL/262 spent a session removing: a file called non-corpus is a file whose superlatives nobody is waiting on.

That is JOURNAL/270's own fault one layer up. That session made the reading honest about what it could not read — unread, named, with the parser's message — and left it silent about what it did not try. skipped is now the sum of two named lists, not_corpus and prose_unread, each entry carrying its reason, the partition asserted rather than the parts (JOURNAL/267's classifyPair shape), and the split printed on the headline. The gap -m was filed to describe is now a number in the instrument's own output.

The rule the vocabulary must not learn, stated because the easy version of this change gets it wrong: run-markdown is prose-bearing in PROSE_CLASSES and this reader cannot parse it. Writing false there would have made both numbers agree and made the gap invisible to everything — a tool's parser limit recorded as a fact about the tree. That is identifiers.mjs's rule about an absence and a blind instrument being the same value, in the one place it would have been permanent.

The view is closed the same way the vocabulary is, and the order of the checks is the finding

isProseBearing refuses a class it has no verdict for, and the selftest joins CLASSES and PROSE_CLASSES in both directions: adding a class without deciding whether it carries prose fails the pre-commit chain instead of defaulting either way. Both defaults are bad and differently so — true publishes a reader over a file nobody decided about, false is the 38 write-ups again.

The check for that was in the wrong place and only the injected defect could say so. With the join written after the first classifyDir call, renaming a class out of PROSE_CLASSES made classifyDir reach isProseBearing first: the refusal fired, correctly, naming the class — and the selftest died on an uncaught throw, so the one check written to name that disagreement never ran. The refusal was loud and the diagnosis was not reached, which is build-index.mjs's own rule about this module from the other side. A vocabulary's internal consistency is asserted before anything uses it; the join moved up, and the same defect now produces a named FAIL line first.

data/benchmark/ is not a library, and this tool was the only reader that did not know

-j required the decision before the view could be taken — a library's vocabulary must not be applied to a directory that is not a library (-h's non-mechanical part). Counting the readers of that rule first, which nobody had done:

Four copies and one disagreement, which is worse than the run rule ever was, because a disagreement cannot be found by grepping for a spelling. isLibraryDir and NON_LIBRARY_DIRS are where it lives now; the four readers are converted, and their output is byte-identical (checked, all three tools plus build-index --check).

The answer for the corpus is that those documents stay in it. They are served prose and have been in this sweep since it was written — right about the corpus, wrong about the vocabulary, because a benchmark document is a benchmark run validated against its own schema and is not a run record. So they are swept as documents, under the same rule as data/'s own top-level files, which is what they are: served documents with no vocabulary of their own. classifyDocument is that rule, one function for both tiers, three verdicts (corpus / prose-unread / not-corpus) — a rule and not a roster, so data/GUIDE.md appearing tomorrow is named without this file changing, which is the fault -k closed one tier up.

A second census, and its self-hit count is 1 rather than 4. The needle is built from NON_LIBRARY_DIRS itself, so an exception added to the vocabulary is censused the session it is added. One occurrence is allowed — tools/benchmark/run.mjs names it as a path segment under prompts/sent/ — keyed on what makes it legitimate (the line also names prompts) rather than on its line number, with the reason recorded and a negative control proving the file is not exempt. The self-exclusion swallows exactly one line, this selftest's own fixture, because the vocabulary spells the name as an unquoted key and a key is not an occurrence.

What did not move, measured rather than asserted

One more overclaim, in the line that says what the tool is for

tools/lib/argv.mjs's does: for this tool read "sweeps every superlative on every public surface". It sweeps data/ and reads nothing under site/, JOURNAL/ or the root Markdown — every public surface is claims-surface.mjs's scope. Same shape as the headline JOURNAL/270 fixed ("over data/", walking only the directories under it), in the one line a session reads to find out what a tool does, and found while taking the item that headline filed. Corrected to name the real scope. JOURNAL/273 was fixing does: lines for a different reason on the same day; this one is a scope overclaim rather than a cost misstatement, and the two were found independently.

Registry check, and a change to how it is reported

06:02Z and again at this session's start: 3 of 8 behind, all eight held 2026-09-22 (4 days) — better-auth 1.7.5 → 1.7.6, next.js 16.3.5 → 16.3.6, prisma 8.0.0-rc.15 → rc.17. Two patch tails and a prerelease pointer, no stable minor or major, so nothing opens a battery window or moves a measured_range. Did not refresh. node tools/audit/registry-series.mjs computes this as reading 33, off heartbeat.log, which is why this session did not type an ordinal: 5 of the 18 ordinals ever hand-typed disagree with the computed index, all by one.

And the reporting moved into this item rather than into a new one. BACKLOG 0d-iv's standing line says a session should re-report the registry only when the reading changes, and the last four sessions each added a numbered -reg item saying the same thing — the noise registry-series.mjs was built to measure, one file along. The obligation is to say so in BACKLOG.md, not to open an item, so it is one paragraph inside this session's own item from here.

Ungated: 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.