fix: retain incomplete JSONL tails#2168
Conversation
devYRPauli
left a comment
There was a problem hiding this comment.
LGTM. I checked this out and verified both the bug and the fix locally.
Running the PR's new test against the pre-fix source (CostUsageJsonl.swift on main, PR test file overlaid) reproduces a real data-corruption bug: jsonl scanner retries an incomplete final record after append fails with the still-forming final record partially consumed on the first pass (firstPass = ["{\"type\":\"message\",\"id\":\"partial"]), resumeOffset advancing to 31 instead of 0, and the record's later completion mis-parsed as a bogus fragment "\"}" on the second pass. On this branch all four tests pass.
So the real-world effect being fixed is: an in-progress final JSONL line (a session record still being written) could be partially read as if complete, its committed offset moved past it, and its eventual completion then decoded as a spurious separate line - silent cost/usage miscount on incremental tailing. The committedOffset (only advanced on a newline-terminated line) plus hasCompleteJSONTail() (only flush a no-newline tail if it already parses as valid, non-truncated JSON) is the right, narrow fix.
Edge cases I looked at and am satisfied by:
- Empty file / file ending exactly on
\n:hasCompleteJSONTail()no-ops, so no behavior change. - Truncated tail: the
!truncated && lineBytes == current.countguard refuses to validate a partial buffer that exceededprefixBytes, which is the conservative choice. - No double-emit: when the tail is committed early and the lone trailing
\narrives on a later scan,appendSegment/flushLineno-op on the zero-length segment, so the record isn't emitted twice.
Two minor, non-blocking notes:
- The second new test (
accepts a complete final record without newline) passes on the pre-fix source too, so it reads as a non-regression/documentation test rather than proof of the fix - theretries...test is the one carrying the regression coverage. hasCompleteJSONTail()parses the tail once to validate, and the consumer parses it again when the line is finally accepted - a small redundant parse, not worth changing.
Nice catch on a subtle one.
|
Codex review: needs real behavior proof before merge. Reviewed July 16, 2026, 3:29 PM ET / 19:29 UTC. Summary Reproducibility: yes. at source level: the former EOF path could not reliably distinguish complete records from still-extendable JSON tails, and the focused fixtures cover the failing append and truncation cases. A real current-installation reproduction transcript has not been supplied. Review metrics: 3 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Retain the structurally aware EOF scanner and cache-version migration, then merge after a representative redacted live run proves partial-write recovery, exactly-once accounting, and acceptable one-time upgrade rebuild behavior. Do we have a high-confidence way to reproduce the issue? Yes at source level: the former EOF path could not reliably distinguish complete records from still-extendable JSON tails, and the focused fixtures cover the failing append and truncation cases. A real current-installation reproduction transcript has not been supplied. Is this the best way to solve the issue? Yes, conditionally: structural EOF classification at the shared scanner boundary is narrower and safer than provider-specific workarounds, while cache invalidation is appropriate only if maintainers accept and prove its upgrade cost. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1b921ecf3930. Label changesLabel changes:
Label justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (21 earlier review cycles; latest 8 shown)
|
|
Merged as Verification:
|
Summary
Checks
git diff --check