From a47bbe8f75d87e07214d33c5d5e2181932e13fc1 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Tue, 6 Oct 2026 22:01:42 -0700 Subject: [PATCH 1/2] =?UTF-8?q?test(frames):=20C15=20(e)=20=E2=80=94=20the?= =?UTF-8?q?=20gap=20#3844=20named,=20pinned=20by=20name:=20a=20same-addres?= =?UTF-8?q?s=20refetch=20enters=20the=20transaction,=20derives=20in=20its?= =?UTF-8?q?=20pass,=20lands=20whole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A refetch of the address a mount SHOWS is a write only the content token carries: `dynamic` delivers a kept resolution only when its address differs, so the bare address would be a `setAddress` no-op and nothing of the frame's graph would run in the transaction that asked (#3844, gap 1). And the fill's derivation must read the NEW arg in the pass that delivers it, not at the commit one flush behind an optimistic intent it dissolves (principles §9.2.2; gap 2). C15 (a) pinned the atomic landing; nothing pinned the pass by name. The arm, C15's shape: body complete, sibling held — the fill HAS derived "two" (`seen` is `["one", "two"]`: the staged read happened in the transaction's pass) while the DOM still shows `a0|v1|one`, no `frame:applied` fired and the frame's version is 1; the release lands the whole in one frame and the fill is not re-read at the commit. Any carrier of the staging — the push (`preview`) today, or a pull — must keep both halves. Green on the push form (residue step 2 measured the pull form and stopped; frames-residue-pass.md §3.1 "As measured"). Co-authored-by: Cursor --- .../consistency/c15-staging-atomic.spec.tsx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/web/test/consistency/c15-staging-atomic.spec.tsx b/packages/web/test/consistency/c15-staging-atomic.spec.tsx index eb2bc9d6e..4d62b6d3c 100644 --- a/packages/web/test/consistency/c15-staging-atomic.spec.tsx +++ b/packages/web/test/consistency/c15-staging-atomic.spec.tsx @@ -278,6 +278,45 @@ describe("C15 — a staged refetch lands at the commit, whole", () => { expect(watch.frames).toEqual(["a0|v1|one", "a2|v3|three"]); }); + // Arm (e) — the gap #3844 named, pinned by name. A refetch resolving to + // the address the mount SHOWS is a write only the content token carries + // (`dynamic` delivers a kept resolution only when its address differs, so + // the bare address would be a `setAddress` no-op and nothing of the + // frame's graph would run in the transaction); and the fill's derivation + // must read the NEW arg in the pass that delivers it — not at the commit, + // one flush behind an optimistic intent it dissolves (principles §9.2.2, + // `frames-optimistic-hold`). So, with the body complete and the sibling + // still held: the fill HAS derived "two" (the staged read happened in the + // transaction's pass) while nothing shows, applies or versions it; the + // release lands the whole in one frame. Any carrier of the staging — + // the push (`preview`) or a pull — must keep both halves. + test("(e) the gap (#3844): a same-address refetch enters the transaction — the fill derives the new arg in its pass, the DOM holds, the commit lands whole", async () => { + const s = setup("c15e"); + await showV1(s); + action(function* () { + s.setTick(1); + })(); + await pump(3); + expect(s.calls.length).toBe(2); + for (const c of response(s.id, 2, "v2", "two")) s.v2.send(c); + s.v2.close(); + await pump(3); + // The fill's derivation ran with the new arg in the transaction's pass… + expect(s.seen).toEqual(["one", "two"]); + // …while nothing of v2 is shown, applied, or versioned. + expect(s.view()).toBe("a0|v1|one"); + expect(s.applied).toEqual([]); + expect(s.host.get(s.id)?.version).toBe(1); + s.sibling.release(); + await pump(3); + s.sample(); + expect(s.view()).toBe("a1|v2|two"); + expect(torn(s.frames)).toEqual([]); + expect(s.frames).toEqual(["a0|v1|one", "a1|v2|two"]); + // Read exactly once, in the pass — never again at the commit. + expect(s.seen).toEqual(["one", "two"]); + }); + // Control: a refetch that moves NO sibling (a different signal re-asks // the call). The staged content lands when the staged call settles — at // body end — whole: root and fill in one frame. From f1270838687f7888325d4c8ec5720826955cd0b0 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Tue, 6 Oct 2026 22:01:43 -0700 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20residue=20step=202=20=E2=80=94=20th?= =?UTF-8?q?e=20preview=20pull=20form=20measured=20and=20stopped=20(carrier?= =?UTF-8?q?=20+483=20br=20over=20a=20=E2=88=92546=20ceiling;=20net=20?= =?UTF-8?q?=E2=88=9263);=20residue=20doc=20=C2=A73.1=20"As=20measured"=20+?= =?UTF-8?q?=20=C2=A77b;=20plan=20=C2=A73=20row=20R2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Measured before writing, per the step's rule, on step 1's head (frames eager 35,250 / 11,654): the deletion ceiling (`stage` whole out) is −1,645 / −546 on this head; the carrier AS THE STEP WOULD HAVE WRITTEN IT — applied to an edited dist copy through the harness's bundler — is +1,414 min / +483 br over it, net −231 / −63 (pages −29 / −37; non-SC 0 / 0). The rule was "carrier > +300 br → stop and report"; nothing of the form reached source. Two findings. (1) The §3.1 sketch (`pullcarrier`, +242) is unsound where the budget was built: it deleted the content token, and `dynamic` delivers a kept resolution only when its address differs — without the token a same-address refetch never runs the follow's compute half or any fill memo in the transaction (#3844's gap 1; C15 (a, b), the optimistic-hold multi-flight arms). The token binding stays in any form: 227 min / 88 br. (2) The honest carrier is twice the sketch because the handler's chunk buffer (≈ 200 min) is replayed through `host.apply` and inherits every semantic the host has — version policy, the landing, L1, per-id routing (nested regions and a flight's several roots stage for free) — while a host-side staged set carries each itself (promote through the store's own `write` + landing tail; the `write` / `settleWait` guards; flight-path per-root staging 176 / 54; nested regions 152 / 53), and the pull read needs a lane gate (the fill keyed by the mount's address accessor, 106 / 29) that the push has by construction, so the read (≈ 750 min) is not smaller than the push it replaces (≈ 700). The unsound floor without flight / regions / gate is still +980 / +339. frames-residue-pass.md: status line; §3.1 "As measured" (the sketch's unsound point, the honest carrier as designed, the attribution table, why the sketch was short by half, the conclusion); §2 row 1c and §4 row 3 corrected to the measured net; §5 item 3 marked stopped; §7b the step's table and what it changes in the path (≈ 120 B of margin instead of ≈ 330 with C1 + claims). frames-savings-pass.md §3: row R2, the surface the form would have added named. frames-consistency-contract.md: C15's pin list gains arm (e), the gap #3844 named. Co-authored-by: Cursor --- documentation/plans/frames-residue-pass.md | 149 +++++++++++++++++- documentation/plans/frames-savings-pass.md | 1 + .../frames-consistency-contract.md | 6 +- 3 files changed, 151 insertions(+), 5 deletions(-) diff --git a/documentation/plans/frames-residue-pass.md b/documentation/plans/frames-residue-pass.md index 8fac516e1..5782a97bc 100644 --- a/documentation/plans/frames-residue-pass.md +++ b/documentation/plans/frames-residue-pass.md @@ -39,7 +39,16 @@ edited dist copy through the harness's own bundler (§1); estimates are marked. without the mirror and the capture arm (2b, 2c, 2e, 2f, 2g), `FRAME_HAVE_*` off the client entry (5a), the lean re-ask (6b) — **frames eager 11,901 → 11,654 br (−757 min / −247 br)**; see §7 for the measured → built table and -what the step found (2d is pinned; 10 has no export to re-use)._ +what the step found (2d is pinned; 10 has no export to re-use). +**Residue step 2 stopped at the carrier budget** (`size/frames-residue-2-pull-form`, +measured on step 1's head, nothing built): the `preview` pull form's sketch +deleted the content token, which is the only carrier by which a same-address +refetch enters the Transaction at all (unsound — #3844's gap (1)); with the +token kept and the carrier built honestly on the edited dist — per-store +staging, a lane-correct read, the flight path, nested regions, the commit's +landing / L1 semantics the chunk replay got for free — it measures **+1,414 +min / +483 br over the −546 ceiling: net −63 br**, against the +242 budget +and the −300 target. §3.1 "As measured" has the attribution; §7 the row._ --- @@ -211,7 +220,7 @@ pattern every tier in #3860 showed). | --: | --- | --- | ---: | ---: | ---: | ---: | ---: | --- | | 1a | `preview` — the staging **push** half out (commit-only form): `FrameImpl#preview`, `host.preview`, `entry.preview`, `stagedContent.preview` | R.stage | −719 / −216 | −227 | −236 | 0 | — | **not a candidate**: C15 ×4 stay green but `frames-optimistic-hold` ×3 go red (`false/false` in the derivation trace — #3844's finding, re-stated) | | 1b | `stage` — **everything** out: the buffer, the token, `stagedContent`, `contentAddress`, `CONTENT_TOKEN`, the token arms of `handle` / `applyFlightResponse`, `followAddress` → a plain rebind effect | R.stage | **−1,643 / −542** | −564 | −558 | — (ceiling) | — | C15 ×4 red, optimistic-hold ×3 red: the refetch's morph lands at body end beside siblings the transaction still holds | -| 1c | **the `preview` pull form** = 1b + the carrier sketch (§3.1): a staged record set per address in the host written through as chunks arrive, promoted at the commit; a version tick per frame written in the follow's compute half; the live props read the staged record through it | R.stage | **−803 / −300** | −217 | −264 | **sketch measured: +840 min / +242 br** over 1b (a first honest cut, not golfed; B's first cut was +233 → +145 after golfing) | **−300** (band −250 … −350) | **design**: C15 ×4, `frames-optimistic-hold` ×3, `frames-morph-in-transition` ×3, principles §9.2.2; `c17-gate-bound-address`; public surface: `FrameHost.preview` / `Frame.preview` / `stagedContent` / `contentAddress` removed (approved 2026-10-06, "not even beta") | +| 1c | **the `preview` pull form** = 1b + the carrier sketch (§3.1): a staged record set per address in the host written through as chunks arrive, promoted at the commit; a version tick per frame written in the follow's compute half; the live props read the staged record through it | R.stage | ~~−803 / −300~~ **−231 / −63** (step 2, the sound carrier as designed, measured) | −29 | −37 | ~~sketch +840 min / +242 br~~ **+1,414 / +483 over 1b** — the sketch deleted the token (unsound: `dynamic` never delivers a same-address refetch without it) and omitted the flight path, regions, the lane gate and the commit's landing / L1 semantics (§3.1 "As measured") | **−63** | **stopped at the carrier budget (2026-10-06)**: C15 ×4, `frames-optimistic-hold` ×3, `frames-morph-in-transition` ×3, principles §9.2.2; `c17-gate-bound-address`; public surface: `FrameHost.preview` / `Frame.preview` / `stagedContent` / `contentAddress` removed (approved 2026-10-06, "not even beta") | | 2a | `_$SC` client mirror | D | −234 / −80 | −70 | −151 | 0 (or ≈ +60 for a minimal `r` without the address arms) | −80 … −60 | **behaviour**: a page whose data scripts carried no reference (so the server never emitted the bootstrap) and a CSR boot have no `_$SC.r` → `handler.component` throws. Needs the server to emit the bootstrap unconditionally on SC pages (≈ +200 B of document output) or the client to keep a minimal `r` | | 2b | `documentAddress` | D | −95 / −28 | −49 | −3 | 0 | −28 | an unbound adopted mount (direct `_$SC.r(id)` placeholder, no binding) binds the function id instead of the call's address — a post-load refetch under the address would not reach it; `frames-adopted-*` pins to check. Deletes cleanly only with 2a's server half (the t = 0 record carries the address to the mount) | | 2c | the second dispose map (`bindings` → `fillScopes`) | D | −82 / −20 | −76 | −24 | 0 | −20 | safe (identical bookkeeping; both maps dispose the previous invocation under the same key) | @@ -321,6 +330,106 @@ refetch — the host's write rule covers it once regions bump per frame). this pass learned apply: budget the carrier at the sketch (+242), not below it. +#### As measured — residue step 2 (2026-10-06): stopped at the carrier budget + +Branch `size/frames-residue-2-pull-form` on step 1's head (`fce81d2c0`: +frames eager 35,250 / 11,654; page base 114,422 / 36,488; live 126,294 / +40,125). Measured before writing, as the step's rule says: the ceiling +re-applied to this head's dist, then the carrier **as the step would have +written it** (the same text, not a sketch) applied on top of the ceiling, +through the harness's own bundler (§0's method; `.wt-logs/res2-edit.mjs`, +`res2-measure.mjs`). Nothing was written to source; no pin ran against it. + +**The sketch is unsound in one place, and it is the place the budget was +built on.** `pullcarrier` deleted the content token with the buffer — the +call resolves the bare per-address binding, `followAddress` reads +`host.staged(address)` off the bare address. But `dynamic` delivers a kept +resolution only when its `.address` differs from the one it last delivered +(`web/src/index.ts`, `resolveBinding` / `sameInstance`): a refetch of the +address a mount SHOWS resolves to the same object, `deliver(address)` is a +same-value write, and neither the follow effect's compute half nor any fill +memo runs in the transaction — the content lands when the body ends, beside +siblings the transaction still holds. That is #3844's gap (1), restated; +the sketch's text could not have kept C15 (a, b) or any multi-flight arm of +`frames-optimistic-hold` green. **The token binding stays in any form** +(`stage`'s mint, `latest`, `settled`, `showing`, one staging target): +**227 min / 88 br** measured — the first thing the +242 did not count. + +**The honest carrier, as designed for the build.** Handler: `showing` +decides at the header as today; a staged response's chunks reach the host +flagged (`host.apply(chunk, true)`; the flight path flags by staged root +prefix, as `regionOf` did); the token binding is minted into `latest` and +the call settles at body end. Host: `apply(chunk, stage)` writes a flagged +chunk whose version is newer than the store's into `store.staged = { +version, records }` under the same policy-A `write` (one response's — a +newer replaces it; once the store is at the version, the rest writes +through, as a committed entry did); `settleArgs` through the store's +version-keyed waits, with `settleWait` re-applying only a record that is +the store's current one (a staged record's data settles silently and lands +at the commit); `staged(id)`; `promote(id, version)` = the store's own +`write` plus the landing tail `apply` runs (shown / open / the pending +landing settled or rejected / frames applied / L1 for the response's +undelivered refs), the waits carried across the bump. Frame: +`#stagedRecord(occurrence)` — the staged set's record, else the parent's +when this frame's store does not shadow the key (the `inherited` rule +`preview` had); `ctx.staged()` resolves it through `#resolveArgs` unless it +adds or renames a region (`R.changed`, as `preview` skipped). Fill: +`liveSlotProps` reads `staged() && ctx.staged() || args()`, where +`staged` is the mount's **gate** — `host.staged(address)?.version === ++version` parsed from the mount's own address accessor (`binding()`), so +the read is keyed by the signal `dynamic` writes in the transaction's pass +and is lane-correct: a write-through recompute between the refetch's header +and its commit (a live op on an adopted boundary, a reveal cascade, a +`settleWait`) reads the committed accessor value and falls to `args()`. +Without the gate the staged set is readable from its first chunk to its +commit — a read outside the pass. Follow: the effect half promotes the +token's version, rebinds, then promotes each region store below +(`regions-tier.ts`, the recursion that replaces `frames()`), so a nested +region's chunks — which the handler's buffer held with the root for free — +land with the root's commit; `Frame.preview`, `FrameHost.preview`, +`stagedContent`, `contentAddress`, the entry's buffer and `named` delete. + +**Bytes** (frames eager; the four non-SC scenarios 0 / 0 on every row): + +| edited dist | min / br | Δ vs head | carrier over the ceiling | +| --- | ---: | ---: | ---: | +| ceiling — `stage` whole out (§2 row 1b, re-measured on this head) | 33,605 / 11,108 | **−1,645 / −546** | — | +| the carrier as designed | 35,212 / 11,651 | −38 / −3 | +1,607 / +543 | +| … promote through `write` + the shared landing tail (**the lean form; sound**) | 35,019 / 11,591 | **−231 / −63** | **+1,414 / +483** | +| lean − the token binding (_unsound_: nothing delivers) | 34,792 / 11,503 | −458 / −151 | token = 227 / 88 | +| lean − the flight path's staging (_unsound_: a mutation's region tears) | 34,843 / 11,537 | −407 / −117 | flight = 176 / 54 | +| lean − regions (no region promote, no inherited record, no structural check; _unsound_) | 34,867 / 11,538 | −383 / −116 | regions = 152 / 53 | +| lean − the gate (the fill reads the staged set unguarded; _unsound_: a read outside the pass) | 34,913 / 11,562 | −337 / −92 | gate = 106 / 29 | +| lean − flight − regions − gate (the unsound floor) | 34,585 / 11,447 | −665 / −207 | +980 / +339 | + +Pages, the lean form: base 114,184 / 36,459 (−238 / −29), live 126,061 / +40,088 (−233 / −37); the ceiling alone −555 / −499. + +**Why the sketch was short by half.** The handler's buffer is ≈ 200 min of +dumb array replayed through `host.apply`, and the replay inherits every +semantic the host already has — version policy, the landing, L1, per-id +routing, so nested regions and the flight path's several roots stage for +free. A host-side staged set has to carry each of those itself (≈ 600 min +across `apply`'s branch, `promote`, the `write` / `settleWait` guards and the +region promote), and the pull read (`#stagedRecord` + `ctx.staged` + the gate ++ the `liveSlotProps` / `slotsFor` threading ≈ 750 min) is not smaller than +the push it replaces (`FrameImpl#preview` + `host.preview` + `entry.preview` +≈ 700): the push writes the signal **in** the pass and is lane-correct by +construction, the pull has to buy that with the gate. Keeping the buffer +and pulling from it instead (a `stagedContent.record(token, id, +occurrence)` read, the host's `settleArgs` exposed, the same frame-side +read and gate) measures the same way: ≈ 720 added for ≈ 710 deleted. + +**Conclusion.** The deletion ceiling is real (−546) but ≈ 480 of it is +carrier under any sound form; the honest net is **≈ −60 br**, a fifth of +the row's −300 and outside the −250 … −350 band. Stopped per the step's +rule (carrier > +300); §2 row 1c and §4 row 3 are corrected below to the +measured net. What it would still buy if taken for its own sake: the +handler loses `staged` / `named` / `stagedContent` module state and the +flight path's `regions` map; the host gains `staged` / `promote` and +`apply`'s second parameter (`@experimental` surface added, see the step's +PR). Not recommended at −63. + ### 3.2 S-ref's pending read belongs to the table (item 12) As built (#3844), the host mints a pending promise per undelivered key per @@ -427,7 +536,7 @@ still has to pay; the "built" column subtracts it. | 0 | head `5c5991cbd` | | **13,083** | | | | | | 1 | **C6** bind tier (`tight-nopair`, in flight) | −1,243 | **11,840** | 0 (C6's glue is in) | 11,840 | 11,548 | C6's pins; `tier-bind-hold`; the click-replay finding (C6 P1) | | 2 | **the D list** (2a–2g) + the s / v stamp (10) + the nested marking (8a) + `FRAME_HAVE_*` off the entry (5a) | −349 | **11,491** | +10 (`readHydratedValue` export); the mirror's server half is output, not client bytes | 11,501 | 11,209 | 2a / 2b need the server to emit the bootstrap unconditionally; 2f / 5a / 10 are public-surface items; 2d / 2g are behaviour changes | -| 3 | **the `preview` pull form** (1c; carrier sketched **and measured in the row**) | −277 | **11,214** | 0 | 11,224 | 10,932 | **design** §3.1; C15 ×4, optimistic-hold ×3, morph-in-transition ×3 | +| 3 | **the `preview` pull form** (1c; carrier sketched **and measured in the row**) | −277 → **−63 as designed** (step 2: the sketch's carrier was unsound and half-counted; §3.1 "As measured") | **11,214** (≈ 11,430 with the honest carrier) | 0 | 11,224 (≈ 11,440) | 10,932 (≈ 11,150) | **stopped** (carrier +483 > +300); C15 ×4, optimistic-hold ×3, morph-in-transition ×3 | | 4 | **R.insert** — every fill through `insert` (3) | −196 | **11,018** | 0 | 11,028 | 10,736 | public surface: the marker-less `createFrame` path | | 5 | the fallback pass (4a) | −75 | 10,943 | DR-4 (its own plan) | — | — | **not recommended** (§3.4); kept to show it does not matter — the rows below are measured **without** it where marked | | 6 | **the lean re-ask** (6b) | −78 | **10,865** (10,940 without row 5) | pages +30 | 10,950 | 10,658 | **design** §3.3 (sf client ctx) | @@ -495,7 +604,9 @@ two seeds clean): sketch's budget (+242 br), with C15 ×4 and optimistic-hold ×3 as the gate. It also simplifies the handler (no `stage` / `latest` / `named` / token) and `applyFlightResponse` (regions bump per frame, no staged-region - map). + map). **Stopped (residue step 2, §3.1 "As measured" / §7):** the token + cannot go (it is the delivery), the honest carrier is +483 br over the + −546 ceiling, net −63; not recommended at that price. 4. **R.insert** (−196) with the marker-less `createFrame` path ruled (an anchor requirement on `createFrame`, documented). 5. **S-ref to the table** (§3.2; ≈ −250 built) — a decode-chunk change plus @@ -599,6 +710,36 @@ server half (§3.7 — the mirror alone, now that `documentAddress` is gone); the capture arm, which needs a ruling on the away/back behaviour of an SSR'd boundary; the stamp, which needs a `solid-js/internal` export. +## 7b. Measured, not landed — residue step 2 (2026-10-06): the `preview` pull form + +Branch `size/frames-residue-2-pull-form` on step 1's head (`fce81d2c0`, +frames eager **35,250 / 11,654**). §5 row 3, measured before writing per the +step's rule (ceiling, then the carrier as it would be written, both on an +edited dist copy through the harness's bundler) — and **stopped at the +carrier budget**: the rule was "if the carrier exceeds +300 br, stop and +report the design". Nothing of the form reached source; the step's one +source change is a pin. + +| item | measured on this head | note | +| --- | ---: | --- | +| the ceiling — `stage` whole out (§2 row 1b) | **−1,645 / −546** (pages −555 / −499) | matches the row's −542 | +| the sketch's carrier (`pullcarrier`, §2 row 1c) | +840 / +242 (the row's figure) | **unsound**: it deleted the content token; `dynamic` delivers a kept resolution only when its address differs, so without the token a same-address refetch never runs the follow's compute half or any fill memo in the transaction (#3844's gap (1); C15 (a, b), the optimistic-hold multi-flight arms) | +| the carrier as the step would have written it (§3.1 "As measured") | **+1,414 / +483** over the ceiling → **−231 / −63 net** (pages −29 / −37) | token 227 / 88 · flight-path staging 176 / 54 · nested regions 152 / 53 · the lane gate 106 / 29 · the host's staged set with promote's landing / L1 / waits ≈ the rest; every one load-bearing | +| the unsound floor (no flight, no regions, no gate) | +980 / +339 | still over the +300 budget | +| **built** | **—** | stopped; frames eager stays **11,654** | + +**Pin added (by name, green on the push form):** `c15-staging-atomic` (e) — +the gap #3844 named: a same-address refetch enters the transaction (the +fill derives the new arg in its pass — `seen` is `["one", "two"]` while the +DOM, `frame:applied` and the frame's version still show v1), and the commit +lands it whole. Any carrier of the staging — push or pull — must keep both +halves. + +What this changes in the path (§4): row 3's −277 becomes ≈ −60 as a sound +build, so the residue proper (rows 2–4, 6) stalls ≈ 215 B higher than §4 +says; crossing 10.0 still needs C1 and the claims chunk, with ≈ 120 B of +margin instead of ≈ 330. The push form stays, as #3844 left it. + --- ## Appendix A — every unit on the head (273 units, 40,000 B; by whole unit) diff --git a/documentation/plans/frames-savings-pass.md b/documentation/plans/frames-savings-pass.md index 35fa99f7b..7c663676b 100644 --- a/documentation/plans/frames-savings-pass.md +++ b/documentation/plans/frames-savings-pass.md @@ -374,6 +374,7 @@ cap on the same terms; no frames / page cap moved. | **C5** | **Assets tier.** `tier-assets.js` = `ensureStylesheet` / `ensurePreload` / `ensureModulePreload` / `applyInlineStyles` / `qualifierValue` / `findHeadElement` / `PRELOAD_QUALIFIERS` / `#processedAssets` / `#styleFlush` / the assets pass; the eager client keeps `chunkToRecords`' `assets` case, `host.write`'s `seg::assets` accumulate, and the `#segmentReady` term. **Pin the two untested functions first** (`ensureStylesheet`, `applyInlineStyles` — the audit's 0-coverage gap) in the same PR. Alternative under decision 2: S10's route-through-`web` instead of a tier. **Landed (2026-10-06, `feat/frames-assets-tier`, measured before written on edited dist copies per re-attribution §7; the two coverage pins committed first, against the code as it was).** The chunk is `@solidjs/web/frames/assets` (frames/src/assets-tier.ts — a NEW `@solidjs/web` export path): the whole head mirror (`ensureStylesheet` / `ensurePreload` / `ensureModulePreload` / `applyInlineStyles` / `qualifierValue` / `findHeadElement` / `PRELOAD_QUALIFIERS`), the per-frame flush a pending sheet wakes (`#styleFlush` → a `WeakMap` in the tier), and the assets pass over one record — modules, typed preloads, **and inline styles** (moved off the reveal: they land at the record's arrival when the tier is resident, before the segment's content as the document face orders them; at the install when it was absent). The tier is import-free and has no `install()`: its exports **are** the dispatch — `prepareTier` stamps the load's `r` with the module (B's `true` → the module; truthy either way), and the eager client calls `gate(styles, frame)` from `#segmentReady` and `apply(record)` from `#flush`'s walk off that stamp. The eager client keeps `chunkToRecords`' `assets` case, `host.write`'s accumulate, the walk over `:assets` records (deduped through the applied map — `#processedAssets` deleted), and the **reveal-readiness term**: `assets.styles && !tierReady("assets")?.gate(assets.styles, this) → not ready` — a style-gated segment is not ready while the tier is absent (`tierReady` starts the load if nothing announced it; the server's fallback stays) and, once resident, until every sheet has settled; the reveal is at max(tier load, stylesheet load). A segment without stylesheets never consults the tier; a stream with no asset records never loads it; an un-announced record met by the walk starts the load and stays pending until the install's flush. | **−684 / ≈ −665 / ≈ −665 / 0** (measured on frames: `T+assets` → `L8`; the `noassets` full-client cut −665) **Measured: −534 / −628 / −496 / 0** br (−1,913 / −1,926 / −1,926 / 0 min) against C3's head `89954fa1b`; vs `next` @ `9d89df731` −455 / −6,912 / −6,944 / 0. The deletion alone −666 / −681 / −627 br (−2,419 min: the whole F.assets group, record cases included) on an edited copy; what stays eager costs +508 min / +131 br on frames — the kept record cases +208 / +64 (the plan's own list), the readiness term + loader entry + `r = module` + the walk's dispatch +300 / +67 — over the ≈ +150-min line, reported in the PR. A folded variant (the walk riding the hole/attr loop, content before its inline style within one flush) measured −562 br and was not taken: the separate walk before the segments keeps a segment's `