fix(claude): tolerate duplicate snake/camel keys in session JSONL - #47
Merged
Merged
Conversation
Claude Code v2.x writes both session_id and sessionId (and other snake/camel pairs) on the same JSONL line; serde's generated struct visitors reject the second occurrence, so every assistant line failed to parse and Claude activity was invisible to Lucarne. Wrap claude JSONL parsing (probe, history, watch) in a DupTolerant deserializer that buffers each object as borrowed (key, raw value) pairs, folds camelCase spellings onto their snake_case field, drops repeats (last occurrence wins, like serde_json::Value), and replays the deduplicated object. Zero-copy borrows into the input line are preserved; nested payloads (e.g. progress) are covered recursively. Benchmark: claude_parse 37ms -> 60ms per full session (~1.6x); the tolerance requires a second pass over the line, inherent to streaming serde deserialization.
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.
Fixes #45
Fixes #46
Problem
Claude Code v2.x (v2.1.206, v2.1.208, …) writes both
session_idandsessionId(and other snake/camel pairs) on the same JSONL line. serde's generated struct visitors reject the second occurrence as a duplicate field, so everyassistant/userline fails to parse — Lucarne sees no Claude activity at all (probe, history, and watch all fail withduplicate field session_id).Fix
Wrap all claude JSONL parsing (probe, history body, watch meta, plus nested content/usage/progress parses) in a
DupTolerantserde Deserializer:(key, raw value)pairs — zero-copy, no allocation for canonical keysserde_json::Value)All logic stays inside the claude provider — no provider ids, cursor formats, or parse rules moved into shared layers (per AGENTS.md boundaries).
Tests
parse_claude_json_str_tolerates_duplicate_keys_last_wins— dualsession_id/sessionId+message_id/messageIdlines, asserts last-winsprobe_session_meta_accepts_dual_key_lines— probe path on the exact bug shapereader_accepts_dual_key_lines_and_nested_progress— watch/history path with dual keys + nestedfullOutput/full_outputprogress payloadTradeoff
claude_parsebench: 37ms → 60ms per full session (~1.6x). Tolerance requires a second pass over the line — streaming serde deserializers can't rewind to dedup in one pass. Watch deltas are single lines (µs-scale), so the practical impact is negligible.