Skip to content

Persist terminal launch context for worktree drafts#1518

Open
juliusmarminge wants to merge 2 commits intomainfrom
t3code/persist-script-terminals
Open

Persist terminal launch context for worktree drafts#1518
juliusmarminge wants to merge 2 commits intomainfrom
t3code/persist-script-terminals

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented Mar 29, 2026

Summary

  • Persist the terminal launch context so newly opened terminals keep the worktree cwd and env during first-send worktree setup flows.
  • Update draft-thread worktree creation to flush state immediately, so the terminal drawer opens with the correct worktree context instead of briefly using the project root.
  • Add a regression test covering local draft threads, worktree setup scripts, and terminal open/write requests.

Note

Medium Risk
Changes the thread start flow to allow server-side bootstrapping (thread create/worktree create/setup script launch), which affects orchestration dispatch ordering and terminal/script side effects. Risk is mitigated by added unit/integration tests, but failures could impact first-send behavior for worktree/local drafts and PR worktree prep.

Overview
Server now owns first-send bootstrapping for worktree/local drafts. thread.turn.start accepts an optional bootstrap payload (create thread, create worktree, run setup script), and wsServer executes these steps before dispatching the real turn-start, recording setup-script activities and cleaning up the thread on bootstrap failure.

Setup scripts are centralized and triggered from backend worktree creation paths. Adds ProjectSetupScriptRunner (reads project scripts from orchestration snapshot, opens a deterministic setup-* terminal with T3CODE_PROJECT_ROOT/T3CODE_WORKTREE_PATH, writes the setup command) and wires it into server layers; GitManager.preparePullRequestThread can now accept an optional threadId to launch setup when a new PR worktree is created (and logs/continues on setup launch failure).

Web client stops creating worktrees/running setup locally and preserves terminal context. ChatView replaces client-side worktree creation + setup execution with bootstrap turn-starts, adds a per-thread terminal launch context (persisted in terminalStateStore) so the terminal drawer uses the intended cwd/worktree env, and updates PR worktree prep to pass threadId while leaving setup execution to the server; tests were updated/added to cover these flows.

Written by Cursor Bugbot for commit 2535cf1. This will update automatically on new commits. Configure here.

Note

Move worktree creation and setup script execution from client to server on first message send

  • The client no longer creates git worktrees or runs setup scripts locally on first message send; instead, it sends a thread.turn.start command with an optional bootstrap payload containing createThread, prepareWorktree, and runSetupScript fields.
  • The server handles bootstrap by optionally creating the thread, calling git.createWorktree, updating thread metadata, running the project setup script via a new ProjectSetupScriptRunner service, and emitting setup-script.* activities before dispatching the turn.
  • A new terminalLaunchContextByThreadId map in the terminal state store persists terminal CWD and worktree path received from server-emitted terminal events, so the terminal drawer opens with the correct context after server-side setup.
  • GitPreparePullRequestThreadInput and gitPreparePullRequestThreadMutationOptions now accept an optional threadId, which GitManager.preparePullRequestThread uses to trigger setup script execution when creating a new PR worktree.
  • Risk: setup script errors during preparePullRequestThread are swallowed (logged as warnings only), and bootstrap failures cause newly created threads to be deleted server-side.

Macroscope summarized 2535cf1.

- keep terminal drawer attached to the created worktree for first-send draft setup scripts
- flush local thread and draft state updates before opening terminals
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

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

Run ID: feee0f0d-7563-478a-9803-b88f6e561be9

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
  • Commit unit tests in branch t3code/persist-script-terminals

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

@github-actions github-actions bot added size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 29, 2026
- launch project setup scripts from the server after worktree prep
- keep setup terminal state tied to the thread and surface activity events
- add coverage for setup runner and bootstrap turn flow
@github-actions github-actions bot added size:XL 500-999 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Mar 29, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

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 and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale "preparing-worktree" phase shows misleading UI state
    • Removed the stale 'preparing-worktree' send phase, its derived isPreparingWorktree flag, and all UI references since worktree preparation now happens server-side and the phase no longer corresponds to any client-side work.

Create PR

Or push these changes by commenting:

@cursor push 88bafdb9f0
Preview (88bafdb9f0)
diff --git a/apps/web/src/components/ChatView.logic.ts b/apps/web/src/components/ChatView.logic.ts
--- a/apps/web/src/components/ChatView.logic.ts
+++ b/apps/web/src/components/ChatView.logic.ts
@@ -75,7 +75,7 @@
   return previewUrls;
 }
 
-export type SendPhase = "idle" | "preparing-worktree" | "sending-turn";
+export type SendPhase = "idle" | "sending-turn";
 
 export interface PullRequestDialogState {
   initialReference: string | null;

diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -658,7 +658,6 @@
   const selectedModelForPicker = selectedModel;
   const phase = derivePhase(activeThread?.session ?? null);
   const isSendBusy = sendPhase !== "idle";
-  const isPreparingWorktree = sendPhase === "preparing-worktree";
   const isWorking = phase === "running" || isSendBusy || isConnecting || isRevertingCheckpoint;
   const nowIso = new Date(nowTick).toISOString();
   const activeWorkStartedAt = deriveActiveWorkStartedAt(
@@ -2573,7 +2572,7 @@
     }
 
     sendInFlightRef.current = true;
-    beginSendPhase(baseBranchForWorktree ? "preparing-worktree" : "sending-turn");
+    beginSendPhase("sending-turn");
 
     const composerImagesSnapshot = [...composerImages];
     const composerTerminalContextsSnapshot = [...sendableComposerTerminalContexts];
@@ -4006,11 +4005,6 @@
                         {activeContextWindow ? (
                           <ContextWindowMeter usage={activeContextWindow} />
                         ) : null}
-                        {isPreparingWorktree ? (
-                          <span className="text-muted-foreground/70 text-xs">
-                            Preparing worktree...
-                          </span>
-                        ) : null}
                         {activePendingProgress ? (
                           <div className="flex items-center gap-2">
                             {activePendingProgress.questionIndex > 0 ? (
@@ -4115,11 +4109,9 @@
                               aria-label={
                                 isConnecting
                                   ? "Connecting"
-                                  : isPreparingWorktree
-                                    ? "Preparing worktree"
-                                    : isSendBusy
-                                      ? "Sending"
-                                      : "Send message"
+                                  : isSendBusy
+                                    ? "Sending"
+                                    : "Send message"
                               }
                             >
                               {isConnecting || isSendBusy ? (

setComposerCursor(0);
setComposerTrigger(null);

let createdServerThreadForLocalDraft = false;
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.

Stale "preparing-worktree" phase shows misleading UI state

Low Severity

beginSendPhase("preparing-worktree") is still called on line 2576 when baseBranchForWorktree is truthy, but the actual worktree preparation was moved to the server (as part of the bootstrap payload). The derived isPreparingWorktree flag (line 661) gates UI elements like a spinner (lines ~4009, ~4118). For server threads needing a worktree, this phase persists through the async auto-title and persistThreadSettingsForNextTurn calls before being overwritten by beginSendPhase("sending-turn") on line 2692, showing a "preparing worktree" indicator when the client is actually doing unrelated work.

Additional Locations (1)
Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant