fix(artifacts): one parsed sidecar per project — the read side of the #318 OOM fix - #335
Merged
Conversation
…318 OOM fix Destin's live app died 2026-08-27 01:01:56 after 12h32m (and 2026-08-23 after 60h): V8 heap OOM at 2,801/2,825 MB. The core dump held 477 parsed copies of youcoded-dev/.youcoded/artifacts.json (6.4 MB, 21,311 versions) ≈ 3.0 GB. PR #318 queued the sidecar WRITER; all 20 read sites stayed unguarded, and one Edit costs ~11 full parses (LIST_SESSION, artifacts:get per visible card, check-existence, the watcher's id map, every open tab at startup). readSidecarShared: one parsed copy per project, validated by size + mtime, concurrent callers share one in-flight parse, a committed writeSidecar seeds it with the object just written, idle copies drop after 60 s. Read-only handlers use it; the five mutate-and-write paths keep the private readSidecar. casWrite: the CAS comparand is read from a 4 KB head probe (regex, no parse) with a whole-file fallback — the old extractor JSON.parsed 6.4 MB to read one timestamp on every write. Reproduced against Destin's real file (now 6.9 MB / 22,017 versions), one process, --max-old-space-size=2800: old N=60: 60 parses, peak heap 1,188 MB old N=477: FATAL ERROR: Ineffective mark-compacts near heap limit new N=477: 1 parse, peak heap 25 MB Guards: tests/artifacts/sidecar-cache.test.ts (burst = one parse, seeded after write, external change re-parses, corrupt backed up once, idle drop), cas-write.test.ts (head probe, fallback), artifact-store.test.ts (zero JSON.parse on the CAS path). verify.sh green: tsc, full suite, knip, eslint, ast-grep. Investigation: docs/active/investigations/2026-08-27-artifacts-sidecar-oom-crash.md The rest of the bug class (transcript replay, Android's missing write queue) is specced and paused: docs/active/specs/2026-08-27-paged-history-and-read-hardening-design.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7YhBJVwWP2iwohNEUSmyY
itsdestin
added a commit
that referenced
this pull request
Aug 27, 2026
docs(artifacts): the shared sidecar read (PR #335) — depth paragraph under Concurrency
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Destin's live app died 2026-08-27 01:01:56 after 12h32m (and 2026-08-23 after
60h): V8 heap OOM at 2,801/2,825 MB. The core dump held 477 parsed copies of
youcoded-dev/.youcoded/artifacts.json (6.4 MB, 21,311 versions) ≈ 3.0 GB.
PR #318 queued the sidecar WRITER; all 20 read sites stayed unguarded, and
one Edit costs ~11 full parses (LIST_SESSION, artifacts:get per visible card,
check-existence, the watcher's id map, every open tab at startup).
readSidecarShared: one parsed copy per project, validated by size + mtime,
concurrent callers share one in-flight parse, a committed writeSidecar seeds
it with the object just written, idle copies drop after 60 s. Read-only
handlers use it; the five mutate-and-write paths keep the private readSidecar.
casWrite: the CAS comparand is read from a 4 KB head probe (regex, no parse)
with a whole-file fallback — the old extractor JSON.parsed 6.4 MB to read one
timestamp on every write.
Reproduced against Destin's real file (now 6.9 MB / 22,017 versions), one
process, --max-old-space-size=2800:
old N=60: 60 parses, peak heap 1,188 MB
old N=477: FATAL ERROR: Ineffective mark-compacts near heap limit
new N=477: 1 parse, peak heap 25 MB
Guards: tests/artifacts/sidecar-cache.test.ts (burst = one parse, seeded
after write, external change re-parses, corrupt backed up once, idle drop),
cas-write.test.ts (head probe, fallback), artifact-store.test.ts (zero
JSON.parse on the CAS path). verify.sh green: tsc, full suite, knip, eslint,
ast-grep.
Investigation: docs/active/investigations/2026-08-27-artifacts-sidecar-oom-crash.md
The rest of the bug class (transcript replay, Android's missing write queue)
is specced and paused: docs/active/specs/2026-08-27-paged-history-and-read-hardening-design.md
🤖 Generated with Claude Code
https://claude.ai/code/session_01H7YhBJVwWP2iwohNEUSmyY