Recover draft thread IDs after failed bootstrap cleanup#4484
Recover draft thread IDs after failed bootstrap cleanup#4484colonelpanic8 wants to merge 2 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0e43e7f. Configure here.
ApprovabilityVerdict: Needs human review This PR introduces new error recovery behavior coordinating between server and client to recover draft thread IDs after failed bootstrap cleanup. While well-tested and scoped, the cross-cutting nature (server, client, contracts, state management) warrants human review to verify the recovery logic handles all edge cases correctly. You can customize Macroscope's approvability policy. Learn more. |
a32d15c to
b1bc769
Compare
Bootstrap creates the server thread before it prepares the worktree, so the client can observe `thread.created` and mark the draft as promoting just before bootstrap fails and deletes that same thread. The renewal guard rejected any non-null `promotedTo`, so exactly the drafts this recovery targets kept their consumed thread id (and stayed hidden from project draft lookups, which skip promoting drafts). Renewal now only refuses when the draft was promoted to some *other* server thread, clears the stale marker it replaces, and uses a nullish check so an absent `promotedTo` is treated as "not promoted", matching `isDraftThreadPromoting`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
b1bc769 to
eaf7e88
Compare

What Changed
Why
A failed new-thread bootstrap can create and then delete its provisional thread before returning an error. The event-sourced aggregate permanently consumes that thread ID, while the retained local draft retries the same ID and repeatedly hits
Thread already exists and cannot be created twice.The server is the only side that knows cleanup completed conclusively, so it now sends an explicit retry signal. The client rotates the ID only for that signal, avoiding unsafe rotation after ambiguous transport failures.
UI Changes
No visual UI changes. The existing failed-send state and draft content remain visible; the recovery only changes the reserved identity used by the next retry.
Validation
vp check— passes in a clean worktree (11 existing warnings)vp test run apps/server/src/server.test.ts -t "cleans up created bootstrap threads when worktree creation defects" --exclude ".worktrees/**" --exclude ".claude/worktrees/**"— 1 passedvp test run apps/web/src/composerDraftStore.test.ts apps/web/src/components/ChatView.logic.test.ts --exclude ".worktrees/**" --exclude ".claude/worktrees/**"— 108 passedvp run typecheck— currently fails in unchanged baseline web files and prompts for the undeclared@astrojs/check; no reported failure is in this patchChecklist
Note
Medium Risk
Touches orchestration bootstrap failure handling and draft identity lifecycle; behavior is gated on an explicit server flag and store validation, but incorrect signaling could remint IDs at the wrong time.
Overview
Fixes retries after a failed new-thread bootstrap that created and then deleted a provisional server thread, which left the local draft stuck reusing a consumed thread ID.
The server adds optional
retryWithNewThreadIdonOrchestrationDispatchCommandError, set only when bootstrap cleanup successfully deletes the provisional thread; failed cleanup is logged and does not signal remint. On send failure, local drafts callrenewDraftThreadIdwhen that flag is set, swapping the reserved thread ID while keeping prompt, mappings, and settings and clearing stale promotion markers from the deleted bootstrap identity, with guards against concurrent promotion or identity changes.Reviewed by Cursor Bugbot for commit eaf7e88. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Recover draft thread IDs after failed bootstrap cleanup
thread.turn.startfails after creating a thread and cleanup (deletion) succeeds, the server now setsretryWithNewThreadId: trueonOrchestrationDispatchCommandErrorin orchestration.ts.shouldRenewDraftThreadIdAfterFailurein ChatView.logic.ts and callsrenewDraftThreadIdon the composer draft store to replace the stale server thread identity while preserving draft content.renewDraftThreadIdin composerDraftStore.ts validates the expected thread ID matches current state and clears any stale promotion marker for the deleted thread before minting the new ID.Macroscope summarized eaf7e88.