Repository navigation
fix(claude): say so when a finished turn never commits a transcript - #1222
Merged
Merged
Conversation
A Claude pane can stream live turns while no committed JSONL ever arrives (debug bundle 2026-06-17T11-50-56-053-a6c70f19: totalEntries 0, transcriptStatus 'ready'). Resume then finds nothing. The disconnect marker existed but could not fire: it was only evaluated when the provider id was first seen, where zero entries is normal, so it was written to refuse 'ready', which is the state the broken pane was in. The check now runs after a completed Claude turn plus a 15 s grace: a healthy pane has JSONL before turn_completed, so still-zero entries and no durable identity at that point is the recorded failure. The first arriving record already resets the status to 'ready', so a slow transcript clears the banner instead of leaving it stuck. Claude only, one pending check per session, cleared on unmount. Tests replay the recorded turn from the bundle (fixture testing/fixtures/transcript-disconnect/): marked after the grace and not before, quiet if a record lands in time, never for a durable identity or another provider. Each guard is mutation-checked (ready excluded, no schedule, any provider, no grace). Refs #290 (the marker slice only; duplicate provider ids and earlier live-stream detection stay open there) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Juliusolsson05
force-pushed
the
fix/claude-transcript-disconnect-marker
branch
from
September 25, 2026 05:58
541ebb0 to
8367921
Compare
Juliusolsson05
added a commit
that referenced
this pull request
Sep 25, 2026
…he check's timer invariants Review round 1 on #1222 (2 Codex; the Pi report is pending): - Major (A): terminal view never mounts TileLeaf's readiness text, and its banner showed only transcriptChannelError/liveChannelWarning, so the new marker was invisible where Claude panes are often watched. A 'disconnected' transcript is the same lifetime class (it lasts until a committed record arrives), so it now takes the banner; transient transcriptError diagnostics still do not. Fail-first renderer test on the real leaf, including that the banner clears on 'ready'. - Test gaps (A, B): one pending check per session and cleanup on unmount were each removable with tests green. Now pinned: a second turn inside the grace keeps the first deadline and schedules no second 15 s timer; an unmount before the deadline leaves the pane untouched. Deferred (B, recorded on #290): the marker lives in renderer runtime state, so a window transfer or full reload before the NEXT completed turn loses it for a pane with no provider id. Carrying it across adoption is part of #290's remaining identity work, not this slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he trigger and the self-clearing Review round 1 (Pi) on #1222, all fixed: - F1 (major, test gap): nothing distinguished the turn_completed trigger from turn_started. A long first turn can run past 15 s before the tail watcher delivers the user row, so a turn_started clock would alarm mid-turn. New test: a started-but-unfinished recorded turn never marks. - F2: the provider kind was checked only when scheduling. A provider switch keeps the session id and resets the runtime, so the Claude check could land a Claude-worded warning on the replacing Codex pane. The kind is re-checked at fire time; tested with a switch inside the grace. - F4: the 'clears itself on the first record' claim is now a test. - F6: the first-sight site no longer carries a permanently-false branch; it just logs the observation. (F3's dedupe/unmount gaps were already pinned in c2a2df0, after the Pi reviewer's snapshot.) Each new test is mutation-checked (turn_started trigger, no fire-time kind). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Juliusolsson05
added a commit
that referenced
this pull request
Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Juliusolsson05
added a commit
that referenced
this pull request
Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 <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.
Problem (#290, P1)
A Claude pane can stream live turns while no committed JSONL ever arrives, and resume later finds nothing. Recorded in debug bundle
2026-06-17T11-50-56-053-a6c70f19(totalEntries 0,lastJsonlEntryAt null,transcriptStatus 'ready', 8 completed turns).The
transcript disconnectedmarker existed but could not fire. It was evaluated only when the provider id was first seen in proxy traffic, where zero entries is normal, so the predicate refused'ready'.'ready'is exactly the state the broken pane was in (see the 2026-09-20 audit note on #290).Change
useIpcSubscriptions.ts: after a Claudeturn_completed, a single delayed check (15 s) marks the panedisconnectedif it still has zero committed entries and no durable transcript identity. A healthy pane has JSONL beforeturn_completed, so the grace only covers the tail watcher's latency. The existing record handler already resets the status to'ready'on the first arriving record, so a false alarm clears itself. The check is one pending timer per session, Claude only, and is cleared on unmount.providerSessionIdentity.ts:'ready'is eligible;'loading','error'and'disconnected'are still excluded.Tests (fail-first, recorded data)
The fixture is the recorded
turn_started/turn_completedpair and provider id from the bundle (testing/fixtures/transcript-disconnect/), replayed through the real hook via the fake SessionFeed:readyif a record lands within the grace;Mutation-checked, each individually:
'ready'excluded, schedule removed, provider guard removed, grace 0. Each turns a test red.Verification
npx tsc -bclean.src/renderer/src/workspaceandsession-runtimepass (182 files, 1,390 tests).Not verified here: a live Claude pane. The grace length is a judgement (15 s); the log row
transcript_never_committedrecords it.Refs #290. This ships the marker slice only. The workspace duplicate
providerSessionIdcleanup, and detection for a pane still mid-turn (for example awaiting a tool), stay open on #290.🤖 Generated with Claude Code
Review (Codex A, Codex B, Pi; two rounds, all MERGE-READY)
c2a2df0c: the banner showsdisconnected, with a fail-first leaf testc2a2df0cbdb68d79bdb68d79, with a switch-inside-grace testbdb68d79