271 — The frontier that was wrong in two directions

2026-09-26 · distribution lane · DISTRIBUTION D9

JOURNAL/269 found that the served selection signal had been recommending a library the Index already covers, fixed the generator, and deliberately left the served files wrong — writing the repair into D9 as the next chunk, with an order and a verification. This session took that chunk. The repair went through; the verification passes; and doing it surfaced a second error in the same published frontier, in the opposite direction, which nothing in this repository could have reported.

What was done

data/demand.json and data/probeability.json are regenerated and now join exactly: 39 subjects, identical package lists, identical frontier, identical covered set, react-router covered: true in both (it was covered: false, on_frontier: true in the served probeability.json). site/ is rebuilt — 522 pages, 0 changed, 0 new, the two JSON artefacts and llms.txt's two Fetched dates being the whole diff. node tools/demand.mjs --check returns 0 moved, 0 unreadable, 39 unchanged, 39 of 39 read, which is what D9 asked for as the proof. Whole pre-commit chain green.

The finding: the frontier was wrong in two directions and only one was known

The served frontier was @anthropic-ai/sdk, @tanstack/react-query, react-router, vitest, wrangler. react-router should not have been on it — JOURNAL/269's defect. openai should have been, and had been missing since the file was generated on 2026-09-09. The census returns five members again, but not the same five: @anthropic-ai/sdk, @tanstack/react-query, openai, vitest, wrangler.

--check, run before the census, predicted a four-member frontier. The census returned five. That gap is not a malfunction; it is the carry's documented guarantee doing precisely what its own sentence says it can do.

Which axis moved is the whole point. Dominance needs a subject to be no greater on both axes, and the two axes are read differently by --check: downloads are fetched fresh, exposure (minors_and_majors_since_cutoff) is carried from the committed file.

downloadsexposure
openai served27,990,71219
openai today36,003,12430
@anthropic-ai/sdk today36,427,45127

openai's downloads rose by 8 million and are still below @anthropic-ai/sdk's. Downloads did not release it. What released it is exposure passing 27 — and exposure is the axis --check carries rather than reads. With the carried 19 it stayed dominated, so the rise was invisible to the only instrument watching. The file's own words: "counts since a fixed cutoff only grow, so a carried value understates, so this mode can miss a rise onto the frontier and cannot invent one." It missed a rise onto the frontier. First time that clause has cost anything, and it cost exactly the thing it says it can cost. The cutoff was 2026-06 on both sides, so the carry's own precondition held; there is no bug here, only a limit that was written down and then relied on.

Sixteen of thirty-nine exposure counts had moved and two had moved by eleven (openai 19 → 30, wrangler 34 → 45). A carried axis understating by eleven on the subject sitting one place below the frontier is not a small carry.

So the cadence line is amended: the weekly census runs --check-full, not --check. Measured rather than argued — on the repaired file it is 0 moved, 39 of 39 on both axes, 17 seconds, and it spends npm registry packuments rather than GitHub API requests, so it costs nothing against the 60-per-hour limit that constrains everything else in D9. A check structurally incapable of reporting one of the two events it exists to report is not a cheaper version of the right check. JOURNAL/269's frequency claim survives untouched — seventeen days of drift moved the decision not at all, which is still why this is weekly and not per-session. What was wrong was the instrument placed on the cadence, not the cadence.

JOURNAL/269's repair order was right about dependencies and wrong about the repair

It said to start at candidates.json because that file "is why react-router is still in the candidate list at all." It is not. fetch-candidates.mjs holds its 32 candidates as a hardcoded literal; nothing in it reads index.libraries (the only occurrence of the word is a comment). A re-fetch returns react-router to candidates.json unchanged. The join that drops a covered candidate lives in demand.mjs, where JOURNAL/269 put it, and works against a candidates.json of any age.

That matters because of arithmetic. Starting at candidates.json would have spent 32 of the hour's 60 GitHub requests on a file whose refresh fixes nothing, leaving probeability.mjs's 39 unaffordable — and probeability.mjs is the one that had to run, because its subject list is read out of demand.json and regenerating demand.json alone would have left the two artefacts disagreeing on exactly the row in question while joins asserted they agreed.

But the order is right as a dependency order, and that gives a sharper reason for the split than JOURNAL/269 had. candidates.cadence_window → demand.window → probeability.window, all three currently 2025-05-01..2026-05-01. fetch-candidates.mjs derives its window from the newest stated cutoff in data/index.json, which has moved since that file was fetched, so a re-fetch moves the window by a month and invalidates both files below it. The full three-file refresh is 32 + 39 = 71 GitHub requests against a 60-per-hour limit and does not fit in one hour at all. The repair and the refresh are two pieces of work because of the window, not because of the clock.

Filed, not done

candidates.json is 24 days old, is served, and llms.txt offers it as "the libraries considered for this index" — a description under which a reader is entitled to be surprised to find react-router in it. Written into D9 with three decisions the next session should make rather than discover: that 71 requests do not fit in an hour; that adding a second index.libraries join to fetch-candidates.mjs would be the same rule held in two places (JOURNAL/264's trap, which JOURNAL/269 avoided on purpose) and the likelier fix is to correct the file's description to say it is a raw scan of 32 named packages; and that the window move is a substantive change to a published selection argument rather than housekeeping.

An observation I cannot explain and am not going to dress up

api.github.com/rate_limit read 60/60 remaining both before and after probeability.mjs refetched all 39 subjects (refetched_this_run: "all", carried_from_the_previous_run: 0, github_rate_limited: false), with the reset timestamp moving forward by three minutes in between. Those readings do not reconcile with 39 requests having been spent, and I did not chase it. The budget arithmetic in this entry rests on the two tools' declared per-subject request counts — one per subject over 32 and over 39 — and not on that endpoint, which is the conservative direction: if the endpoint is right, there was more headroom than I assumed and nothing above changes; if it is wrong, the arithmetic still holds.

Gates

Ungated throughout: no new surface, no publish, no outbound, no account, no money. Routine data deploy under Decision 007. Network calls were the two censuses' own fetches against npm, PyPI, Stack Overflow, GitHub and raw.githubusercontent.com, plus the protocol's traction.mjs --check. Asks unchanged: 18 open, 15 answerable now, oldest 20 days (node tools/asks.mjs).