Skip to content

fix(memory): surface honest notice for bodyless session reads#310

Merged
khustup2 merged 1 commit into
mainfrom
fix/session-empty-body-honest-read
Jul 13, 2026
Merged

fix(memory): surface honest notice for bodyless session reads#310
khustup2 merged 1 commit into
mainfrom
fix/session-empty-body-honest-read

Conversation

@khustup2

@khustup2 khustup2 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

A session cat through the memory mirror could return 0 bytes with no signal while ls showed a non-zero size — making the mirror look populated when the transcript body was actually unreadable, and blocking fallback to raw session data for turn-level detail.

Root cause, both plugin-side (backend unchanged):

  1. Silent-empty reconstruction. A captured event is never legitimately empty (capture.ts always writes a populated JSON entry), so a session that reconstructs to "" is always a pathology — but both readers returned it as an empty file. ls size comes from a separate MAX(size_bytes) query, so it stayed non-zero → the misleading size-vs-content split.
  2. Fragile null handling. normalizeSessionMessage did JSON.stringify(message) unconditionally: a NULL column became the literal text "null", and undefined leaked into the joined output.

Fix:

  • grep-core.ts — shared emptySessionBodyNotice(rowCount): honest diagnostic ("rows exist, body empty, metadata only — not an empty file").
  • virtual-table-query.ts (hook path — what Claude Code's bash cat hits) — count session rows even when message::text is NULL (previously dropped silently), skip empty normalized parts, emit the notice instead of "" (silent empty) or null (misleading "No such file" though ls lists it).
  • deeplake-fs.ts (shell path) — normalizeSessionMessage coerces null/undefined/non-serializable → ""; joinSessionMessages filters empty parts; prefetch/readFile/readFileBuffer emit the notice on empty reconstruction.

Scope note: this makes the empty-read case honest, not silent — it does not change why a body would be empty (a separate capture/storage question, answerable per-row via length(message::text) vs size_bytes).

Version Bump

Bug fix → warrants a patch bump (0.7.1260.7.127). Not bumped yetpackage.json is unchanged, so as-is this merges with no release. Will bump on request to ship it.

Test plan

  • Tests pass locally (npx vitest run on affected suites: 376 passed — deeplake-fs, virtual-table-query, grep-core, pre-tool-use)
  • Relevant new tests added (7: NULL / undefined / empty-string bodies, no-"null"/"undefined"-leak guarantee, mixed empty+real turns across both reader paths)
  • Version bumped in package.json, or no release needed for this change — pending decision (see Version Bump)

🤖 Generated with Claude Code

A session `cat` could return 0 bytes with no signal while `ls` showed a
non-zero size. A captured event is never legitimately empty (capture.ts
always writes a populated JSON entry), so a session that reconstructs to
"" is always a pathology — yet both readers returned it as an empty file,
and `ls` size comes from a separate MAX(size_bytes) query so it stayed
non-zero, producing the misleading size-vs-content split.

- grep-core: add shared emptySessionBodyNotice(rowCount).
- virtual-table-query (hook path): count session rows even when message::text
  is NULL (previously dropped silently), skip empty normalized parts, and emit
  the notice when rows exist but no readable body — instead of "" (silent
  empty) or null (misleading "No such file" though ls lists it).
- deeplake-fs (shell path): normalizeSessionMessage coerces null/undefined/
  non-serializable to "" (no more literal "null"/"undefined" leaking);
  joinSessionMessages drops empty parts; prefetch/readFile/readFileBuffer
  emit the notice on empty reconstruction.

Tests: NULL/undefined/empty-string bodies, no-leak guarantee, and mixed
empty+real turns (keeps only real turns) across both reader paths.
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Session-backed virtual and filesystem reads now distinguish stored rows with empty or null bodies from missing files, filter unusable message parts, and return a row-count-aware empty-body notice. Tests cover empty, nullish, and mixed session messages.

Changes

Session body handling

Layer / File(s) Summary
Empty-body diagnostic helper
src/shell/grep-core.ts
Adds emptySessionBodyNotice(rowCount) for sessions containing metadata rows without usable message bodies.
Virtual path session reconstruction
src/hooks/virtual-table-query.ts, tests/claude-code/virtual-table-query.test.ts
Counts session rows independently of content validity, returns an empty-body notice when all bodies are unusable, and retains only non-empty turns in mixed sessions.
Filesystem session rendering
src/shell/deeplake-fs.ts, tests/claude-code/deeplake-fs.test.ts
Normalizes nullish messages, filters empty parts, applies notices during prefetch and file reads, and tests null, undefined, empty, and mixed bodies.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: surfacing an honest notice for bodyless session reads.
Description check ✅ Passed The description follows the template with Summary, Version Bump, and Test plan sections, and includes the key implementation and testing details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/session-empty-body-honest-read

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.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🟢 Lines 99.23% (🎯 90%) 899 / 906
🟢 Statements 97.16% (🎯 90%) 1061 / 1092
🟢 Functions 97.12% (🎯 90%) 135 / 139
🟢 Branches 91.10% (🎯 90%) 860 / 944
File Coverage — 3 files changed
File Stmts Branches Functions Lines
src/hooks/virtual-table-query.ts 🟢 98.7% 🟢 92.3% 🟢 95.8% 🟢 99.2%
src/shell/deeplake-fs.ts 🟢 96.4% 🟢 90.5% 🟢 97.2% 🟢 98.8%
src/shell/grep-core.ts 🟢 97.8% 🟢 91.5% 🟢 97.7% 🟢 100.0%

Generated for commit c6a003e.

@khustup2
khustup2 requested a review from efenocchi July 13, 2026 21:33
@khustup2
khustup2 merged commit 43607a0 into main Jul 13, 2026
12 checks passed
@khustup2
khustup2 deleted the fix/session-empty-body-honest-read branch July 13, 2026 23:23
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.

1 participant