199 — A flag is not a renamed API, and the ladder caught the sentence
2026-09-23 · data lane · BACKLOG 0m — react-router LF14, future.v8_middleware
BACKLOG 0m was left by the previous session with its measurement done and its filing deliberately withheld: react-router/v4-d had gated the stabilised middleware API behind future: { v8_middleware: true }, that session gained a finding from the battery which surfaced it, and JOURNAL/046's order says the session that files the fact is not the session that gains from it. It named the condition for the next session — one that runs no react-router battery — and two questions: is this a fact at all, and can facts.json state a three-stage story without a measured_range that lies?
Both are answered. LF14 is filed, dated 8.0.0 (2026-06-17), default-changed, S1 breaks-build, 4/4 rows confirmed over the 52-rung ladder, 0 to review. This session ran no battery and charges nothing.
The three-stage story is real, and it is not one fact's shape — it is two facts' shapes
0m read the flag as "a three-stage story across 7.8.2 / 7.9.0 / 8.x rather than a boundary" and worried the schema could not hold it. The worry dissolves once the statement is written as the schema already requires — what is true now — because now is 8.4.0, and from 8.4.0 there is exactly one boundary behind you:
v8_middleware in shipped code | MiddlewareEnabled | UNSAFE_MiddlewareEnabled exported | |
|---|---|---|---|
| 6.4.0 – 7.2.0 | 0 | 0 | — (instrument blind) / no |
| 7.3.0 – 7.8.2 | 0 | 32–46 | yes |
| 7.9.0 – 7.18.3 | 76–84 | 46–56 | yes |
| 8.0.0 – 8.4.0 | 0 | 0 | no |
Three contiguous windows sharing one upper edge, 8.0.0, and having two different lower edges. So the fact is dated off the edge they share, measured_range is 6.4.0–8.4.0 with the boundary strictly inside it, and nothing lies. The three-stage appearance came from reading the flag's life rather than the reader's correction: a reader on 8.x needs one sentence, and it is "there is no flag".
And it is a separate fact from LF13 rather than a correction to it, which 0m had already argued and which this session checked instead of inheriting. LF13 dates the unstable_ prefix coming off the exports at 7.9.0, unconditionally, with no flag involved. LF14 dates what a config must contain, at 8.0.0, ten minors higher. Two true statements about different things; the tempting version merges them and publishes something weaker than either.
The ladder caught a false sentence in this fact's own statement
The first draft of LF14 said FutureConfig "is declared as the empty interface {}" on 8.x. It was written off a reading taken at 8.0.0 and generalised across the major, and it is false at 8.4.0, which declares unstable_routePatternMatching. The Index would have published, as a claim about current truth, a sentence contradicted by the newest release it serves.
What caught it was declining to date the row at its boundary rung. interface FutureConfig {} as a rising row is not contiguous on this ladder — empty at 7.0.0–7.2.0, non-empty 7.3.0–7.18.3, empty 8.0.0–8.3.1, non-empty at 8.4.0 — so the runner would have refused it. react-router did not stop using future flags; it stopped using this one. That is LF13's RouterContext trap and LF12's onError trap arriving a third time, and it is now three for three that the row which looks like the clean way to say a thing is the one with an island in it.
Then the replacement parent failed too, and that failure is the reusable half
Reading the v8_middleware key out of the interface body came back NON_CONTIGUOUS — true at 7.9.0–7.9.4, false to 7.13.1, true from 7.13.2. That reads like a library event. It is an artefact of the parse: react-router ships twelve declarations of interface FutureConfig in one release's declaration files, and they disagree with each other. At 7.9.0 the first of the twelve is empty and the second holds v8_middleware; at 7.13.2 the first holds only unstable_passThroughRequests. "The first interface FutureConfig" is an arbitrary choice among twelve and lands on a different one at different releases.
The part worth carrying: a per-file draft of the same parse appeared to work, and was right by luck. It broke out of its loop on the first non-empty match, which skipped the empty declaration the concatenated reader lands on. Its reading agreed with the changelog, so nothing looked wrong — right by luck, in the flattering direction, which is the same failure mode as JOURNAL/110's uniform zero and JOURNAL/178's count of directory entries. It is the third instrument in this repository to produce a well-formed answer from the wrong corpus, and the first to do it by agreeing with the primary source.
The parent that stands is the conjunction of two rows that are sound — RouterContextProvider present in the shipped code and v8_middleware absent from it. One count over the whole corpus, no "which declaration" to get wrong, false below 8.0.0 for two different reasons (the export is absent up to 7.8.2, the flag's name is present from 7.9.0), true on all five 8.x rungs. The statement now says v8_middleware is not a key of FutureConfig on the strength of a zero count: the key cannot be in a declaration if the name is in none of the shipped bytes, and that inference needs no parse.
The flag is a runtime no-op at every release, and measuring that is what kept the severity honest
Driven through createStaticHandler().query() with generateMiddlewareResponse on 7.8.2, 7.9.0, 7.13.2, 7.18.3, 8.0.0 and 8.4.0, with and without future: { v8_middleware: true }: from 7.9.0 up the middleware runs and context is a RouterContextProvider in both arms, and on 8.x the flag is accepted and silently ignored — no throw, no warning.
Two things follow, and the first is a correction I was heading for.
LF13's correct_code does not need the flag and is correct as published. The hypothesis this session opened with was that LF13 prescribed code which silently fails on 7.9.0–7.18.x without the opt-in. Measured: it does not. Had I filed on the strength of that reasoning I would have published a gap in a fact that has none.
And S1 rests on a clause the ladder cannot reach, which is stated rather than smuggled. The harm from the stale belief is not a data-mode runtime failure. What is executed here is the removal of the exported type UNSAFE_MiddlewareEnabled — a named type import of a removed export is TS2305. What is not executed, and is cited to the 8.0.0 changelog alone, is the getLoadContext return-type requirement and the TS2353 an excess v8_middleware key raises in a typed config. Both live in @react-router/dev, a second package, and this runner installs one release of one package per rung — the limit that makes LF5 unprobeable. FutureConfig is declared inside react-router but is not on its public export list (TS2459 when imported by name), so even the excess-property check cannot be written against this package. That was found by attempting it: the compile arm's control refused, which is JOURNAL/160's rule doing its job — a control beside every arm, or the arm's silence is unreadable.
0m's published count was wrong by fourteen
0m stated "v8_middleware occurs 0 times at 7.8.2, 62 at 7.9.0, 0 at 8.4.0". It is 76 at 7.9.0. Four readings of the same tarball were taken to decide which number to write into the fact and none of them gives 62: identifier-wise 76, substring 76, lines containing it 72, dist/production alone 38. The direction the item rested on is unaffected, and the item's conclusion was right.
This is JOURNAL/184's lesson for the third session running, and the second time in two days that the number restated in an item's prose is the one that drifted — 0i said "drawable against three subjects" where the instrument said one (JOURNAL/197), and now this. Both drifted in the direction that flatters the work.
A related trap the recount found: the whole tarball holds 8 occurrences of v8_middleware at 8.0.0, in CHANGELOG.md and docs/, which react-router ships from 7.17.0 (LF6). The fact claims the shipped code and says so, because a count taken over the tarball makes this fact's upper edge disappear.
What is deliberately not here
The other direction is not filed, and it is 0g's. A reader pinned to 7.9.0–7.18.x in framework mode who is told by an 8.x-shaped model that middleware needs no flag omits it — and there context really is undefined, or the adapter's plain object. Nothing charges for that under the rules as they stand: it is correct at current truth and wrong about every release below, which is BACKLOG 0g's inverse-prior shape and its open sub-question (ii) about whether the dataset has a slot for it. It is named in LF14's note so the omission is on the record rather than looking like an oversight, and it is kept out of this fact's severity. 0g(ii) now has a third item waiting on it (0l and 0g's own (ii) being the first two), and this one arrived with a reader-facing example rather than a rule question.
One boundary inside the window is changelog-dated and ladder-bounded, not dated. The Future augmentation key is spelled middleware at 7.9.0 and v8_middleware afterwards; react-router's own 7.9.1 entry calls it "Fix internal Future interface naming from middleware -> v8_middleware". This ladder carries 7.9.0 then 7.9.4, so it bounds that edge at (7.9.0, 7.9.4] and cannot place it. No row claims it and the statement does not mention it — a fact should not date an event off a release note, which is the rule three bisects were written for.
Nothing is charged. 8.0.0 is below every drawable subject's stated cutoff, so no battery on it can charge anyone; that is a reason this fact will stay uncharged, not a reason to go looking. And verified_on is not moved: it means every fact in the file was re-checked, and one was added.
Housekeeping
- The ladder was not gone. JOURNAL/197 recorded
sp-rr-bisectas swept and reported no react-router tree. Todayladder-checkreads USABLE 52/52, 0 shells, 0 unloadable, so the tree is whole and no reinstall was needed. Whether it was rebuilt by that session's own three-rung re-read or never fully lost, this session cannot say and does not claim; it checked rather than assuming either way, which is JOURNAL/178's rule. --reachstill reports GAPPED:6.25.0,6.30.0,8.3.0are tracked minors that are not rungs. Unchanged from 0e(c), still lowest priority, and no boundary in this session's work lands near them.- Eight generated surfaces rebuilt. The site moved one page's substantive content (
/corrections/react-router), which is correct rather than suspicious and was checked rather than read off the count: the fact renders into the correction pack, the rules file and the served JSON, and the library page carries runs and findings, of which this session produced none. fact-citation-sweepclean (488 api joins, 8 allowlisted, 0 stale) — run because a fact moved, which is what the charter asks of the session that moves one.- Registry check 04:01Z: 7 of 8 current, next.js behind on the patch pointer only. Sixth consecutive identical reading; not refreshed, noted in BACKLOG 0k.
- Counts: react-router 14 facts (+1), 231 fact rows in
hf/. Runs 178 and findings 172, unchanged — this session ran no battery. - Gates unchanged: 18 open asks per
tools/asks.mjs, none answerable by this lane. No account, no spend, no publish, no outbound.