Skip to content

fix(card): seed dialog history into restored-session card#138

Merged
Time4Mind merged 1 commit into
mainfrom
fix/restore-card-seed-history
Jun 20, 2026
Merged

fix(card): seed dialog history into restored-session card#138
Time4Mind merged 1 commit into
mainfrom
fix/restore-card-seed-history

Conversation

@Time4Mind

Copy link
Copy Markdown
Owner

Symptom

When a session is restored via claude --resume, its live card shows
only 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) latched
state.seed_attempted = True before reading the JSONL. A restored
session builds its card while claude --resume is still flushing the
resumed transcript, so that early read returns [] — but the flag is
already latched. By the time the full transcript lands (and the first
live event fires _ensure_seeded again), the guard short-circuits, so
nothing is ever seeded.

Traced on a live restore (f852609a "latest logs", window @6):
repost_card … events=0 right after the resume, then events=2 (only
the live turn) — never a card_seeded line. The 2.8 MB transcript on
disk 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

  • Latch seed_attempted only on a successful (non-empty) seed. An
    empty read leaves it clear and retries 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 — one stat() per call,
    full parse only when the file actually grows.
  • The three non-destructive re-seed sites (stale-pause reset, carrier
    release, false-stall recovery) reset seed_mtime alongside
    seed_attempted.

Tests

tests/ccbot/handlers/test_card_seed.py:

  • replaces the old test_seed_attempted_only_once (which encoded the
    buggy latch-on-empty behaviour)
  • test_successful_seed_latches — non-empty seed latches
  • test_empty_seed_not_latched_retries_when_transcript_lands — direct
    regression for this bug: empty → not latched → seeds once the
    transcript appears
  • test_unchanged_empty_transcript_not_reparsed — mtime gate suppresses
    redundant re-parses

Full suite: 747 passed. ruff + pyright clean.

🤖 Generated with Claude Code

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>
@Time4Mind Time4Mind merged commit 7cad238 into main Jun 20, 2026
4 checks passed
@Time4Mind Time4Mind deleted the fix/restore-card-seed-history branch June 20, 2026 08:41
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