Tracking issue for Sheetwrite 0.4.0. Two themes — performance and delivery size — plus the rendering-correctness defects found in the macOS / worker-renderer audit.
The sections below are ordered: each one unblocks the next. Nothing here is set in stone — every performance and size item is gated on measurement, and an item whose benchmark or bun run size:report does not show the win is dropped rather than shipped on an estimate.
A prior audit found no dead code and no unused dependencies under the checks run so far (evidence and method limits), so deletion is not currently available as a size lever. Optionality and compiler settings are.
1. Foundations — blocks everything below
2. Rendering correctness — blocking
Evidence and acceptance criteria
3. Measurement — gate for every item in §4 and §5
Evidence and acceptance criteria
4. Performance — each item ships only if §3 justifies it
5. Delivery size
Baseline numbers and acceptance criteria
Compiler settings — cheapest lever, no code change
Bundle attribution — attribution first, cutover only if proven
6. Maintainability
7. Spikes — time-boxed, may not land in 0.4.0
Exit criteria
Explicitly out of scope
- Splitting the WASM crate purely to enable lazy loading. Two crates still link into one artifact; only separately loaded artifacts reduce first load, and that needs a serializable boundary across a cycle recalc crosses constantly. Superseded by the §7 spikes.
- New npm packages for the optional modules. Subpath exports get the same result without version skew across five lockstep packages.
- Decomposing
grid.ts (3,146 LOC, fan-out 42). Real debt, and the earlier subpath experiment shows it is what blocks a minimal delivery boundary — but it is a 0.5.0 project. Doing it inside a performance release is how regressions ship.
Tracking issue for Sheetwrite 0.4.0. Two themes — performance and delivery size — plus the rendering-correctness defects found in the macOS / worker-renderer audit.
The sections below are ordered: each one unblocks the next. Nothing here is set in stone — every performance and size item is gated on measurement, and an item whose benchmark or
bun run size:reportdoes not show the win is dropped rather than shipped on an estimate.A prior audit found no dead code and no unused dependencies under the checks run so far (evidence and method limits), so deletion is not currently available as a size lever. Optionality and compiler settings are.
1. Foundations — blocks everything below
0.4.0(PR fix(ci): gate every pull request against its base #39)KIND_*wire contract one source of truth (PR refactor(core): share WASM cell-kind wire tags #42)2. Rendering correctness — blocking
Evidence and acceptance criteria
VisibleWindowView(PR fix(core): preserve cached worker view buffers #43)devicePixelRatiochanges without a CSS-size change (PR fix(core): repaint when device pixel ratio changes #46)OffscreenCanvascontext-loss policy (PR fix(core): fail closed when paint workers fail #44 — fatal fallback)3. Measurement — gate for every item in §4 and §5
Evidence and acceptance criteria
twiggy, orwasm-opt --print-function-sizes— already in the wasm-pack cache).eval+calcis 238,064 shallow bytes: 79.33% of named Sheetwrite function bytes and 32.34% of the name-preserving Twiggy total (results)4. Performance — each item ships only if §3 justifies it
5. Delivery size
Baseline numbers and acceptance criteria
Compiler settings — cheapest lever, no code change
opt-level = 3:2saves 2.08% Brotli but slows resolved 100k formula paths 5.72–8.46%;"s"saves 6.72% but slows them 8.60–11.71% (results, PR perf(bench): record wasm profile tradeoff #47)opt-level3,"s"} × {wasm-opt-O3,-Os,-Oz}, scored onsize:reportand the existing bench gates.wasm-opt([package.metadata.wasm-pack.profile.release]) is a separate post-link knob needing no crate boundary — try it firstCargo.tomljustification comment with the v0.4.0 interleaved size/formula result (PR refactor: enforce internal export boundaries #51)Bundle attribution — attribution first, cutover only if proven
sync,collaboration,rebase,persistence; measure both fixtures again — measured and dropped because all four modules were absent from both first-paint graphs, so a subpath would save zero bytes (PR perf: finish v0.4.0 release evidence and size hardening #50)index.ts— condition not met; the measured exports were absent, so no breaking cutover was opened (PR perf: finish v0.4.0 release evidence and size hardening #50)@sheetwrite/core— package maps excluded; browser bundles retain maps for attribution (PR perf: finish v0.4.0 release evidence and size hardening #50)grid.tsimport chain — measured clipboard variant rejected because Next grew and both fixtures slowed (PR perf: finish v0.4.0 release evidence and size hardening #50)6. Maintainability
store/data-engine.ts(3,691 LOC, fan-in 1 — no public API impact) — paged admission/preflight extracted in PR perf: finish v0.4.0 release evidence and size hardening #50api:check(PR refactor: enforce internal export boundaries #51)7. Spikes — time-boxed, may not land in 0.4.0
--no-default-features— killed at gate 1: the retained fail-closed probe identifiesFormulaEntry::astandCellStore::dep_index; no store-only size is claimed (PR perf: finish v0.4.0 release evidence and size hardening #50)opt-leveloverride — rejected for 0.4.0 because no profile passed formula latency gates and the cyclic ownership boundary plus fat LTO leaves no measured size case (PR perf: finish v0.4.0 release evidence and size hardening #50)Exit criteria
bun run size:reportregenerated; every size number traced to it (PR perf: finish v0.4.0 release evidence and size hardening #50)bench/src/formula-bench.tsbefore any size-oriented compiler setting ships (PRs perf(bench): record wasm profile tradeoff #47 and perf: finish v0.4.0 release evidence and size hardening #50)Explicitly out of scope
grid.ts(3,146 LOC, fan-out 42). Real debt, and the earlier subpath experiment shows it is what blocks a minimal delivery boundary — but it is a 0.5.0 project. Doing it inside a performance release is how regressions ship.