Skip to content

[mem] Stream saved transcripts and stage large search writes - #30702

Merged
nwparker merged 3 commits into
mainfrom
OrcaWin/np-OOMs-10-19-transcripts
Oct 20, 2026
Merged

nwparker merged 3 commits into
mainfrom
OrcaWin/np-OOMs-10-19-transcripts

Conversation

@OrcaWin

@OrcaWin OrcaWin commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​457 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​457
Prod 13 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​253 $\color{#cf222e}{\Huge{\mathbf{−}}}$​98 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​155

ELI5

Long saved conversations could consume several copies of their size in memory while Orca scanned them for the session list and search. Read and index them incrementally while preserving the complete conversation and the previous search results until a replacement is ready.

What Changed

Fixes three independent sources of memory growth in the transcript pipeline:

  • Local Hermes, Gemini JSON, Devin and Cline files now use the existing streaming document readers, rather than reading and decoding the entire file first. Messages still reach every transcript consumer. Duplicate message fields retain JSON's last-field semantics, malformed Cline sidecars remain metadata-only, and interrupted reads discard their unpublished messages.
  • Search reads without a session identity spill through the existing 32 Mi-character transaction threshold into a private temporary SQLite file. They still publish atomically at the end. Commit, discard, rollback and store closure release the temporary database.
  • The streaming JSON tokenizer assembles only selected scalar values. Ignored attachment strings and numbers no longer accumulate before the document projection discards them. Selected message text and Unicode remain complete.

Why

These address separate allocations: whole-file text plus decoded arrays, anonymous index buffers, and ignored scalar tokens inside otherwise streaming reads. Reusing the existing folds and SQLite adapter keeps format handling and search publication policy in their existing owners. There are no new content caps, truncation rules, early search publication, schema migrations or paired-host protocol changes.

The attached before/after measurements below reproduce all three removed allocations at the pinned PR commit, including the production search-staging threshold and all four document providers.

Linked Issue

Follow-up to the user-requested OOM audit in #30650. This PR is stacked directly on that branch, independently of #30698.

Visual Proof

N/A — no visual-state or layout change. Before/after resource evidence follows.

Measured proof

Before and after retained heap for PR 30702

Raw measurements: all 84 samples · Runnable reproduction bundle, methodology and test log

Measured at e57ef2f4aaaadd0d6720a6a1f4e45755f429d516, with before sources from base 907694dcbb23b1d2f60805c102b0f2a0ff999e75. macOS arm64, Apple M4 Max, Node 24.20.0; generated files, real production readers, real Node SQLite. Each sample runs in a fresh background Node process, alternating before/after order. The bundle extracts both pinned source trees and reuses installed workspace dependencies. The branch was subsequently updated with main at 693414a4fcd1 to pick up upstream formatting fixes; the transcript, tokenizer, search-index and SQLite code is unchanged by that update.

Memory: median of three before/after pairs. Values are additional retained JavaScript heap after two forced GCs at the same checkpoint, not peak RSS or total Orca memory.

Scenario and checkpoint Before After
Hermes: 8,192 messages / 32.74 MiB JSON, first delivered message 65.78 MiB 0.28 MiB
Devin: 8,192 messages / 64.88 MiB JSON, first delivered message 130.18 MiB 0.28 MiB
Gemini: 8,192 messages / 32.74 MiB JSON, first delivered message 65.78 MiB 0.28 MiB
Cline: 8,192 messages / 32.74 MiB sidecar, first delivered message 65.79 MiB 0.34 MiB
32 MiB ignored string, before its closing quote 40.20 MiB 0.22 MiB
32 MiB ignored number, before its delimiter 40.20 MiB 0.22 MiB
64 MiB anonymous search text, before publication; production 32 Mi-character limit 65.71 MiB 1.14 MiB
32 million characters, before publication; regression-test 64 Ki-character limit 31.86 MiB 1.13 MiB

All four document cases deliver all 8,192 messages with identical before/after SHA-256 hashes, final text and model metadata. Search exposes zero rows before commit and preserves the complete input character count afterward. The scalar cases still return the selected metadata and following message. The attached 60-test run also checks Unicode, cancellation, duplicate JSON fields, malformed Cline sidecars, atomic replacement, and temporary-database cleanup on discard, removal, rollback and close.

Latency cost: median of six alternating pairs; third operation per process after two warmups, with no forced GC or message hashing inside the timed interval. Filesystem caches are warm from fixture creation. Index timings include temporary staging and final commit, excluding database opening and cleanup.

Operation Before After Added time
Hermes scan: 100 messages / 0.40 MiB 1.46 ms 6.31 ms +4.86 ms (333.5%)
Hermes scan: 10,000 messages / 39.97 MiB 99.56 ms 163.93 ms +64.37 ms (64.7%)
Anonymous index: 64 MiB; production staging limit 1030.91 ms 1129.88 ms +98.97 ms (9.6%)

These synthetic checkpoints demonstrate the removed allocations and measured local cost. They do not establish peak memory, end-to-end UI responsiveness, low-disk behavior, or Linux/Windows/live-SSH performance. Memory can still reach the existing staging threshold between checkpoints, and selected individual messages remain complete in memory.

Testing

  • Locally exercised on macOS with real files, Node SQLite and forced garbage collection; all test launches used ORCA_BACKGROUND_LAUNCH=1.
  • Automated before/after memory regressions and semantic/lifecycle tests added.
  • Fresh proof run at e57ef2f4aaaa: 84 benchmark samples and 7 suites / 60 tests passed; reproduction files and raw results are attached above. Broader validation reported during implementation follows.
  • Transcript/search/parser run: 69 suites, 767 tests passed. Additional WSL access, quarantine and message-delivery run: 45 tests passed. Node runtime-boundary, SQLite staging and Unicode/parity checks: 55 tests passed.
  • pnpm tc, pnpm run check:code-quality:changed and pnpm run build:electron-vite passed. Full PR CI, including the PR checks passed aggregate, passed for updated head 693414a4fcd1; separate Mobile, WSL orcad, and Linux/Windows/Windows ARM server checks also passed. The first full run caught a missing WSL storage inventory entry for the temporary table; that entry is now explicit, and the inventory/staging regressions (10 tests), full typecheck, changed-code checks and build all pass again.
  • Native Linux/Windows and live SSH sessions were not exercised locally. WSL routing tests preserve the existing gated filesystem path; remote wire formats are unchanged.

Review

Negative user-facing trade-offs:

  • Slower transcript scans: session metadata and replacement search results can take longer to arrive. On the measured fixtures, the added scan time is approximately 5 ms for 100 messages and 64 ms for 10,000 messages; this is a local source benchmark, not an app interaction measurement.
  • Extra temporary-disk space and I/O for large anonymous reads: staging adds approximately 99 ms (9.6%) to the measured 64 MiB index operation. Staging uses synchronous SQLite writes; slower storage can increase delays. Ordinary writes below the existing threshold retain the in-memory path.
  • A temporary-storage failure can prevent an index refresh: a full or unavailable temp volume causes the large anonymous read to fail; unpublished staging is discarded and the previous published search index remains available. Low-disk behavior was not exercised in this evidence run.

No new content caps or intentional truncation are introduced. Selected message text remains complete; memory still scales with the largest selected message and existing preview/index thresholds. The measurements above are retained-heap checkpoints, not a constant-memory guarantee.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

The execution host continues to read its own transcript files through the existing filesystem gate. Folder workspaces and Git providers require no special handling. SQLite's empty-filename database is private and deleted on close (SQLite documentation).

Checklist

  • This PR is focused on the transcript memory pipeline.
  • I explained the before/after, mechanism and choice of approach.
  • Visual proof is N/A with the reason above.
  • Self-reviewed for correctness, security and performance.
  • Cross-platform, SSH and compatibility impact considered.
  • Full repository checks and packaging pass in CI for the exact PR commit 693414a4fcd1.

@nwparker
nwparker force-pushed the OrcaWin/np-OOMs-10-19-transcripts branch from c13186e to a1edee1 Compare October 19, 2026 23:26
@nwparker
nwparker changed the base branch from OrcaWin/np-OOMs-10-19 to main October 19, 2026 23:26
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0e700c7d-9f08-42a5-a317-bd144c930c3c
📥 Commits

Reviewing files that changed from the base of the PR and between ad5ef0d and a1edee1.

📒 Files selected for processing: 19
  • config/scripts/vitest-sqlite-database-runtime-files.mjs
  • src/main/ai-vault-search/session-search-anonymous-write-memory.test.ts
  • src/main/ai-vault-search/session-search-index-writer.ts
  • src/main/ai-vault-search/session-search-write-buffer.test.ts
  • src/main/ai-vault-search/session-search-write-buffer.ts
  • src/main/ai-vault/session-document-consumer-replacement.test.ts
  • src/main/ai-vault/session-document-discarded-scalar-memory.test.ts
  • src/main/ai-vault/session-document-stream.ts
  • src/main/ai-vault/session-scanner-agent-parser.ts
  • src/main/ai-vault/session-scanner-cline-parser.ts
  • src/main/ai-vault/session-scanner-devin-parser.ts
  • src/main/ai-vault/session-scanner-gemini-parsers.ts
  • src/main/ai-vault/session-scanner-hermes-parser.ts
  • src/main/ai-vault/session-scanner-json-document-memory.test.ts
  • src/main/ai-vault/session-transcript-channel.ts
  • src/main/ai-vault/session-transcript-consumers.ts
  • src/shared/json-token-reader.test.ts
  • src/shared/json-token-reader.ts
  • src/shared/wsl-move/wsl-move-storage-dispositions.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

📝 Walkthrough

Session document parsing now supports selective JSON scalar retention and streamed reads for Hermes, Gemini, Cline, and Devin, including abort signals and message-sink resets. Session-search writes use a bounded buffer that can spill rows to temporary SQLite storage. Added tests cover memory retention, duplicate message arrays, cancellation, and buffer cleanup.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to a1ede

The transcript-streaming and search-staging changes appear mergeable after normal checks; no concrete blocking issue remains.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 19 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the two main changes: streaming saved transcripts and staging large search writes to reduce memory usage.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description follows the repository template. It includes the ELI5 summary, scope, rationale, linked issue, visual proof, measured evidence, testing details, review trade-offs, compati…
Full details: Description check

Explanation

The pull request description follows the repository template. It includes the ELI5 summary, scope, rationale, linked issue, visual proof, measured evidence, testing details, review trade-offs, compatibility notes, and completed checklist. The description also identifies untested native Linux, Windows, and live SSH scenarios.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker
nwparker force-pushed the OrcaWin/np-OOMs-10-19-transcripts branch from a1edee1 to e57ef2f Compare October 20, 2026 01:39
@nwparker
nwparker merged commit 509f198 into main Oct 20, 2026
45 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.

2 participants