Skip to content

fix(worktree): show setup status for blank creates - #27

Merged
nplez1 merged 2 commits into
nplez1/mainfrom
nplez1/Setup-bug
Sep 24, 2026
Merged

nplez1 merged 2 commits into
nplez1/mainfrom
nplez1/Setup-bug

Conversation

@nplez1

@nplez1 nplez1 commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​115 $\color{#cf222e}{\Huge{\mathbf{−}}}$​18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​97
Prod 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​40 $\color{#cf222e}{\Huge{\mathbf{−}}}$​32 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​8

ELI5

When you create a workspace and pick no agent, Orca still runs the repo's setup script (pnpm install, or whatever the repo declares). While that script runs, the sidebar is supposed to show a pulsing amber Setting up… dot. For a blank create it never did: the workspace went straight to green Active, so one that was still installing looked ready to use. The shell that runs setup was being started by the window instead of by the Orca host, and only the host can watch a setup script and report on it. Now the host starts that first shell and the Setup tab for blank workspaces too, so the dot appears and clears exactly as it does for agent workspaces.

What Changed

Before

  • A blank create (no agent) with a repo setup script bailed out of spawnLocalStartupAndSetupTerminals on its first guard, !startup — with or without configured default tabs — and returned didSpawnSetup: false, so the host spawned nothing.
  • The renderer then created Terminal 1 and the Setup tab itself and queued the raw runner command. Setup still ran, but that tab was created and queued by the renderer rather than by the create path, so nothing armed the host's setup observer: worktreeSetupRunnerState was never announced and the sidebar showed plain green Active for the whole setup. That is the exact state feat(sidebar): show setting up while a repo's setup script runs #19 exists to remove; feat(sidebar): show setting up while a repo's setup script runs #19 only covered creates where the host already spawned a startup terminal, which is why the report was "not working in 1.4.197-np.11".

After

  • For a blank create with a setup script, the host spawns the first shell and the Setup tab, wraps the runner with the completion marker, and arms the runner observer — the same three things it already did for an agent create. The amber Setting up… dot now appears and clears through the existing marker / OSC 133;D / PTY-exit / watchdog paths.
  • The renderer sees startupTerminal.spawned on the result and keeps that shell instead of creating a second one, so the no-default-tabs case still ends up with exactly one primary shell and one Setup tab rather than two of either.

The mechanism is a rewritten gate plus one fallback shell:

  • The first guard now names the cases it means (src/main/ipc/worktree-remote.ts:408-412): canSpawnSetupWithoutStartup is true when there is a setup launch and either there are no default tabs, or setup runs in its own tab. Nothing else moved — an agent create with configured default tabs still bails, and a create with no setup script still bails.
  • else if (setup && !hasDefaultTabs) (:474-479) spawns the fallback primary shell with activate: true and records it as startupTerminal. That record is what makes the renderer treat the host as the owner of the tab (src/renderer/src/lib/worktree-initial-terminal-seeding.ts:100-120).
  • Setup creation, the observed-command wrapper and armWorktreeSetupRunner are untouched; the blank case now reaches them. If the host fails to spawn setup, didSpawnSetup stays false and the result still carries setup, so the renderer's existing retry path runs unchanged.
  • An existing test already pinned the renderer-queued behavior: two cases in worktrees-setup-launch-sparse-checkout.test.ts asserted that the create returns a setup payload for the renderer to launch. They now assert the host spawn and expect(result).not.toHaveProperty('setup'), which is the behavior change stated as a test.

Also in this diff: a two-line correction to quick-open-file-list.react.test.tsx, unrelated to the setup path — its onState probe was a no-op function, so the states.at(-1)?.files assertion it guards could never hold. Folded in to get this PR's CI green.

Why

  • The host has to own the fact. The setup runner is a script the host writes and spawns, and every end signal (completion marker, OSC 133;D, PTY exit) is readable on the PTY the host created. A renderer that queued the command has no handle and no end signal, so a dot it set itself would either stick or need a timer — the stuck-dot failure mode feat(sidebar): show setting up while a repo's setup script runs #19 was built to avoid. This is also the rule in docs/reference/agent-status-store.md: the execution host owns status, and readers keep only presentation policy.
  • Why not have the renderer report the Setup tab it creates. That adds a renderer→host message carrying a tab id, and the host still cannot produce a completion token for a PTY it did not create — it would have to adopt a renderer-named terminal and trust it. Spawning the shell host-side is the smaller change and leaves one owner.
  • Why mirror the runtime path instead of inventing one. provisionWorktreeTerminals (src/main/runtime/runtime-worktree-terminal-provisioning.ts:104-106) already spawns a fallback primary terminal when there is no startup terminal and no default tab, then arms the runner. The local IPC path did not, and that divergence is what the reported build showed. This brings the local path into line with the runtime one.
  • Why keep it narrow. Spawning a shell for every blank create would add a host terminal to creates that have no setup script to observe, where the renderer already creates exactly one; and creating a startup terminal for an agent create with configured default tabs would duplicate the tabs the renderer is about to make. Only blank-with-setup moved.

Linked Issue

N/A — no tracking issue. GitHub issues are disabled on this fork (nplez1/orca), and a search of upstream stablyai/orca issues found no matching report, so there is nothing to link. Reported directly: the setup-running indicator never appeared in 1.4.197-np.11.

Visual Proof

Not provided — no capture, and the app was not run for this change. This is a waiver rather than an N/A: the template reserves a bare N/A for a change with no visible effect, and this one has one — the sidebar status lane gains the pulsing amber dot #19 added, for a case that previously stayed green. The $electron skill this repo's AGENTS.md names for rendered-UI checks is not installed in this worktree (the installed skills are computer-use, find-skills, orca-cli, and orchestration), and no 1.4.197-np.11-equivalent build was run against a repo with a slow setup script, so there is nothing to attach.

Stated instead — and all of it is the indicator #19 already renders, not new pixels:

  • Before: blank create + a setup script that takes ~20s → the sidebar dot is solid green, tooltip Active, identical to the same workspace once setup has finished.
  • After (expected from the existing indicator, not observed in a running app): the same create → amber dot pulsing in the same lane, tooltip Setting up…, returning to green when the runner ends or is interrupted.
  • What this PR changes is when the host arms that indicator, which is asserted rather than eyeballed: worktrees-local-create-flow.test.ts checks that the host spawns the shell and the Setup tab and calls armWorktreeSetupRunner. The dot's rendering is already covered by feat(sidebar): show setting up while a repo's setup script runs #19's StatusIndicator.test.ts.

Testing

  • pnpm test on the four touched suites — worktrees-local-create-flow, worktrees-setup-launch-sparse-checkout, worktree-activation-setup-script, quick-open-file-list.react — 60 passed. Covers the new blank-create case (spawn order, observed Setup command, armWorktreeSetupRunner, no setup echoed back, startupTerminal.spawned), the two updated cases that previously asserted the renderer-queued payload, and the renderer case that a backend-spawned shell plus Setup tab is kept rather than duplicated.

  • pnpm test src/main/runtime/orca-runtime.test.ts — 1,298 passed, 1 skipped (the adjacent provisioning/host suite).

  • pnpm tc — clean (main, node, web, cli).

  • pnpm run check:code-quality:changed — 0 new findings across the 5 changed files (casting, focused plugins, type-aware, React Doctor, design system, casting-SAFETY), gate passed since 1f6cbb2603.

  • Fails without the fix: with only src/main/ipc/worktree-remote.ts reverted to origin/nplez1/main, worktrees-local-create-flow.test.ts fails with expected "vi.fn()" to be called 2 times, but got 0 times — the host spawned no terminal at all for a blank create — and two cases in worktrees-setup-launch-sparse-checkout.test.ts fail too, because they had encoded the old payload. All pass with the change.

  • CI on d55e74f10d: 17 checks pass, 12 skip, none fail.

  • Not run: full pnpm test and pnpm build. The suites above were run on macOS; the app itself was not run on any platform.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

No manual run: the missing state was located and verified at the create handler and the renderer seeding function, which is where it came from. Manual steps for a reviewer: add a workspace with no agent to a repo whose setup script sleeps ~20s → the dot should pulse amber and then clear; Ctrl+C in the Setup tab mid-run → the dot should clear immediately.

AI Disclosure

Authored with an AI coding agent (pi, gpt-6-luna) under human direction, and verified against the repo gates and the failure mode above. This description was written by a later pi session (deepseek-flash) from the branch diff.

Review

Start with spawnLocalStartupAndSetupTerminals in src/main/ipc/worktree-remote.ts (:398-479): the rewritten gate and the new fallback-shell branch are the whole fix, and the two cases that must still bail are an agent create with defaultTabs and any create without a setup script. Then the renderer half, src/renderer/src/lib/worktree-initial-terminal-seeding.ts:100-120, which decides from startupTerminal.spawned that the host already owns the shell. The tests are the contract: worktrees-local-create-flow.test.ts and the updated worktrees-setup-launch-sparse-checkout.test.ts for the host, worktree-activation-setup-script.test.ts for the renderer.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

  • Security: no new surface. No new IPC channel and no new event — this reuses the existing worktreeSetupRunnerState host→client event, which carries a worktree id and a boolean. No path handling or shell interpolation is added; the runner command comes from the same buildObservedSetupCommand / buildSetupRunnerCommand helpers the agent path already uses.
  • Cross-platform: no OS-specific code path touched. The new shell is created with the same createTerminal options the startup path uses, and the observed-command wrapper already has its PowerShell and cmd.exe branches. Not manually tested on Windows or Linux; the change is gate logic plus one createTerminal call.
  • SSH / remote: unchanged, and deliberately. A direct-SSH create goes through createRemoteWorktree, which never spawned setup host-side and still returns the runner to the client — the reason feat(sidebar): show setting up while a repo's setup script runs #19 left the SSH dot green. A paired-host create goes through createRuntimeRemoteManagedWorktree, which is untouched: its headless branch already provisions and arms, while its interactive branch with no startup still hands the runner back to the client, so that path keeps today's behavior. The confirmed repro was local, so this PR does not widen scope there.
  • Mobile: no mobile code path changed. Hosted creates on the runtime path are unaffected.
  • Folder workspaces: unaffected — runtime-folder-worktree-create.ts is a separate path, and this function is keyed to a worktree.
  • Backwards compatibility: host-side only, and the result shape is unchanged (startupTerminal was already optional and is already read as backendStartupTerminalSpawned). No persisted state, no stored setting, no wire change; a paired client that predates it reads the optional field exactly as before.
  • Performance: a blank create with a setup script now gets its first shell from the host instead of the renderer — the same single PTY, just created earlier in the sequence, so there is no net terminal or process. The setup PTY additionally carries the OSC 133 / completion-marker scan the agent path already had, for the life of the runner only.
  • Known gap, deliberate: setupScriptLaunchMode set to a split, with configured defaultTabs and no agent, still takes the renderer-queued path (the gate still bails, because that split needs a primary pane and the renderer owns the tabs in that case). Arming it would mean spawning a host primary alongside the renderer's default tabs — a second terminal for a corner case.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason — not captured; see Visual Proof
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred) — pnpm tc, the three suites above, and the changed-code quality gate pass locally; full pnpm test and pnpm build are left to CI

@nplez1 nplez1 changed the title Setup bug fix(worktree): show setup status for blank creates Sep 24, 2026
@nplez1
nplez1 merged commit 5278041 into nplez1/main Sep 24, 2026
29 checks passed
@nplez1 nplez1 mentioned this pull request Sep 24, 2026
5 of 9 tasks
nplez1 added a commit that referenced this pull request Sep 25, 2026
* fix(worktree): show setup status for blank creates

* test(worktree): align setup launch coverage with host spawn
nplez1 added a commit that referenced this pull request Sep 27, 2026
* fix(worktree): show setup status for blank creates

* test(worktree): align setup launch coverage with host spawn
nplez1 added a commit that referenced this pull request Sep 29, 2026
* fix(worktree): show setup status for blank creates

* test(worktree): align setup launch coverage with host spawn
nplez1 added a commit that referenced this pull request Oct 1, 2026
* fix(worktree): show setup status for blank creates

* test(worktree): align setup launch coverage with host spawn
nplez1 added a commit that referenced this pull request Oct 6, 2026
* fix(worktree): show setup status for blank creates

* test(worktree): align setup launch coverage with host spawn
@nplez1
nplez1 deleted the nplez1/Setup-bug branch October 24, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant