Skip to content

fix(runtime-snapshot): trim dependency payload and verify runtime closure - #3096

Merged
code-yeongyu merged 14 commits into
mainfrom
fix/3083-snapshot-closure
Oct 11, 2026
Merged

code-yeongyu merged 14 commits into
mainfrom
fix/3083-snapshot-closure

Conversation

@code-yeongyu

@code-yeongyu code-yeongyu commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Refs #3083 (part B)

Changes

Retain the complete nested/hoisted runtime dependency graph, but omit dependency development trees, root Markdown and extensionless README/changelog/history files, and redundant source TypeScript when an existing built JavaScript entry makes it redundant. Manifest entry roots override directory conventions. Source-only codemode, source assets, skills, licenses, native binaries, and wasm remain.

Extract tree planning and package-root shipping policy from layout. The package-root policy introduced in #3084 is preserved.

Add a real offline CLI inventory regression under both Node and Bun: help, an external TypeScript extension using typebox and SDK types, a faux print turn, Claude executable resolution, photon wasm, and an actual codemode JS tool call. The test asserts successful delivery and snapshot-local runtime paths rather than maintaining a sampled package allowlist. It probes the grep loader but does not assert a native binding loads; the packed-install review recorded native-unavailable under both runtimes.

Replace the redundant manifest-only shipping assertion with a glob-exclusive file plus an unshipped sibling. Add a dependency fixture bounded to 20 files and less than 4 KiB.

SDK decision

Keep the SDK and its platform executable eagerly snapshot-local. Both runtime inventories resolve the platform executable there. Lazy copying from the original install would fail after an update removes that install; a separate content-addressed store adds ownership/GC and hashing contracts.

A five-trial macOS arm64 probe of the 235,017,328-byte SDK executable measured copy medians of 0.115709 ms under Bun 1.4.2 and 0.098 ms under Bun 1.4.3, both in clone mode. Node 26.7.0 reported clone-or-copy mode with a 135.228209 ms median. These are executable-copy measurements only, not full cold-launch or physical-storage measurements. The paired packed-install run will establish the complete user-visible delta.

Runtime inventory

The latest npm-installed-package probe recorded 260 snapshot paths under Node and 222 under Bun. Node resolved files from these 15 packages:

  • @anthropic-ai/claude-agent-sdk
  • @anthropic-ai/claude-agent-sdk-darwin-arm64
  • @babel/parser
  • @code-yeongyu/senpi-codemode
  • @earendil-works/chord
  • @earendil-works/pi-pty
  • @silvia-odwyer/photon-node
  • @xterm/headless
  • cross-spawn
  • isexe
  • jiti
  • path-key
  • shebang-command
  • shebang-regex
  • which

Bun resolved the same set except jiti (14 packages). Typebox operations execute through the CLI extension loader's virtual modules, so they produce no external Typebox filesystem path. The list is evidence about the representative scenarios, not an allowlist: all packages in the dependency closure remain eligible for copying.

Evidence already captured

  • TMPDIR=<temporary directory> SENPI_INVENTORY_PACKAGE=<npm-installed package> bun run --cwd packages/coding-agent test test/runtime-snapshot/: 8 files / 24 tests passed against the final materializer and inventory code.
  • The quiet MacBook first exposed macOS's /tmp → /private/tmp alias in the containment assertion. The check now canonicalizes both sides; the exact TMPDIR=/tmp npm-install inventory reproduction passes.
  • Non-timing Mini gate on the exact PR head: changelog gate, complete Bun build, root tsc --noEmit, bun run check, rebuilt runtime-snapshot suite (8 files / 24 tests), and CLI self-test (8/8) all passed.
  • Unchanged Bun 1.4.3 Node-bundle smoke cases passed 7/7 with the original timeout. They are under five seconds, but not well under it: custom exec 4,737 ms, help 4,506 ms, and external TypeScript extension 4,214 ms were the three slowest. No timeout was raised.
  • Changed TypeScript files passed biome check --error-on-warnings; git diff --check passed. The preload is excluded by the repository's Biome configuration; node --check passed and both runtimes executed it in the inventory.
  • node scripts/check-pr-changelog.mjs --base origin/main: passed after both commits.
  • Killed mutants: remove dependency filtering (87 files exceeds the fixture bound), omit the SDK platform package (real inventory fails), disable include-glob expansion (glob-only metadata is missing), restore pre-fix(runtime-snapshot): copy only the shipped package files #3084 whole-root copying (unshipped files leak), and disable manifest entry-root protection (a legitimate docs/index.js entry disappears).
  • Typebox operations execute through the extension loader's virtual modules, so no external typebox filesystem resolution is claimed. SDK types are type-only; actual SDK executable resolution is exercised separately.

Packed-install measurements

Quiet macOS arm64 box; exact baseline 0f23d4cd40 and measured head 8221504fd7; separate worktrees; each built and packed, then installed with npm into separate run-scoped prefixes. Bun 1.4.2 and 1.4.3 were downloaded only into that run's /tmp tree and invoked by full path. Five first launches used fresh HOME, TMPDIR and agent directories. Five direct cold --help launches used the resulting snapshot. Values are median / minimum.

After measurement, main advanced only in packages/senpi-codemode/**; the required behind-zero rebase produced reviewed PR head 013679c8f8. The measured packages/coding-agent/**, root manifest/lock, and build/pack scripts are byte-identical between the measured and rebased heads. The post-rebase real runtime-snapshot suite, including codemode bootstrap inventory, passes 8 files / 24 tests.

Snapshot payload Main after Part A Part B Delta
Total files 14,552 12,594 -1,958 (-13.5%)
node_modules files 12,674 10,713 -1,961 (-15.5%)
Total logical bytes 379,533,907 364,956,216 -14,577,691 (-3.8%)
node_modules logical bytes 330,752,975 316,170,995 -14,581,980 (-4.4%)
Total allocated KiB (du -sk) 408,916 391,200 -17,716 (-4.3%)
node_modules allocated KiB 357,100 339,380 -17,720 (-5.0%)
Runtime Main first launch Part B first launch Main snapshot copy Part B snapshot copy Main direct cold help Part B direct cold help
Bun 1.4.2 1,965 / 1,933 ms 1,820 / 1,753 ms 1,100 / 1,079 ms 956 / 946 ms 43.8 / 43.0 ms 44.4 / 44.3 ms
Bun 1.4.3 2,690 / 2,652 ms 2,597 / 2,518 ms 1,087 / 1,068 ms 953 / 950 ms 42.2 / 41.0 ms 41.9 / 41.4 ms

Part B reduces median real first launch by 145 ms (7.4%) on Bun 1.4.2 and 93 ms (3.4%) on Bun 1.4.3. Direct snapshot help is effectively unchanged at 42-44 ms and is far below five seconds. The dominant SDK binary remains, so byte savings are intentionally smaller than file-count savings.

The remote cleanup receipt is clean: run directory removed, self-excluding process match zero, and global Bun resolution/version unchanged (~/.bun/bin/bun, ~/.vite-plus/bin/bun; 1.4.2 before and after).

No timeout was increased. No package is removed based solely on the representative inventory. The complete graph and native executable remain necessary for upgrade resilience; the dominant SDK logical bytes are not claimed as removed.

Review follow-up (M1, L1, L2, L4)

  • M1: add a source-only dependency fixture with main index.ts, src/helper.ts, and no built JavaScript. It asserts the helper contents copied by the real snapshot materializer.
  • Mutation RED: deleting built && makes the fixture fail with ENOENT for snapshot node_modules/nested-dep/src/helper.ts; 1 failed / 1 passed, exit 1.
  • Restored GREEN: the same fixture command passes 2/2 tests, exit 0: bun run --cwd packages/coding-agent test test/runtime-snapshot/dependency-files.test.ts.
  • L1: removed the redundant existsSync loop because inventoryRuntime already realpaths every recorded file and enforces snapshot containment. The independently meaningful SDK-resolution assertion remains.
  • L2: corrected the coverage claim above. The grep-loader probe does not assert that a native binding loads; the packed-install review observed native-unavailable under both runtimes. No native binary requirement was added to CI.
  • L4: documented top-level-only dependency folder pruning; nested folders such as yaml/dist/doc contain real runtime code.
  • Follow-up gates: runtime-snapshot suite 8 files / 25 tests passed; changed-file Biome checked 3 files without fixes; direct root bunx tsc --noEmit exited 0; node scripts/check-pr-changelog.mjs --base origin/main passed. Conflict-marker scans and git diff --check were clean. Merge with refreshed origin/main was already up to date (behind 0). No timing work was run.

Local evidence hashes (SHA-256):

Receipt SHA-256
m1-red.log 785af961efc918ec7a46ddda08e9bf71bdfd7d9be75661d542b108fa2dab5f81
m1-green.log 4e97a755ec718150945b2371566350c47da60b5705ebdbc1453384e36149b91b
suite.log a7cb9b3c897f73b66480d33bf367df6b7aa6126c775663d21152a40f2e87ed8c
biome.log 7d1a98da4187850be6b4e787ce8ce917de43c67316c7eb5a63a5fca674a7f3ca

Known gaps (not addressed in this follow-up)

  • M2: the representative inventory does not exercise the pi-pty native binding, codemode Python/WebView kernels, Node/jiti alias targets, optional bundle externals (bufferutil, utf-8-validate, canvas, kerberos, supports-color), or interactive TUI mode. This is not exhaustive node_modules runtime coverage.
  • L3: manifest entry-root discovery visits main/module/exports/bin/pi, not package imports maps. The review found an inactive babel-src condition in the current closure; future runtime imports targets under pruned roots remain a known gap.

Replace the manifest-only assertion with a glob-exclusive file and an unshipped sibling. The fixture detects disabled glob expansion and whole-package copying.

Refs #3083 (part B)
Retain the full nested and hoisted closure and snapshot-local native assets. Filter inert dependency trees and redundant sources with manifest entry exemptions, and govern the real Node/Bun CLI graph with an offline inventory regression and a fixture budget.

Refs #3083 (part B)
Use the matching Bun loader while recording every JavaScript and TypeScript module extension reached by representative CLI paths.

Refs #3083 (part B)
Cover the tracker requirement beyond README and changelog while preserving licenses and every manifest-declared entry root.

Refs #3083 (part B)
Keep the fork tracker aligned with the full Part B payload policy.

Refs #3083 (part B)
Prove a manifest-named root Markdown asset overrides the dependency pruning convention.

Refs #3083 (part B)
Treat macOS /tmp and /private/tmp as the same tree when proving every observed runtime path stays inside the snapshot.

Refs #3083 (part B)
@code-yeongyu
code-yeongyu force-pushed the fix/3083-snapshot-closure branch from 8221504 to 013679c Compare October 10, 2026 18:41
@code-yeongyu
code-yeongyu marked this pull request as ready for review October 10, 2026 20:31
@code-yeongyu code-yeongyu added the review-queued Queued for batched Opus review label Oct 10, 2026
@code-yeongyu
code-yeongyu merged commit c58470f into main Oct 11, 2026
38 checks passed
@code-yeongyu
code-yeongyu deleted the fix/3083-snapshot-closure branch October 11, 2026 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-queued Queued for batched Opus review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant