Repository navigation
fix(native-chat): a local chat this computer can't run opens the agent in a terminal - #25947
Conversation
… terminal A local structured launch opened its chat tab before this machine said whether it could create the chat, so a "no" (a Claude account bound to WSL, for example) left a failed chat whose Retry failed the same way and no terminal. Local launches now ask first through the same admission a paired server's launch already uses: nothing of the chat exists until the host answers, and a "no" runs the caller's own launch as a terminal.
…2025/acp-local-declined-terminal
…; bound that wait Round 1 review of the ask-first local launch: - A launch waiting on its host now counts as a pending chat create, so the first-terminal watcher no longer seeds "Terminal 1" beside a local chat (watched creates and the empty-workspace default chat). - This machine's answer is waited on for at most 3 s; past it, or while a new workspace is not resolvable yet, the chat opens and its own create reports, as before. Only a real "no" opens a terminal. - The empty-workspace default chat seeds its plain shell on a "no" instead of starting the agent in a terminal nobody asked for, through a narrow launcher option.
… host The wait was held in a separate record only the passive watcher read, so re-activating the still-empty workspace during the wait seeded "Terminal 1" through the activation's reseed. The wait now lives in the empty-workspace default-surface claims both seeders already read, and the separate record is gone.
Review summaryThe problem. With "Use updated structured native chat" on, a chat launched on this machine opened its tab before Orca asked whether it could create that chat here. When the answer was no (today: a Claude account tied to WSL; next: OpenCode accounts Orca cannot pin), you got a failed chat whose Retry failed the same way, instead of the agent in a terminal. Launches on a paired server already asked first and fell back to the terminal. The change for you. A launch on this machine now waits for that answer first: yes opens the chat as before; no opens the agent in a terminal with your prompt, once; no answer within 3 seconds opens the chat, which reports its own problem as before. Fixed during review (two rounds of fresh reviewers):
Deferred: the declined-shell callback reads a store snapshot up to 3 seconds old; server-pushed agent availability (no wait, no double ask) is the better end state and a follow-up. Verified: 1,324 tests in the 122 affected files; CI green on 6704f98 after merging main with #25225 landed (typecheck, five test shards, relay, packaging incl. Windows); the merge took main's widening of structured launches to any registered agent into the renamed host-admitted launch. Not verified: a running Orca, real WSL timing (if the local answer routinely takes over 3 s there, the WSL-account case still shows a failed chat), Windows/Linux by hand. |
…lined-local-launch terminal fallback Main widened structured launches to any registered agent; the renamed host-admitted launch takes that widening.
…l-declined-terminal
|
Ready for review.
|
📝 WalkthroughWalkthroughStructured chat launches now request admission from the local machine or a paired environment before opening a chat. Admission distinguishes unresolved workspaces and applies a three-second wait to local requests. When a host declines, callers can open a terminal. Empty-workspace launch waits hold the default surface until admission completes, and related tests cover routing, terminal fallback, and activation behavior. Priority: ➖ Normal Merge Risk: 🔵 Low · up to A newly created background worktree may open without a terminal when its chat is declined. Users can open one manually, but the fallback should be fixed before merge if practical. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
04d33b6c-2365-46aa-8d3d-c9290f8bb899
📒 Files selected for processing (28)
src/renderer/src/components/tab-bar/QuickLaunchButton.launch-status.test.tsxsrc/renderer/src/components/tab-bar/QuickLaunchButton.tsxsrc/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.tssrc/renderer/src/components/terminal/initial-terminal-admission-wait.test.tsxsrc/renderer/src/lib/agent-launch-caller-routing.test.tssrc/renderer/src/lib/empty-workspace-default-agent-chat.test.tssrc/renderer/src/lib/empty-workspace-default-agent-chat.tssrc/renderer/src/lib/empty-workspace-default-surface-claims.tssrc/renderer/src/lib/launch-agent-in-new-tab-structured-route.tssrc/renderer/src/lib/launch-agent-in-new-tab-structured.test.tssrc/renderer/src/lib/launch-agent-in-new-tab-structured.tssrc/renderer/src/lib/launch-agent-in-new-tab.tssrc/renderer/src/lib/launch-agent-structured-chat-guard.test.tssrc/renderer/src/lib/launch-structured-agent-session.tssrc/renderer/src/lib/launch-work-item-direct-agent-routing.tssrc/renderer/src/lib/run-quick-command-in-new-tab.test.tssrc/renderer/src/lib/run-quick-command-in-new-tab.tssrc/renderer/src/lib/structured-agent-session-empty-chat-reuse.test.tssrc/renderer/src/lib/structured-agent-session-host-admission.tssrc/renderer/src/lib/structured-agent-session-launch-admission.test.tssrc/renderer/src/lib/structured-agent-session-launch-admission.tssrc/renderer/src/lib/structured-agent-session-provisional-tab.test.tssrc/renderer/src/lib/structured-agent-session-provisional-tab.tssrc/renderer/src/lib/worktree-activation-default-agent-chat.test.tssrc/renderer/src/lib/worktree-activation-launch-awaiting-host.test.tssrc/renderer/src/lib/worktree-creation-structured-session-host-decline.test.tssrc/renderer/src/lib/worktree-creation-structured-session.tssrc/renderer/src/lib/worktree-initial-terminal-seeding.ts
💤 Files with no reviewable changes (3)
- src/renderer/src/lib/launch-agent-in-new-tab-structured.test.ts
- src/renderer/src/lib/launch-agent-in-new-tab-structured.ts
- src/renderer/src/components/tab-bar/QuickLaunchButton.tsx
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
| * The host's "no" to a create the user moved away from opens the agent terminal the way a | ||
| * background terminal create does: in place, without selecting the workspace (#23974). A create the | ||
| * user is still watching keeps the default, which opens it as a new agent tab there. | ||
| */ | ||
| function openBackgroundDeclinedTerminal( | ||
| args: LaunchStructuredWorktreeSessionArgs | ||
| args: LaunchStructuredWorktreeSessionArgs, | ||
| host: RuntimeClientTarget | ||
| ): StructuredLaunchTerminal { | ||
| ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, { | ||
| startup: buildWorktreeCreationStartupOpt(args.request, false), | ||
| agent: args.request.agent, | ||
| activate: false | ||
| }) | ||
| return { opened: true } | ||
| const startup = buildWorktreeCreationStartupOpt(args.request, false) | ||
| if (host.kind === 'environment') { | ||
| ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, { | ||
| startup, | ||
| agent: args.request.agent, | ||
| activate: false | ||
| }) | ||
| return { opened: true } | ||
| } | ||
| if (!startup) { | ||
| return { opened: false } | ||
| } | ||
| // This machine seeds the agent terminal itself; a background workspace mounts it without showing it. | ||
| const tabId = ensureWorktreeHasInitialTerminal( | ||
| useAppStore.getState(), | ||
| args.worktreeId, | ||
| startup, | ||
| undefined, | ||
| undefined, | ||
| undefined, | ||
| { activateCreatedTabs: false, createNewTerminalForStartup: true } | ||
| ) | ||
| mountCreatedWorktreeStartupTabsInBackground(args.worktreeId) | ||
| return { opened: tabId !== null } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '1,165p' src/renderer/src/lib/worktree-creation-structured-session.ts
rg -n 'buildStartupOptions|openBackgroundDeclinedTerminal|ensureWorktreeHasInitialTerminal' src/renderer/src/lib/worktree-creation-structured-session.ts src/renderer/src/lib/worktree-initial-terminal-seeding.tsRepository: stablyai/orca
Length of output: 7213
🏁 Script executed:
printf '%s\n' '--- startup builder ---'
sed -n '1,240p' src/renderer/src/lib/worktree-creation-flow-startup.ts
printf '%s\n' '--- initial terminal seeding ---'
sed -n '1,290p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
printf '%s\n' '--- builder and seeder references ---'
rg -n -F -- 'buildWorktreeCreationStartupOpt(' src/renderer/src
rg -n -F -- 'ensureWorktreeHasInitialTerminal(' src/renderer/src
printf '%s\n' '--- creation request and caller references ---'
rg -n 'WorktreeCreationRequest|launchStructuredWorktreeSession|launchDraftPrompt|quickPrompt|startupCommand|command' src/renderer/src/lib/worktree-creation* src/renderer/src | head -240Repository: stablyai/orca
Length of output: 41310
🏁 Script executed:
printf '%s\n' '--- execute creation flow ---'
sed -n '120,285p' src/renderer/src/lib/worktree-creation-flow-execute.ts
printf '%s\n' '--- creation request type and startupPlan producers ---'
rg -n 'startupPlan[?:]|startupPlan:' src/renderer/src/lib src/renderer/src/components | head -100
rg -n 'type WorktreeCreationRequest|interface WorktreeCreationRequest' src/renderer/src/lib/pending-worktree-creation.ts
sed -n '1,190p' src/renderer/src/lib/pending-worktree-creation.ts
printf '%s\n' '--- declined-launch implementation and tests ---'
rg -n 'onHostDeclined|host declined|declined|startupPlan' src/renderer/src/lib/structured-agent-session-provisional-tab.ts src/renderer/src/lib/worktree-creation-structured-session.test.ts
sed -n '1,220p' src/renderer/src/lib/structured-agent-session-provisional-tab.ts
sed -n '1,235p' src/renderer/src/lib/worktree-creation-structured-session.test.ts
printf '%s\n' '--- terminal auto-create contract ---'
rg -n 'shouldAutoCreateInitialTerminal|function shouldAutoCreateInitialTerminal' src/renderer/src/components/terminal/initial-terminal.tsx src/renderer/src/components/terminal/initial-terminal.ts
sed -n '1,120p' src/renderer/src/components/terminal/initial-terminal.tsx 2>/dev/null || sed -n '1,120p' src/renderer/src/components/terminal/initial-terminal.tsRepository: stablyai/orca
Length of output: 38360
🏁 Script executed:
printf '%s\n' '--- route and startup-plan producers ---'
rg -n 'agentLaunchRoute|startupPlan' src/renderer/src/components/worktree-creation src/renderer/src/lib/worktree-creation-flow* src/renderer/src/lib/launch-agent-startup-prompt-plan.ts src/renderer/src/lib/launch-work-item-direct-route-preparation.ts
printf '%s\n' '--- host-decline behavior test ---'
sed -n '1,260p' src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts
printf '%s\n' '--- remaining creation dispatch ---'
sed -n '270,360p' src/renderer/src/lib/worktree-creation-flow-execute.ts
printf '%s\n' '--- PR diff for reviewed helper ---'
git diff --unified=8 988f87e743ec52aa24ae1f25a308e66d6d8d2e59 6704f98a10343056e5aa0c6441abe70e125174aa -- src/renderer/src/lib/worktree-creation-structured-session.tsRepository: stablyai/orca
Length of output: 16795
🏁 Script executed:
printf '%s\n' '--- non-test startupPlan references ---'
rg -n 'startupPlan|WorktreeCreationRequest|agentLaunchRoute' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*'
printf '%s\n' '--- worktree creation source files ---'
rg --files src/renderer/src | rg 'worktree.creation|worktree-creation|composer-submit|agent-startup-prompt-plan'
printf '%s\n' '--- request producers around shared submission APIs ---'
rg -n 'pendingFirstAgentMessageRename:|quickTelemetry:|launchDraftPrompt:|promptDelivery:' src/renderer/src --glob '!**/*.test.*' --glob '!**/*.spec.*'Repository: stablyai/orca
Length of output: 43495
🏁 Script executed:
printf '%s\n' '--- quick startup plan ---'
sed -n '1,155p' src/renderer/src/hooks/composer-state/quick-startup-plan.ts
printf '%s\n' '--- quick creation request assembly ---'
sed -n '105,260p' src/renderer/src/hooks/composer-state/quick-creation-execution.ts
printf '%s\n' '--- full submit startup-plan construction ---'
sed -n '155,255p' src/renderer/src/hooks/composer-state/full-submit-preparation.ts
printf '%s\n' '--- quick request contract ---'
sed -n '1,130p' src/renderer/src/hooks/composer-state/quick-creation-request.tsRepository: stablyai/orca
Length of output: 20358
🏁 Script executed:
printf '%s\n' '--- structured route resolver ---'
sed -n '1,220p' src/renderer/src/lib/agent-launch-routing.ts
printf '%s\n' '--- startup builder declaration and implementation ---'
rg -n 'export function buildAgentStartupPlan|function buildAgentStartupPlan' src/renderer/src/lib/tui-agent-startup*
sed -n '1,260p' src/renderer/src/lib/tui-agent-startup.ts
printf '%s\n' '--- local terminal host authority ---'
sed -n '1,220p' src/renderer/src/lib/workspace-terminal-host-authority.ts
printf '%s\n' '--- default terminal tab seeding ---'
sed -n '1,240p' src/renderer/src/lib/worktree-default-terminal-tabs.ts
printf '%s\n' '--- seeder decision and initial terminal tests ---'
sed -n '125,255p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
rg -n 'ensureWorktreeHasInitialTerminal\\(.*|shouldAutoCreate|creates.*terminal|initial terminal' src/renderer/src/lib/worktree-activation-agent-startup.test.ts src/renderer/src/lib/worktree-activation-default-agent-chat.test.ts src/renderer/src/lib/workspace-terminal-host-authority.test.tsRepository: stablyai/orca
Length of output: 23303
🏁 Script executed:
printf '%s\n' '--- structured route resolver ---'
sed -n '1,220p' src/renderer/src/lib/agent-launch-routing.ts
printf '%s\n' '--- startup builder declaration and implementation ---'
rg -n 'export function buildAgentStartupPlan|function buildAgentStartupPlan' src/renderer/src/lib/tui-agent-startup*
sed -n '1,260p' src/renderer/src/lib/tui-agent-startup.ts
printf '%s\n' '--- local terminal host authority ---'
sed -n '1,220p' src/renderer/src/lib/workspace-terminal-host-authority.ts
printf '%s\n' '--- default terminal tab seeding ---'
sed -n '1,240p' src/renderer/src/lib/worktree-default-terminal-tabs.ts
printf '%s\n' '--- seeder decision and initial terminal tests ---'
sed -n '125,255p' src/renderer/src/lib/worktree-initial-terminal-seeding.ts
rg -n 'ensureWorktreeHasInitialTerminal\(|shouldAutoCreate|creates.*terminal|initial terminal' src/renderer/src/lib/worktree-activation-agent-startup.test.ts src/renderer/src/lib/worktree-activation-default-agent-chat.test.ts src/renderer/src/lib/workspace-terminal-host-authority.test.tsRepository: stablyai/orca
Length of output: 26557
🏁 Script executed:
printf '%s\n' '--- shared startup-plan builder ---'
rg -n 'function buildAgentStartupPlan|export function buildAgentStartupPlan|const buildAgentStartupPlan' src/shared/tui-agent-startup.ts
nl -ba src/shared/tui-agent-startup.ts | sed -n '1,240p'
printf '%s\n' '--- exact quick-create route/build locations ---'
nl -ba src/renderer/src/hooks/composer-state/quick-startup-plan.ts | sed -n '20,130p'
nl -ba src/renderer/src/hooks/composer-state/quick-creation-execution.ts | sed -n '120,250p'
printf '%s\n' '--- exact local fallback seeding locations ---'
nl -ba src/renderer/src/lib/worktree-initial-terminal-seeding.ts | sed -n '77,110p;132,250p'
printf '%s\n' '--- exact changed helper ---'
nl -ba src/renderer/src/lib/worktree-creation-structured-session.ts | sed -n '33,68p'
printf '%s\n' '--- local decline test input and assertions ---'
nl -ba src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts | sed -n '75,180p'Repository: stablyai/orca
Length of output: 33936
🏁 Script executed:
printf '%s\n' '--- declined-terminal admission contract and consumer ---'
nl -ba src/renderer/src/lib/structured-agent-session-launch-admission.ts | sed -n '1,230p'
printf '%s\n' '--- launch-command resolver null conditions ---'
rg -n 'export function resolveAgentLaunchCommand|function resolveAgentLaunchCommand' src/shared/tui-agent-launch-command.ts
nl -ba src/shared/tui-agent-launch-command.ts | sed -n '1,230p'
printf '%s\n' '--- creation completion for declined structured launch ---'
nl -ba src/renderer/src/lib/worktree-creation-completion.ts | sed -n '1,100p'
printf '%s\n' '--- remaining seeder tab creation ---'
nl -ba src/renderer/src/lib/worktree-initial-terminal-seeding.ts | sed -n '245,315p'Repository: stablyai/orca
Length of output: 21281
Seed the default terminal when no startup plan exists.
If a background structured-chat create has no startup plan and its local host declines, this guard returns before ensureWorktreeHasInitialTerminal runs. The creation flow skipped initial seeding because it expected the chat to provide the surface, so the worktree can be left without a terminal. Let the seeder run with startup undefined; it creates the default terminal for a fresh, empty local worktree.
Suggested fix
- if (!startup) {
- return { opened: false }
- }
// This machine seeds the agent terminal itself; a background workspace mounts it without showing it.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * The host's "no" to a create the user moved away from opens the agent terminal the way a | |
| * background terminal create does: in place, without selecting the workspace (#23974). A create the | |
| * user is still watching keeps the default, which opens it as a new agent tab there. | |
| */ | |
| function openBackgroundDeclinedTerminal( | |
| args: LaunchStructuredWorktreeSessionArgs | |
| args: LaunchStructuredWorktreeSessionArgs, | |
| host: RuntimeClientTarget | |
| ): StructuredLaunchTerminal { | |
| ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, { | |
| startup: buildWorktreeCreationStartupOpt(args.request, false), | |
| agent: args.request.agent, | |
| activate: false | |
| }) | |
| return { opened: true } | |
| const startup = buildWorktreeCreationStartupOpt(args.request, false) | |
| if (host.kind === 'environment') { | |
| ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, { | |
| startup, | |
| agent: args.request.agent, | |
| activate: false | |
| }) | |
| return { opened: true } | |
| } | |
| if (!startup) { | |
| return { opened: false } | |
| } | |
| // This machine seeds the agent terminal itself; a background workspace mounts it without showing it. | |
| const tabId = ensureWorktreeHasInitialTerminal( | |
| useAppStore.getState(), | |
| args.worktreeId, | |
| startup, | |
| undefined, | |
| undefined, | |
| undefined, | |
| { activateCreatedTabs: false, createNewTerminalForStartup: true } | |
| ) | |
| mountCreatedWorktreeStartupTabsInBackground(args.worktreeId) | |
| return { opened: tabId !== null } | |
| * The host's "no" to a create the user moved away from opens the agent terminal the way a | |
| * background terminal create does: in place, without selecting the workspace (#23974). A create the | |
| * user is still watching keeps the default, which opens it as a new agent tab there. | |
| */ | |
| function openBackgroundDeclinedTerminal( | |
| args: LaunchStructuredWorktreeSessionArgs, | |
| host: RuntimeClientTarget | |
| ): StructuredLaunchTerminal { | |
| const startup = buildWorktreeCreationStartupOpt(args.request, false) | |
| if (host.kind === 'environment') { | |
| ensureWebRuntimeWorktreeTerminalAfterWake(args.worktreeId, { | |
| startup, | |
| agent: args.request.agent, | |
| activate: false | |
| }) | |
| return { opened: true } | |
| } | |
| // This machine seeds the agent terminal itself; a background workspace mounts it without showing it. | |
| const tabId = ensureWorktreeHasInitialTerminal( | |
| useAppStore.getState(), | |
| args.worktreeId, | |
| startup, | |
| undefined, | |
| undefined, | |
| undefined, | |
| { activateCreatedTabs: false, createNewTerminalForStartup: true } | |
| ) | |
| mountCreatedWorktreeStartupTabsInBackground(args.worktreeId) | |
| return { opened: tabId !== null } |
…l-declined-terminal # Conflicts: # src/renderer/src/components/tab-bar/QuickLaunchButton.tsx
ELI5
With Settings > Chat UI > "Use updated structured native chat" on, opening an agent in a new tab on this computer showed the chat tab immediately, before Orca had checked whether this computer could actually run that chat. When the answer was "no", you were left with a failed chat. Its Retry button failed the same way, and no terminal opened for the agent. Now Orca checks first. If the answer is "no", the agent opens in a terminal tab instead, with the prompt you typed. Launches on a paired Orca server already worked this way.
What Changed
The problem. When you start a structured chat (the chat view Orca draws itself, as opposed to the agent's own terminal screen), the computer that runs it has the final say on whether it can create that chat. One real "no" today: Claude, when the active Claude account is tied to WSL (the Windows Subsystem for Linux). Upcoming OpenCode support adds another: an OpenCode release the chat view can't run (2.x, or older than 1.18.31). On a paired Orca server, the desktop already asked the server first, and a "no" opened the agent in a terminal. On this computer, the chat tab opened at once and the question was asked while the chat was being created. So a "no" surfaced as a failed chat.
What you see now.
The mechanism. All structured launches now go through one path, for every host. Before anything of the chat exists here (no tab, launch record, queued prompt or focus record), Orca sends the owning host its existing "can you create this chat?" request (
agentSession.createSupport). That host can be this computer or a paired server.What keeps a stray "Terminal 1" out. While a launch waits for its host's answer, it claims the workspace's first surface. The claim lives in the same in-memory registry that already holds a workspace's first surface while the default agent list loads. Both things that seed a shell into an empty workspace read that registry: the background first-terminal watcher, and the reseed that runs when you click the workspace. So neither adds a shell beside the coming chat. The claim ends as soon as the answer's tab or terminal has opened.
Code changes:
structured-agent-session-paired-admission.tsbecomesstructured-agent-session-launch-admission.ts, andbeginPairedStructuredLaunchbecomesbeginHostAdmittedStructuredLaunch). It now serves this computer too.structuredLaunchPairedOwnerbecomesstructuredLaunchOwner.launch-agent-in-new-tab-structured.ts) and thelocal-agent-sessionlaunch-surface kind are removed, since nothing produces them anymore.Why
There were two ways to fix this:
How this compares with the common pattern. It matches in one respect: no server-side chat record exists until the host has answered. It differs in three:
Other differences from the common pattern:
Linked Issue
None. This is internal follow-up work on structured native chat.
Visual Proof
Live QA in a hidden Orca build on macOS. A Mac has no Claude-on-WSL refusal, so the test build was this branch merged locally with #25845 (OpenCode over ACP), whose host refuses an OpenCode chat it cannot run. The QA machine's real
~/.codex,~/.claude,~/.orca,~/.grokand OpenCode login files were hashed before and after every check: no change came from the test app.opencode unsupported: installed-agent check refused; New tab > OpenCode opens exactly one terminal-backed OpenCode tab running that program, with no chat tab, no "Chat could not be started" card, no toast and no stray extra terminalIn the "no" screenshot the terminal tab shows Orca's existing chat view of a terminal agent, because that profile opens agent tabs in chat view by default; the process behind it is the terminal program.
Not verified live: a typed prompt being pasted once into the terminal after a "no" (no launch surface in the test build takes a prompt before the tab opens; it is covered by the launcher test), the paired-server path, Windows, Linux, SSH and mobile. Also found: inline OpenCode credentials were refused only at create time, so they still produced a failed chat card. #25845 no longer refuses that setup at all (the chat opens with your OpenCode variables as they are), so no OpenCode refusal is left that only create detects.
Testing
New tests:
initial-terminal-admission-wait.test.tsx, with the real first-terminal watcher and the real launch: a watched create, and an empty workspace's default chat, no longer get "Terminal 1" beside the chat; the claim is released once the answer's surface opens.worktree-activation-launch-awaiting-host.test.ts, with the real store, activation and reseed: clicking the still-empty workspace during the wait seeds no shell and starts no second launch.I checked that the tests detect the bugs by turning each fix off:
Then I ran 122 test files covering every structured launch caller, the empty-workspace seeding, activation and the first-terminal watcher: all 1,324 tests pass. oxlint, oxfmt, the anti-slop lint, the changed-code quality gate and the max-lines ratchet pass on the changed files. I ran no typecheck locally; CI's typecheck passes (see CI notes).
Review
What I verified / didn't
CI notes
verify. An earlier head failed typecheck on one test-fixture type error, which I fixed.qoder-history-search-downgradetest also fails on unrelated PRs; if it is red here, that is not caused by this change.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)