Read git ls-files and git diff --name-only with -z so non-ASCII paths are
not C-quoted and still match ast-grep's real filenames; otherwise new
flaky-test findings in such files were silently dropped.
Fixes#34395
Co-authored-by: Neil <neil@stably.ai>
* refactor(mobile): share the queued-card projection with desktop
Image-only queued drafts showed a blank card on the phone.
* fix(mobile): honor the host's Queue follow-ups setting
With it off, a mid-turn send from the phone still queued while desktop sent it into the running turn.
* fix(mobile): keep a queued-card edit when the card leaves the queue
The phone closed the editor and dropped the typing; it now goes to the chat box as on desktop.
* fix(mobile): hold /clear and /compact for background work and sends
The phone fired the command where desktop holds it, showed start failures twice, and left the agent unnamed in failures.
* fix(mobile): hand a queued edit back to the chat it came from
* fix(mobile): scope a command's send hold to its own chat
* fix(mobile): read a published null queue as empty
* refactor(mobile): share the returned queued-card caption rule
* test(mobile): drop queued-card tests the shared rules already own
* refactor(native-chat): share the needs-attention rule and drop leftover wrappers
* fix(mobile): type the structured sends mock, drop the moved queued-cards path and ship queued edit copy at boot
* fix(native-chat): drop the queued card's unused image-label import after the rebase
* fix(mobile): order the phone's queue reorder preview by position, as the shared cards sort
* test(mobile): keep the queued-messages test within its line budget
* fix(mobile): settle terminal chat working state and turn timing with desktop's rules
Move desktop's live-status merge, Stop suppression, hook-state resolver and
terminal turn timing to src/shared/native-chat. Mobile reads the transcript
lifecycle the host already sends, so a provider turn end settles Working the
same way desktop does. Terminal chats now get the turn bar, timer and folding,
and the streaming preview uses the shared deriveNativeChatStreamingText.
* fix(mobile): send desktop's terminal Stop keys, a second Escape only for OpenCode
Desktop sends one Escape on Stop. It adds a paced second Escape only for
OpenCode, which confirms an interrupt that way. Claude Code and Codex read
Esc-Esc at an idle prompt as rewind. The rule moves to
native-chat-agent-support, and the phone now paces the second Escape at
desktop's answer step instead of 80ms.
* fix(mobile): word Orca-stop and usage-limit notices like desktop
Move desktop's Orca-stop sentences to src/shared with an English table that
desktop translates. The phone names the Orca host it pairs with, and when
that host is unnamed it says "the remote server". Usage-limit rows rebuild
desktop's sentence from the failure fact. The usageLimitLineText fallback,
which only the phone used, is deleted.
* test(mobile): pass the overlay's host name in the prompt controller harness
* test(mobile): re-record Stop goldens at desktop's step, split status-notice tests, pin the turn clock and ship notice copy at boot
* fix(mobile): offer structured Stop while a send is out and read queue-sends-next as working
Move desktop's structured Stop rule, its Stop-joining helpers and the
queue-sends-next rule to src/shared. Both sides now use them. The phone
counts its in-flight sends, so a conversation-stop host offers Stop before
the turn opens. Between queued turns the phone reads as working, and
commands that can't wait in line hold like they do on desktop. Untargeted
Stops are no longer joined on legacy hosts.
* fix(mobile): offer terminal Stop again when its Escape went nowhere
Stop hides Working on the phone like it does on desktop. The phone's Escape
can fail outright, though, while desktop writes to a local PTY. When no Escape
could have landed, the phone now drops that suppression. The streaming preview
also treats a pending echo as the transcript's tail, as desktop does, so an
earlier reply can't hide a new preview.
* fix(mobile): hold structured Stop while a send it covers is in flight
On desktop, Stop's held and Stopping state depends on whether the chat can
stop, and that includes a send still in flight. The phone now does the same.
Once Stop is pressed before the turn opens, the button holds and repeated
presses send no more untargeted cancels.
* fix(mobile): end the terminal Stop hold when a new message is sent
Desktop clears Stop's working suppression on an optimistic send, so a new turn
that keeps the same hook epoch (Claude with working subagents) still offers
Stop. The phone now does the same when a new pending echo appears.
* refactor(mobile): trim chat turn wiring after the desktop rule move
Drop the controller's unread nativeChatAgentWorking field. Re-render only when
a chat starts or stops having a send out. Keep the structured turn indicator
stable across stream frames. Type the Stop test table instead of casting.
* refactor(mobile): track structured sends in flight once for Stop and command holds
* test(mobile): re-record Stop goldens now that Stop settles with its Escape delivery
* fix(mobile): escape terminal image paths like desktop drops
* fix(mobile): keep launch-draft prefill over older user turns
* fix(mobile): rank @ file matches with desktop's fuzzy Quick Open search
* fix(mobile): use desktop's composer triggers for mid-prompt / and $ picks
Picked @ files are now quoted when the path has spaces.
* fix(mobile): send picked skill paths on structured chats
* fix(mobile): list the host's disk-scanned skills in the / and $ menus
The phone names the workspace and the host resolves its folder, refusing SSH
workspaces as desktop does; hosts without skills.discover-workspace.v1 list none.
* fix(mobile): forget a picked skill once its token is deleted
* fix(mobile): drop a picked skill as soon as its token is deleted, and retry failed skill scans
* fix(mobile): treat a picked leading skill as chat and stop failed skill scans from refiring
* test(mobile): drop the menu ranking case the shared picker tests own
* refactor(mobile): keep skill picks on the draft record and reuse shared send rules
A rejected send now keeps its picks and leading-skill origin on both send paths.
* fix(mobile): replace a re-picked skill's file and restore picks with a rejected send
* fix(mobile): read the chat agent for the skill catalog from the active resolution
* style(native-chat): format the composer autocomplete files
* fix(native-chat): import the picker's apply and send-classify rules from the shared composer module
* refactor(mobile): import the composer's draft-store hooks once
* style(mobile): format the notice row's style import
The free windows-11-arm and macos-15-intel lanes already set the matrix's wall
time; the paid 8-vCPU Blacksmith Windows lane finished minutes earlier for ~$48/day.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
check:changed --base=<ref> resolved a merge base for the gates it drives
directly, but the flaky-test ratchet subprocess re-derived its scope from
origin/main. On a checkout whose origin is a stale fork that pulled in
unrelated files and could fail the run. check:changed now forwards its
merge base via ORCA_CODE_QUALITY_BASE (the env the other changed-code
gates already honor), and the ratchet uses it before falling back to
origin/main.
Fixes#33763
Co-authored-by: Neil <neil@stably.ai>
Both arch jobs overlapped ~2.5 min of setup with qualification, then polled
for the template for 400-560 s on the most expensive runner we use.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Pin commit.gpgsign=false and tag.gpgsign=false in the throwaway repos
built by the release script tests so a global signing config cannot turn
lightweight fixture tags into annotated tags that abort with
"fatal: no tag message?".
The runs API serves at most 1000 results per query. On 2026-10-27 one hour
crossed that and the daily report threw. Halve any oversized interval until
each query fits.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(wsl): let an idle WSL distro stop once its orcad idle-exits
The WSL host's holder kept the distro (and the WSL VM) running after Orca quit, even with
nothing running in it. orcad now leaves a release request in ~/.orca when a managed idle stop
retired its terminal daemon; the holder waits for that orcad to exit, claims the request and
ends, so WSL can stop the distro. Live terminals, working agents, enabled automations or a
returning client keep the distro held as before. A host whose distro is stopped at startup
reads as stopped instead of unreachable, and the real-WSL lane gains a phase that proves the
release and records memory.
* fix(wsl): key the holder release on the WSL-host launch marker; name the lane phase
The real-WSL lane showed orcad idle-stopping with its daemon retired while the distro stayed
up: the setsid'd orcad's environment is not guaranteed to carry WSL_DISTRO_NAME, so the
release request was never written. Use ORCA_WSL_ORCAD_HOST, which only WSL-host launches set.
Phase 3 now carries its phase name and path filter, and records what kept a distro running.
* test(wsl): dispose the idle-release lane's guest transports
Each guest command left its exec loop running, a wsl.exe client that kept the distro up after
the holder had already let go (the receipt showed no holder, no orcad, no request left).
* test(wsl): record which wsl.exe clients keep the idle-release lane's distro up
* test(wsl): satisfy the flaky-test ratchet; feed the new idle port in the release test
* test(wsl): snapshot guest processes after launch to find what holds the session
* test(wsl): locate the step that leaves the zombie holding the idle distro
* test(wsl): log the idle-release lane's guest commands with times to place the zombie
* test(wsl): follow the remote runtime client's move
* test(wsl): record when each leftover WSL session init started
* fix(wsl): keep the distro while the user still runs anything, and never trip on the request file
Review of #33084:
- The holder now checks, before claiming a release, that no process of the user runs outside
the holder itself (session plumbing aside: systemd --user, (sd-pam), dbus-daemon). A detached
tmux, nohup job or dev server keeps the distro; it fails toward holding.
- A new holder drops any request left from before it started, so an older orcad's request can
never end a holder a client just asked for.
- orcad touches the request file only for WSL-host launches and never throws over it.
- The real-WSL lane proves a detached job keeps the holder and its request, and the distro
stops once the job ends.
* fix(wsl): a running container keeps the distro, whoever owns its shim
Docker's containerd-shim processes run as root, so the user-only scan missed them and a
release would have stopped running containers. Any containerd-shim process now keeps the hold.
* fix(wsl): read each process status only up to Uid; document what keeps the hold
Delta review of #33084: dash reads a status file a byte at a time, so stop at Uid. Document
that a root default user never releases, that per-user agents hold it, and the hidepid
exception. The lane's pkill no longer matches the shell running it.
* fix(orcad): materialize the app's agent-browser driver into the local slot
Replaces #34111's ORCA_AGENT_BROWSER_DRIVER hand-off, Windows profile copy and
claimed snapshot store. Packaging now references the app's own
resources/agent-browser-<target> from the orcad template manifest
(packagedBrowser, like packagedRipgrep), so the materialized slot carries it and
every local orcad launch finds it beside orcad-server.js. macOS signs the driver
before hashing; Windows reseals it after SignPath. The resolver now also tries
the musl driver name on Linux.
Refs #33846
* build(orcad): let a checkout keep template drivers with ORCAD_OMIT_AGENT_BROWSER=0
* fix(browser): focus newly opened image tabs
Focus the window before the webview so Command+W closes the new
image tab instead of the original tab after a plain image click.
Add tests for focus order, image loading, and close-shortcut forwarding.
Fixes#34114
* test(browser): verify image tab focus with native keyboard input
* refactor(ci): keep browser E2E routes within registry size limit
* fix(ci): enable background launch for native browser regression
* test(ci): include browser routes in isolated detector fixture
---------
Co-authored-by: Neil <neil@stably.ai>
devalue rejects Error objects, so when a NODE_RUNTIME_INCLUDE test file
failed to load, the pool's send() threw DevalueError and the run ended
without a summary. Give both ends of the IPC channel one codec with a
matching Error reducer/reviver (name, message, stack, cause) so the load
failure reaches the reporter as a normal FAIL.
Fixes#29854
Co-authored-by: Neil <neil@stably.ai>
`npx` and `npm run` set npm_execpath to npm's own npm-cli.js/npx-cli.js,
so resolvePnpmCliInvocation ran npm with pnpm's arguments. Skip npm's CLI
by file name and fall back to PNPM_HOME / pnpm on PATH. Route
build-workspace-packages and terminal layout parity through the shared
resolver instead of their own npm_execpath parsing.
Fixes#29438
Co-authored-by: Lance Woodson <65194841+woodsonl@users.noreply.github.com>