Commit Graph
15521 Commits
Author SHA1 Message Date
efcf8f0888 fix(mobile): keep terminal scroll gestures from becoming taps (#31445)
Fixes #25457
Fixes #25458

Supersedes community PR #25432, community PR #25435. Original contributor credit is preserved below.

Co-authored-by: Prateek Rungta <prungta2@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 01:16:49 -07:00
NeilandWooseong Kim 251bbf7bb0 fix(mobile): mount sessions with saved preferences (#31443)
Fixes #30517

Supersedes community PR #30518. Original contributor credit is preserved below.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 01:16:35 -07:00
NeilandiBakuman 11a1cc4d8b fix(mobile): load folder workspace agent presets on the correct host (#31442)
Fixes #16215

Supersedes community PR #16216. Original contributor credit is preserved below.

Co-authored-by: iBakuman <67883044+iBakuman@users.noreply.github.com>
2026-10-21 01:16:19 -07:00
NeilandWooseong Kim 7a2057e65c fix(mobile): retire old touch gestures before snapshot replay (#31437)
Fixes #30300

Supersedes community PR #30312. Original contributor credit is preserved below.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 01:16:03 -07:00
NeilandWooseong Kim 21322a56aa fix(mobile): cancel stale shortcut list drags (#31436)
Fixes #30302

Supersedes community PR #30314. Original contributor credit is preserved below.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 01:15:49 -07:00
NeilandWooseong Kim ea401d7164 fix(mobile): restore dictation after iOS audio interruptions (#31435)
Fixes #27498

Supersedes community PR #30297. Original contributor credit is preserved below.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 01:15:29 -07:00
NeilandWooseong Kim 0dbc6aa3c4 fix(mobile): select Unicode words at rendered cell boundaries (#31434)
Fixes #30301

Supersedes community PR #30325. Original contributor credit is preserved below.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 01:15:12 -07:00
Neil e2bcd6e09c Reuse Windows server native prebuilds across app version bumps (#31450)
* Measure Windows ARM qualification worker concurrency

* Reuse Windows server prebuilds across app version bumps

* Match native cache measurement to adhoc compiler settings

* Keep measured cache fix and remove temporary comparison modes
2026-10-21 01:10:17 -07:00
Neil 7f21cc91b8 fix(codex): Codex copy from a remote pane reaches the viewer clipboard (#31453)
* fix(codex): copy from a paired client's or WSL Codex reaches the viewer's clipboard

* test: type the Claude launch fixture
2026-10-21 00:58:03 -07:00
Brennan Benson 920a033928 fix(native-chat): typing fast after focus moves keeps keys in order (#31203)
* fix(native-chat): typing fast after focus moves keeps keys in order

Typing a burst right after focus left the composer (Move Tab to Split, a
paired chat, a click on the transcript) could scramble the first letters:
"Reply exactly" came out as "Rply exactly...e".

The first key was routed into the composer programmatically: focus moved
at once, but the caret was set a frame later from a position computed
before the key. The next keys landed natively, then that late caret write
pulled the caret back. Quote "Add to chat" and picker completion had the
same deferred caret write.

Each programmatic write now puts the text and caret into the editor in one
synchronous step, read from the editor's live value, then focuses it. No
write happens while an IME composition owns the field.

* fix(native-chat): retain saved chips after synchronous composer edits
2026-10-21 00:42:44 -07:00
NeilandWooseong Kim 8b9ba72c3b fix(mobile): retire stale terminal stream fits and callbacks (#31432)
Retire old terminal stream callbacks and delayed fits across reconnects, unsubscribe, terminal replacement, and synchronous termination. Preserve fresh output and the latest accepted fit.

Fixes #30505
Adapted from community PR #30506.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 00:31:18 -07:00
NeilandWooseong Kim 46b83a9529 fix(mobile): drag terminal selection with the long-press finger (#31433)
Let the finger that creates a long-press selection extend it through the existing selection-drag dispatcher. Verified with native iPhone touches and focused lifecycle tests.

Fixes #30554
Adapted from community PR #30555.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 00:30:43 -07:00
Neil 9577c531c7 fix(ssh): a managed host's 'no desktop is sharing its browser' error names the browser routing setting too (#31444)
* fix(ssh): the host's 'no desktop is sharing its browser' error names the browser routing setting too

The desktop withholds the browser bridge from a managed SSH host whose pages browse from the
desktop's own network, but the host's CLI was told only to turn on the per-host CLI control
switch, even when it already was on (#31421).

* fix(ssh): name the per-host routing opt-out too, and guard the labels against UI copy drift
2026-10-21 00:28:46 -07:00
Neil c7f722bbdf fix(pairing): offer only addresses the runtime listener answers (#31447)
The runtime WebSocket listener widens to 0.0.0.0, which never accepts
IPv6, yet the Connection address picker and the default pairing address
offered global IPv6 interfaces, minting links nothing answers. Filter the
interface list by the family of the host the listener binds when serving
the network.

Reported-by: @fongyuan-2011
2026-10-21 00:11:21 -07:00
Neil 73930bedee Prepare server template assembly alongside qualification (#31422)
* Prepare template assembly during qualification and cache Intel downloads

* Limit early template qualification polling to fifteen seconds

* Verify Windows retains its download cache policy

* Drop Intel store restoration after measuring its overhead
2026-10-21 00:06:40 -07:00
Neil 5fc6cb46b0 fix(plugins): a development plugin reloads an edit saved while its watcher was starting (#31394)
* fix(plugins): a development plugin reloads an edit saved while its watcher was starting

Orca scans a development plugin folder, then starts watching it. The watcher goes live
about a second later and reports only changes made after that, so a save in between was
never picked up and the plugin kept running the old files. Once a development root's
subscription goes live, schedule one catch-up refresh through the existing debounce.

* test(plugins): count a replaced dev root's catch-up refresh regardless of timing
2026-10-21 00:02:39 -07:00
NeilandShahar Mor 45ee56e1a5 fix(mobile): preserve native iOS dictation placeholders (#31356)
Preserve UIKit recognition placeholders during equal-text Fabric synchronization and reset recognition state when the native input is recycled.

Adapted from community PR #26644 by Shahar Mor. Verified with matched source-built native Debug lifecycle probes for both input classes; physical spoken dictation remains in the final manual batch.

Co-authored-by: Shahar Mor <shaharmor1@gmail.com>
2026-10-20 23:52:49 -07:00
Neil a04b2ebc33 fix(release): avoid API rate limits waiting for the mobile bundle (#31431)
* fix(release): replace mobile bundle API polling with job dependencies

* test(release): update qualification token permission expectations
2026-10-20 23:49:57 -07:00
Brennan Benson 92732bb0f9 fix(native-chat): bring back Working-only-while-held (#30134) without shutting down Claude chat workers (#30577)
* fix(native-chat): a Claude chat worker keeps running after Claude acknowledges its task

Claude's adapter dropped an echoed send from the sends it holds before
reporting the send's answer to the host, and emitting the echo's turn
can publish at once. That publish read the still-pending send as held
by nobody, so it read as "couldn't confirm": orchestration worker-start
then closed the new Claude worker, and every other answer wait and
client saw a one-frame doubt.

A frame's settled sends now stay held until their answers are reported,
when the host's handover window takes them over; a Stop's rejected
sends are released and reported one at a time, as Pi and ACP already do.

Tracked in STA-11160.

* test(native-chat): assert the settled send's dispatch state through the send result

* Reapply "fix(native-chat): Claude, Pi and ACP chats are Working only while the agent holds the send (#30134)" (#30572)

This reverts commit 3ef5a46061.

* fix(native-chat): a Claude send stays held until its settlement reaches the host, on the reapplied held-sends read

* fix(native-chat): a Claude send stays Working, not in doubt, while a Stop is still ending its CLI

* test(pi): drop the removed idle-release dependency from the adapter fixture
2026-10-20 23:46:45 -07:00
Jinwoo Hong cfdc347bd8 Layout-owner build: main shows live terminals that lost their tab, at the same moments the window did (dark) (#31430)
* feat(layout): main places a swept workspace's live terminals; the window sweep only reads (switch-on builds only)

The window's activation sweep (first visit, an empty or sleeping activation, a phone wake) now
asks main, through its existing terminal.list call, to place that workspace's unshown live
terminals before listing them. With the switch on the sweep writes nothing to the window store and
counts a pane main names as shown, so it no longer seeds a fresh shell beside a terminal main just
placed. Main's "still binding" check also covers a registration in flight.

* fix(layout): a sweep's placement pass holds back only its own workspace's first surface (switch-on builds only)

* test(layout): switch-on activation-gate and census-call tests read main's listing, not window binds
2026-10-21 02:33:56 -04:00
Neil 2bf77e605b fix(ssh): draw every host list through one bounded, searchable host picker, so a 13k-host ssh config can't crash any of them (#31426)
* fix(ssh): draw every host list through one bounded, searchable host picker

A generated ~/.ssh/config can import 13k+ SSH hosts, and several surfaces drew a
row per host: Settings > General workspace directory 'Apply to', Notifications >
Machines, the sidebar and Agents Hosts submenu, the composer run-target picker,
the Add Project host selector and remote step, the skill-install machine pickers,
Settings > Servers SSH host picker, repository 'Add to another host' and the
automation host field and filter.

They now share components/host-picker: boundHostList (pinned hosts plus a ranked,
capped match list), HostCombobox for single choice (a Command in a Popover that
mounts nothing until opened, replacing Selects that mount every item), and
useBoundedHostRows with HostListSearchField and HostListOverflowNote for
checklists. A host-list-render ratchet stops new JSX from mapping straight over a
host collection.

* fix(ssh): host lists keep room for search results, menu search is keyboard-reachable

- boundHostList gives pinned hosts and other matches separate limits, and narrows
  pinned hosts by the query, so a large selection never hides what the user types.
- In a dropdown, the host search is a menu item: arrow keys reach it, focus moves
  into the input, and arrows leave it again.
- The Add Project host picker hides its setup actions while searching, so Enter
  picks the matching host.

* fix(ssh): re-picking the current host in a host picker is not a change

The Selects these pickers replaced suppressed same-value changes; the skill
install form clears its workspace and target on a machine change.

* chore(ci): let the shared ratchet runner pick up the host-list render ratchet

main now discovers every check-*-ratchet.mjs through run-ratchets.mjs, so the
separate workflow step would run it twice.

* fix(ssh): host lists rank pinned matches and keep input order; label and scroll fixes

- boundHostList ranks pinned and other hosts by the same match order before
  cutting each to its limit, then keeps input order among equal ranks, so a
  muted or checked exact match is always reachable and clicking a row never
  moves it.
- The Servers SSH host picker is named by its form label again.
- The sidebar Hosts submenu scrolls its host rows instead of running off screen.
- The composer's overflow note sits outside the listbox.
2026-10-20 23:33:36 -07:00
Brennan Benson 505d6719c0 Refuse a workspace parent that would close a loop, even when the worktree cache is cold (#29340)
* fix(worktree): refuse a parent loop even when the worktree cache is cold

The parent cycle walk read each workspace's current instance from the
resolved-worktree cache, which the reparent itself invalidates, so it only
knew the two endpoints and accepted A->B->C->A. Read saved worktree
metadata instead.

* fix(worktree): fall back to locator metadata and tolerate loops saved earlier

* fix(worktree): skip another host's locator metadata in the parent loop walk

* fix(worktree): read loop-check identities from a read-only, ownership-scoped view

The per-host metadata accessor migrates a hostless legacy row into the
queried host, so the loop check could adopt another host's worktree and
refuse a valid parent. Read one read-only host snapshot instead, counting
a row only when its project is registered on that host (the resolved
list's ownership rule).

* fix: import host-qualified worktree meta helpers once

* test: match main's host-targeted lineage write contract
2026-10-20 23:23:02 -07:00
Brennan Benson 7dfcfbf735 fix(codex): resume native chats by conversation ID (#31041) 2026-10-20 23:11:28 -07:00
Brennan Benson 786a40ef1f Custom tab shortcuts work right after a keyboard worktree switch (#30941)
* fix(shortcuts): app shortcuts run before the terminal, whatever order they mounted in

App shortcut owners and terminal panes both listened on window capture,
so React's effect order decided who saw a chord first. After a worktree
switch the new pane registered before the tab shortcut listener and
turned a user-bound Cmd+Right into Ctrl+E; clicking a tab flipped the
order back. A keyboard router installed before React renders now runs
every app owner, in registration order, ahead of any pane listener; a
key no owner claims still reaches the terminal's own handling. Held tab
chords are claimed on every repeat but switch once.

Tracked in STA-11205 (same bug as STA-4602).

* test(shortcuts): type the router mock with key event names

* fix(shortcuts): install the keyboard router in the web client before it renders

* test(shortcuts): floating panel specs read the routed key listeners from the window stub
2026-10-20 23:10:59 -07:00
Brennan Benson fc8439e05f fix(notifications): a pane with hooks gets its finished banner from its hook turn only (#30620)
* fix(notifications): tie a completion banner to the hook turn it announces, not a 10 s window

A title-only working->idle edge (Claude's /btw side question fires no hook) raised a
generic "Task complete" banner because the pane's hook row was older than 10 s. On a
hook-reporting pane the hook row now decides: a row not yet announced supplies the
banner content however old it is; an already-announced row means no turn ended, so
no banner. Hookless agents keep the generic title fallback.

* fix(notifications): a pane with hooks gets its completion banner from its hook turn only

The title lanes (mounted pane and parked watcher) announced a finished task on every
working->idle title edge, even on panes whose agent reports turns through hooks. A title
spin that ends no hook turn (Claude's /btw side question, Codex's /compact) raised an extra
banner that could only be filled with the workspace name and tab title.

On a pane the hook lane is tracking, with a settled Done row from the same agent, the title
lane now defers before it claims the turn, so the hook lane's banner (with the last message)
is the only one. A BEL in the same burst waits on the hook lane's pending Done. Hookless
agents, restored rows, hooks switched off and the post-restart re-arm keep the title banner.
This replaces the previous commit's announcement memory in the dispatcher.

* fix(notifications): hook-owned turn ends settle pane bookkeeping and announce re-entered Stops

- A title idle edge that defers to the hook lane now settles the pane's turn, so quitting the agent after a /btw no longer raises a generic "Task complete" banner.
- A BEL held while the hook lane's Done is pending is superseded by that Done at fire time instead of being held until the next turn start.
- A Stop re-entered after a blocking Stop hook carries its new final message in its identity, so the hook lane announces it instead of deduping it against the first Stop.
- Title lanes ask the hook lane whether it can announce the turn end (live, agent evidence, not re-arming) instead of copying its conditions, and fall back when the agent's own hooks are switched off.
- The hook lane's non-quiet Done forwards its own snapshot, so its banner content no longer depends on the stored row's age.
- The whole-flow test now drives the real pane wiring, the real hook lane and the real dispatcher.

* fix(notifications): the host marks each real turn end, so a repeated status can't raise a second banner

The renderer told a Stop re-entered after a blocking Stop hook from a
restatement by its final message. The message also changes when the
listener's cache resets (a session reset, then an idle reminder), so a
restated finished turn raised a second, empty "finished" banner.

The execution host now classifies each provider's own turn-end event by
name and stamps an optional `turnEndedAt` on the status row. Idle
reminders, session events, replays, child re-emits and result retries
restate it; a turn-end event on an already finished row is a new end only
when the agent says it re-entered (`stop_hook_active`). Local hosts and
SSH hosts (orcad) mint it in `attachStatusTiming`; the WSL relay mints it
at admission and sends it on its envelope. The field is persisted,
forwarded over IPC and the paired-server session projection, and the
renderer's done identity is `turnEndedAt ?? stateStartedAt` (after the
existing `turnCompletedAt` precedence). `stateStartedAt` and every other
clock are unchanged.

Paired servers that predate the stamp advertise no
`agent-status.turn-ended-at.v1` capability; their panes keep the
terminal-title completion fallback exactly as before.

* test(notifications): type the old-server pane setup in the completion flow test

* fix(notifications): a blocking Stop's final banner reaches the user; /compact and quitting an idle agent stay quiet

Real-agent QA on the hook-only completion design found three gaps:

- A Stop re-entered after a blocking Stop hook could be dropped twice
  over: inside 1 s of the first banner by the replay guard (a new host
  turn-end stamp is now a real end), and by main's 5 s per-workspace
  desktop cooldown. A banner with the same notification id and changed
  text now replaces the one already shown instead of being dropped;
  every other cooldown rule is unchanged.
- On a mounted pane, a hook-owned idle edge (Codex /compact) no longer
  claimed a completion, so the burst's BEL went out as a Bell banner. The
  deferred idle edge now absorbs that BEL like a claimed completion did.
- Quitting an idle agent after a title spin raised a generic process-exit
  banner. On a pane whose hook lane owns turn ends and whose last turn is
  done, an exit ends no turn; a crash mid-turn still gets its banner.

* test(runtime): the projection reference keys on the host turn-end stamp too

* test(relay): restore the Copilot retry test while CI isolates a runner error

* test: key the hot-path projection reference on the turn-end stamp; relay retry restates the end

* fix(notifications): host turn identity per agent session; keep later blocking-Stop banners past main's cooldown

- The execution host (local main and the WSL relay, one shared reducer) now tracks which turn an
  agent session is in: Codex by its own turn id, Command Code by its transcript prompt position,
  every other agent by its prompt-opening hook. A turn end mints a new end when its turn differs
  from the last ended one, so a Codex turn woken by agent mail and a text-only Command Code turn
  get their banner; duplicates, replays, tool reopens and result retries restate it. The record
  is saved with the status row (validated on restore) and never published.
- Main's desktop and mobile notification cooldowns are back to main's code exactly.
- A later end in the same done segment (a Stop re-entered after a blocking Stop hook) that lands
  inside main's cooldown is held, latest only, and sent once after that cooldown through main's
  unchanged gates; the renderer learns main's answer to anchor it.
- One pane check gates every changed pane behavior: the pane's bound PTY runs on a host that
  stamps turn ends and its agent's continuation signal is verified (Claude, Codex). Every other
  pane, and an unbound PTY, keeps main's behavior.
- A hook-owned idle edge absorbs its burst's BEL for main's full 1.5 s; exits and replacements
  ask the owner check with the exiting agent; a title spin on a hook-owned pane opens no turn.

* test(notifications): run the whole-pane harness through main's shared desktop cooldown, not main's service module

* fix(notifications): decide hook ownership from how the host identified each turn end

The execution host now labels every classified turn end with one optional
disposition beside its end stamp, by the same rule for every agent:
identified (an unused prompt opening, a new native turn id, or a verified
re-entered Stop) mints a new end; a same-turn repeat is a proven duplicate
only when the agent supplies both a turn id and a verified continuation
flag; anything else is ambiguous and never mints. Replays and retries
restate the decided answer, and local and relay hosts carry an end only
within a done segment.

The renderer replaces the Claude/Codex pane gate with one evidence
decision: identified ends are hook-owned, ambiguous ends keep the title
lane's role, and rows without a disposition (older hosts, unclassified
agents) keep the previous behavior exactly. A title working edge on a
hook-owned pane keeps its turn bookkeeping and only the hook receipt; an
owned idle edge settles after the existing max wait, or announces through
the title path if the end turned out ambiguous. Idle and working title
edges resolve the same owner (the title's agent, else the foreground one).
The held later banner for identified continuations is unchanged.

* test(notifications): give the whole-pane harness's host event its local connection id

* fix(notifications): a settled cancellation ends the open turn, so its late Stop folds as on main

A cancel Orca inferred from Esc or Ctrl+C writes a done row with no turn-end event. The host reducer now completes the current turn key on that settled cancellation, so the same turn's late Stop is a repeat (ambiguous, no new stamp) instead of a second identified end that requested a second banner.

* fix(notifications): a settled cancellation ends the open turn on every lane, so a relay's late cancel end restates none

The desktop restates a WSL relay's end as decided. The relay never learns a
cancel the desktop inferred for a non-Claude agent, so Grok's own late
stop_cancelled reached the desktop as an identified end and gave a second
banner. One predicate now decides both lanes: after a settled cancellation
that carried no end, a restated end keeps the shown one, and a child event
carrying the cancellation also ends the open turn. The relay's own cancel
row is the main agent's, never a child's.

* fix(notifications): a relayed done segment shown without its own end keeps showing none

The restatement fold tested the previous row's cancel verdict, so the row it left behind (a late
plain stop) no longer matched and the relay's next restatement brought its end back. Fold on the
row's turn-end fact instead, which the folded row satisfies again.
2026-10-20 22:59:57 -07:00
Brennan Benson 6de974ca5b Mobile chat: remove the Tools toggle and its open-everything mode (#31188)
* fix(mobile): remove the chat's Tools toggle and its open-everything mode

* test(mobile): keep the scroll-ownership tests' runs drawn without the Tools default

* test(mobile): the scroll-ownership harness opens settled turns as the old Tools default did
2026-10-20 22:56:11 -07:00
Brennan Benson ae9bc8d120 A slept native-chat workspace reads asleep and hides (#28457)
* fix(sidebar): a slept chat workspace reads asleep and hides

* fix(sidebar): a wake during Sleep's teardown keeps the workspace awake

* test(sidebar): give the activation-race mock store the chat tab map Sleep reads

* feat(native-chat): the host marks the chats an explicit Sleep stopped

The fact "the user's Sleep put this chat to rest" now lives on the host
that runs the chat, on its session record, instead of in the window.

- New runtime capability agent-session.workspace-sleep-mark.v1 and an
  optional markAsleep on agentSession.sleepWorkspace (strict params, so
  only hosts advertising the capability are sent it).
- The Sleep close decides each chat's receipt inside the same per-session
  step that stopped it; with markAsleep it writes sleptAt in that step's
  record write, so a chat is marked exactly when it is in the receipt.
- The next send the host hands over clears sleptAt, whichever client sent
  it; the idle sweep and plain stops never mark.
- sleptAt rides the status summary (optional field), its equality, and
  the retained summary of a closed chat, so it survives restart through
  the persisted tab index.

* refactor(sidebar): read a slept chat from its host's mark, not a window record

The window no longer keeps its own saved record of which chats Sleep
stopped (explicitlySleptChatsByWorkspace). That record had to mirror every
host event that should end it (sends, replaced conversations, renames,
profile and WSL moves, removal, wakes during teardown), and each missed
mirror was a bug. The host now owns the fact (previous commit).

- Delete the record, its schema, store slice, workspace-session field
  registrations, moves, renames, purges, migrations and hydration, and the
  renderer clearing hooks. Those files are back to main.
- Opening a workspace no longer wakes its slept chats (product ruling):
  drop the chat Sleep-attempt tokens and their set-active-worktree hook. A
  slept chat wakes only when a send reaches its host, from any client.
- The status bridge mirrors each chat tab's published sleptAt into a
  store map keyed by the chat's pane key, dropped when its projection
  unmounts and never saved; a chat whose host runs it again is not slept.
- The asleep derivation, hide filter, palette, board and context menu read
  that map. Explicit Sleep sends markAsleep only to the local host and to
  hosts last probed with the new capability; a project move never does.

* fix(native-chat): preserve host-owned Sleep across fallback and legacy tabs

* fix(native-chat): clear Sleep when a new send is admitted

* chore: restore incoming documentation after merge hook formatting

* fix(native-chat): let admitted sends supersede pending Sleep receipts

* fix(sidebar): wait for host Sleep state before showing restored chats

* fix(sidebar): retain host Sleep answers when chat projections remount

* fix(native-chat): clear Sleep when queued drafts become sends

* test(sidebar): cover unknown and known chat Sleep state on the board

* test(vault): expect both host status capability checks

* test(native-chat): consolidate Sleep behavior coverage

* fix(native-chat): commit Sleep wake with send admission

* test: pin visibility while chat host is unidentified

* test: consolidate workspace sleep regression flows

* test: keep status feed regression within renderer boundary
2026-10-20 22:42:56 -07:00
Jinjing c9d34bba1a Open new Markdown changes in the rich editor (#31399) 2026-10-20 22:31:24 -07:00
Neil 309d71c003 Build adhoc Mac architectures in parallel (#31415)
* Parallelize adhoc Mac architecture packaging

* Include parallel Mac jobs in release timing reports

* Cover parallel Mac publication and failure gates

* Explicitly scope archive targets to each Mac architecture
2026-10-20 22:30:47 -07:00
NeilandClaude 546304fa11 Discard pauses autosave of the file in every workspace; read-only Markdown/CSV tabs can't overwrite unsaved edits (phase 1b-3) (#31413)
* Editor: git Discard pauses that file's autosave in every workspace; read-only markdown/CSV never pushes over edits (phase 1b-3)

- A Source Control discard holds autosave (and waits for in-flight saves) for every tab of that
  file in every workspace, keyed by its model save key, until the discard finishes or fails; the
  quiesce now returns the release, called in a finally. Watcher notices don't release it. A 120 s
  backstop only shows the changed-on-disk banner on dirty tabs and never reopens autosave. This
  closes the race where the compare fallback on older hosts reads disk just before git restores it.
- A read-only tab renders raw Monaco source whatever its type, so it counts as a code view: a clean
  read-only .md/.csv tab can no longer push disk text over a writable tab's unsaved edits.

Only removes or delays writes. One autosave timer per model moves to 1b-4.

Co-Authored-By: Claude <noreply@anthropic.com>

* Editor discard hold review: release on a failed quiesce, cover unstaged diffs, re-show an overdue banner (phase 1b-3)

- A quiesce that throws (e.g. an unresolved CSV cell) releases its hold and rejects, so the discard
  reports the error instead of hanging with autosave off; a bulk discard frees the holds the other
  paths took.
- Holds key on the physical file (host + path), so an unstaged diff tab of the file in another
  workspace is paused and drained too; save-queue keys are unchanged.
- Past the backstop, a dirty held tab whose banner was dismissed gets it back, so the pause is
  never silent. Taking a hold no longer re-syncs; the save-time check already refuses.

Co-Authored-By: Claude <noreply@anthropic.com>

* Editor discard hold: key the physical file with the 1b-1 path rules, not the client's URI rules (phase 1b-3)

The hold key went through the Monaco model URI, which normalizes paths by the client platform, so
on a Windows client `/repo/a\b.ts` and `/repo/a/b.ts` on a POSIX host shared a key. It now uses
the owner key plus `editorBufferOwnerPathKey` (new, in editor-buffer-identity), which applies the
1b-1 rules: POSIX backslashes kept, separators folded only for proven-Windows paths, UNC server
case, and WSL aliases on the local Windows client. Same on the tab and the discard-path side.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-20 22:17:18 -07:00
Brennan Benson 2187f0b6b2 ci: one list for Windows boundary tests and one step for ratchets (#30643)
* ci: one list for Windows boundary tests and one step for ratchets

The Windows boundary step's 74 test paths were hand-listed in pr.yml and
again in WINDOWS_PACKAGE_TESTS, and the copies had drifted: six files ran
in the step but could not trigger the job. The step now prints
WINDOWS_BOUNDARY_TESTS, which WINDOWS_PACKAGE_TESTS spreads.

The ten per-ratchet preflight steps and the eight lint entries become one
run-ratchets.mjs that runs every check-*-ratchet(s).mjs it finds.

* fix(ci): preserve existing local lint ratchet scope
2026-10-20 22:13:36 -07:00
Brennan Benson b2370f1c6e fix(terminal): hidden tabs reconnect to sessions that survived a restart (#31254)
* fix(terminal): hidden tabs reconnect to sessions that survived a restart

pty:hasPty answered from the provider's attached-this-run set, so right
after a restart every surviving session read as exited and #29459's
hidden-agent hold kept live hidden tabs disconnected until shown. On an
attached-set miss, ask the host through the provider's existing liveness
probe; providers without one (the in-process provider, its own sole
owner) keep their answer.

* test: remove liveness fixture type assertion
2026-10-20 22:12:27 -07:00
Jinwoo Hong 0a9980b141 fix(terminal): a saved terminal folder on a dead network drive no longer freezes the Orca window (#31381)
* fix(terminal): a folder on a dead network mount fails the terminal start instead of hanging it

Bound the local folder checks a terminal start runs (folder-workspace admission and the
worktree-folder cwd fallback) with a 5s deadline, and make the fallback check async so it
can no longer block the main process. A timeout refuses the start with the existing
folder_workspace_path_unavailable code.

* refactor(terminal): build the folder deadline on the existing working-directory validation helper

Drop src/main/local-folder-stat-deadline.ts. statLocalFolderWithDeadline now lives in
working-directory-validation.ts and reuses its abandon-on-signal waiter and UNC route lane,
so a dead SMB share caps how many folder checks can hold fs threads.

* fix(terminal): keep only the non-blocking saved-folder check; drop the folder-check time limit

Per owner decision, a hung folder waits as before. Folder status and working-directory
validation return to main; the saved-folder fallback check stays async so main never blocks.
2026-10-21 01:04:35 -04:00
Brennan Benson 818a25d430 test(vitest): derive the Node SQLite test list from imports (#30647)
* test(vitest): derive the Node SQLite test list from imports instead of a hand-kept list

The two SQLite list files held ~511 test paths that every PR adding a SQLite test had to edit,
which caused frequent merge conflicts. The vitest config now finds tests that directly import a
SQLite module or harness when it loads, using the classifier the boundary test already had, with
a modification-time cache so unchanged files skip parsing. The 42 tests that reach SQLite only
through a shared helper or a dynamic import stay listed in the Node runtime list.

* test(vitest): skip unused SQLite derivation and reject malformed cache entries

* test(vitest): use typed test convention for cache contract
2026-10-20 22:01:13 -07:00
Neil 6964f819b9 Reuse qualified templates and batched signing for production releases (#31397) 2026-10-20 21:55:01 -07:00
Neilandm4air 01562f8bcc fix(ssh): window the SSH Hosts list and search past the Remote Hosts cap, so a 13k-host ssh config no longer crashes the renderer (#31405)
* fix(ssh): the Remote Hosts menu lists a capped set of idle SSH hosts

* fix(ssh): the SSH Hosts settings list renders hosts a page at a time

* fix(ssh): window the SSH Hosts settings list and keep project hosts in the capped menu

Settings > SSH Hosts now windows its rows against the Settings scroller with the
shared VirtualizedList instead of paging 100 at a time, so every host stays
reachable by scrolling or the filter while only the rows near the viewport mount.
The Remote Hosts menu cap rises to 25 idle hosts, fills it with hosts that own
projects first, and the overflow row says to search the rest in Settings.

* fix(ssh): search the Remote Hosts menu's capped idle hosts in place

Web clients reach SSH hosts through this menu but have no SSH Hosts settings
pane, so a link to Settings stranded every host past the cap. The menu now shows
a search box whenever idle hosts are hidden; it narrows them by name, and the
overflow line says how many are left. Also gives the two existing status-bar
store mocks the repos field the menu now reads.

* fix(ssh): typing in the Remote Hosts menu goes to its host search

The search box is not a menu item, so arrow keys can't reach it; letters typed
while the menu has focus now land in the search instead of Radix typeahead.

* fix(ssh): the Remote Hosts search keeps focus and drives its results by keyboard

The host rows are not menu items, so the menu's arrow keys never reach them. The
search box is now a combobox over the connectable results: it keeps focus, arrow
keys move the active result (announced through aria-activedescendant), and Enter
connects it through the same path as the row's Connect button.

* fix(ssh): Remote Hosts search only reaches hosts that match, and keeps the active one in view

Every not-connected row now has to match the search, so Enter can't connect a
failed host the user didn't type. Arrow keys scroll the active result into view.
Also drops restyles on the search Input and fixes the singular overflow default.

* refactor(ssh): find hidden Remote Hosts in a searchable picker, not a search box inside the menu

A search box inside the dropdown kept fighting the menu over focus, Tab, Enter
repeat and IME input. The overflow row is now a menu item that opens a host
picker (Command in a Popover, as the style guide prescribes for single choice
with search). It ranks exact names first so any host is reachable by typing its
name, mounts at most 50 results, and says how many more match. The menu keeps
config order when nothing is capped.

* fix(ssh): closing the host search from the keyboard returns focus to the status button

The picker is anchored, not triggered, so Radix had nowhere to restore focus and
it fell to the page. A keyboard close now focuses the Remote Hosts button; a click
elsewhere keeps focus where the user clicked. Also drops the padding restyle on
PopoverContent.

* fix(ssh): the host search marks the keyboard-selected host with the palette highlight

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-20 21:51:27 -07:00
NeilandWooseong Kim 12e0b37293 fix(mobile): keep dictation controls in sync with host settings (#31349)
Honor enabled voice settings across terminal/chat controls and capture. Refresh hold/toggle state on connection, focus and foreground while fencing stale replies.

Adversarial review: 63 existing tests plus six disable/late-finish/unmount/settings ownership probes pass; final required CI and aggregate gate pass. Existing native screenshots and microphone lifecycle exercise are attached to the PR.

Fixes #30307. Fixes #30511.
Adapted from community #30316 and #30513 by @innocarpe.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-20 21:45:10 -07:00
Neilandbbingz e2196403c3 fix(terminal): realign mobile snapshot residual metadata after query stripping (#31348)
Align plain mobile/legacy residual metadata after snapshot-boundary query stripping; preserve genuinely transformed span metadata.

Fixes #9682. Based on the report and suggested correction by @bbingz.

Co-authored-by: bbingz <zzb@gxsmjx.com>
2026-10-20 21:41:02 -07:00
Jinjing d8508da889 fix(ui): clarify continuing conversations between chat and terminal agents (#31402) 2026-10-20 21:36:09 -07:00
Jinwoo Hong 87fe8424eb fix(runtime): a server with no window answers its first terminal create, not 'handle stale' (switch-on builds only) (#31391)
* fix(runtime): a server's new terminal keeps answering under its own handle after main's pane adopts it (switch-on builds only)

* fix(runtime): keep handle branching on the leaf path; only callers with no leaf fallback read the adopted own handle (switch-on builds only)

* fix(runtime): with no window, a terminal's own handle keeps naming it after main's pane adopts it, as before main listed panes (switch-on builds only)
2026-10-21 00:34:08 -04:00
fa35698db5 fix(mobile): keep keyboard accessory row height stable (#31363)
fix(mobile): keep keyboard accessory row height stable
Stretch the dismiss control within the accessory row so showing it no
longer adds height and needlessly reduces the terminal viewport.

Adapted from community PR #23511 by @innocarpe.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:32:47 -07:00
NeilandWooseong Kim f19ce3635b fix(mobile): show the bundled whale on DeepSeek Build tabs (#31359)
fix(mobile): reuse DeepSeek whale for Build tabs
Adapted from community PR #30525 with a focused component regression and native current-shell validation.

test(mobile): use checked icon renderer guard


test(mobile): keep icon diagnostic with review evidence
Native before/after captures cover this low-impact asset mapping; retain the local fail/pass diagnostic outside the product diff.

Use checked optional type for mobile icon lookup

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-20 21:32:39 -07:00
NeilandWooseong Kim 8bec7323ef fix(mobile): preserve Claude chat after renaming its pane (#31358)
fix(mobile): retain Claude chat when its pane is renamed
Distinguish a manual title override from a later shell OSC title, keeping transcript identity and live hook selection while preserving exit retirement.

test: cover restored mobile chat after manual shell-like rename

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-20 21:32:31 -07:00
Neilandbbingz 3fd90e94e0 fix(mobile): preserve live palettes on identical themes (#31355)
fix(mobile): skip identical terminal theme assignments
Preserve live TUI palette changes and contrast caches on repeated normalized host themes. Keep explicit foreground recovery repaints and published desktop contrast overrides.

Co-authored-by: bbingz <zzb@gxsmjx.com>
2026-10-20 21:32:23 -07:00
NeilandWooseong Kim 3ed0bb4dfc fix(mobile): match home-indicator inset to terminal input panel (#31354)
fix(mobile): paint terminal dock bottom safe area
Adapted from community PR #30318.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-20 21:31:25 -07:00
NeilandWooseong Kim 6537842f0c fix(mobile): blend workspace header into status-bar safe area (#31353)
fix(mobile): match workspace header safe-area surface
Adapted from community PR #30531 with native iOS simulator before/after validation.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-20 21:31:20 -07:00
Brennan Benson b0d873d86a test(claude): isolate real CLI fixtures and cover chat option requests (#31201)
* test(claude): real-claude suite passes on any signed-in machine and current model aliases

Model ids come from the CLI's own model list instead of dated ids, tests that need a SessionStart hook or an effort bring their own project settings, and the supervised-stop test launches and reads its transcript under one Claude home, as acquisition does.

* test(claude): one real-claude flow for the model, effort and permission pickers and the context meter

Drives the live CLI through the production send path (model list, set model, apply effort, read settings), checks the turn's context report, and relaunches into a new permission mode the way Orca changes it. CI only meets these requests through stand-ins.

* test(claude): accept the ran options a send now reports

Since #30561 a Claude send resolves with the options it ran under; the stop test only needs the send admitted, and the options flow checks the ran options are exactly the per-message pick.

* test(claude): an alias pick's dispatch report may omit the model the CLI resolved
2026-10-20 21:25:11 -07:00
Neil d1ed73ba9b fix(orcad): refuse new terminals while an update stops the server (R32) (#29834)
* fix(orcad): refuse new terminals between an update's last census and the stop

The last census before an SSH or WSL update stops the server now closes the
server's terminal admission first. Every spawn lease (acquireWorktreeTerminalSpawn,
which both spawn paths hold across the provider spawn) passes the fence, the
census waits for leases taken before the close, and an update that leaves the
incumbent serving reopens it. A reattach to a session the daemon still runs is
still admitted.

* fix(orcad): fence-exempt reattaches spawn attach-only; reopen admission when no stop was sent

* fix(orcad): stop nothing once the recount's admission fence may have lapsed
2026-10-20 21:23:41 -07:00
Neil b3e6705337 fix(orcad-migration): a sidebar view setting never blocks moving an SSH host to its server (#31374)
* fix(orcad-migration): a sidebar view preference never blocks moving an SSH host to its server

* fix(orcad-migration): keep repo filters on the desktop, keep later acknowledgements; e2e repro
2026-10-20 21:15:56 -07:00
Jinwoo Hong ce71535bbf Layout groundwork (switched off): main starts every split and new pane, the window only attaches (#31258)
* feat(terminal): the window's store shows main's pane start status (layout-owner build)

A status-only frame changes one store entry and no pane tree, so the pane's view stays mounted.

* refactor(terminal): keep the frame sync within its line budget

* feat(terminal): every pane main starts shows 'starting' from before it is written (layout-owner build)

* feat(terminal): a pane main is starting waits and attaches; a failed start shows why (layout-owner build)

Fixes owner-visible row 15: a failed agent or setup start no longer opens a plain shell.

* test(terminal): iterate store listeners directly

* fix(terminal): an exit that reports no code keeps its pane, as lost contact (layout-owner build)

* test(terminal): a pane main started attaches to main's terminal at its own grid

* test(terminal): detach census tests assert main's move and frame with the switch on

* test(terminal): exited-pane tests assert main's close with the switch on

These 8 have failed switch-on since #31130 moved the exit close to main.

* feat(terminal): main writes and starts every split of a pane it holds; the window attaches (layout-owner build)

The window's split, a startup split with its command, and a CLI split all go through main's
terminal.split; main's frame mounts the pane, with or without a window intent. Deletes the
window's split wait and withdraw, the split appear intents, and the queued-split skip and cancel.
The e2e helper uses the real split. A startup split leaves focus on its source pane.

* fix(terminal): the attach-only wait leaves sleeping, resuming and held panes to their restore path

* fix(terminal): a split pane attaches to the terminal main bound it to; a taken pane id is refused

Also restores the proof that a split closed while it starts gets one answer and leaves no
pane or start status behind.

* fix(terminal): a window split main cannot start still shows its pane, failed, with why

* fix(terminal): check a taken pane id first, keep the start's own error, clear a gone pane's status

Three test stores gain the pane-start field the window's store always has.

* test(terminal): the admission stub answers whether its pane is still written; a closed pane's failed start clears

* refactor(terminal): the rebound workspaces' bindings show through one call in the bindings module

* test(terminal): a pane main wrote and the user closed mid-start drops its failed status

* fix(terminal): a split main starts is listed to phones and session.tabs without a window's report

* fix(terminal): a reconnect shows its pane starting and why it failed; a lost owner race shows no failure; a split no one asked to see keeps the phones' tab

* fix(terminal): a kept create another owner won shows no failure; the window attaches to the winner
2026-10-21 00:14:06 -04:00