…egression of #3540) (#3824)
* fix(signals): a fresh Loading over a held value shows its fallback in a flush (#3540, L2 regression)
L2 regressed A29's boundary exemption in two ways:
- In a flush, a first pass under a loading boundary that has not shown
content joined the flush to the hold it read, so the whole tick (the
Show that mounted the boundary included) waited for the hold. A first
pass that reads a hold now asks the boundaries up its chain
(GlobalQueue._fresh); one such boundary catches it, and the pass joins
pass-scoped (passTx), as a mount outside a flush does: the boundary's,
not the tick's. A derivation outside the boundary in the same flush
still holds the tick. A flush that set passTx clears it at its end.
- A render effect born held under a fresh loading boundary (content bound
in JSX: the tree never reads the held value) was not caught, so the
boundary revealed empty content, in a flush and out of one. It is now
collected as a reader, and the output's first pass shows the fallback
whenever the boundary has collected readers.
Refs #3540
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(signals): the first-pass catch asks only the nearest loading boundary, and only one that has not shown content (#3540)
A revealed boundary (re-armed by `on` or not) neither catches nor records
a never-committed first pass, and no boundary past the nearest is asked:
which boundaries catch what is unchanged beyond A29's boundary exemption.
catchStatus is back to next's.
Refs #3540
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(signals): ask the nearest loading boundary through _catch; no new hook (#3540)
A pass that read a hold asks GlobalQueue._catch with STATUS_UNINITIALIZED
in its flags. Only a first pass is caught, and only by the nearest loading
boundary if it has not shown content (`_initialized`, not `_armed`): a
revealed boundary, re-armed by `on` or not, neither catches nor records
it, and no boundary past the nearest is asked. Pins the revealed-boundary,
nested and `on` shapes against next.
Refs #3540
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(size): Size-Exception for #3824 — five caps at CI-measured + 10 B (A29 restore, #3540)
app: hydrating + every store primitive family 28.87 -> 28.90 KB,
app: compiled CSR 25.13 -> 25.22 KB, app: compiled hydrating 30.93 ->
30.97 KB, page: base server components 44.84 -> 44.90 KB, page: live
server components 48.51 -> 48.53 KB. Approved by the maintainer.
Refs #3540
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(size): reset #3824's caps from CI with the recorded minified (#3822 format)
Size-Exception (approved): hydrating + stores 28.85 KB, CSR observe 14.48 KB,
compiled CSR 25.23 KB, compiled hydrating 30.95 KB, base page 44.86 KB,
live page 48.56 KB, each CI brotli (Size run 37450070886) + 10 B with the
minified from the same run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* proto(signals): A29's boundary scope — a boundary that has not shown content owns its subtree (#3540)
Prototype for the 2026-10-06 ruling: membership is decided by the boundary
scope, not per pass. A read of a held node from under a loading boundary
that has not shown content, or that `on` re-armed, joins nothing: the reader
is the hold's stale reader (re-derived at its commit) and the read is
pending, so the boundary shows its fallback. Exempt when the boundary's own
display is the hold's (the flush joined it, the output is held by it, or
the mounting pass joined it): the boundary then appears at the commit.
Replaces the per-pass attempt (passTx exemption in joinPassTx, the
first-pass marker, the creation-branch pending report, the prune
uninitialized keep, and the output's A29 exemption). LOADING_ON_OUTSIDE_HOLD
reports only a re-arm whose swap is held.
Not for #3824 as-is; the maintainer decides from the report.
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* proto(signals): an owned held computation re-derives at the read (#3540)
Semantic fuzzer (rev 19, seed 91501, branch-boundaries #870): an `on`
re-arm left the tree held by an earlier hold; the output read it, the scope
made the read pending, and the tree was recorded as a reader that had
already settled — the boundary redrew forever. A held computation read from
the scope that owns it is content, not a hold: it leaves the transaction
(`leaveHold`, shared with `recompute`'s head) and re-derives at the read.
Pins the stale-content finding (boundaries #1078) as `it.fails`: membership
decided mid-flush is not revisited when the flush later joins the hold.
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(signals): A29's boundary scope — ownership decided at the seam, behind boundary hooks (#3540)
Lands the 2026-10-06 boundary-scope ruling. A loading boundary that has not
shown content, or that `on` re-armed, owns its subtree: a read of a hold
from under it is pending there (the boundary shows its fallback, no hold
waits for it), and the reader re-derives at the hold's commit.
- Hooks: the ownership logic lives in boundaries.ts behind
`GlobalQueue._owns(c, el)` (asked at the hold's doors: `read`, a store
draft, `recompute`'s head, `propagateStatus`) and the new
`GlobalQueue._boundaryPark(t)`; core pays the null checks.
- The seam: the reads the scope made pending, and the nodes that left a
hold, are revisited once the flush has parked (its membership final). A
read whose hold the flush joined after it was the hold's after all and
re-derives as its work; a swap parked with another change takes the
hold its content read (or left) with it.
- A re-arm that finds nothing pending owns what an earlier change holds
under it with no flight (an action's staged write): it leaves the hold
and re-derives now (mount-under-hold `rearm-committed`).
Pins the A15 landing tear a re-arm exposes (`it.fails`; boundaries #75's
reduction shows it on next without a boundary). Open, not the scope's: a
boundary a verdict reader mounts derives in the verdict lane (the
first-pass lane rule, #3835's seam).
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(signals): regenerate RULES-INDEX without local probe files (#3540)
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(signals): rebuild #3540's L2 fix on revision 1; defer re-arm
Replaces the boundary-scope machinery (ownership hook, held-node leave,
re-arm `ownHeld`, the seam park) with revision 1's fresh-mount fix
(0479053) on today's next: a first pass that reads a hold asks the
boundaries up its chain (GlobalQueue._fresh); when a loading boundary
that has not shown content catches it, the pass joins pass-scoped
(passTx) instead of joining the flush, so the mount publishes and the
boundary shows its fallback. A render effect born held under the
boundary is caught the same way. catchStatus is unchanged.
Re-arm under the boundary scope is deferred to a separate change,
pending its ruling: those tests are pinned it.fails, the re-arm
amendment text is back to next's wording with a dated note, and
async.ts, store.ts and flow.ts are back to next.
Refs #3540
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Follow-up to #3821, implementing the maintainer's ruling on its open question.
Problem
#3821 measured the 20 B minified allowance per PR, against each PR's base. A scenario that merged over its brotli cap on noise stays over it, because the ratchet only lowers caps. From then on, every PR was held only to +20 B over its own base, so minified growth could pile up across PRs without limit. The #3821 warning also promised "cap will be re-based at the next ratchet", which the lower-only ratchet can't do for an over-cap scenario.
The rule
Every cap now records the minified size measured when the cap was set:
capMinifiednext to an inlinelimitinscenarios.js, and{ "cap", "minified" }entries infloor-caps.json.MINIFIED_ALLOWANCE(20 B)Size-Exception:stays the override.check-floor-caps.mjsnow freezes each floor's recorded minified the same way as its cap: raising or removing it needs the exception. It accepts the old string entries at the base, so this PR's own format change passes.--minified-onlyleaves every cap alone; this PR seeds with it.Seed
The values come from CI, not macOS: the
size-headartifact of the Size run on the push ofnext@fff1615ee(#3811; run 37443080193). They were written byratchet.mjs --minified-onlywith a datedRatchet (2026-10-06, seed, …)ledger line on each scenario, plus header and README ledger notes. Every cap is unchanged.A first seed from
c54fb10ebwas replaced. #3812, #3814 and #3811 moved minified by +30 to +69 B after it, and keeping it would have failed the 7 over-cap scenarios on arrival.Seven scenarios sit over their brotli cap on
next; they landed with noise warnings under #3821's gate. Each now has 20 B of minified headroom against its recorded size and passes with a warning. It fails only once minified grows more than 20 B past what is recorded now. The lower-only ratchet cannot re-base these 7; they stay over until code shrinks or a PR raises the cap with a reason.Creep dry run
These runs go through
gate.mjson the tip's CI measurement with the seeded values. Each simulated PR adds +15 B minified to every over-cap scenario, with brotli unchanged:next@ fff1615 as isTests
gate.test.mjshas 12 tests, which CI runs incheck. The new ones cover:Public API changes
None. This is tooling only (
scripts/size/), with no changes underpackages/*/src, so there is no changeset. Internal tooling surface touched:capMinifiedscenario field;floor-caps.jsonentry format ({ cap, minified }; the string form is still read);ratchet.mjs --minified-only;decide()verdicts gainagainst,headroomandbaseDelta.🤖 Generated with Claude via Cursor