* refactor(renderer): organize host and terminal implementation owners
* fix(refactor): align active adoption reliability references
* refactor(terminal): remove forwarding shells and stale mocks
* fix(renderer): consolidate direct owner imports
* fix(refactor): route incoming issue trust type to its owner
* test(runtime): give independent status viewers separate pairing tickets
* style(refactor): normalize rebased terminal mock formatting
* refactor: organize remaining terminal pane features by owner
* refactor(terminal): consolidate replay guard type imports
* refactor(terminal): retarget notification mocks and generated consumer import
* refactor(terminal): repair benchmark owner lookup and stale mock export
* Separate editor tab placement from content ownership
* Show scoped Source Control sections for attached worktrees
* Preserve active diff refresh and validate section catalogs
* Complete folder section test worktree metadata
* Use contextual types for scoped Source Control actions
* Preserve optional scoped action parameters
* Preserve editor ownership through restore, mirroring and workspace removal
* Retire Source Control drafts only after confirmed owner removal
* Limit section work to visible expanded repositories
* Keep editor follow-up actions and bulk closes in their invoking views
* Track the consolidated editor order writer in its owning module
* Use complete typed fixtures for repair contracts
* Preserve live editor placement during layout readback
* Pass projected editor placement directly to serialization
* Read attached worktree lineage from shared graph module
* Retain inferred owner checks and scope placement retirement
* Preserve selection in untouched workspaces during purge
* Exercise editor placements in existing launch and lifecycle fixtures
* Preserve shared buffer prompts and editor strip order
* Pause inactive section context subscriptions
* Keep section recovery and file actions in the folder workspace
* Describe attached Source Control in folder workspace CLI help
* Attribute Git metadata signals once per dispatch
* Watch mounted source control sections outside the warm budget
* Resolve section watches only for served filesystem roots
* Restore restart-stable editor identity across SSH opens
* Apply section terminal routing to explicit Chat choices
* Include cross-store descendants in folder workspace panels
* Keep cross-host recovery on its existing workspace route
* Start cross-host section agents in their workspace without navigation
* Update memoized Source Control labels on language changes
* Remove unused terminal close-to-right implementation
* Suspend inactive Source Control body observers
* Retain base ref query while section observation pauses
* Cover retained base ref search across section visibility
* Resolve note menu ownership from the retained snapshot
* Preserve section agent working directories and background placement
* Keep Source Control sections active while their overlays are open
* Read current settings when Source Control commands execute
* Adapt editor and browser test stores to view subscriptions
* Preserve bound store hooks for ordinary app views
* Trust agent starts outside the placement workspace
* Trust the section worktree through transient launch ownership
* Extract editor follow-up file actions from the panel
* Fail section launches when trust capability lookup fails
Preserve explicit unsupported-host fallback, propagate lookup failures to the existing launch error path, and let the existing transport retry refresh rejected capability probes.
* Follow the moved pane launch test in web configs
* Preserve section file ownership during refresh and close
* Share tab order removal across editor close paths
* Verify companion editor close through the real store
* Close all workspace split copies for legacy mobile file requests
* Keep unmatched mobile close requests within their workspace
* Reuse the agent menu render snapshot
* Keep attached folder roots scoped to their resolved host
* Apply repository trust decisions to the launch trust owner
* Show OpenCode choice questions in chat
* Preserve form replies for omitted fields and cover rendered questions
* Keep cross-process form tests out of renderer typecheck
Use one shared agent picker for creating workspaces, continuing conversations, and launching agents from Source Control and its settings.
Preserve saved agent choices, model and effort selection, custom arguments, keyboard navigation, and agent settings. Match the Create worktree menu to its field while keeping other menus compact, with stable model labels and responsive setup placement.
* Read Linear drawer details in one issue query
* Keep readable Linear details after optional field errors
* Select only Linear fields needed by each issue reader
* Explain the typed Linear partial response boundary
* Typecheck Linear integration in the mixed-process project
* Update Linear integration fixtures for raw issue reads
* Keep opened GitHub work items bound to their exact repository
* Bind GitHub work item ownership through consumers and dispatch
* Keep empty mention lists from consuming selection keys
* Preserve GitHub action and consumer ownership across resource changes
* Exercise repository account updates through their production store action
* Exercise account publication and cache settlement through GitHub consumers
* Simplify fork PR contract verification
* Make contract verification imports and clocks deterministic
* Correct mobile task test fixtures and model types
* Correct task recording fixtures and CI types
* Correct task fixture types and static checks
* Consolidate replay test RPC imports
* Separate GitHub metadata imports from Linear hooks
* Scope mobile PR sidebar reads to the selected resource
* Separate desktop GitHub changes from phone updates
* Fix relay authority probe census across topology cases
* Repair repository owner proof fixtures and compiler allocation
* Preserve repository row observation in host lookup
* Seed metadata fixture before server-side store acquisition
* Preserve nullable fixture settings and discover mixed test types
* Preserve GitHub detail argument types and optional capabilities
* Let keyed details queries own pending read replacement
* Repair GitHub RPC and relay test fixture boundaries
* Verify expected GitHub account at repository execution capture
* Preserve captured GitHub account expectations through client dispatch
* Retain observed runtime status after switching environments
* Fix account contract fixture types and RPC setup timing
* Restore the negative-capability switch probe assertion
* Align account fixtures with runtime identity and configured origin
* Consolidate details test imports
* Drop duplicate fork pull request identity tests
* fix(native-chat): an open chat moves to the account you switch to before its next message
A running chat's agent read its account only when it started, and nothing compared it with the
account a start would use now, so open chats kept answering on the old account after a switch.
The agent a chat runs now records the account its launch used, and the host's replace-before-the-
next-send check replaces an idle one whose account is no longer what a start would use. A chat
mid-reply finishes on the old account first. Claude chats follow; agents whose chats keep the
account they were created on are never compared.
A chat's model-list reads and saves now use the account it runs on, so a moved chat no longer
writes its new account's list under its first account (STA-11281).
Tracked in STA-11211.
* fix(native-chat): declare the launch-account read for the runtime layer that wires the host
* fix(claude): find custom chat history before managed-account resume
* test(claude): assign the complete custom account home
* feat(native-chat): offer account switching and hand-off at usage limits
* fix: track the provider in account-list reload effects
* fix(native-chat): target recovery actions at the chat host and transcript
* fix(native-chat): verify account recovery for each chat on its host
* refactor(native-chat): retain the existing image-support result
* fix(native-chat): withdraw account recovery after failed eligibility reads
* fix(native-chat): leave unavailable recovery facts absent
* Align Japanese usage-limit recovery copy with catalog convention
* Preserve complete chat context through usage-limit recovery
* Keep Continue callback focus optional
* Keep recovery fixtures and account effects type safe
* Keep chat recovery on native sources and native accounts
* Validate native recovery files after path admission
* Use the optional cursor shape in recovery fixture
* fix(native-chat): exclude source CLI family from limit handoff
* Resolve recovery context in the owning workspace directory
* Preserve unknown account model facts and legacy option replay
* Clarify terminal account-switch feedback in translated locales
* Offer usage-limit Continue Now only once the limit resets
* Keep usage-limit reset timers current and bounded
* Keep option mutation plans checked against their contract
* Cover unknown model choices across chat consumers
* Keep consumer fixtures aligned with canonical types
* Match the mobile picker label in its consumer test
* fix: clarify terminal scope in translated Claude account settings
* Update integration tests for current session and remote client contracts
* Keep native worker replies on their receiving account
* Preserve receiving accounts across cold starts and nested reports
* Preserve the delivery receiver when reading chat commands
* Clarify native reply matching and consolidate imports
* Resolve catalog scope without re-entering conversation queues
Give owned option mutations and delivery settlement a resolved catalog input while external reads acquire scope once. Preserve account routing, catalog lifetime and current response obligations.
* Align catalog and mobile test fixtures with current contracts
Explain the existing timeout budgets and provide the idle turn identity and truthful model-only picker label in the mobile fixture.
* Align receiving-account readers and fixtures with typed contracts
* Repair account-switch fixtures and measure receiving work
* Use the checked account-home path in catalog tests
* Restore executed-option recorder context
* Control receiving catalog deadline test clock
* Align unknown-model consumer fixtures with send capture
* Consolidate session host type imports
* Align cross-version catalog fixtures with host read contracts
* test(native-chat): bind Runs through the current start authority in receiving fixtures
Main now resolves a worker start lazily through bindRun with objective and task references, and creating a Run no longer unbinds a coordinator's earlier Runs; run-use does. The receiving fixtures follow the same production owners so every original scenario, input and bound is exercised unchanged. The receiving runtime subclass moves verbatim into its own support module.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(native-chat): follow the selected account before its folder exists
A live Claude chat compared its account against the selected one only after that account's folder passed an availability check, so an account still being set up (or whose setup failed) left the chat silently answering on the previous account, and a resting chat refused its start before preparation could run. The retirement comparison and the start pre-read now ask the router for the selected route as identity, through the existing resolver purposes; catalog, create and probe reads keep checking availability. Preparation, typed launch failure and parking are unchanged.
Session History details show the account a chat was last launched under when the host recorded one, else its founding home.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(ai-vault): build the stale acquisition receipt without a type assertion
The History account case now reads the fixture receipt through the typed acquisition reader instead of spreading the record field through a cast; the three assertions and their inputs are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): narrow the rig acquisition link before relinking it
The account-setup cases spread an optional link, which typed its id as possibly undefined; the rig always links a handle, so a guard that cannot fire narrows it. No assertion or input changes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): follow main's chat admission in fixtures and bound the History account projection
Chat admission no longer reads an experimental host setting, so the continuation-context test drops the spy main dropped in its own fixtures. The two History consumers that install a minimal chat host give their fake store the record accessor and host id the real store has, since the projection now reads the acquisition receipt. An operation-count contract holds the production History projection to one roster read and one record lookup per owned row at 10 and 100 rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(native-chat): give the declared-effort catalog fake its capture port
The declared-effort fixture builds a catalog service without the capture member the options reader asks every catalog for, which failed its desktop, catalog and phone consumers; it now supplies the inert test operation like the other fakes. The History projection contract also drops an unused map parameter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Consume main's outcome change feed in the receiving-account index
Main's keyed journal records deliver each change after an epoch's frozen prefix as an
outcome sequence naming touched keys instead of typed rows. The receiving-account index
now re-reads those keys at their current values: queue cards by id, a deleted submission
retires its exact notice, and a deleted item drives the existing recovery rebuild, with
Stop marks still excluded. The in-process read row exposes the already-persisted
queue.cards keys and per-key deletion facts; itemIds and submissionIds keep listing every
touched key. The mobile unknown-consumer test mocks MobileSwitch as main's picker test does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test: validate historical usage-limit fixture before rendering
* fix: scope handoff choices and share account observation
* fix: align account menu lifetime and local projections
* test: preserve account observation contracts in quality checks
* fix: preserve typed account observation project coverage
* fix: restore handoff feedback for empty host inventory
* test: name exported handoff mocks with public types
* test: follow settings navigation consumption in feature tip adapter
* Keep usage-limit recovery actions beside their lead-in
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Make explicit hosted review refresh read current provider state
* Name the right provider and keep GitHub's own copy when a Checks refresh fails
A failed manual refresh now names the repo's provider (from the review, its link, or the remote), clears once a newer read lands for the panel, never stops GitHub checks and comments from refreshing, and gives way to GitHub's classified sign-in or rate-limit copy. The copy is one interpolated line in every locale.
* Read after the write when Orca merges, closes, reopens or edits a GitLab MR
Every GitLab merge request write now drops the host's cached review lookups for the repository, and the Checks panel's follow-up read can't join a poll that began before the write, so merging in Orca shows Merged and Delete Workspace without a manual Refresh. A user refresh also forgets Gitea's shared repository scan before reading.
* Clear a failed Checks refresh only on a real provider read
The host now tells the desktop store, when asked, whether its answer came
from a provider read it stored as current or from a cache or last-good
fallback. The Checks panel keeps a failed Refresh until such a read lands,
a click succeeds, or the context changes. On GitHub the PR read owns the
refresh status, so a failed review-card read no longer raises the generic
failure.
* Don't let a joined host lookup clear a failed Checks refresh
A poll that joins a lookup already running for an earlier request didn't
start that read, so its answer can't show the provider recovered after the
failure. Only the request that started the stored lookup reports a provider
read now. The host read also uses the shared reply outcome directly, and the
renderer writes the provider-read generation where it publishes the entry.
* Type the Checks refresh breadcrumb for every review provider
The breadcrumb already received Bitbucket, Azure DevOps and Gitea values; its
annotation now says so. Tests build typed fixtures instead of asserting types.
* Test the read-outcome reply at IPC, RPC and runtime, and the post-merge read
The flag reaches the runtime, and only a caller that asks gets the envelope,
including for a definitive null. After Orca merges, the card read starts after
the write, and its failure leaves the merge reported as done.
* Use a boolean for the empty-state fixture's remote-operation flag
* Trim redundant hosted-review refresh tests
* Keep the signed-out GitHub empty-state test
On some WSL 3.0.3 hosts `wsl.exe --list` exits 0 with nothing on a
redirected stdout. Orca read that as no distros installed and none running,
so the project runtime lost WSL and the background-read gates reported a
running distro as stopped.
When the installed list comes back empty, read the registered distros from
the Lxss registry (default first). If the registry has distros, this host
loses list output, so running distros are read through a console instead,
where wsl.exe still writes them. Only a painted registered name counts: a
blank console or the localized none-running text leaves the answer
unverifiable, never stopped. Only list calls run, so no distro starts.
* Chat UI leaves Experimental: each agent's Chat | CLI choice decides launches
Remove the experimentalNativeChat switch and every gate on it. The Chat
settings page, Default Agent Chat | CLI choice, + menu Chat/CLI rows,
source-control surface choice, Antigravity chat section and orchestration
chat-error switch are always present. What a new launch opens comes only
from the per-agent choice (no saved choice opens a chat) plus the existing
support checks.
- One lookup, nativeChatPreferenceFor, keyed by agent id so host-registered
agents read the same way; unreadable settings still open a terminal.
- A relayed desktop stamp (nativeChatDefault) maps onto the launched agent;
an explicit surface still wins. Old phones' createSupport keeps admitting
by the host's per-agent choice.
- settings.get stops publishing the retired switch keys; nothing in the
repo reads them from a host.
- Startup chat sync and hydration run when some agent can open as a chat
or the machine already holds chats.
- The retired key is stripped on load. The one-time upgrade tip from the
chat upgrade is retired with its audience record; a new record, captured
once from the raw profile, marks which existing profiles had Chat UI off.
- The window-side AI-button tab path, which only ran with the switch off,
is deleted.
* Keep profiles that had Chat UI off on the terminal
Existing users who never turned Chat UI on would otherwise see new agent
launches open as chats, since an agent without a saved choice now opens as
a chat. The one-time load step that records the new audience also sets
every known agent to CLI for those profiles, in the same save and only
when the record was absent, overwriting saved chat choices the old switch
masked. Profiles that had Chat UI on and new profiles are untouched.
Browser profiles keep their own settings, so the web client does the same
once: a stored switch that is not on seeds CLI choices, and dropping the
key marks the step done.
Agents first seen after this step open as chats.
* Agents without a saved Chat | CLI choice open as their CLI
An agent with no saved choice now opens in the terminal for everyone,
including fresh installs and agents first seen later. The one-time load
step carries what launches opened while Chat UI was experimental: with
the switch on, every known agent without a saved choice is saved as
chat; with it off, chat choices the switch masked are saved as CLI.
Existing profiles join the launch tip audience whether the switch was on
or off. Browser profiles get the same carry from their saved key.
The startup chat restore and host agent list now run only when some
agent is saved as chat or the runtime holds chats.
* Preserve owned terminal launches and local chat choices
* Clarify chat and terminal support
* Explain worker CLI integration timeout budget
* Use checked annotations for chat graduation test fixtures
* Store agent surface default separately from per-agent overrides
* Preserve explicit Chat intent across default changes
* Preserve current browser settings during host replies
* Correct browser settings factory and regression types
* Name exported launch fixture mock types and clarify agent defaults
* Preserve settings fallbacks and complete launch test lifecycles
* Check allocated tab identity before launch fixture interpolation
* Register combined integration project for compiler parity probes
* Use checked settings in the launch test fixture
* Preserve retired Chat preference data privately
* Align chat E2E fixtures and capture pane lifecycle evidence
* Use typed raw settings in retention contracts
* Consolidate transcript diagnostic imports
* test: align chat fixtures with live pane and native menu contracts
* test: capture relay tab selection loss diagnostics
* test: wait for owned shells before synthetic chat fixtures
* test: preserve all-host visibility in SSH observer
* test: bind synthetic transcript fixtures before provider seeding
* test: assert selected chat owner after narrow image preview
* ci: allow fifth compiler project to complete comparison
* Type the web-terminal fixture's chat map without an assertion
* Right-click the selection itself in the chat copy spec
* Let automations run in Chat or Terminal through the shared agent launcher
* Preserve automation targets and complete owned terminal lifecycles
* Keep stale prompt projection available when recomputing
* Count repeated desktop lifecycle resource cleanup
* Preserve automation targets and observe committed chat work
* Scope automation observation and reconciliation work to each operation
* Restore automation server composition and process import boundaries
* fix(automations): preserve busy chat reuse and runtime boundaries
* Correct shutdown test acquisition and exported measurement types
* Correct automation fixture contracts and duplicate imports
* Keep Chat automations separate from Terminal execution
* Preserve Chat automation intent while support loads
* Resolve automation Chat validation diagnostics
* Remove unused automation style directives
* Make automation surface classes statically inspectable
* Move automation surface styling into a toggle group variant
* Correct automation launch test receipt and output fixtures
* Account for pane and prompt journal reads in delivery test
* Preserve admitted automation surface across save effects
* Separate automation page test actions from their lifecycle owner
* Complete automation route test fixture input
* Reopen saved automation chats through the existing record owner
* test: isolate session action fixtures and complete tab groups
* fix(agents): preserve turn verdicts and monitoring in secondary views
* fix(agents): publish accepted native status changes to dashboard
* fix(agents): suppress equal rebuilt status notifications
* test(agents): explain proxy read forwarding in perf fixture
* test(agents): type production status fixtures completely
* test(agents): stabilize status fixture formatting
* Align web session test mocks with status transactions
Main runs one pass at startup and each window load, with or without a window
(desktop, orca serve, orcad): every live local terminal no pane names is bound
to the pane its record names while main still holds it free, else shown in a new
tab bound to it. The window no longer asks main to adopt terminals; it only reads
that main holds them. The window's layout.apply loses its adopt field.
Main also derives activeWorktreeIdsOnShutdown, remoteSessionIdsByTabId and
activeConnectionIdsAtShutdown from its own layout, terminal records and SSH
connections, replacing the window's copies in every save and the quit checkpoint.
Co-authored-by: E2E Test <e2e@test.local>
* fix(layout): restored terminals named only on their tab reattach after a restart (dark, layout-owner build only)
In a layout-owner build, a terminal recorded only on its tab (an older session, or an SSH tab
whose relay session was never visited) started a new shell after a restart and orphaned the old
one, and an unvisited SSH tab lost its relay session id on the next save.
- Main's Loader binds a one-pane tab's own terminal to its unbound pane in a workspace that was
live at shutdown, as the window's reconnect does, so main's frames and saves hold it.
- The window's reconnect writes the restored row terminal again: ptyId is a field the frame merge
leaves to the window, so main's frames never set it on a restored row.
* fix(layout): main binds a restored tab's own terminal once at load, not on every Loader run (dark, layout-owner build only)
The Loader re-derived the binding on each load from fields the window rewrites live, so an exit
main recorded could be undone. A switch-gated Store load repair now binds a one-pane tab's own
terminal to its unbound pane, as the window's reconnect does, and main's saves keep it. It skips
a terminal another mounted pane or a tab-bar tab holds, and a paired server's partition.
* fix(layout): an SSH workspace's restored tab binds only its relay session id; repair runs after the load purge (dark, layout-owner build only)
* test(layout): the reconnect gate test expects the row ptyId write; cover the repair's purge order and SSH repo predicate
---------
Co-authored-by: E2E Test <e2e@test.local>
* fix(remote): keep a paired host's undelivered tab close across a reload
* fix(remote): show a tab again when its held close expires
* fix(remote): one choke point shows every held close the store drops
* fix(remote): keep held closes while the pairing is unknown; tidy dispositions
* fix(remote): save a held close for a long path-named editor tab
* test(sidebar): the desktop window and a feed reader draw the same list (dark)
* fix(sidebar): a feed reader lists a paired server's folders and nested workspaces as the window does (dark)
* fix(sidebar): the paired-server shadow compares the lineage the server's check keeps (dark)
* fix(sidebar): paired-server rows carry the unread flag Smart Done reads (dark)
* refactor(sidebar): the stamp-sorted window list runs no settle, latch or saved order (dark)
With ORCA_SIDEBAR_INPUTS=main the window's sort hook is a pure read of the
rows' stamps: no settled-epoch store listener, no Smart latch, no saved Smart
order writes, no class telemetry over the old attention map. Switch off: the
existing hook, unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): stamp sort hook reads no live sources; stronger switch-on tests
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): clear the order-save mock between tests
Co-Authored-By: Claude <noreply@anthropic.com>
* wip: main keeps sleeping retention only
* fix(sidebar): stamp sort reads a shared empty-sources constant, not a renderer layout literal
The layout-writer ratchet counts layout field keys written in the renderer;
the empty sources object now lives beside SmartSortSources in shared code.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(sidebar): main keeps sleeping retention only; no order of its own (dark)
Clients sort by the owning host's stamps, so main's temporal owner loses its
settle window, Smart latch, create grace and catalog-listing classifier, and
the shadow check stops comparing order. Retention, its clock and its shadow
comparison stay.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(sidebar): every retention listing shares one sequence; publish after the first
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): the first listing bumps the retention revision
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
src/main/ipc/worktree-path-comparison.ts kept its own areWorktreePathsEqual and
dedupeWorktreesByPath. Its Windows branch lowercased a WSL UNC path's ext4 tail, so two
case-twin WSL checkouts merged into one row in removal safety, folder-upgrade dedupe and
the SSH listing fallback. Every caller now uses git/worktree-path-comparison, which gains
the macOS /private/tmp fold the ipc copy had. worktreePathComparisonKey keeps its exact
bytes (retirement namespace keys persist it) in worktree-path-comparison-key.ts, and the
worktree-logic re-export goes.
* fix(sidebar): main takes its row baseline from the session's catalog and skips unchanged listings
* fix(sidebar): read catalog listings the way the window's merge does
* fix(sidebar): a seeding listing re-samples, and listings compare with the window's equality
* feat(sidebar): dark phone feed of the sidebar inputs (sidebarInputs.read)
* fix(sidebar): feed review: fresh read on ready, notices kept per revision, one flush owner, closed connections never subscribe
* fix(sidebar): feed skips values JSON cannot hold; a failed read forgets its notice
* fix(sidebar): name the feed batch key by where the batch starts from
* refactor(sidebar): move the Smart comparator and attention scoring to src/shared/sidebar-list
Main needs them to own the frozen order. Renderer helpers they depend on move with
them; the window imports the shared copies. No behaviour change.
* refactor(sidebar): one shared ranking for the frozen sort order
The window's sort memo and the palette's Smart sort now call rankWorktreesForSort,
so main can produce the same order from its own records. The pure state-history
step moves to shared for the same reason. No behaviour change.
* feat(sidebar): main keeps the list's time rules on one clock, checked against the window in dev and test
* test(sidebar): fake-clock parity of main's temporal owner against the window
* fix(sidebar): main restarts the settle window only where the window's sortEpoch bumps
* test(sidebar): type the owner harness without an assertion
* fix(sidebar): main's temporal owner stays idle until a dev or test window begins a session
* fix(sidebar): main reads Hide sleeping from the field the window persists
* refactor(sidebar): keep the begin params in their own contract module so the snapshot schema compiles out
* chore: restore files the merge's formatter rewrote to their origin/main content
Main's Store.migrateWorktreeIdentity re-keys every stored layout field before it sends the
rename event the window re-keys on, so the window's save only mirrors it. The new oracle checks
main's migrated layout model equals the model of the window's post-rekey save and the model's
identityRenamed transition; the first-work test pins migrate-before-notify.
Preserve safe ADF hyperlinks and rich-text marks through the existing Markdown renderer, with escaping and punctuation regressions.
Refs #13604.
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(wsl): start host-side wsl.exe probes through process-host so they never open a console
wsl --list, wsl --status, the WSL home and directory probes, and WSL UNC
deletes were started with node:child_process directly. The synchronous ones
omitted windowsHide, so each spawn from the GUI-subsystem main process
flashed a conhost window that could take keyboard focus. Route them through
@orca/process-host, which pins windowsHide, and keep the execFile-shaped exit
errors the availability cache classifies.
Refs #27390
* build(tc): list wsl-exe-process.ts in the web typecheck project
wsl-availability.ts is in the explicit web file list and now imports it.
* refactor(wsl): drop the unused sync availability probe; classify ProcessResult directly
No production code calls the sync isWslAvailable (the IPC and RPC paths
already assert they never do), so delete it and its sync guest-probe twin
instead of porting them. The async probe now reads the --status
ProcessResult itself: an exit code is definitive, a timeout or signal kill
is retryable, ENOENT on spawn is definitive. The WSL2-missing-kernel exit
codes live in one named set. Availability tests now drive the async probe.
* refactor(wsl): shrink wsl-exe-process to a spec builder and stdout reader
With the availability cache classifying ProcessResult itself, nothing reads
the execFile-shaped code/status error any more. wslExeFailure returns a
plain reason string; wslExeStdout throws it; the UNC delete folds it into
its own error. The home probe builds its argv with buildWslExecArgs.
* test(wsl): mock process-host in the empty running-list case from main
* docs(wsl): the availability probe no longer blocks; drop the stale word
* build(ratchet): prune the wsl.exe sync launches this branch removed
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging.
Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan.
Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews.
Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
* perf(startup): stop queueing window creation behind the proxy apply and i18n
Three independent, measured startup wins, all free:
1. Park the initial Chromium proxy apply on `mainProcessState` instead of
awaiting it mid-`initializeReadyFoundation`. `setProxy` still starts at the
identical moment; the default-session request guard (which holds, not
cancels) is what actually fences fetchers on it, so only window creation
stops waiting. Runtime launch still awaits it before the desktop relay and
before every headless-serve fetcher.
2. Run `initializeMainProcessI18nAndMenu` concurrently with
`initializeMainProcessRuntimeLaunch`. Nothing in window creation reads a
translated string or the native menu.
3. Load `emojibase-data` in main through `createRequire` on first use instead
of a static import, keeping 166 KB of JSON off `out/main/index.js` and its
~2 ms parse off every launch. The renderer keeps its eager copy unchanged.
out/main/index.js 7,210,071 -> 7,040,147 bytes. No renderer behaviour changes.
* fix(packaging): ship the emoji shortcode dataset main lazily requires
app.asar carries no node_modules, so main's bare requires resolve only out of
Resources/node_modules. emojibase-data is a devDependency and is not in the
packaged runtime allowlist, so the new createRequire in
deferred-emoji-shortcode-dataset.ts threw MODULE_NOT_FOUND in every packaged
build — breaking sanitizeWorktreeName, and with it workspace creation.
Copy the single 166 KB dataset (not the 49 MB package root) into
Resources/node_modules, and gate every createRequire'd bare specifier in
src/main against the packaged resource plan. verifyPackagedMainRuntimeDeps
cannot catch these: the bundler renames the require binding.
* test(proxy): fail CI when a main-process fetcher escapes the default-session guard
The hoist relies on installElectronProxyRequestGuard(session.defaultSession) holding every app-owned request until the persisted proxy lands. Nothing enforced that every fetcher actually lands on defaultSession. Two source-anchored rules do now: no net.fetch/net.request may name a session/partition, and every non-net .fetch( call site is counted against an allowlist.
* test(proxy): close the shorthand and chained-receiver holes in the fetch call-site audit
The audit caught `net.request({ session: x })` and `ident.fetch(`, but not the two
shapes a real regression is just as likely to take: the `{ url, session }` shorthand
that both `net.request` overloads accept, and a receiver with no bare identifier
(`session.fromPartition(...).fetch(`, `ctx.session.fetch(`). Rule 1 now also matches
the shorthand key; rule 2 scans every `.fetch(` and excludes only a literal
`net`/`globalThis`/`global` receiver. Audited counts are unchanged (2/2/1).
* fix(startup): scope the deferred emoji loader to the projects that own it
TS6307: the composite web project lists src/main/ipc/worktree-logic.ts, which
now imports the deferred dataset loader, and the shared lazy test reached into
src/main from a project that has no src/main files. Add the loader to
tsconfig.tc.web.json and move the cross-project case into a src/main test.
Also close the last two review gaps: gate the runtime-RPC startup failure
dialog (the only launch-phase translateMain reader) on a published i18n
barrier so a concurrent i18n phase cannot leave a non-English user with the
English fallback, and let the fetch call-site audit match `net.fetch (url)`.
* fix(wsl): name an explicit Windows cwd for wsl.exe spawns
Removing the worktree Orca was launched from broke every wsl.exe spawn for
the rest of the session. The WSL command builders passed `cwd: undefined`
meaning "the directory is inside the command" -- but CreateProcessW reads
NULL as "inherit the parent's", and the parent's was a \\wsl.localhost path
Linux had just deleted.
Fixes#16463
* fix(wsl): name the spawn directory at the six remaining wsl.exe sites
The first commit fixed the WSL command builders. Six spawn sites were left
inheriting the process cwd, which is the same deletable `\\wsl.localhost`
worktree: `wsl-availability` (both probes), the WSL filesystem watcher, the
agent-hook relay launch, the UNC delete, and the local worktree filesystem.
`wsl-availability` is the one that matters most, and it turns the bug into a
latching false negative. `isRetryableWslProbeFailure` returns false for ENOENT,
so a spawn that failed only because the inherited cwd was gone is cached as
"WSL is not installed" on the 10-minute definitive TTL with exponential
backoff up to 30 minutes. Git keeps working and Orca reports WSL unavailable --
worse than the bug being fixed.
ENOENT stays non-retryable. It is answer-shaped for the reason it is meant to
be -- wsl.exe is not on PATH -- and naming the directory is what removes the
one cause that was not. Making it retryable would instead re-probe every
non-WSL Windows machine on the short window, and would leave the false ENOENT
in place for the other five sites, which have no cache to correct.
Three of these are also on the `runWslProcess` W3 migration allowlist; this is
the interim until they move, and matches what #17837 does inside the runner.
On Windows, seven production call sites reach listWslDistrosAsync and on a cold
cache each spawned its own `wsl.exe --list --quiet` (5s timeout each): the
wsl:listDistros IPC behind the renderer capability read, the host.wsl.listDistros
RPC, the skill-install IPC, CLI registration reconciliation, the hook relay deps,
the kimi runtime home, plus relay preflight in the relay process. Concurrent
callers in one process now share one spawn.
Joining happens ahead of the negative cache, which also fixes a stranding bug: a
synchronous listWslDistros() landing an empty result mid-probe arms the 15s retry
window, and later async callers read that [] even though the pending probe is
about to see a distro that just finished provisioning. The non-empty-cache
short-circuit sits ahead of the join so a list already found synchronously is
still returned without waiting; that is main's existing behaviour preserved, not
a new fast path.
The shared promise cannot reject -- `catch` sits ahead of the stored promise, so
joiners get the same fail-safe [] the old per-caller catch returned -- and the
slot is cleared on settle, by the owning probe only.
wsl-directory-probe-command.ts is a verbatim move of the guest directory-probe
marker protocol and its parser out of wsl.ts, for oxlint max-lines headroom:
inlining it back makes wsl.ts 306 effective lines against a cap of 300. It takes
WslUncPathInfo from ../shared/wsl-paths -- the actual type of every value passed
at both call sites -- so it does not import from wsl.ts. _resetWslCachesForTests
and _setWslCachesForTests now share one resetWslDistroListState() instead of
repeating the same six assignments.
Per-platform delta:
- WSL on Windows: fewer wsl.exe spawns under startup fan-out, and a distro
provisioned while a probe is pending is no longer hidden for the retry window.
- Native Windows without WSL: no behavioural change. The empty/failure retry
windows, their backoff and the cache sequence guard are unchanged; N concurrent
callers now cost one failed spawn instead of N.
- macOS, Linux, folder workspaces: no change. Both new early returns are
unreachable off win32.
- SSH remote: no change for macOS/Linux hosts; a remote Windows host gets the
Windows behaviour in its own process. No wire change -- host.wsl.listDistros
keeps its string[] shape and its [] failure value.
- Relay: same single-flight inside the relay process. It stays per-process; the
relay and main process still probe independently, as before.
Costs: a never-settling execFileUtf8 now pins the shared slot for the process
lifetime rather than only its own callers -- transient-to-permanent, not identical
exposure. And a joiner inherits the first probe's failure instead of making an
independent attempt.
* fix(worktrees): preserve user workspace names across branch changes
* test(worktrees): cover pinned rename metadata
* fix(workspaces): address display-name review edge cases
* fix(workspaces): keep automatic names fresh across refreshes
* fix(workspaces): preserve legacy CLI labels
* fix(workspaces): preserve display-name provenance across hosts
* fix(workspaces): honor legacy display-name provenance
* fix(workspaces): fence display-name refresh races
* fix(workspaces): accept peer renames from provenance-less hosts
The old-host preserve fence kept a pinned local label on every refresh,
which also suppressed a legitimate rename another client persisted
through the same host until app restart. Narrow it to labels the host
re-derived itself (branch short name, or path basename when detached);
any other changed label in a mode-less response is explicit meta a peer
wrote there. Stale prior-label responses stay covered by the downstream
staleness fence, in-flight writes by the pending fence.
* refactor(workspaces): unify display-name pin derivation
Three call sites (renderer optimistic update, local IPC updateMeta
handler, remote worktree.set handler) each restated the same formula;
a future edit to one would silently skew provenance between paths.
* fix(wsl): scan sessions only in running distros
* test(ai-vault): pin WSL discovery platform
* fix(wsl): suspend transcript watchers for stopped distros
* test(wsl): pin transcript scan gate platform
* fix(wsl): settle stopped transcript loading
* fix(wsl): add last-known-good fallback and backoff to running-distro discovery
listRunningWslDistrosAsync failed closed on any probe error (timeout, ENOENT,
wsl.exe hiccup), indistinguishable from "no distros running". A 2s poll
(wsl-transcript-running-observer.ts) calls it indefinitely while any WSL
transcript tab is open, so a persistently broken wsl.exe silently made every
WSL session vanish app-wide with no way to tell "discovery broken" from
"distro stopped", and re-spawned wsl.exe every 2s forever.
Extract a dedicated cache/backoff module (wsl-running-distro-cache.ts,
mirroring the sibling machinery already in wsl.ts for the full distro list)
so a probe failure falls back to the last-known-good running-distro list and
backs off further probes, while a genuine empty result (no distros running)
stays authoritative. Add a consumer-level test simulating a sustained wsl.exe
outage across a live transcript-watcher polling session, asserting the
observer keeps reporting "running" and that real wsl.exe spawns stay bounded.
* fix(build): list the new WSL cache module in the web typecheck project
config/tsconfig.tc.web.json enumerates its files explicitly, so a new
module imported by wsl.ts fails the full typecheck with TS6307 until it
is listed. pnpm tc:node passes without it, which is how this got missed.
src/main/wsl.ts(13,8): error TS6307: File 'src/main/wsl-running-distro-cache.ts'
is not listed within the file list of project 'config/tsconfig.tc.web.json'.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
`src/preload/api-types.ts` was 3,752 raw lines (3,533 counted, 11.8x the
300-line budget) behind an `eslint-disable max-lines`. Almost all of it was a
single `PreloadApi` object type whose ~83 namespace properties were declared
inline, so any IPC surface change meant editing one 2,600-line type.
Give each namespace a named type in its own module under `src/preload/api/`
(`pty-api.ts`, `filesystem-api.ts`, `github-pull-request-api.ts`, ...) and
recompose `PreloadApi` from those names. `api-types.ts` keeps the `declare
global` Window augmentation and re-exports every moved name, so all 52 import
sites are untouched.
Two shapes needed care to stay type-identical rather than merely compatible:
- Three keys (`gh`, `git`, `ui`) are composed from two modules each. A plain
intersection is NOT identical to the original flat object literal, so those
use a `Merged<T>` mapped type; a negative control confirmed that dropping it
fails the parity assertion.
- Keys whose module groups several namespaces use indexed access
(`fs: FilesystemApi['fs']`) to preserve exact identity and source order.
`config/tsconfig.web.json` and `tsconfig.tc.web.json` enumerate files by path,
so they need `src/preload/api/**/*` alongside the existing `api-types.ts` seed
or the web projects fail TS6307.
Verified by exact type identity, not assignability: 41 assertions of the form
`Equals<Now.X, Before.X>` against a frozen pre-split snapshot, covering every
exported name, plus a per-key pass over all 83 `PreloadApi` keys. All three
projects typecheck clean with those assertions active.
Verification note: these tsconfigs are `composite: true`, and `tsc --noEmit`
will reuse a stale `.tsbuildinfo` and report clean for a state that genuinely
fails. Every result above was produced after deleting the buildinfo, including
a negative control confirming the gate still fails on deliberate drift.
Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
* perf(windows): stop the capability poll respawning blocking wsl.exe probes
#11295 added a 30s renderer interval to `useWindowsTerminalCapabilities` whose
early-return only fires when WSL is available with at least one distro, so on the
common Windows host (no WSL) it re-ran a full capability read forever. Each read
IPCs four probes whose main-process handlers were synchronous `execFileSync` calls
to wsl.exe/pwsh.exe, blocking the Electron main event loop for up to 5s a time.
The un-latching intent is kept: a host that answers "no WSL" is still re-checked,
now on an exponential backoff (30s, +60s, +120s) that parks once the answer stops
moving, re-arms on window focus, is shared by all consumers of an owner key, and
stops entirely when the last consumer unmounts. The wsl/pwsh IPC handlers now use
async twins that share the existing caches and back off identically.
* fix(windows): classify async wsl/pwsh probe failures with the execFile error shape
The async twins feed `execFile` callback errors into classifiers written for
`execFileSync`: a non-zero exit lands on `error.code` as a number rather than
`error.status`, and a timeout is a SIGTERM kill rather than ETIMEDOUT.
So a Windows host without WSL (wsl.exe ships in System32, so it exits non-zero
instead of ENOENT) was cached as retryable, shrinking the shared window from
10min to 45s and making the still-sync callers re-pay their blocking spawn ~13x
more often; and a pwsh cold start past 5s cached "pwsh missing" for 30s,
demoting the user's PowerShell 7 preference — the exact case the ETIMEDOUT
branch exists to prevent.
Also drops a literal NUL byte from the new re-probe module's signature
separator, which made the file binary to git, and seeds `lastProbeAt` at
registration so focus churn right after mount cannot defer the first re-probe
indefinitely.
Co-authored-by: Orca <help@stably.ai>
* perf(windows): route relay host-capability probes through the async wsl/pwsh twins
A paired web/mobile client resolves `useWindowsTerminalCapabilities` to a local
target (TabBar's `isWebClient` gate, and `useSettingsNavigationMetadata` forces
`{kind:'local'}`), so the new re-probe arms there too. But `window.api.wsl/pwsh`
on a web client is not the ipc/app.ts channel — it is `host.wsl.*`/`host.pwsh.*`
over the runtime RPC, which still ran the sync probes and blocked the desktop
main event loop on `execFileSync('wsl.exe' | 'pwsh.exe')` for up to 5s per call.
Switch those handlers and the relay preflight capability probe to the async
twins added here; they share the same caches, dedupe and backoff, so remote
callers see no behavior change.
* fix(windows): harden async capability reprobes
* fix(windows): dedupe PowerShell shell probes
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>