fix(local): recover truncated JSONL transcript tails - #3590
Open
GautamSharma99 wants to merge 1 commit into
Open
fix(local): recover truncated JSONL transcript tails#3590GautamSharma99 wants to merge 1 commit into
GautamSharma99 wants to merge 1 commit into
Conversation
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.
Summary
Fixes #3580.
Local transcript loading, suffix reads, migration, and search each parsed
messages.jsonlindependently. A single interrupted final append caused full history loading to throw, while search caught the same parse failure at file scope and silently returned no messages at all.This PR gives all local transcript consumers one byte-aware JSONL implementation with deliberately narrow recovery semantics:
What changed
Shared transcript JSONL owner
Added
src/backend/local/transcript-jsonl.tsand routed these consumers through it:LocalStore;The parser operates on
Bufferbyte ranges rather than splitting a decoded string. This keeps byte offsets accurate and lets suffix reads discard a clipped initial row before UTF-8 decoding, including when the byte window begins inside a multibyte character.Narrow corruption handling
Rows terminated by a newline are considered committed. If one cannot be parsed,
LocalTranscriptJsonlCorruptionErrorincludes:Search no longer wraps the entire JSONL parse in a blanket catch, so committed corruption is surfaced instead of being converted into an empty conversation.
Only an invalid final row without a terminating newline is recoverable. The reader returns preceding records and emits a deduplicated warning describing the file and recovery behavior. A complete final JSON value without a newline remains readable and is terminated before a later append.
Safe append recovery
Before appending a transcript entry, the store examines only the final physical row, avoiding an O(file size) parse on every message append.
For an incomplete tail it:
messages.jsonlto a uniquely namedmessages.jsonl.corrupt-tail-backup-*sidecar;The copy must succeed before truncation occurs. The backup therefore preserves both every durable row and the exact damaged bytes for inspection or manual recovery.
Repository maintenance
Extracting the duplicated readers reduced
local-store.tsfrom 3,594 to 3,558 lines, so the source-size baseline is lowered in the same change. The new tests live in responsibility-sized adjacent files rather than growing the already grandfatheredlocal-backend.test.ts.Tests
Added coverage for:
Validation completed:
bun run check: all 12 repository checks passed.Scope
This does not attempt to guess around committed mid-file corruption or silently skip arbitrary bad rows. Those cases remain failures because continuing across them could hide lost ordering, parent links, or transcript state. Recovery is limited to the crash-consistent case produced by an interrupted final append.