* fix(layout): a workspace only main held shows main's tab on the window's first visit (dark, layout-owner build only)
* fix(layout): a live terminal no pane shows goes through main, which names the tab it holds; a pane close main has no pane for closes here (dark, layout-owner build only)
* test: no type assertions in the CLI-first tests
* refactor: the switch-off build keeps today's live-terminal mint as it was
* review: frame sync under the line limit; the first-visit test names exactly main's tabs
* fix(layout): adopt takes the recorded pane only while it is unbound
* fix(layout): a refused live-terminal adoption is declined, not counted as shown
* 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>
* fix(terminal): a file dropped on a pane makes that pane active
Dropping a file on an inactive pane's title strip pasted the path but left the
other pane selected, because the drop only focused the terminal. Activate the
pane instead (which also focuses it). Tracked in STA-9893.
* test(e2e): give the title-strip drop the source owner real drags carry
* test(e2e): preserve pane selection when checking title-strip drops
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)
* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's
* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows
* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear
* chore(sidebar): merge origin/main; locality helper moved to shared
* feat(sidebar): sort Recent and Smart by host stamps behind the sidebar-inputs switch
With ORCA_SIDEBAR_INPUTS=main, the window, main and the phone order Recent by
lastActivityAt and Smart by smartTier then smartAt (Needs you, Done, Working,
then the rest by Recent). The window holds its displayed order while the
pointer is over the list or a drag runs. Switch off: today's sort path.
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: restore files the merge commit's formatter rewrote
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(sidebar): stamp-order review fixes behind the sidebar-inputs switch
The window writes no Recent stamps with the switch on (terminal start and
exit, unread marks, comment edits), the hover hold covers only Recent and
Smart and ends with a drag, main follows only explicit metadata saves, and
the phone picks its sort from the unfiltered rows.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): folder writers and a Manual drag under the stamp switch
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): name the stamp type in the ps summaries test
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Require a full hover delay before each tab preview
Tab previews opened almost immediately after a previous preview had been shown, so sweeping across the tab strip caused a flurry of popups. Raise the tab tooltip delay to 800 ms and drop the warm-start chain: the shared open-state context, the 300 ms skip delay, and the slide animation that carried position between cards. Every tab now waits its full dwell time, and keyboard focus still shows the preview immediately.
* Test that tab hover cards wait a full dwell on every hover
The tab hover card no longer slides instantly between neighboring tabs. Each hover now waits for the full delay before showing a preview. The e2e test still expected instant sliding, so it was rewritten to match.
- Remove the frame-recording helpers and the slide and reversal assertions.
- Check that no card appears before the dwell, and that the card appears after it.
- Check that leaving a tab cancels a pending preview, and that time spent on other tabs does not count toward the dwell.
- Check that re-entering the same tab requires a fresh dwell.
Removes the re-export lines in 22 src/main/runtime modules (rpc schemas, skill surface, worker
identity, terminal viewport, teardown, git, e2ee crypto and more); importers and test mocks name
the real modules.
Moves legacy-relay-bridge/ plus the relay-only census and endpoint files into ssh/legacy-relay/,
so the 2027-01-15 retirement deletes one folder. Live install, exec and framing modules that only
carried the relay name become remote-*. git mv plus import, path-literal, routing and workflow
path-filter rewrites only; no remote file name or path changes.
* fix(layout): an SSH pane's terminal binds in its own session partition (dark, layout-owner build only)
Main keeps its own layout over a window save, so a binding only a save carries is lost. A window
or runtime spawn of an SSH pane bound in the local partition; it now binds where the pane's layout
lives.
(cherry picked from commit beb2275b45)
* test(layout): the SSH binding test uses the shared spawn commit dependencies
(cherry picked from commit 672b641897)
* fix(layout): main moves a tab between a local and an SSH workspace itself (dark, layout-owner build only)
moveTabToWorkspace across two partitions was refused as other_partition and left to the window's
save. Main now reads both partitions, moves the tabs as within one, and writes both in one durable
mutation.
(cherry picked from commit ee27a0bb07)
* chore(layout): one binding write for the partition choice; shipped builds keep their exact replaced-binding read
* fix(layout): window saves keep main's layout and give it only the window's view and facts (dark, layout-owner build only)
windowViewSaveForMain/windowViewPatchForMain replace windowSaveForMain: main's own layout joined
with the window's view, facts and carried fields, written only into the records that change. A
paired server's partition passes through. A visited workspace main holds keeps its terminal row
(D1). The P10 pending case is on.
(cherry picked from commit 0c2fc15974)
* fix(layout): the save divergence detector becomes a report-only drift check behind its log (dark, layout-owner build only)
It no longer wraps the write: it compares the window's raw save with main's layout model, and
only when ORCA_LAYOUT_SAVE_DIVERGENCE_LOG is set.
(cherry picked from commit 6bc1e5556f)
* fix(layout): tab reconcile and terminal reconnect stop writing tab structure (dark, layout-owner build only)
Main's frames draw the tab bar and rows: reconcile still returns its counts but writes nothing,
and reconnect still advertises restored terminals but leaves the rows to main.
(cherry picked from commit ef8eb625ca)
* fix(layout): a layout command main cannot apply is a logged refusal, not the window's own change (dark, layout-owner build only)
other_partition, a thrown call, and host_not_owned for a workspace that is not a paired server's
are refused and logged: a window save no longer carries the change, so drawing it would last only
until the next frame. A twin main may route to a paired server keeps today's path, as does the
first terminal's host_not_owned fallback.
(cherry picked from commit 2c513b2d10)
* fix(layout): the window's refusal code type keeps main's host refusals
(cherry picked from commit 36d46bdcc2)
* fix(layout): the quit checkpoint records only the quit capture's resume records in main (dark, layout-owner build only)
Main keeps its own resume records over a window save, so the quit capture's changes, which reach
main as async commands, could miss the quit. The capture now sends only what it changed with the
synchronous stage, and main writes them through the record-only sleep command before staging; a
wake main recorded that the window had not seen stays.
(cherry picked from commit 2a0f433c34)
* test(layout): the quit capture test builds a typed resume record
(cherry picked from commit d92d1246ee)
* chore(layout): one layout write per save handler
* chore(layout): shipped builds keep their exact quit capture
* fix(layout): the drift check compares structure only, not records main writes itself (dark, layout-owner build only)
Pane bindings and resume records, default-tab marks, closed-tab records and terminal row owners
are main's own writes; a save that has not caught up with them asks main for nothing, so they are
no longer reported.
(cherry picked from commit 93e8b2627c)
* test(layout): relaunch first-terminal cases, and the side-by-side resume case moves its tab the way the window does
(cherry picked from commit ab381332b1)
* test(layout): the relaunch case also checks a reopen that cleared read-only
(cherry picked from commit bbf3d040c8)
* docs(layout): the drift check names the record kinds it ignores
* revert(layout): drop the SSH binding partition change; SSH terminal starts are refused before any binding is saved
getProvider refuses every non-local host, so neither spawn path reaches persistPtyBinding for an
SSH pane, with the switch on or off.
* test: run A2's two real-SQLite session tests in the Node runtime project
The folder is the cloud relay that reaches phones; under its old name it read as the legacy SSH
relay. git mv plus import and path-literal rewrites only.
* feat(sidebar): sort stamps reconcile stored tiers at startup against live hook rows and terminals (dark)
* test(sidebar): a restart clears an unbacked local tier and keeps an SSH host's
* fix(sidebar): run the sort tier reconcile once hook rows and PTYs settle, seeding hydrated rows
* fix(sidebar): clear a stored tier only on proof its terminals died; a rest seed adds no clear
* chore(sidebar): merge origin/main; locality helper moved to shared
convertThenRetire was never exported, so the spec failed to load; convertAndRetain is what the other upgraded-profile cells use. Also narrows the deployment union so the helper typechecks.
* feat(sidebar): the owning host stamps sort keys on its workspace records (dark)
* fix(sidebar): sort stamps title phase switch is exhaustive
* fix(sidebar): sort stamps reconcile seeded tiers, respect host and WSL move fence, follow PTY swaps
* fix(sidebar): one clock read per sort stamp event
* fix(sidebar): a first idle title is not Done; the WSL fence spares SSH records
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.
The nudge request parsed and stored a rollout block, but no build ever read a flag from it.
Drops the module, its call site, its tests, and the e2e env that fed it.
* feat(layout): main records a workspace's default-tabs mark from the window (dark, layout-owner build only)
* fix(layout): record the default-tabs mark after the tab creates it covers (dark, layout-owner build only)
* test(e2e): strict default-tabs mark and exact tab count across a crash (dark, layout-owner build only)
* feat(sidebar): main produces the local workspace rows, checked against the window (dark)
* fix(sidebar): workspace rows invalidate the listing only on metadata saves; prove it on the real cache
* fix(sidebar): workspace rows merge the listing fresh instead of invalidating the shared cache
* chore(sidebar): workspace rows comments say what the fresh merge reads
A bare exit returns the shell's last status. Under the e2e profile on macOS,
/etc/zshrc leaves $? at 1, so the pane is kept behind the exited card on
purpose and the layout check never settles.
* 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
* fix(ssh): a tab closed while a managed host is disconnected stays closed after Connect
#30130 made a user's Disconnect mark the host's managed server manually
disconnected, so the desktop now answers its calls itself with
runtime_manually_disconnected. The tab close path read that as the host's
answer and dropped the close instead of holding it for replay, so the tab
came back on reconnect. Treat it as an undelivered close, and route the
Disconnect sources to the managed-orcad Docker lane.
* test(e2e): close the disconnected host's tab the way a user does
The journey called the store's bare closeTab, which sends only terminal.close
and holds nothing; it passed only because the disconnected host used to be
redialed in the background, which #30130 stopped. Click the tab's X instead,
the path that holds and replays an undelivered close.
* test(e2e): the shared-home spec tells a host's own server from a PID that only collides
Each Docker host has its own PID namespace, so the PID host A records in the
login-home slot can belong to an unrelated live process on host B; kill -0
alone then read it as B running from A's slot. Count a recorded PID only when
its process runs from that slot.
* fix(layout): main's Loader repairs a terminal bound in two panes of one tab (dark, layout-owner build only)
* fix(layout): a window save stores main's repaired pane tree, so the duplicate-terminal repair survives the save (dark)
* test(layout): the SSH duplicate-terminal case builds an SSH-hosted tab
* feat(sidebar): main serves workspace icons as small data URLs for the phone, unused until the phone switch
* fix(sidebar): settle review findings in the icons RPC and the manual-redirect port fix
* docs(sidebar): scope the icon fetch-slot bound to a lone read
* fix(sidebar): gate the icons RPC behind the sidebar-inputs switch; prove re-encoding end to end
* fix(sidebar): restore two files the merge reformatted; one pure annotation for the icons params
* fix(sidebar): restore two files the pre-commit formatter rewrote during the merge
* fix(sidebar): icons fetch through their own manual-redirect request; shared desktop HTTP client back to main
* fix(sidebar): settle review findings in the icons manual-redirect request
* fix(sidebar): end a bodyless icon response at once; clearer fetchRepoIcon doc
* fix(native-chat): a Codex chat is Working only while Codex holds the send
Working read 'a send still pending delivery', so any echo or idle signal
that never arrived (a hook-blocked prompt, an answer naming no turn, idle
before a timed-out send was written) kept the chat Working, Stop up, the
queue frozen and /compact refused until the agent exited.
Codex now reports which sends it still holds: unanswered, answered into a
turn that has not ended, or answered into none while a status read after
that answer says the thread runs. Replies and statuses are ordered as they
are read, so a stale status cannot release a newer send. The host reads
that set (plus the short window while it hands a send over) for Working,
publishes a send the child stopped holding as in doubt on every page and
frame so older clients agree, and republishes when the set changes with no
row. The Codex idle latch and its release are removed.
* fix(native-chat): name the handover journal type; reverse without mutating
* test(native-chat): a lost Codex answer while idle is no longer the chat's work; run the held-sends test with the database runtime
* fix(native-chat): bound work updates and own command handovers
* refactor(native-chat): reuse session delivery plumbing
* test(native-chat): allow separate ownership restatements
* test(wire): resolve the release tab predicate after relocation
* test(native-chat): reuse existing promise and empty-batch primitives
* feat(sidebar): ui.setCollapsed changes one section's collapse, ordered per writer
* fix(sidebar): ui.setCollapsed accepts ui.set's callers; require a fresh writer per counter
* feat(sidebar): register ui.setCollapsed only with ORCA_SIDEBAR_INPUTS=main
* test(sidebar): hidden-app check of ui.setCollapsed in both switch builds
* test(sidebar): off-census covers every gated method; e2e skips before launching
* test(sidebar): type the gated method-name set as strings
* test(sidebar): type the collapse test's dispatch context
* fix(terminal): resume a relaunched workspace's sleeping agents only when shown
After a relaunch every terminal tab of the opened workspace mounts, and each
pane's connect resumed its agent from the saved sleeping record whether or
not the pane was visible, so all of them restarted at once.
A hidden pane with a sleeping record now arms the same reveal wake a
hibernated pane uses and spawns nothing. A hidden local reattach first asks
the host whether the session is still alive; only an authoritative "exited"
holds it, and the reveal re-runs the same reattach. Held panes count as
owning their record so the activation sweep does not relaunch them. Targeted
background mounts (phone, CLI, orchestration) still start their tabs'
agents immediately, and the phone wake still wakes held panes.
* fix(terminal): keep a held resume through an exit its wake did not start
* feat(sidebar): main keeps a registry of workspace creations in progress, checked against the window in dev and test
* fix(sidebar): open the runtime creation entry after the create's own repo resolve
* refactor(sidebar): one creation handle opened after the create's repo lookup; move create startup resolution to its own module
* feat(sidebar): put the creation registry behind the ORCA_SIDEBAR_INPUTS dark build switch
* test(sidebar): cover the creation registry with the switch off and the gated creation id
* refactor(terminal): let account-switch pane restarts name their agent
The queued account-switch restart only knew how to relaunch Codex. The
queue now records which agent each pane restarts as, and the resume
startup builder takes that agent, so the same mounted and detached
restart paths can move Claude sessions too. Codex behaviour is unchanged.
* feat(accounts): offer to move running Claude sessions after an account switch
Switching the selected Claude account only reached Claudes started later,
so users had to /exit and resume every running session by hand. After a
switch from the status bar or Settings, Orca now finds local terminal
panes running a resumable Claude session in the switched lane and shows
one toast offering to move them. Confirming restarts idle sessions with
claude --resume <id> in place; sessions mid-reply move when their turn
ends, and are dropped if the pane closes or the user switches again.
* fix(accounts): wait out permission prompts and keep hand-started Claude args
A Claude session at a permission prompt is mid-turn, so a move now waits
for it like a session that is replying. Resuming a Claude pane with no
recorded launch config no longer adds the user's default agent arguments
or environment, so a hand-started claude is not resumed with a
permission bypass. Codex keeps its existing fallback.
* feat(accounts): track which Claude account each terminal pane runs on
Orca had no record of the account a running Claude used, so an account
switch could only assume every session was on the old one. A renderer
tracker now attributes sessions that start after launch to their lane's
selected account and records the account Orca restarts a session on.
Sessions it cannot attribute stay unknown and keep today's behaviour.
* feat(accounts): ask in a dialog before restarting Claude sessions on a switch
Replaces the move toast. After a Claude account switch, a dialog names the
new account and how many sessions are still on the old one, with Keep as
is and Restart, plus Remember my choice. Closing or Escape keeps sessions.
The remembered choice lives in Settings > Accounts as When you switch
accounts while Claude is running: Ask, Always restart, Never restart.
* refactor(accounts): read Claude session accounts from their transcripts and use one restart queue
Which account a running Claude uses now comes from the transcript path its
hooks already report (<CLAUDE_CONFIG_DIR>/projects/...), so the renderer
tracker and its guesses are gone and unknown sessions are never counted.
Restarts queue straight into the store's pending map through one
queuePaneAccountRestarts action; the pane effect and the detached
scheduler hold a Claude restart while its turn runs and skip it when the
session can no longer be resumed. Codex behaviour is unchanged.
* style(accounts): left-align Keep as is in the Claude account switch dialog
Co-Authored-By: Claude <noreply@anthropic.com>
* copy(accounts): say busy rather than replying, and drop the menu's switching note
The deferred restart waits on any working or blocked turn, not only a reply. The
switch dialog now covers running sessions, so the menu footer note is redundant.
* copy(accounts): say busy sessions wait for idle before restarting
* test(accounts): type the switch-session store stub without broad object
* fix(accounts): keep a queued Claude restart when Codex restart notices are cleared
Codex notice cleanup shares the pending-restart map with Claude switch restarts;
it now drops only Codex entries.
* fix(accounts): wait out Claude questions and keep accepted restarts across switches
- A Claude permission prompt or question reports 'waiting'; a queued restart now
waits for 'done' instead of only 'working'/'blocked', so it no longer drops them.
- A later switch keeps accepted restarts still waiting on a turn (they relaunch on
whatever is selected) and drops only those whose session is already on the new
account, scoped to the switch's lane. The blanket replaceQueued option is gone.
- A server account switch returns before touching the local dialog or queue.
- Pane restart state reads through one selector to stay inside the subscription
budget, and the focus-read baseline follows the restart-startup rename.
* test(accounts): feed the account-restart e2e fixture the shared restart queue
* feat(accounts): make the switch dialog's checkbox one-way, like native chat's resume prompt
"Don't ask again (restart automatically)" turns on automatic restarts however the
dialog is left, including Escape; Settings → AI Provider Accounts turns it off.
* feat(accounts): choose which Claude sessions restart after an account switch
With more than one session, the switch dialog lists them by workspace using the
native-chat resume tree rows, all ticked; Restart covers only the ticked ones.
* Merge origin/main into claude-sign-in and rename the host-twin teardown's restart queue
* fix(accounts): localize the session tree's agent label
* fix(accounts): restart a pane on the PTY it has when the dialog is answered
A reattach can rebind a pane while the dialog is open and a closed pane has no
PTY, so the ids it listed may name nothing.
* fix(accounts): read a layout's pane PTYs optionally
* fix(accounts): offer to move sessions of a saved account that runs on System default
A saved account with no login of its own runs on System default, so its sessions'
transcripts read as System default. The switch now compares where launches run
(runsOnSystemDefault on the account summary) and asks nothing when that is unchanged.
* refactor(accounts): give the Claude account switch one boundary and key the whole prompt
The post-switch coordinator now takes the before/after account snapshots and
derives launch folders and labels itself, so the status bar and Settings stop
assembling the same request. The status bar's settings projection reuses the
host's row, which keeps runsOnSystemDefault: a covered account switched from
the status bar now finds its System default sessions. Never restart returns
before any session scan. The outcome collapses to show-confirmation | handled.
The dialog keys one component that owns the whole Dialog with a plain boolean.
* refactor(terminal): subscribe mounted panes to this tab's ready account restarts
Replaces claudeTurnStatus, the whole agent-status map passed through the
controller as a re-run signal, with a shallow selector of the PTY ids whose
queued restart can run now. Other tabs' status updates no longer re-render or
re-run every mounted pane once a Claude restart is queued. The effect re-checks
readiness against the current store before consuming. The detached Claude test
now runs through the scheduler's real subscription instead of manual sweeps.
* docs(accounts): shorten Claude account-move comments to their whys
* test(terminal): give the account-restart e2e a callable store and a queued restart
The mounted pane now reads its ready restarts from the store through useShallow.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(sidebar): main produces per-workspace PR facts, checked against the window in dev and test
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(sidebar): share the PR cache key candidates instead of splitting an import
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
On a host with no window, chat tabs live only in the runtime's session snapshot. A forced rebuild from the saved session (closing a saved editor tab, an expired SSH reattach) dropped them, and every terminal-only group merge removed them from the tab order.
Refs #29968
Co-authored-by: Claude <noreply@anthropic.com>
Naming a chat runs a one-shot agent CLI request in the chat's folder, and the CLI saves it beside the user's chats. Session History listed it, and opening it from a phone resumed the naming request instead of the chat.
Fixes#29973
Co-authored-by: Claude <noreply@anthropic.com>
* feat(sidebar): main keeps one live set of paired-server rows, checked against the window in dev and test
* fix(sidebar): settle review findings; main also publishes paired-server host facts
* feat(sidebar): main publishes desktop focus, pins, project and host order, and SSH/WSL host state, checked against the window in dev and test
* fix(sidebar): read pins through the host-filtered metadata helper; name pins as workspaces
* fix(mobile): edit queued messages in place
* fix(mobile): use the platform input font size for queued edits
* fix(mobile): handle missing host support while probing queued edits
* Keep phone queued edits owned through save settlement
* Keep phone queued edits local to mounted chat
* fix(mobile): retire queued editor when its card disappears
* fix(mobile): retain queued edits while reconnecting
* test(mobile): await reconnect timer inside act callback
* fix(mobile): publish queued edit transport after commit
* test(mobile): await queued Edit inside act callbacks
* fix(mobile): hand a stale queued Save to the composer and reopen on the newer text
* fix(mobile): close an unchanged queued Save locally and keep stale typing in its own chat
* fix(mobile): explain disconnected queued edit refusal
* fix(mobile): combine queued session contract imports
Typed hosts like github.io, vercel.app, or readthedocs.io were treated as bare public suffixes and sent to search. They are registrable domains, so they now navigate directly.
- Suffix check now uses ICANN-only rules. The private-section list made `foo.github.io` a domain, but it also let the bare suffix pass, so the classifier now treats the hosting-provider suffix itself as a navigable domain.
- Website navigation now outranks a name match only when the query equals a worktree, tab, or page name in full. Partial matches such as "project.dev server" no longer block the "Go to" row.
- Browser tab creation from a URL forwards the target group as `clientTargetGroupId`, so older hosts that publish the page before the create response still place the tab in the invoking split.
* feat(sidebar): main produces per-workspace activity facts in shadow mode
* perf(sidebar): agent pings keep last-read surfaces; shadow counts comparisons
* fix(sidebar): settle review findings in the activity-facts producer and shadow check
* fix(sidebar): refresh on inventory verdict clears; resync agents only when tabs move
* fix(sidebar): scope the shadow comparison by each row's owner, not the focused host
* refactor(sidebar): name the shadow scope after what it keeps
* fix(sidebar): classify sidebarInputs as host-owned in the operation ownership census
Partially reverts #27540 and the plain-http part of #27552, which broke web
pairing on upgrade for 1.4.227 users who open Orca at a LAN or Tailscale address:
- The host no longer refuses a first pairing from a plain-http page on another
machine (insecure_origin, ORCA_ALLOW_INSECURE_WEB_PAIRING).
- Such a page keeps its pairing code in local storage and signs in with it, as
in 1.4.227, so reloads, new tabs and bookmarks stay paired; no notice.
- Session-only browser keys (pairing.device-key-ephemeral.v1, the idle sweep)
stay for browsers without Ed25519.
- A web client whose credentials are refused shows the host's reason instead
of an empty workspace and "Session restore failed".
* fix(native-chat): retire acknowledged send echoes after catch-up
* test: follow phone lint rules in reconnect regression
* test: format reconnect message expectation
* Keep current chat requests independent of loaded history
* Read current chat requests in bounded cursor pieces
* Bound desktop catch-up after independent request support
* Fix current request subscription static checks
* Match generated request schema import ordering
* fix(native-chat): correct current request type boundaries
* Accept readonly client capability fixtures
* Exercise current request reads in wire compatibility inventory
* Align current request fixtures with provider commit and RPC surface
* test(native-chat): include current request browser capability
* Keep the mobile chat session hook within its line budget
* Format mobile current request readers
* fix(native-chat): reopen once for a current-request version whose read keeps failing
A deterministic read failure reopened the subscription, whose first frame re-read the same version and reset the backoff, so the pane resubscribed every 750 ms forever.
* test(native-chat): type the current-request snapshot fixture
* test(native-chat): wait for the hydrated open before driving the subscription
* fix(layout): E1 follow-ups: closed waiting splits, paired-server tabs by host, first-tab e2e wait
- A pane main closes before its tab mounts drops that tab's waiting split for it.
- Mounted tabs skip main's pane tree for paired-server workspaces by host kind, as frames do.
- ensureTerminalVisible waits for the activation tab before creating one.
- Main-asked splits write their pane through admitRuntimePane; startInAdmittedPane loses its single-caller `shown` option.
* refactor(layout): paired-server check in its own module, out of the frame sync
* fix(ssh): allow a host's orca CLI to control Orca by default, and stop resetting saved choices
An absent allowRemoteCliControl now means on; only an explicit false turns the
desktop browser bridge and relay-era orchestration commands off. Removes the
one-time reset of saved true values, the consent stamp writes and the reset
notice, which would have silently turned off hosts upgraded from stable.
* fix(ssh): keep the CLI control choice when adding a host from the sidebar
* fix(ssh): read an older client's omitted CLI control switch as off over RPC
* fix(ssh): check the managed fence before routing settings in browser bridge candidates
* fix(native-chat): retire acknowledged send echoes after catch-up
* test: follow phone lint rules in reconnect regression
* test: format reconnect message expectation
* Keep current chat requests independent of loaded history
* Read current chat requests in bounded cursor pieces
* Fix current request subscription static checks
* Match generated request schema import ordering
* fix(native-chat): correct current request type boundaries
* Exercise current request reads in wire compatibility inventory
* Align current request fixtures with provider commit and RPC surface
* test(native-chat): include current request browser capability
* Keep the mobile chat session hook within its line budget
* Format mobile current request readers
* fix(native-chat): reopen once for a current-request version whose read keeps failing
A deterministic read failure reopened the subscription, whose first frame re-read the same version and reset the backoff, so the pane resubscribed every 750 ms forever.
* test(native-chat): type the current-request snapshot fixture
* test(native-chat): wait for the hydrated open before driving the subscription
* Run automated workspace setup in a dedicated tab
* Preserve native-chat setup route coverage after placement retirement
* Preserve Setup tab design and independent review evidence
* Classify activation test fixtures consistently for routing checks
* Sync runtime English catalog after removing setup preferences
* Update listener savings model after retiring setup action