Skip to content

test(launch): pin what each agent launcher produces on main today - #27189

Merged
brennanb2025 merged 29 commits into
mainfrom
brennanb2025/conv-c0a-producers
Oct 12, 2026
Merged

brennanb2025 merged 29 commits into
mainfrom
brennanb2025/conv-c0a-producers

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​693 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​693
Prod 7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1274 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1274

ELI5

Orca starts a terminal agent (Claude, Codex, OpenCode and so on) from many places: the "+" menu, a typed prompt, the fix-checks and other AI buttons, automations, the phone, workers. Those launchers reach the agent through two separate code paths. One is planned in the desktop window and spawned when the tab's pane mounts (the window lane). The other is planned and spawned by the host process (the host lane). The two were written at different times and have drifted: they disagree on which shell runs the command, how the command is quoted on Windows and WSL, which environment variables the agent sees, and which telemetry fires.

A coming migration moves every launcher onto one path. It has to keep each launcher behaving as it does today, except where a change is decided on purpose, and nothing currently pins that. This PR writes down what each launcher produces on main right now, as tables the later PRs run against their new path.

It keeps only the cases a named later PR of the migration checks against (each suite says which one). Cases that only one later PR (the workspace-creation and paired-tab move) uses move to that PR, which will pin main as it is when that work starts.

Coming in a small follow-up PR, not here. These wait until #27173 (the revert of #26489) merges, because until then main's "+" behaviour is the route being reverted:

  • row 1, the "+" launchers: the tab-bar "+" menu, the searchable "+", Quick Launch, the new-agent shortcut, the floating "Open " button, and the dashboard spawn;
  • row 3's window-side route: which AI buttons go to the host;
  • what a never-mounted "+" tab does after a restart, and how a "+" tab in a \\wsl$ folder is quoted.

What Changed

Tests only. No production file changes, so nothing is different for users. (The size table above counts the seven *.test-fixture.ts / *.test-cases.ts files as production because the counter only recognises *.test.ts; they are test-only tables and fixtures that nothing in production imports.)

How the tests are built. Each launcher ("producer") has one table of cases: a workspace (repo or folder; local C:\, \\wsl$ or POSIX; macOS, Linux or Windows), its saved settings, and what main produces for it. The tests observe each lane at the layer the migration keeps:

  • Window lane, renderer half: the real producer runs against the real app store, the store opens the tab, and the real pane connection builds the pty:spawn request it sends to main. The table holds that exact request.
  • Window lane, main half: the same request goes through the real pty:spawn handler to a fake terminal provider. The table holds what reaches the provider: command, folder, the pane's size, shell, WSL distro, the pane's ORCA_* keys, the agent's saved env values, whether main marks the terminal hidden, agent_started telemetry and the saved pane record.
  • Host lane: each host producer's real call (createTerminal, or the host's own RPC handler for paired and OpenCode requests) goes through the real runtime controller to the same fake provider. The table also holds the phone's view of the tab, the saved pane record and telemetry. The AI-button rows and the phone quick-command rows enter at the createTerminal options the host's launch handler (agent.launch) builds, not at agent.launch itself: the phone path from its launch request to those options needs the native-chat database, and the AI buttons' route is unified away by C2.

Each suite starts with a one-line comment saying it pins main's current launch behaviour as the convergence parity baseline, and names the later PR that checks against it.

Tables (one per producer):

Producer Cases Table Run by Later PR that checks against it
Typed prompt into a new tab (agent-prompt quick commands, a terminal's "continue in new session") 12 src/shared/launch-parity-window-cases.test-fixture.ts launch-parity-window-tab.test.ts (renderer), pty-launch-parity.test.ts (main) C1 (the Windows/WSL quoting and shell rule, moved to the host), C2 (typed-prompt tabs onto one launch path), C5 (spawn pipeline merge)
Continue from transcript (Antigravity) 4 same table same suites C3
Desktop automations 5 src/shared/launch-parity-automation-cases.test-fixture.ts launch-agent-background-session-launch-parity.test.ts (renderer), pty-launch-parity.test.ts (main) C7 (automations onto the host), C5
AI buttons, a phone's quick command, orchestration workers 5 pty-launch-parity-host.test-cases.ts pty-launch-parity.test.ts C1, C2, C5
Paired-desktop new tab (current host), local workspace-creation startup (run verbatim), OpenCode pane with a model pick 5 pty-launch-parity-caller.test-cases.ts pty-launch-parity.test.ts C1, C2, C4, C5

Outside the tables (pty-launch-parity.test.ts): a failed window-lane spawn reports agent_error (2 cases, C5), an orchestration worker asks the window to reveal its titled tab without activating it (C5), and main rewrites stale pane identity keys from the request's tab and leaf (C5). terminal-startup-queue-restart.test.ts pins that a queued startup that never reached a pane is not saved across a restart (C2).

Findings on main (pinned as-is). The tests keep these exactly as they are, so the migration changes each one only on purpose:

  • ORCA_WORKSPACE_ID is always sent by a window pane, never by an automation, and by the host side only for folders.
  • The host side hands the terminal the default-shell setting untrimmed; the window side trims it.
  • A paired desktop's or OpenCode pane's launch source never reaches the host, so agent_started says unknown.

What this round removed, and why

This round halves the PR: it goes from 21 files and 3,844 added lines to 11 files and 1,967. Every remaining case is one a named later PR checks against.

  • SSH cases, all of them. Main deleted the SSH terminal provider (refactor(pty): delete the SSH PTY provider and its registry slot #27499) and moved SSH panes onto the remote-runtime connection (refactor(terminal): panes have two transports, local or remote runtime #27489), so SSH launches no longer go through either local path. Re-pinning SSH waits for the SSH-runtime work.
  • The host's hostAdmittedMembership assertion. Main removed that field (Save runtime-started tabs in the tab bar before their terminal starts #27083).
  • Cases for behaviour already decided to change: disabled agents (now allowed everywhere), the AI buttons' and OpenCode panes' host reveal and pane request (moving to the pane in C2), host-side agent_error (decided to report it), the Codex no-prompt shell wait (decided: always wait), and rules a moving PR rewrites anyway (the host's \\wsl$ repo quoting, the deleted-folder fallback, an automation in a C:\ WSL project, a work item's PowerShell quoting).
  • Cases existing tests already pin, or that repeat another row's facts: the Linux repo row, extra Windows AI-button rows, automation folder and cmd.exe rows, the paired automation (row 5r) params and their fallback, the OpenCode refusal, the hidden 0×0 pane, the extra daemon \\wsl$ rows (pty-subprocess-wsl-launch.test.ts is back to main).
  • Moved to C4 (workspace-creation launchers send intent; paired new tabs drop the old-server command), saved outside the repo for that PR: the quick composer, work-item, folder-composer and worktree-create startup tests, the paired new-tab producer test and its case table, the paired-host fixture, and the paired new-tab wire block in web-runtime-session-terminal-host-authority.test.ts. They pin main as of before this round's merge, so C4 re-pins them when it starts.

Why

The migration is meant to change nothing a user can see until each difference is changed on purpose. Without a written baseline, a reviewer can't tell a deliberate change from an accidental one. The tables are data, so the PR that moves a launcher can feed the same cases to the new path and must produce the same facts.

Alternatives considered:

  • Testing each internal function separately was the first version of this PR. It pinned state that the migration deletes, so it could not gate the migration.
  • Pinning every producer and platform combination (the previous round) cost twice the lines and pinned behaviour that is already decided to change, so a later PR would mostly rewrite those expectations.

Linked Issue

N/A (maintainer change; no issue).

Visual Proof

Tests only, so there is no UI change to show. These are the terminal outputs.

All 4 changed test files pass (57 tests) at 18ad96f59aa, merged with main, with no production file changed:

All 4 parity test files pass, 57 tests, at 18ad96f59aa with no production change

One ablation: the line that makes a local Windows C:\ path quote for the client's OS is replaced, the Windows C:\ folder row goes red (Linux quoting comes back), the line is restored, the tree is clean, and the suite is green again:

C: folder ablation: 1 row fails, line restored, tree clean, 16 pass

Testing

  • The 4 changed test files pass on this branch, merged with current main (57 tests).

  • Ablation: for each remaining table and suite, I changed one production line it pins and confirmed the suite fails, then restored the line by writing back the original text. I made 14 changes. 13 turned their suite red. One stayed green because the transcript path is rewritten in two places; I replaced it with a change to the shared function both use, which turned red. The tree was clean after every change.

  • pnpm tc:node, pnpm tc:web and the CI static checks pass locally. The one local red is typecheck:ci, which needs a newer Bun than this machine has (bun check is missing in 1.3.14); tc:node and tc:web cover the two projects this PR touches.

  • Platforms: the tests simulate Windows, macOS and Linux clients and hosts. I ran them on macOS.

  • I manually tested these changes locally

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

Open questions the tests answer

What shell does a \\wsl$ folder tab get, and how is its command quoted?

VERIFIED by running the real producer, store and pane. createTab gives every local tab on Windows a shell, and for a \\wsl$ path, folders included, that shell is wsl.exe. The window treats a \\wsl$ folder's host as Linux, so the command is quoted for Linux, and main receives wsl.exe with distro Ubuntu. The two agree. Not run on a real Windows machine.

Do automations quote by the same rule as window tabs?

VERIFIED, and pinned. Yes: both quote for the OS of the host that runs the agent. An automation's terminal shell is wsl.exe only for a local \\wsl$ workspace; everything else gets the settings shell.

Which rules does an OpenCode pane with a model pick use?

VERIFIED, and pinned. The host's: the command is quoted by the host's rule and the shell comes from the host's spawn path. With cmd.exe as the Windows shell, a Windows C:\ repo gets cmd quoting in cmd.exe.

Is a queued tab startup saved across an app restart?

VERIFIED with a test: no. A window-side new tab is saved with its agent identity but not its queued command, so after a restart it opens as a plain shell.

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change 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

Tests only. No wire, host, storage or UI change.

Author: @BrennanKB5

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
  • 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)

…ty, pane size, env values, hidden and paired-host spawns

Round-2 review fixes: local Windows ConPTY panes withhold the kitty flag, the pane's
measured grid reaches the provider, agent/caller env values and the hidden mark are
provider facts, rows 5r/7 run through the host's RPC handlers, SSH folder rows quote an
apostrophe, Windows clients use their real '' default shell, titled automations only,
the quick composer drives its real selection hook, and inputs no producer builds are gone.
… 10, real desktop caller, older-host seeding, local Windows folder composer, gone automation root
…their OS, wsl$ and SSH folders quote for Linux, older paired hosts get the host's quoting
@brennanb2025

brennanb2025 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Review status (coordinator)

The problem. Orca starts terminal agents from about ten different places (typed-prompt tabs, AI buttons, OpenCode panes, desktop automations, Antigravity "continue", paired-server tabs, the workspace composers, phones, workers and other host callers). They go through two separate planners and two separate spawn code paths, which have drifted apart: different shells, quoting, environment variables, start sizes, telemetry and folder fallbacks. A planned series of PRs moves every launcher onto one host launch path, and each step must keep each launcher exactly as it behaves today. Nothing recorded what "today" is.

The user-facing change. None. Tests only; no production code changes. The tests record, per launcher, what main produces at the point where the terminal process is started (both code paths, observed at the same layer) and what the desktop window sends to the main process, in one table per launcher, so each later PR can run the same table against its new path. The "+" launchers, the AI buttons' window-side route and the "+"-specific open questions come in a small follow-up PR once #27173 (the revert of #26489) merges, because until then main's "+" behaviour is the route being reverted.

Review. Five rounds with fresh reviewers each round, tracked in a ledger:

  • Round 1 (a reviewer whose only job was to prove each test describes what main does, two adversarial reviewers, and an architecture challenger that first sketched its own plan from the problem alone): no test encoded a fix, but three tests pinned values main never actually sends, and the design pinned code the migration will delete with the two paths observed at different layers. The PR was restructured around one table per launcher, both paths driven to the terminal process, and cut from 6,048 to about 3,400 lines.
  • Round 2 (full, since the design changed): the restructure delivered; found one more value main never sends (the keyboard protocol flag on local Windows PowerShell/cmd panes), several inputs no real launcher builds, and coverage the restructure had dropped. Fixed.
  • Rounds 3-4: confirmed the fixes; found and fixed the last inputs no real launcher builds (quick-command shapes, an invented command-line caller, the AI buttons' per-button launch source).
  • Readiness: caught that main fix(hosts): agent launch platform and shell come from the execution host #27259 (launch platform and shell now come from the execution host) had changed the pinned behaviour after the last push, while GitHub still showed the PR mergeable and green. The branch merged main once, the affected rows were re-pinned to what main does now, and a fresh reviewer confirmed the merge brought no production change and that breaking fix(hosts): agent launch platform and shell come from the execution host #27259's logic turns the re-pinned rows red.
  • Deferred: a few overlapping cases and naming nits; the phone path from its launch request to terminal options is not pinned (it needs the native-chat database; phone launches do not move in the plan).

Proof that the tests pin main. Production code in this branch is identical to main. Across rounds, fresh reviewers independently removed one production line per suite and per launcher table (over 70 in total, mostly different lines from the builder's), and every test went red for the right reason and green again when restored; the few that stayed green exposed rows that could not fail, which were fixed or removed. Two images in the PR body show the suites passing and one such removal.

Live QA. Not applicable: no production change, so there is nothing to exercise in the app. The Windows and WSL cases are pinned at the code level; their behaviour on a real Windows machine was not checked.

CI. Green at 3cbc1cb (after the one merge of main): all 14 required checks pass (static analysis + typecheck, repository guards, the 5 test shards, relay integration, both package jobs, line-count and selection checks), 17 skipped by path filters; no failures, so nothing to classify against main's known failures.

Not verified.

Update after the value audit (head 18ad96f)

Why it changed. Brennan asked that every line of these tests earn its place, and an independent audit listed the cuts. They were applied exactly:

  • SSH rows dropped (main deleted the SSH terminal provider; they wait for the SSH-runtime work).
  • Rows that pin behaviour already decided to change were dropped: disabled agents, the OpenCode request-and-reattach shape, the Windows AI-button rows C1 changes, the Codex no-wait rows, the deleted-folder pass-through and the automation WSL quirk.
  • Duplicates of main's own tests were dropped and near-copy rows collapsed.
  • The workspace-creation and paired new-tab suites (about 830 lines) moved out to C4, the only PR that uses them, and are saved for it.

3,844 added lines in 21 files became 1,967 in 11.

Re-checked. The branch merged main once (no production change), and nothing left needed re-pinning. A fresh reviewer confirmed the audit was applied as written, the moved files match byte for byte, and the suites pass on the newest main. It broke one production line per remaining suite: each went red. One break stayed green because production sets that value in two places, so a different line was broken and that one went red. Every remaining suite names the later PR that relies on it.

CI. Green at 18ad96f, no failures.

Not pinned here. The "+" launchers come in C0a-2. The phone's path from its launch request up to the terminal options is not pinned (it needs the native-chat database). SSH launches wait for the SSH-runtime work.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 10, 2026 15:22
@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fa33f7c2-a6a0-4005-beb6-ae40d8982efd



📥 Commits

Reviewing files that changed from the base of the PR and between ad46526 and 18ad96f.




📒 Files selected for processing (11)
  • src/main/ipc/pty-launch-parity-caller.test-cases.ts
  • src/main/ipc/pty-launch-parity-host.test-cases.ts
  • src/main/ipc/pty-launch-parity.test-fixture.ts
  • src/main/ipc/pty-launch-parity.test.ts
  • src/renderer/src/components/terminal-pane/launch-parity-window-tab.test.ts
  • src/renderer/src/lib/launch-agent-background-session-launch-parity.test.ts
  • src/renderer/src/lib/launch-parity-renderer.test-fixture.ts
  • src/renderer/src/store/terminals/terminal-startup-queue-restart.test.ts
  • src/shared/launch-parity-automation-cases.test-fixture.ts
  • src/shared/launch-parity-window-cases.test-fixture.ts
  • src/shared/launch-parity-window-request.test-fixture.ts



Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.





📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

The PR adds shared fixtures for window, automation, host, and caller launch scenarios. Renderer tests compare generated PTY requests with expected requests and check background-session phone state. Main-process tests exercise window and host launch paths, then check provider facts, telemetry, bindings, and presentation state. Additional tests cover spawn failures, reveal options, pane identity, and startup-queue serialization across simulated platforms.





Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 18ad9

The parity tests introduce no established merge-blocking risk. Windows behavior remains simulated rather than tested on a real Windows machine.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 54.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 35 functions across 21 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main change: adding launch parity tests that pin current agent launcher behavior.
Description check Passed The description is complete and relevant. It explains the purpose, scope, test design, platform coverage, deferred work, validation results, and the absence of production changes. The unchecked full c…

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR





  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

…s against

Drop SSH rows (the SSH PTY provider is gone from main), rows whose behaviour is
already decided to change, rows existing tests already pin, and the hostAdmittedMembership
assertion main removed. Workspace-creation and paired new-tab producer tests move to C4.
Each remaining suite names the later PR that relies on it.
…ture

The focus-read ratchet counts getDefaultSettings in a new test fixture; the suites
build the settings instead. Type the row 8 telemetry literal.
@brennanb2025
brennanb2025 merged commit e5a4e0c into main Oct 12, 2026
31 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/conv-c0a-producers branch October 12, 2026 00:03
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