Skip to content

feat(emitter): #46 — row-chunk monster sheet modules behind a same-named facade#70

Merged
ebootheee merged 1 commit into
mainfrom
feat/row-chunk-monster-modules
Jun 10, 2026
Merged

feat(emitter): #46 — row-chunk monster sheet modules behind a same-named facade#70
ebootheee merged 1 commit into
mainfrom
feat/row-chunk-monster-modules

Conversation

@ebootheee

Copy link
Copy Markdown
Owner

What

Closes #46. V8 fatally allocates ~2× a module's bytes to UTF-16-decode its source at import, so a transpiled monster sheet crashed node before compute() ever ran — A-2's ~305MB Debt module was the observed 609,447,784-byte fatal alloc that made a converged A-2 base unobtainable.

The emitter now rotates any sheet module crossing --max-module-mb (default 64; --max-module-mb=N; 0 disables) into <Sheet>.partNNN.mjs modules behind a same-named facade:

  • One logical compute() — the facade exports the same SHEET_NAME / SHEET_DEPENDENCIES / compute contract and invokes parts in emission order. engine.js, per-sheet-eval, cone emission, cell-exprs: zero consumer changes.
  • Statement-boundary splits only; the intra-sheet convergence loop is indivisible (checkpoint before it, never inside).
  • Under-cap sheets stay byte-identical single files (golden master untouched); stale parts from previous builds are swept so sheets/*.mjs sweeps can't pick up stale cells.
  • Companion fix: per-sheet-eval's OFFSET/INDIRECT scan follows the facade's part imports — pre-fix it read only the facade and would have approved GT-seed scoping for exactly the monster sheets most likely to use OFFSET.

Gates

🤖 Generated with Claude Code

…med facade

V8 fatally allocates ~2x a module's bytes to UTF-16-decode its source at import
time, so a transpiled monster sheet crashed node before compute() ever ran (the
real A-2 Debt sheet: ~305MB module -> 609,447,784-byte fatal alloc -> no
converged A-2 base obtainable). The emitter now rotates any sheet module that
crosses --max-module-mb (default 64, single-token --max-module-mb=N form, 0
disables) into <Sheet>.partNNN.mjs modules behind a same-named facade:

- ONE logical compute(): the facade exports the same SHEET_NAME /
  SHEET_DEPENDENCIES / compute contract and invokes the parts in emission
  order — engine.js, per-sheet-eval, cone emission, cell-exprs and every other
  consumer import the sheet by the exact same path as before
- splits happen only at statement boundaries in the linear regions; the
  intra-sheet convergence loop is INDIVISIBLE (one checkpoint before it, none
  inside)
- a sheet under the cap stays a single byte-identical file (golden master
  unaffected); stale part files from a previous differently-sized build are
  swept so sheets/*.mjs sweeps can't pick up stale cells
- per-sheet-eval's OFFSET/INDIRECT scan now follows the facade's part imports
  (it read only the facade and would have approved GT-seed scoping for exactly
  the monster sheets most likely to use OFFSET — red pre-fix in the new test)

Real-model gate (A-2, the #46 crasher): Debt emits as 5 parts (4x64MB + 53MB)
and imports in 2.4s on a DEFAULT heap; the full 21-sheet engine imports in
8.9s. Synthetic regression test-row-chunked-modules.mjs: 19 asserts — facade/
part layout + size bounds, convergence-loop indivisibility, GT reproduction,
split-vs-single IDENTICAL values, cell-exprs surface parity, stale-part sweep,
and the eval-scan companion (negative-controlled via stash). cargo tests 29/29,
full npm test green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ebootheee ebootheee merged commit 7417b04 into main Jun 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Post-#43 A-2 rebuild: Debt sheet transpiled code 3x (104→315 MB) → V8 'invalid size' crash (609 MB string) on multi-pass recompute

1 participant