Skip to content

feat(sidebar): show setting up while a repo's setup script runs - #19

Merged
nplez1 merged 1 commit into
nplez1/mainfrom
nplez1/Setup-State
Sep 22, 2026
Merged

nplez1 merged 1 commit into
nplez1/mainfrom
nplez1/Setup-State

Conversation

@nplez1

@nplez1 nplez1 commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​333 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​329
Prod 20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​300 $\color{#cf222e}{\Huge{\mathbf{−}}}$​24 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​276

ELI5

When you add a workspace, Orca runs that repo's setup script (install dependencies, copy config, whatever the repo needs) before you can really use it. That script is a plain shell script, not an AI agent, so Orca had nothing to read about it and showed the new workspace as ordinary green — the same as an idle terminal. This makes it show a pulsing amber "Setting up…" dot until the script actually finishes, and it is built so that dot can never get stuck.

What Changed

Before

  • Creating a workspace with a repo setup script showed the sidebar status dot as plain green Active for the entire setup, because the dot's only evidence was "this worktree has a live terminal".
  • active is also what an idle shell shows, so there was no way to tell "still installing" from "ready".

After

  • The workspace shows a pulsing amber "Setting up…" dot while the setup script runs, then returns to its normal state (green idle, agent spinner, review icon, or grey inactive).
  • It is a new WorktreeStatus member (setup), not a reused working: it renders a plain pulsing dot rather than the agent spinner, and its tooltip says Setting up… rather than Working, so it never claims an agent is doing something.
  • The dot wins over the review/branch/sleep glyphs in the new-card status lane (QUIET_REVIEW_REPLACEABLE_STATUSES deliberately does not include it), and the setup dot appears in both compact and detailed cards.

The mechanism, since the renderer could not see this on its own:

  • The setup terminal is spawned by main, and the renderer never receives setupReceipt/terminalHandle — there is no renderer-side way to identify the runner. So the execution host owns the fact, per docs/reference/agent-status-store.md.
  • It is announced on the existing host→client event stream (RuntimeClientEvent) as worktreeSetupRunnerState { worktreeId, running }, not over a new channel, and the renderer keeps it in one small store slice (setupRunningWorktreeIds) that the status hooks read.
  • Completion reuses the machinery that already exists for the agent-launch gate: the setup command is wrapped with buildObservedSetupCommand + a token, so the wrapper prints a completion marker whatever shell the user runs.

Clearing is deliberately redundant. A stuck "Setting up…" is worse than a missing one, so four independent host-observed signals clear it:

Signal Covers
Completion marker (__ORCA_SETUP_COMPLETE__) Normal success, non-zero exit, set -e abort — and cmd.exe, which emits no OSC 133
OSC 133;D on the setup PTY An interrupted setup (Ctrl+C): the wrapper's marker never prints, but the prompt returns
PTY exit, via the PTY reaper Tab closed, PTY killed/crashed, worktree deleted, daemon/SSH teardown
Hour-long watchdog A shell wedged in its own rc, where the queued command never runs at all

Plus two properties that are not signal-dependent: the state is in-memory only, so a main-process restart cannot carry it over; and it is replayed to every subscriber (getSetupRunnerClientEventSnapshot), so a renderer reload or an event dropped during a transport blip converges on the host's current truth instead of stranding the dot. The host clears its own set on completion regardless of who is listening, so the replay is always correct even after a missed running: false.

Why

  • Why the host, not the renderer. The setup terminal is created by main (spawnLocalStartupAndSetupTerminals), and the renderer holds no handle for it. A renderer-side heuristic would have to guess from the tab title, which is localizable text main also sets for other tabs.
  • Why a new status instead of reusing working. working drives the agent spinner and the Working tooltip, and it is what the agent sidebar keys off. Setup is not an agent, and the state deliberately does not appear as an agent row anywhere.
  • Why not the agent-status store. Guidance in agent-status-store.md is about agent status; writing a synthetic row there would surface a fake agent in the sidebar and in worktree ps. The client-event stream is the existing host→client state channel (worktreeTerminalSleepState is the closest precedent).
  • Why OSC 133;D as well as the marker. Verified in local-pty-shell-ready-bash-rcfile.ts that bash's 133;D is gated on __orca_in_command (set by the 133;C preexec), so it fires only when a command genuinely returns to its prompt — it cannot fire spuriously at shell startup, and it does fire after Ctrl+C. PowerShell's bootstrap has the equivalent HasSeenPrompt gate. A marker-only design would have been stuck on every interrupted setup.
  • Why a watchdog at all. Nothing event-driven can see "the runner never started". One hour is far above any real setup, so a false clear is unlikely, and a false clear only restores the plain green dot this replaces.
  • Amber via an existing token. bg-workspace-status-progress (#d4a300) with animate-pulse and motion-reduce:animate-none — no new palette value, so the design-system gate stays clean and reduced-motion users get a static dot.

Linked Issue

No tracking issue — this was requested directly.

Visual Proof

No attachments: the $electron skill is not installed in this environment (the available skills are computer-use, find-skills, orca-cli, and orchestration), and I did not run the app, so I am describing the difference rather than showing it.

  • Before: while pnpm install (or whatever the repo's setup script does) runs in the background Setup tab, the workspace's sidebar dot is solid green, identical to the same workspace once it is idle and ready. Tooltip: Active.
  • After: the dot is a smaller amber dot pulsing inside the same status lane. Tooltip: Setting up…. When the runner ends the dot returns to green/agent/review/inactive within the same frame the host reports completion.
  • The rendering itself is asserted rather than eyeballed: StatusIndicator.test.ts renders the production component for setup and checks the amber token class, animate-pulse, motion-reduce:animate-none, the absence of the agent spinner, and the Setting up… tooltip.

Testing

  • pnpm tc — full typecheck (main, preload, renderer, web, cli, e2e).
  • pnpm run check:code-quality:changed — including the casting, type-aware, design-system, React Doctor, and casting-SAFETY gates: 0 findings across 29 changed files.
  • pnpm run verify:rpc-params-catalog, verify:localization-extraction, verify:localization-coverage — clean (the new tooltip label is a plain string in the existing STATUS_LABELS map, alongside Active/Done, so there is no new i18n key).
  • pnpm test (full suite): 86834 passed, 1 expected fail, 653 skipped, 14 failed. All 14 failures are pre-existing and unrelated — src/main/browser/browser-manager-viewport-ownership.test.ts (12), tests/e2e/e2e-worker-env-isolation.unit.test.ts (2), and tests/e2e/relay-region-correction.unit.test.ts (1). I confirmed they fail identically on the base commit 3bb346380a by stashing this work and re-running exactly those three files (14 failed, 1 passed).
  • The pty-exit-per-pty-map-reaper-ratchet test caught a real omission during development — a new per-PTY map has to be reaped by onPtyExit — and now classifies the field against its reaper helper.

New tests (13):

  • terminal-creation-and-readiness-part-06.spec.ts — announces on arm and clears on the completion marker; clears on an interrupted runner given 133;D;130 with no marker; clears on PTY exit and does not resurrect; clears on the watchdog (fake timers); does not announce for an already-dead handle; keeps the worktree setting up until every runner for it has ended.
  • client-events.test.ts — the setting-up snapshot is replayed on subscribe.
  • worktree-setup-runner.test.ts — the store slice sets, clears, is identity-stable on a repeat, and tracks worktrees independently.
  • worktree-status.test.ts — setup outranks interrupted/done/active, and loses to permission/working/monitoring.
  • StatusIndicator.test.ts — the dot's classes and tooltip.
  • use-worktree-activity-status.test.tsx — the store flag reaches the resolved status.
  • worktrees-local-create-flow.test.ts — the desktop create wraps the runner and arms the observation.

Not run: pnpm build, and no manual app run. Manual steps a reviewer can follow: add a workspace whose repo has a setup script that sleeps for ~20s → the dot pulses amber and returns to green when it ends; Ctrl+C in the Setup tab mid-run → the dot clears immediately; close the Setup tab → the dot clears.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

AI Disclosure

Authored with an AI coding agent (pi, deepseek-flash) under human direction. Every change was reviewed against the repo's gates, and the failure-mode behaviour was verified by tests rather than assumed.

Review

Start with src/main/runtime/orca-runtime-setup-runner-state.ts — it is the whole state machine, and the four clears are the part worth scrutinising. Then src/main/ipc/worktree-remote.ts (spawnLocalStartupAndSetupTerminals, where the runner is wrapped and armed) and the two renderer hops: runtime-client-ipc-bridge.ts into store/slices/worktree-setup-runner.ts, and lib/worktree-status.ts where setupRunning is placed in the precedence ladder. StatusIndicator.tsx is the pixels.

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

  • Security: no new privilege surface. The change adds no path handling, no shell interpolation (the runner wrapper reuses the existing resolveSetupRunnerCommand quoting), and no new IPC channel exposed to the renderer — it is one new member of an already-broadcast state event. The heartbeat is a worktree id and a boolean.
  • Cross-platform: the completion marker is the only end signal on shells without OSC 133 integration, which is exactly the Windows cmd.exe case (windows-shell-args.ts gives cmd no OSC 133) — so the wrapper is load-bearing there rather than decorative. Amber uses a theme-independent token. Not manually tested on Windows or Linux; the OS-specific branch (buildObservedSetupCommand's PowerShell path) is exercised by existing runtime setup tests.
  • SSH / remote: the desktop SSH create path is unaffected and stays green (it never arms), and the RPC/worker/managed path arms only where it already produced a completion token. The event is additive on the mirrored client-event stream, so a paired client that updates later simply ignores it and keeps today's green. Verified there is no zod schema on that stream that could reject an unknown type — the renderer's isRuntimeClientEvent gate drops it, which is the safe degradation.
  • Remote wire compatibility: additive. A new member of the RuntimeClientEvent union is delivered only to clients that already handle it; older clients ignore the type and show the previous green dot. No opcode, no handshake, no capability change.
  • Mobile: out of scope by request — mobile renders the worktree dot from its own reader and is unchanged. The event does reach it, so wiring it up later is a reader-only change.
  • Folder workspaces: unaffected; this is keyed to a worktree id and only ever armed by a repo setup runner.
  • Performance: two map/set writes per setup run, one extra OSC 133 scanner per setup PTY (only for the life of the runner, unsubscribed on clear), and one unref'd timer. No per-chunk fan-out was added for agent PTYs and the renderer's status hook reads one extra boolean per card.
  • Known gaps (deliberately not covered): the renderer-owned setup fallback (queueSetupAndIssueCommands, used when main fails to spawn setup) and the wait-for-setup sequenced variant stay green. Both are separate launch paths from the one this fixes, and arming either would need its own completion token.

A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
@nplez1
nplez1 merged commit a6dbb73 into nplez1/main Sep 22, 2026
32 checks passed
nplez1 added a commit that referenced this pull request Sep 22, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
nplez1 added a commit that referenced this pull request Sep 25, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
nplez1 added a commit that referenced this pull request Sep 27, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
nplez1 added a commit that referenced this pull request Sep 29, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
nplez1 added a commit that referenced this pull request Oct 1, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
nplez1 added a commit that referenced this pull request Oct 6, 2026
A new workspace's setup script runs as a plain shell script in a terminal
that main spawns, so the sidebar could see only a live PTY and rendered the
workspace as plain green "Active" for the entire setup.

The execution host now announces the runner over the existing host-to-client
event stream, and the sidebar renders a pulsing amber "Setting up…" dot with
its own #WorktreeStatus so it is distinct from the agent working spinner.

Clearing is deliberately redundant, because a stuck dot is worse than a
missing one: the completion marker (shell-agnostic, so cmd.exe is covered),
OSC 133;D on the setup PTY (an interrupted runner returns to its prompt
without ever printing the marker), the PTY exit reaper, and an hour-long
watchdog for a shell that never runs the queued command at all. State is
in-memory and replayed on subscribe, so a reload or a dropped event cannot
strand it either.
@nplez1
nplez1 deleted the nplez1/Setup-State 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