Repository navigation
fix(server): Claude prompts no longer hang on a uuid the session already holds - #16897
Conversation
…ady holds Claude prompt uuids were a hash of the run attempt id, which is only the thread id and run ordinal. A dev server seeded from a copy of the database resumes the same Claude session and writes prompts under the uuids the real server uses for its next runs. Claude finishes such a prompt with a lone command_lifecycle "completed" and never runs a turn, so the thread stayed "Working" forever and the message was silently lost. Each prompt offer now gets a fresh random uuid. As a backstop, a prompt Claude reports completed without ever starting fails its turn with an error instead of hanging. Ports the uuid change from #16254 by @saphid onto the Effect Crypto service. Co-Authored-By: saphid <saphid@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that replaces a known prompt hang with a reusable failure and uses fresh per-offer UUIDs to prevent transcript collisions. Normal prompt processing remains unchanged, with targeted regression tests covering both affected and successful lifecycle paths. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (3)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughThe Claude adapter now generates UUIDs for non-continuation prompts and tracks their lifecycle states. Tests use the UUIDs of offered prompts and cover UUID uniqueness, lifecycle failures, and successful completion. ChangesClaude prompt flow
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The change is mergeable after normal checks; no concrete unresolved risk has been identified. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Upstream sync (run on request ahead of a build): 13 commits to f570bd2, including Claude session fixes (pingdotgg#16897, pingdotgg#16287), background subagent work showing while the parent is idle (pingdotgg#16486), inline MCP apps (pingdotgg#16236) and worktree cleanup changes (pingdotgg#14847, pingdotgg#15150, pingdotgg#15834, pingdotgg#14917). The one conflict, ClaudeAdapterV2.ts, was additive: upstream's per-subagent toolCallsFor delete is kept ahead of the fork's Claude task-tools block. The fork's Codex image fixture gains pingdotgg#16236's MCP-app initialize extension. Attached worktrees stay outside every new cleanup path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
## What's Changed * fix(web): replace Lineage timers with Stop on hover by @Bil0000 in pingdotgg/t3code#16791 * fix(clients): running subagent cards stay visible after their parent turn settles by @juliusmarminge in pingdotgg/t3code#16878 * feat: MCP apps render and run inline in threads by @juliusmarminge in pingdotgg/t3code#16236 * fix(server): a background Claude subagent's work shows while its parent is idle by @Vantrongs in pingdotgg/t3code#16486 * fix(mobile): hide threads from switched-off environments by @entity in pingdotgg/t3code#16886 * fix(server): a refused Claude turn no longer throws away its session by @SunkenInTime in pingdotgg/t3code#16287 * fix(web): onboarding Continue no longer locks on computers that won't connect by @juliusmarminge in pingdotgg/t3code#16887 * fix(server): worktree cleanup no longer deletes files hidden by showUntrackedFiles=no by @SunkenInTime in pingdotgg/t3code#15834 * fix(server): merged-worktree cleanup removes worktrees after squash merges by @tris203 in pingdotgg/t3code#14847 * fix(server): free worktrees for terminal thread statuses by @ANSHSINGH050404 in pingdotgg/t3code#15150 * fix(server): Windows worktrees with long paths no longer fail or strand by @That1Drifter in pingdotgg/t3code#14917 * fix(server): main typechecks again after a test used renamed helpers by @juliusmarminge in pingdotgg/t3code#16895 * fix(server): Claude prompts no longer hang on a uuid the session already holds by @juliusmarminge in pingdotgg/t3code#16897 ## New Contributors * @Vantrongs made their first contribution in pingdotgg/t3code#16486 * @entity made their first contribution in pingdotgg/t3code#16886 * @ANSHSINGH050404 made their first contribution in pingdotgg/t3code#15150 * @That1Drifter made their first contribution in pingdotgg/t3code#14917 **Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261007.2774...v0.0.46-nightly.20261007.2787 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261007.2787
## What's Changed * fix(web): replace Lineage timers with Stop on hover by @Bil0000 in pingdotgg/t3code#16791 * fix(clients): running subagent cards stay visible after their parent turn settles by @juliusmarminge in pingdotgg/t3code#16878 * feat: MCP apps render and run inline in threads by @juliusmarminge in pingdotgg/t3code#16236 * fix(server): a background Claude subagent's work shows while its parent is idle by @Vantrongs in pingdotgg/t3code#16486 * fix(mobile): hide threads from switched-off environments by @entity in pingdotgg/t3code#16886 * fix(server): a refused Claude turn no longer throws away its session by @SunkenInTime in pingdotgg/t3code#16287 * fix(web): onboarding Continue no longer locks on computers that won't connect by @juliusmarminge in pingdotgg/t3code#16887 * fix(server): worktree cleanup no longer deletes files hidden by showUntrackedFiles=no by @SunkenInTime in pingdotgg/t3code#15834 * fix(server): merged-worktree cleanup removes worktrees after squash merges by @tris203 in pingdotgg/t3code#14847 * fix(server): free worktrees for terminal thread statuses by @ANSHSINGH050404 in pingdotgg/t3code#15150 * fix(server): Windows worktrees with long paths no longer fail or strand by @That1Drifter in pingdotgg/t3code#14917 * fix(server): main typechecks again after a test used renamed helpers by @juliusmarminge in pingdotgg/t3code#16895 * fix(server): Claude prompts no longer hang on a uuid the session already holds by @juliusmarminge in pingdotgg/t3code#16897 ## New Contributors * @Vantrongs made their first contribution in pingdotgg/t3code#16486 * @entity made their first contribution in pingdotgg/t3code#16886 * @ANSHSINGH050404 made their first contribution in pingdotgg/t3code#15150 * @That1Drifter made their first contribution in pingdotgg/t3code#14917 **Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261007.2774...v0.0.46-nightly.20261007.2787 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261007.2787
Problem
A Claude thread can swallow a message: the run shows "Working" forever, nothing streams, and only Stop ends it.
Claude prompt uuids were a hash of the run attempt id, which is just the thread id and run ordinal. A dev server seeded from a copy of the database (
migrate-dev-db) keeps those ids and the provider session binding, so it resumes the real~/.claudetranscript and writes prompts under the uuids the real server will use for its next runs. When the real server gets there, Claude finds the uuid already in its transcript and replies with a lonecommand_lifecyclecompleted: noqueued,started,system/initorresult. The adapter waits forever.Seen on thread
f9ac0fac: a dev instance had sent test prompts there as runs 17–20, and the user's real runs 17 and 19 were silently dropped (3h20m and 1h49m "Working", the second ending in a manual Stop). Each dropped prompt's uuid matched a transcript entry written four days earlier by the dev server.Fix
Cryptoservice instead ofclaudePromptUuid(attemptId). Echo routing is unchanged: the same uuid is still stored on the turn context and matched againstuser_message_uuid. Replay already maps recorded prompt uuids to replayed ones by matched offer, so the recorder uses random uuids too.completedbefore ever reporting itstarted, the turn now fails with "Claude acknowledged the message but never started a turn for it." instead of hanging. The thread stays reusable, so the next message works. Across the local provider logs, every prompt that ran gotqueued→started(→completed);completedwith nostartedappears only for the dropped prompts.The uuid change ports #16254 by @saphid onto current
main. That PR now conflicts:mainmovedclaudePromptUuidonto the EffectCryptoservice since it was opened. This PR adds the backstop on top. #16254 can be closed in favor of this one.Verification
fails a prompt Claude completes without starting a turn for it: times out waiting for a terminal without the backstop, and passes with it.keeps a started prompt running until its result: aqueued→started→completedsequence followed by the echoing result still completes normally, covering a CLI that reportscompletedbefore the turn's result.gives the same run attempt a fresh prompt uuid on every offer, and its migration of the echo-routing tests to read the offered uuid.vp test runonClaudeAdapterV2.test.ts,ClaudeAutomaticDelivery,ClaudeReplayFixtures,OrchestratorReplayRecovery,OrchestratorReplayFixtures,OrchestratorReplayRestartBackgroundNote,ThreadMergeBack, andThreadFork: 8 files, 310 tests passed.tsc --noEmitinapps/serveris clean for these files. TheProviderSwitch.integration.test.tserrors are already present onmain.Made with Claude Opus 5.5 in Claude Code (running in T3 Code).
🤖 Generated with Claude Code