Skip to content

Recover draft thread IDs after failed bootstrap cleanup#4484

Open
colonelpanic8 wants to merge 2 commits into
pingdotgg:mainfrom
colonelpanic8:fix/recover-bootstrap-thread-id
Open

Recover draft thread IDs after failed bootstrap cleanup#4484
colonelpanic8 wants to merge 2 commits into
pingdotgg:mainfrom
colonelpanic8:fix/recover-bootstrap-thread-id

Conversation

@colonelpanic8

@colonelpanic8 colonelpanic8 commented Jul 24, 2026

Copy link
Copy Markdown

What Changed

  • Mark bootstrap dispatch failures as safe to retry with a fresh thread ID only after provisional-thread cleanup succeeds.
  • Atomically remint the local draft identity while preserving its prompt, attachments, project mapping, and settings.
  • Guard the remint against concurrent promotion or identity changes.
  • Add focused server, classifier, and draft-store regression coverage.

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 passed
  • vp test run apps/web/src/composerDraftStore.test.ts apps/web/src/components/ChatView.logic.test.ts --exclude ".worktrees/**" --exclude ".claude/worktrees/**" — 108 passed
  • vp run typecheck — currently fails in unchanged baseline web files and prompts for the undeclared @astrojs/check; no reported failure is in this patch

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Screenshots are not applicable because there is no visual UI change
  • Video is not applicable because there is no animation change

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 retryWithNewThreadId on OrchestrationDispatchCommandError, set only when bootstrap cleanup successfully deletes the provisional thread; failed cleanup is logged and does not signal remint. On send failure, local drafts call renewDraftThreadId when 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

  • When a bootstrap thread.turn.start fails after creating a thread and cleanup (deletion) succeeds, the server now sets retryWithNewThreadId: true on OrchestrationDispatchCommandError in orchestration.ts.
  • The web client detects this flag via shouldRenewDraftThreadIdAfterFailure in ChatView.logic.ts and calls renewDraftThreadId on the composer draft store to replace the stale server thread identity while preserving draft content.
  • renewDraftThreadId in 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.
  • Failed cleanup now logs a warning with the thread ID and cause instead of silently ignoring errors.

Macroscope summarized eaf7e88.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 783886cc-960f-4676-a040-5266829dbbcc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 24, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread apps/web/src/composerDraftStore.ts
@macroscopeapp

macroscopeapp Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@colonelpanic8
colonelpanic8 force-pushed the fix/recover-bootstrap-thread-id branch from a32d15c to b1bc769 Compare July 25, 2026 02:21
colonelpanic8 and others added 2 commits July 24, 2026 20:20
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>
@colonelpanic8
colonelpanic8 force-pushed the fix/recover-bootstrap-thread-id branch from b1bc769 to eaf7e88 Compare July 25, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant