fix(card): seed dialog history into restored-session card#138
Merged
Conversation
A restored (`claude --resume`) session builds its live card before claude has flushed the resumed transcript to disk. `_ensure_seeded` ran during that window, read an empty/partial JSONL, returned [], but latched `seed_attempted = True` *before* the read — permanently blocking the seed. By the time the full transcript landed (and the first live event arrived) the guard short-circuited, so the card showed only new turns and the prior dialog history never appeared. Plain bot-restart worked because the JSONL was already fully written, so the first attempt succeeded. Latch `seed_attempted` only on a *successful* (non-empty) seed; on an empty read leave it clear and retry on a later event. Gate the retry on the transcript mtime advancing (new `CardState.seed_mtime`) so a burst of events during the resume window doesn't re-parse a multi-MB JSONL each time. The three non-destructive re-seed sites reset `seed_mtime` alongside `seed_attempted`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Symptom
When a session is restored via
claude --resume, its live card showsonly the new turns — the prior dialog history is never picked up.
A plain bot-restart seeds history fine.
Root cause
_ensure_seeded(handlers/notifications.py) latchedstate.seed_attempted = Truebefore reading the JSONL. A restoredsession builds its card while
claude --resumeis still flushing theresumed transcript, so that early read returns
[]— but the flag isalready latched. By the time the full transcript lands (and the first
live event fires
_ensure_seededagain), the guard short-circuits, sonothing is ever seeded.
Traced on a live restore (
f852609a"latest logs", window@6):repost_card … events=0right after the resume, thenevents=2(onlythe live turn) — never a
card_seededline. The 2.8 MB transcript ondisk has 30 end-turn boundaries that should have seeded.
Plain bot-restart works only because the JSONL is already fully written,
so the first attempt succeeds.
Fix
seed_attemptedonly on a successful (non-empty) seed. Anempty read leaves it clear and retries on a later event.
CardState.seed_mtime) so a burst of events during the resume windowdoesn't re-parse a multi-MB JSONL each time — one
stat()per call,full parse only when the file actually grows.
release, false-stall recovery) reset
seed_mtimealongsideseed_attempted.Tests
tests/ccbot/handlers/test_card_seed.py:test_seed_attempted_only_once(which encoded thebuggy latch-on-empty behaviour)
test_successful_seed_latches— non-empty seed latchestest_empty_seed_not_latched_retries_when_transcript_lands— directregression for this bug: empty → not latched → seeds once the
transcript appears
test_unchanged_empty_transcript_not_reparsed— mtime gate suppressesredundant re-parses
Full suite: 747 passed. ruff + pyright clean.
🤖 Generated with Claude Code