Commit Graph
16225 Commits
Author SHA1 Message Date
OrcaWinandOrcaWin ca3b0fdb02 test: remove redundant cases and strengthen state-transition assertions (#33309)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 17:05:58 -07:00
NeilandClaude 44f97fa958 fix(agent-status): keep a running Codex turn on paired and mobile views while /side is shown (#33308)
* fix(agent-status): keep a running Codex turn on paired and mobile views while /side is shown

Codex's terminal title follows the thread it displays, so opening /side idles the title while the main turn runs. The paired/mobile session projection read that idle title, newer than the main turn's last hook, as the turn ending and published a title-only done row with no prompt. A Codex row whose hooks report the main turn running now owns the title interval; the turn's end still arrives by hook or rollout.

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

* fix(agent-status): keep a Codex main turn waiting on approval through /side on paired views

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

* fix(agent-status): leave restored Codex rows to the existing title rules

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

* fix(agent-status): stop a running Codex turn at a PTY process replacement

A replacement process on the same PTY id invalidates its predecessor's title-interval evidence; record when that happened so a predecessor's running Codex hook cannot outlive it.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 17:01:30 -07:00
Neil ba43f8539c fix(runtime): a cold-restored terminal keeps its agent session without showing the old Done (STA-12043) (#33302)
* fix(runtime): a pane's new process does not wear its previous run's Done (STA-12043)

* fix(runtime): keep the superseded turn's segment and date process start at spawn request

* fix(runtime): take the identity row's segment from the turn clients were shown
2026-10-24 17:00:48 -07:00
Sunhy 759e536e5f Preserve concurrent edits during remote Hermes reinstall (#20631)
Hermes remote install only rewrites config.yaml when its content changed, so a stale snapshot can no longer overwrite a concurrent edit (matches local install).
2026-10-24 17:00:33 -07:00
Hoang Le QuocandNeil c580d7f2d3 fix(terminal): stop "104;255" leaking onto agent prompts after a tab switch (#31411)
A CSI scan now stops at ESC/CAN/SUB (as xterm's parser does), so a hidden-startup carry ending mid-CSI no longer swallows the next OSC's introducer and leaks `104;255` onto the prompt. Fixes #31410.

Co-authored-by: Neil <neil@stably.ai>
2026-10-24 16:59:59 -07:00
Neilandggbdpq d170b5a806 fix(opencode): accept OpenCode release ranges for launch-time model selection (#33307)
OpenCode model selection accepts per-major floors (1.x >= 1.18.30, 2.x >= 2.0.14; prereleases and 3.x refused) instead of exact version allowlists, so current releases launch with a picked model again. Fixes #26611. Supersedes #26683, #31985.

Co-authored-by: ggbdpq <67929842+ggbdpq@users.noreply.github.com>
2026-10-24 16:59:04 -07:00
NeilandDamHV 2a4f8e65d8 fix(agent-hooks): stop Claude agent-hook runs from minting phantom subagent rows (#33298)
Claude tool events from agent-hook runs (`hook-agent-<uuid>` ids, which never send SubagentStop) no longer mint phantom "Working - Agent" subagent rows. Fixes #27775. Supersedes #27776, #15326.

Co-authored-by: DamHV <251416543+DamHV@users.noreply.github.com>
2026-10-24 16:55:06 -07:00
Neil a02a635764 fix(agent-hooks): stop the first-work name generator from retrying without limit (#33299)
First-work branch/workspace-name generation backs off after a failed attempt (30s, 2m, then every 30m) instead of re-running on every working event, bounding cost to ~2 sessions/hour/workspace and recovering after a limit resets. Fixes #9705.
2026-10-24 16:41:50 -07:00
OrcaWinandOrcaWin 45bc63a187 test: strengthen persistence, session and failure-result assertions (#33303)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 16:40:41 -07:00
Neil d778369541 refactor: group runtime session ownership (#33301) 2026-10-24 16:40:25 -07:00
866ea1b579 fix(kimi): read usage from the scoped OAuth slot Kimi Code 2.x records in config.toml (#33290)
Kimi usage resolves the credentials slot and usage URL exactly like Kimi Code (resolveKimiCodeRuntimeAuth / resolveKimiCodeOAuthKey), so scoped-environment logins (e.g. api.kimi.ai) stop showing "session expired". Fixes #24176. Supersedes #24200, #30494.

Co-authored-by: Paramon <7700920+Paramon@users.noreply.github.com>
Co-authored-by: Bensonluo <18586615+Bensonluo@users.noreply.github.com>
2026-10-24 16:31:49 -07:00
NeilandBen Grynhaus e18752acea fix(terminal): answer a background pane's startup queries while its paired view is paused (#33273)
* fix(terminal): answer a background pane's startup queries while its paired view is paused

A paired desktop pauses the output of a terminal it is not showing. The host
drops that output, but it still counted the paused view as the one answering
terminal queries, so main's query responder stayed silent too. A fish started
in such a pane (for example by `orca terminal create`) sent DA1, got no
answer, and stalled 10 s before warning.

A multiplex stream now registers with its pause state, and main answers the
queries of output a paused view drops, exactly as it does for output the
hidden-pane gate drops. A view that receives the output still answers it, so
each query is answered once.

Fixes #32944

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>

* fix(terminal): answer queries in output a pausing remote view discards unsent

A chunk ingested while the view was still receiving yields its queries to
that view. If the view then pauses, the host drops the chunk from the
output batcher, or clears it from the ack backlog, before the view ever
parses it. Nothing answered those queries.

The pause now carves the query bytes out of what it discards, with the
same extraction the hidden-pane gate uses, and hands them to main's
existing responder. The responder answers only while no other view is
receiving that terminal's output.

Refs #32944

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>

* revert: answer queries in output a pausing remote view discards unsent

This reverts commit 3fc500fe09. Answering those queries later through
main's emulator injects bytes into its live parser. That can cancel a
partial escape sequence, it answers cursor and mode queries against
state that has since moved on, and it can answer twice when two views
pause one after the other. An exact fix has to capture the replies when
the bytes are first parsed and track delivery per view, so it is left as
a follow-up.

Also renames a test helper to say what it reads.

---------

Co-authored-by: Ben Grynhaus <ben.grynhaus@wiz.io>
2026-10-24 16:14:02 -07:00
OrcaWinandOrcaWin 2479c5e20f test: consolidate redundant checks and verify behavioral boundaries (#33292)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 16:09:45 -07:00
Pr1p 04e07a6643 fix(opencode): prevent stale credential status after save (#33081)
OpenCode Go credential status: a late initial read can no longer overwrite a save/clear result, and pending/failed reads show Checking…/Status unavailable instead of Not saved. Fixes #33068.

Co-authored-by: Pr1p <48266347+Pr1p@users.noreply.github.com>
2026-10-24 16:08:47 -07:00
Neilandoutof-place c788a071d4 fix(terminal): fold Codex's inner thread-name spinner frame so session tabs stop republishing per tick (#33291)
Fold a standalone copy of the leading spinner frame in agent titles (Codex 0.155–0.163 `⠙ renaming... ⠙ | x`) so the title tracker stops republishing session tabs every 100ms. Fixes #27598. Supersedes #27599.

Co-authored-by: outof-place <69466055+outof-place@users.noreply.github.com>
2026-10-24 16:08:28 -07:00
Neilandzhangxaochen 70d9c97b98 fix(rate-limits): find agy for Antigravity usage in the env agent launches use (#33289)
Antigravity usage probe uses the same host shell env agent launches use, so agy is found under Git Bash on Windows. Fixes #31459, #28036. Supersedes #32032, #31809.

Co-authored-by: zhangxaochen <3416667+zhangxaochen@users.noreply.github.com>
2026-10-24 16:08:07 -07:00
NeilandDavid De Smet 2aae4577da fix(claude): restore scrubbed pane identity in the managed Claude hook (#33286)
Claude's hook script refills ORCA_PANE_KEY / ORCA_AGENT_LAUNCH_TOKEN from the scrub-safe aliases when CLAUDE_CODE_SUBPROCESS_ENV_SCRUB strips them, so scrubbed Claude panes report status and resume again. Fixes #31832. Supersedes #31834.

Co-authored-by: David De Smet <2607383+daviddesmet@users.noreply.github.com>
2026-10-24 15:54:59 -07:00
Neil 9c8ce8d818 refactor: group repository execution settings (#33261) 2026-10-24 15:46:51 -07:00
NeilandBingZ c344f3f0ab fix(source-control): drop Kimi flags Kimi Code rejects (--quiet, --thinking) (#33285)
Kimi Code (the current `kimi`) rejects --quiet/--thinking/--no-thinking; send only --prompt [--model]. Fixes #14771. Supersedes #14790.

Co-authored-by: BingZ <11211020+bbingz@users.noreply.github.com>
2026-10-24 15:44:18 -07:00
Neil 08ec2b3aac fix(orcad): a liveness probe must not mistake its own shell for orcad (#33158)
* fix(orcad): a liveness probe must not mistake its own shell for orcad

After a host or distro restart the recorded orcad PID is free, and the probe's own
'sh -c' (whose argv carries the slot paths) can be given it. The argv identity
check matched and kill -0 succeeded, so the probe answered LIVE: a wake skipped
the start and the next probe found nothing. The probe now treats its own PID, or
any shell process, as a reused PID.

* fix(orcad): only the probe's own shells count as reused, never another shell

Review: an older launcher (before exec) recorded a shell that waits on a live
orcad, and the setsid launch path holds an sh between the PID write and exec;
calling every shell reused turned those into DEAD, which permits GC and relaunch.
Only the probe's own shell and the shell wrapping it are now excluded.

* test(orcad): drop an unused parameter

* test(orcad): reap the waiting shell's node child after the liveness test

* test(orcad): keep the wrapping shell alive so the parent-PID case is exercised on dash
2026-10-24 15:33:52 -07:00
OrcaWinandOrcaWin c1a33b7372 test: assert persisted state and observable terminal and mobile behavior (#33282)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 15:30:18 -07:00
NeilandClaude 05165e759d fix(agent-status): keep Codex status on the main task through /side chats after a restart (#33275)
* fix(agent-status): keep Codex /side threads from taking over the pane after a restart

The /side guard keyed on transient caches (the parsed rollout and the
listener's own row), so after an Orca or relay restart a side thread's
hooks claimed the pane and its Stop showed the pane finished while the
main turn was still running. Decide by the stored row's thread instead,
on the execution host and again when a relayed hook arrives.

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

* fix(agent-status): recognise Codex side threads on the execution host only

Record ephemeral forks from their own SessionStart and fall back to the stored row's thread, so the host keeps dropping /side hooks across restarts without a desktop rule that cannot tell a fork from a new conversation.

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

* fix(agent-status): keep a running Codex turn when a relay restarts mid side chat

A relay that restarts during a /side chat has lost the fork's SessionStart, so the desktop keeps a running main turn against the rest of that thread's relayed hooks. Any relayed SessionStart still passes, so a new conversation can replace the row.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 15:13:35 -07:00
OrcaWinandOrcaWin 6b469405ec test: expose masked persistence and send-handler regressions (#33276)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 15:10:18 -07:00
Neil bfbe9637e9 Run generated worktree cleanup during automatic dev maintenance (#33277) 2026-10-24 15:09:18 -07:00
Neil 8c95a9bb5d fix(daemon): refuse input to a live terminal whose PTY stopped taking it instead of reporting delivery (#33269)
* fix(daemon): report input to a live terminal whose PTY stopped taking it instead of claiming delivery

* fix(daemon): treat master-retiring errors as closed input on every platform; keep same-incarnation close across spawn reply

* fix(daemon): track closed input per incarnation so a replacement's early report survives its spawn reply
2026-10-24 15:06:40 -07:00
NeilandSahilZ0810 0c27fd5f91 fix(ssh): move a managed tunnel off an unbindable saved local port (#33271)
* fix(ssh): move a managed tunnel off an unbindable saved local port

When the saved local port of a managed SSH server's tunnel can no longer
be bound (EADDRINUSE from another listener, or EACCES from a Windows
excluded port range), pick a free port, save it with the pairing and the
deployment link together, and build the tunnel again. The pairing, server
identity, remote port and projects are unchanged.

A move interrupted between its sidecar and store writes reads the moved
port from the stored endpoint, so the deployment link is never dropped.

Fixes #33182

Co-authored-by: SahilZ0810 <sahil@zamp.ai>

* test(e2e): drop squatter connections so teardown never waits on them

* fix(ssh): move the tunnel port before pairing repair and retire old transports

Move outside buildWithPairingRepair so a refusal on the moved port is
repaired against the revision the move saved, and retire transports and
status owners still dialing the old port, as a repair does.

* fix(ssh): refresh maintenance pairings on the current tunnel port

Update, rollback and restart reread the deployment's local port when they
save the new pairing, since the census before them may have moved it.

* fix(ssh): let concurrent tunnel builds follow a port another caller moved

---------

Co-authored-by: SahilZ0810 <sahil@zamp.ai>
2026-10-24 15:03:39 -07:00
Neil 2679464ead fix: let capable paired hosts plan composer terminal agent launches (#32192)
A workspace created from the composer on a paired host launched its terminal
agent with the desktop's command, arguments and environment. Hosts that
negotiate agent.launch.v2 now get startupAgent (and startupPrompt) and build
the launch through their own planner and settings. Older hosts, failed probes,
hosts that predate the agent's name, linked-item drafts and VM recipes keep the
desktop-built command. When the host planned the launch, the window never runs
the desktop plan or its own agent recovery, and says it couldn't confirm the
agent started if the host reported no agent terminal.
2026-10-24 15:01:29 -07:00
Neil 293bfe795d fix(browser): closing a host page retires a window-saved tab whose ids differ (STA-11998) (#33266) 2026-10-24 14:55:44 -07:00
Neil c197b9f3f1 fix(new-workspace): search and offer Linear on the same host in Create worktree (#33270)
* fix(new-workspace): read Linear status on the host the search runs on

Create worktree searched Linear on the selected repo's host but offered the
Linear tab from the focused host's status. With Linear connected only on the
desktop and a project on a paired server, the tab appeared and every search
silently came back empty. The repo-derived Linear source now reads its own
host's status, as explicit sources already did, so the tab and the selected
workspace follow the host that will answer.

* fix(new-workspace): keep Linear status per host while it re-reads, and treat loading as unknown

The host status read is keyed by host, not project scope, and a same-host
re-read (repo switch, workspace change, reconnect) keeps the last status
instead of reading as disconnected, so the Linear tab and the chosen mode no
longer reset. While the first read is pending, a chosen Linear tab stays
selected and a pasted Linear URL keeps blocking Enter. The credential-error
test now stubs the repo host's status read it depends on.
2026-10-24 14:49:24 -07:00
81a6cea193 fix(cli): say a headless terminal create found no workspace, not that the window is missing (#33264)
* fix(cli): say a headless terminal create found no workspace, not that the window is missing (#33044)

On a windowless `orca serve` host, `orca terminal create` from a folder
outside any workspace failed with "No renderer window available". The
create falls through to the window-only path because no workspace was
resolved, so name that and point at --worktree instead. Behavior is
unchanged; only the error message for this already-failing create.

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

* fix(runtime): pass the no-workspace message to the window lookup instead of relabeling every error

A catch-all would rename any other window-lookup failure as a missing workspace.

---------

Co-authored-by: tnfhrnsss <tnfhrns@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:46:49 -07:00
Neil e2b0e9a19e fix(runtime): a host status publication keeps the probed capability readable (STA-11921) (#33263)
* fix(runtime): a status publication that answers a pending probe leaves the capability readable (STA-11921)

* fix(runtime): keep negative-capability re-probe across a published replacement; split the test

* refactor(runtime): one compatibility-entry factory keeps the cache under max-lines
2026-10-24 14:45:31 -07:00
OrcaWinandOrcaWin 621a09def5 test: repair masked assertions and remove eight low-value checks (#33268)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 14:43:09 -07:00
Neil 2c500bffbe perf(orcad): start managed servers with a smaller V8 young generation (#31494)
* perf(orcad): start managed servers with a smaller V8 young generation

* test(orcad): tighten the launch byte ceiling to its measured size
2026-10-24 14:38:14 -07:00
NeilandClaude ad991175da fix(runtime): a split agent tab's shell panes restart as shells, not fresh agents (#32418)
Starting a saved pane with no live terminal (a paired client opening it, or a restore)
fell back to the tab's launchAgent for every pane with no recorded session, so a shell
split beside an agent pane came back as a fresh agent. The host's tab projection now
publishes the tab-wide launchAgent only on a sole pane, as the window already does, so
the pane start and every reader of that projection see the same per-pane evidence.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 14:35:02 -07:00
Neil 333be496a4 fix(ssh): Reconnect & Delete only deletes the workspace you selected (STA-11912) (#33253)
* fix(ssh): Reconnect & Delete stays pinned to the workspace the user selected (STA-11912)

* fix(ssh): say the workspace list changed and clear the shared delete state on a send-time refusal
2026-10-24 14:33:56 -07:00
NeilandBearMett 917899bf8d fix(runtime): stop paired desktops republishing mirrored editor tabs (#33259)
* fix(runtime): stop paired desktops republishing mirrored editor tabs

When two desktops pair both ways, B mirrored A's editor tabs and then
published them in its own session inventory as if B had opened them. A
then applied B's copy, which took over A's own tab.

isMobilePublishableOpenFile now skips rows marked
mirroredFromRuntimeSession, the marker is part of the open-files sync
projection so a flip republishes, and the marker is saved with the
session and restored so a relaunched client does not republish its
restored mirrors before the host's first snapshot.

Based on #25877 by BearMett.

Co-authored-by: BearMett <agomdole@gmail.com>

* fix(runtime): keep restored mirrors through unpublished frames and out of headless publication

A restored mirror now carries its marker, so an unpublished host frame
must not cull it (and its recovered draft). The windowless publication
skips persisted mirrored rows for the same reason the window does.

Co-authored-by: BearMett <agomdole@gmail.com>

---------

Co-authored-by: BearMett <agomdole@gmail.com>
2026-10-24 14:28:33 -07:00
Neilandethereal49 976fa11e81 fix(github): send a paired server its own repo id when an SSH row shares the path (#33257)
* fix(github): route runtime repo paths by execution host

When a repo path is registered on both SSH and a paired runtime, path-only lookup could send the SSH-side repo ID to the runtime. Prefer the row owned by the target runtime, then retain the original first-path fallback.

AI-Assisted: OpenAI Codex

Signed-off-by: ethereal49 <hongyeluo49@gmail.com>

* fix(github): resolve the runtime repo on the target host by the caller's repo id

getRuntimeRepoTarget now takes the caller's repo identity and resolves the
row through the repo lookup index, preferring the row on the target runtime
with that id, then the row on that runtime for the path. This keeps the
refresh sweep's repo reads linear (a per-call list scan broke that ratchet).
Legacy rows with no host stamp still fall back to id-or-path lookup.

Removes the test that pinned the off-host fallback; it asserted sending
another host's repo id to the server.

---------

Signed-off-by: ethereal49 <hongyeluo49@gmail.com>
Co-authored-by: ethereal49 <hongyeluo49@gmail.com>
2026-10-24 14:13:50 -07:00
NeilandClaude 7a0c7af69d fix(ssh): sign an SSH conversion's manifest as the host parses it (#32369)
The conversion hashed the exported objects, but the manifest parser keeps only the fields it knows.
A runtime-only field in the live session (gone after the next load) made the conversion's own
journal and the host refuse the digest until the app restarted. Sign the parsed form of the JSON.
Manifests without such a field sign exactly as before.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 14:10:08 -07:00
OrcaWinandOrcaWin 5be204618a test: strengthen rejection and callback coverage, remove redundant checks (#33256)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 14:09:35 -07:00
OrcaWinandOrcaWin c90d0f93c0 test: exercise real mobile controller and repair weak assertions (#33248)
* test: exercise mobile controller and strengthen audit assertions

* test: use array syntax required by React Doctor

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 13:54:31 -07:00
Neil 5ee74f4f83 refactor: group browser cookie source modules (#33250) 2026-10-24 13:51:37 -07:00
0302e0a5fd fix(git): preserve unmerged branches with no net file changes (#32181)
* fix(git): preserve unmerged branches with no net file changes

* fix(workspace): show launch actions in empty tab groups (#32182)

* fix(workspace): show launch actions in empty tab groups

* fix(workspace): resolve empty-pane browser policy by owner

* Scope daemon inventories for safe worktree teardown (#32171)

* Fix scoped daemon inventories without changing terminal input routes

* Serialize daemon owner inventory refreshes safely

* Serialize daemon owner inventory refreshes safely

* test(daemon): protect baseline inventory budgets and recovery

* fix(daemon): clear route proof during synthetic exits (#32196)

* docs: condense test anti-pattern guidance (#32201)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* refactor: colocate repository draft input and tests (#32155)

* test: consolidate discovery coverage and replace copied oracles (#32211)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: retire copied oracles and implementation inventories (#32206)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: remove classification inventories and verify settled boundaries (#32216)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: keep explicit regressions without legacy algorithm copies (#32218)

* test: keep explicit regressions without legacy algorithm copies

* test: use Number.NaN in latency fixtures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* ci: avoid slow Ubuntu mirrors during WebKit setup (#32205)

* ci: reuse bounded Ubuntu mirror setup for WebKit

* ci: preserve Ubuntu setup for dispatched older refs

* perf(renderer): scope runtime connection and parking subscriptions (#32199)

* fix(chat): allow visuals to declare open without breaking scripts (#32226)

* fix(native-chat): keep queued messages in the chat's own record, so a refused one can't be skipped (#30418)

* fix(native-chat): a refused queued message still blocks the queue when its card write fails

Every reader of a queued card goes through one derived view of the journal's dispatch facts,
so a card whose message was refused reads as returned even when its card-table update and heal
both failed. One decision and one writer store the derived state for every action.

* refactor(native-chat): keep each queued message in the chat's journal, not a second table

A queued message lived in its own SQL table, and a hook that may be skipped copied
each refusal from the journal into it. When that write failed, a refused card
vanished: the cards behind it sent, and its Send and Delete did nothing.

Each queued message is now a `queue` row in the chat's journal beside the
submission that hands it off and the dispatch row that answers it. The reducer
folds the card from those rows, so a refusal returns the card and holds the
cards behind it with nothing written, and replay reads the same. Every action
writes its own rows in one transaction. Cards an earlier build stored are read
through the same classification until they move: each action moves its own
card, and a bulk import runs once per open. A new epoch restates every card
still owed an answer in the transaction that deletes the old rows.

The settlement hook and its savepoint, the open-time heal, the drain's heal and
the derived-card view are gone. The published card list is unchanged.

* test(native-chat): adapt the queue tests to cards kept in the journal

Cards are written through the journal, so tests queue them with their body
alone and hand them off under their real fingerprint. Tests of the removed
settlement hook, heal and SQL store are deleted; the text-update spec now
covers the edit decision alone, and a test of an earlier build's stored holds
seeds the legacy table and reads the imported cards. Rolling over an epoch now
keeps a card whose hand-off was never handed over, so that test checks the
retried id never sends rather than that nothing sends.

* test(native-chat): a refused card holds the queue through rewind, a new epoch and a restart

One whole-flow test through the host: the refusal writes no card row, a rewind
that retires the refused hand-off and a rolled-over epoch keep the card
returned, a restart and Resume send nothing behind it, its own Send refused
again still holds the queue, and its Delete releases the next card in order.

* test(native-chat): an earlier build's queued cards move into the journal once, each read the same

One mixed-import test through the host. The first open's import fails and
moves nothing, yet every card reads as it will; Delete and Send each move only
their own card. The next open moves the rest in one transaction, writing a
refusal only where its hand-off is gone, nothing for answered cards, and leaving
an unreadable row on disk. A later command turn spends a moved command card, a
third open writes nothing, an old Delete replays from its receipt, and a chat
with only answered legacy rows clears them in a delete-only transaction.

* test(native-chat): adapt typechecked tests to cards kept in the journal

The row-kind table lists a queue row, the clear and legacy-import tests
match the current signatures, and the downgrade test keeps only the
leftover-send case: a queued card is now a newer-version journal row an
older build keeps read-only, and users don't downgrade.

* refactor(native-chat): drop the journal row writer's unused transaction hook

No caller passes one since the queue's savepoint bookkeeping left.

* test(native-chat): downgrade probes write rows at the version older builds read

The highest version this build reads is now 4, the queue row's. A newer
build's row in these probes is written at version 3 so the older build
meets its kind, not a newer version, and the Stop-event probe hands the
older build the rows as stored, since a read raises each row's version.

* test(native-chat): unit tests follow the queue row's version 4

The legacy-import test opens real SQLite, so it runs in the Node runtime
project. The newest version this build reads is now 4, and a read raises a
row's version to it, so the turn-lifecycle test no longer pins the
in-memory version (the written one stays pinned by journalRowSchemaVersion).

* fix(native-chat): one batch echoing two refused queued messages spends both

The per-row delivery check returned only the first waiting card an echo
proved delivered, so when one settlement carried echoes for two cards the
second stayed waiting and was sent again. It now returns every card the
row proves delivered, and the fold withdraws each; replay folds the same.

* fix(native-chat): a rewind moves an earlier build's cards into the journal first

While the open's import had failed, an earlier build's card that read as
refused through its hand-off lost that reading when an in-place rewind
retired the hand-off: it disappeared and the card behind it sent. The
rewind now adopts every legacy card ahead of its tombstones and deletes
their rows in its own transaction, as an epoch replacement already does,
so the fold latches each card's reading before its hand-off is gone.

* fix(native-chat): keep an earlier build's card whose hand-off is still unconfirmed

The import dropped a legacy card handed off as a send whose delivery was
unknown, though the fold keeps such a card linked and a later refusal
returns it. The import now keeps every card whose hand-off a later row can
still answer, through the same check the reducer folds dispatch rows by.

* fix(native-chat): one delivery echo withdraws at most one queued card

One provider echo proves one delivery, yet it withdrew every waiting card
with the same text whose hand-off was refused, so a second identical card
was lost. The fold now pairs each echo with one card: on its own row the
earliest-positioned match no other echo of that row took, and for a card
that waits again the earliest applied echo no card has taken. Paired echoes
live in the fold's state, so live and replay pair the same; readers of
cards not yet imported work on a copy.

* fix(native-chat): a queued message handed to the agent is sent again only on proof it never got it

Pressing Stop after Orca handed a queued or steered message to the agent,
before the agent confirmed it, put the card back in the queue, and Resume
or the next send sent it again. A check matching the agent's echo of the
text was the only guard against a double send, and it kept trading a
resend for a lost card. Whether the card waited again was decided by the
rejection's category, not by proof the agent never got the message.

A refused hand-off now waits again only where Orca proves the agent never
received it: the host still held it, or whoever settled it recorded that.
A failure still returns the card and holds the cards behind it. Anything
else may have reached the agent, so the card is spent and its message
reads as sent, derived on every fold from the stored rejection: with the
turn it was answered into, as that turn's unconfirmed message where it was
handed over; withdrawn before any turn opened, as a person's own send a
Stop took back. The reopen mark ignores a recovered doubt, and the restart
reconciler no longer judges a queued hand-off unsent from what history
lacks. The echo text matching and its pairing are deleted.

A close or quit while the agent starts settles sends with the unrun hold.
The host hands nothing to a child before its start answers
(structured-agent-session-delivery-loop.ts: a handover needs phase
ready), so such a send went to an earlier child and may have run: it now
reads as sent instead of waiting again.

* fix(native-chat): Claude's report that it removed a queued message unrun lets the card wait again

Claude says when it removed a send without running it: the interrupt
receipt's cancelled list, or a command_lifecycle cancelled, discarded or
refused frame for a command that never started. Every one settles through
rejectClaudeDispatchWaiters, which now marks the settlement unrun. For a
queued card's hand-off the host records that as proof the agent never got
it, so the card waits again under the queue's pause. A direct send keeps
the plain refusal: there the proof would owe it as stopped history.

* fix(native-chat): an earlier build's card stopped after hand-over follows the same rule on upgrade

Earlier builds put a card back to waiting after any refusal without a
verdict and cleared its link, so a card the agent may already have would
be sent again after the upgrade. The card's own hand-offs in the journal
decide now: unchanged and last sent as one that may have reached the
agent, it is spent on that hand-off and its message reads as sent; edited
or sent again since, it is held for its own Send under a host-only marker
that publishes as a plain hold; with no hand-off in doubt, including one
that never left Orca, it waits as before. The import writes a card spent
on a hand-off in doubt, so its message shows.

* fix(native-chat): read a queued row's hand-off before the lookup that uses it

The lookup's callback lost the narrowing of the row's change, which the
mobile test typecheck rejects.

* test(native-chat): a card steered into a turn a Stop ends stays sent in the Stop tests

Three tests pinned the old rule, that a card handed into a running turn
comes back to waiting when a Stop ends it. Under the new rule nothing
proves the agent never got it, so the card is spent: with the turn named,
its message reads as that turn's unconfirmed one; without, as a send the
Stop took back. Neither is ever sent again.

* fix(native-chat): a queued message stopped before Orca handed it to the agent waits again

A Stop that aborts a hand-off after its row is written but before the agent call now records
the withdrawal as never reaching the agent, for queued hand-offs only, so plan D's rule returns
the card to waiting under the Stop's pause instead of spending it. Direct sends keep the plain
withdrawal (the flag would owe them as stopped history). The agent's own refusal while aborted
is kept rather than overwritten, and an abort thrown during the call stays in doubt.

* fix(native-chat): a queued message stopped while the agent prepares it waits again

The abort signal reaches only an adapter's preparation (its options, the previous turn opening),
never its write, so the signal's own reason thrown back is the adapter saying nothing was
written. The handover now reads that, for queued hand-offs only, as never reaching the agent,
so the card waits again under the Stop's pause. Claude no longer wraps that abort as an option
refusal, which had returned a stopped card as a refused one blocking the queue. A real refusal
keeps its cause and its barrier; any other throw stays in doubt and is never resent.

* test(native-chat): the usage-limit Continue backlog case writes its card through the journal queue

Main's #30396 wrote its backlog card with the stored fingerprint and host-instance stamp the
journal queue derives instead; the card the Continue must yield to is the same waiting card.

* test(claude): the real-CLI queued Stop expects the unrun mark Claude's cancelled-unrun receipt carries

* fix(native-chat): retain spent legacy messages when import fails

* Bound per-write chat selector work and guard ownership scans (#32109)

* Guard per-write chat selector work with shared ownership indexes

* Use checked sender session identities in the scale fixture

* Read each indexed tab row once during snapshot rebuilds

* Fix lexical binding and cache matching in selector lint rules

* Keep behavioral selector guards and resolve imported bindings

* Require selector binding definitions to declare functions

* Key performance subscribers by saved chat scope

* Show activity status with worktree-style counts (#26443)

* Show prioritized agent counts on the activity bell

* Move activity badge into the corner and shrink its count

* Use a fixed side column for activity status and counts

* Fix activity count spacing and status alignment

* Use worktree-style superscript counts for activity

* Keep activity count positions and update only number styling

* Keep idle layout fixtures out of active agent status updates

* Show child workspace statuses in the children toggle (#32224)

* Show child workspace status counts in lineage toggle

* Give child status summaries room and wrap narrow layouts

* Use solid working dots and keep child summaries on one line

* Restore children icon and clarify worktree tooltip

* Keep child totals clear of status counts in narrow layouts

* fix: preserve workspace review references when clearing active reviews (#32263)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* Saving a file, then closing that tab mid-save, no longer leaves its other tabs looking clean (STA-11786) (#32259)

* fix(editor): settle a save's shared record when the saving tab closed mid-write

With one file open in two workspaces, closing the saving tab while its write was in
flight left the other tab's disk baseline, dirty flag, draft and loaded text on the
pre-save version: every completion step looked the closed tab up by id and found
nothing, and the saved event listed only the closed tab as the record's views. An undo
back to the old text then read as clean and closed without asking.

The write now captures its record id with no await before the write, and completion
(rebaseline, dirty/draft settle, conflict clear or refusal banner, saved event's view
list) goes through a live view of that record, preferring a writable one. The save
runner and the write-lock wait do the same, so a save queued by a tab that closes
still writes through the record's remaining view. The chat visual's saved-event
listener also falls back to the record's views.

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

* fix(editor): follow a closed saver's record through re-keys, kept records and listeners

Review follow-ups to the closed-saving-tab fix:

- Completion now finds the record through the view ids captured before the write,
  not the record id: a re-key mid-write (an unpinned runtime tab resolving its
  pairing) keeps views but changes the id, which brought the original bug back.
- A fallback writer (asking view closed while queued or waiting on the file lock) is
  chosen like autosave/restart's writer: writable and on a current connection, so a
  stale-connection sibling no longer throws when a current view exists.
- With no view of the record left, a kept record (no-prompt removal) is dropped when
  the write saved its text, else rebaselined; other open views of the file (a
  detached peer) take the written baseline, keeping their edits.
- A save may mark a record clean through a read-only or CSV-preview view (markFileDirty
  `written`); such views still never become dirty.
- Markdown preview refresh and the CSV pending-cell listener match the saved event's
  record views, not only the saving view's id.

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

* test(editor): name the closed-view save harness as a test fixture

The focus-read ratchet scans non-test sources; the `.test-fixture.ts` suffix marks this
harness as test-only like the repo's other fixtures.

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

* test(ci): list the closed-view save rig as a unit support bridge

The rig builds on the autosave test fixture, which takes the editor slice harness, so
the routing check saw it as a product consumer of that harness. Bridges are an explicit
list, like the browser drawn-from-main fixture.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Show task names for Codex subagents (#32243)

* fix: name Codex subagents from rollout metadata

* Hide generic default role in child agent rows

* Reject non-regular Codex identity transcripts without blocking

* [comment audit] Shorten preload comments (#32257)

* chore: audit preload root comments and track repository coverage

* docs: track repository comment audit coverage

* chore: keep comment audit tracker local

* chore: shorten preload API contract comments

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix: offload macOS deletion spawns and reduce branch cleanup (#32227)

* fix: reduce worktree deletion branch cleanup overhead

Refresh only the remote base branches needed to prove a branch merged, with automatic maintenance disabled. Skip merge simulation when no common ancestor is available and preserve the branch when its remote base cannot be refreshed.

* fix: preserve fetch mappings and live branch cleanup fallbacks

* fix: spawn macOS worktree deletion commands off the main thread

* test: remove redundant cases and start comprehensive value tracker (#32269)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix(web): the browser client advertises the desktop's chat capabilities, so its host opens chats (#32253)

* fix(web): the browser client advertises the desktop's chat capabilities, so its host opens chats

The browser client's handshake never named agent-session.structured.v1, so a paired
host refused every agentSession call with structured_agent_session_unsupported and
withheld chat tabs. The desktop window, the Electron remote handshake and the browser
client now build their lists from one shared array of what the renderer's chat implements.

Fixes #32145

* refactor(web): keep the browser client's capability list beside the desktop's in shared

The host-side test parses both handshakes with the host's own parser, which the node
typecheck project can only import from src/shared.

* test(web): assert close-intent support in the auth frame, not the whole capability list

* fix(terminal): a timed-out CLI split names its pending pane, and a retry can never re-run its command (#32278)

* fix(terminal): a timed-out CLI split names its pending pane and can be retried without splitting again

Fixes #32169

* fix(terminal): a blank retry id or a closed pane never re-runs a split's command

* refactor(terminal): a split retry only waits for its pane and never asks the window again

* fix(terminal): a split missing from the graph is reported pending, never closed

* fix(ssh): never block or over-read on an SSH identity file (#32274)

Every place Orca reads an SSH key or identity file now goes through one
reader that refuses directories, FIFOs and devices (including through
symlinks) with a nonblocking open, and caps the read at 1 MB. An auth
failure now names any identity file that was skipped and why.

Fixes #31965. Reported by @Huge, who also proposed the first fix in #31966.

Co-authored-by: Martin Vlach <mr.huge@seznam.cz>

* test: finish fixture work before unit-test cleanup (#32285)

* fix(mobile): keep a terminal tab's chat identity while its handle is unchanged (#32251)

* fix(mobile): keep a terminal tab's chat identity while its handle is unchanged

A status frame that omits agentType or providerSession flipped chat back to the
terminal for tabs with no launchAgent (#31977), and left a launched tab with no
session to read (#32134). Mobile now remembers the last resolved agent and
session per tab and handle, so a missing field is treated as missing evidence.

* fix(mobile): keep remembered chat read-only and drop it on relaunch

A chat resolved only from this phone's memory may sit on a bare shell, so its
writes stay locked until live status confirms the agent. A live agent frame
without a session no longer inherits the previous conversation, and the
latest remembered agent outranks an older launch hint.

* fix(mobile): gate native-chat image sends on a confirmed live agent

An image send from a chat only this phone remembers could paste into a bare
shell; it now shares the confirmed-agent gate with text sends.

* fix(mobile): type remembered chat identity structurally

Importing MobileSessionTab into the eligibility module pulled React Native
globals into the e2e program through nested-agent-idle-session-owner.spec.ts,
retyping setTimeout as number there.

* fix(terminal): never present a half-built synchronized-output frame on reveal or recovery (#32281)

Settled reveal and every other atlas recovery forced the current xterm buffer
onto the screen even while a TUI held DEC 2026 synchronized output. Codex
erases rows inside each held redraw, so a reveal mid-redraw showed a blank
pane for a frame (#32198).

Viewport presents now have one mode: they keep the last coherent canvas and
let xterm present when the TUI closes the frame, bounded by xterm's 1s
synchronized-output safety timeout. Structural replay keeps its retained frame
up until an open synchronized frame closes, bounded by its existing hold cap.

Co-authored-by: YEONGUK LEE <contact@abruption.dev>

* refactor(renderer): colocate task page pagination rules (#32297)

* perf(test): bundle Zod once per isolated Vitest invocation (#32298)

* Use incremental tsrs locally and update Bun to 1.4.4 (#32276)

* Use incremental tsrs for local typechecks

* Update pinned Bun to 1.4.4

* fix(deps): bump dompurify to 3.4.17 and docs next to 16.4.1 for security advisories

dompurify 3.4.17 patches GHSA-cg2g-xh64-v4q7, GHSA-mrv2-3pqr-j5jr and
GHSA-c6gr-qvv4-5v3f in the root and mobile lockfiles. next 16.4.1 patches
GHSA-2464-fhr4-w9xw, GHSA-673q-mjm6-r5xr and GHSA-p3m5-59mp-jv5r in the
docs site.

* Prepare mobile bundle for smoke benchmarks

* Give native compiler benchmarks a larger ARM runner

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* test: trim redundant coverage and fix vacuous assertions (#32305)

* test: remove redundant cases and repair behavioral assertions

* docs: reconcile audit records with current main

* fix(deps): apply security updates from #32101

Apply the six-file DOMPurify 3.4.17 and Next.js 16.4.1 patch from PR #32101 to resolve the dependency security gate for this test audit. Original patch by Brennan Benson.

* test: satisfy changed-line quality gates

Add required braces and replace filter indexing with find. Rerun both CI quality gates and the 27 affected relay tests; refresh audit hashes and record the corrections.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* fix(ci): retain ambiguous unit-test import consumers (#32366)

* test: exercise real boundaries and trim redundant cases (#32353)

* test: replace redundant checks with observed behavior

* docs: reconcile completed audit batches and upstream review

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix(git): retain zero-net history with current merge-base guards

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Martin Vlach <mr.huge@seznam.cz>
Co-authored-by: YEONGUK LEE <contact@abruption.dev>
2026-10-24 13:26:44 -07:00
OrcaWinandOrcaWin e802f4db7c test: repair behavioral coverage and remove export inventory (#33246)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 13:22:32 -07:00
Neil a1246ea166 refactor: colocate Task Page source availability hook (#33245) 2026-10-24 13:21:58 -07:00
Neil ceec67b8ec fix(wsl): no synchronous distro-home probe at startup, and no 5 s kill of a booting distro (#33179)
* fix(wsl): no synchronous distro-home probe on the main process at startup, and no 5 s kill of a booting distro

Follow-up to #33175, from the startup wsl.exe trace:
- getAllowedRoots (file-access authorization) resolved a WSL-mirrored workspace
  root with the synchronous home probe; it and isPathAllowed are now async (every
  caller already was).
- skills:discover resolved a WSL target with the synchronous probe on load; the
  skill target resolution is now async.
- the async home probe waits up to 60 s instead of killing wsl.exe at 5 s, since a
  kill mid-boot leaves the distro failing every call with E_UNEXPECTED.

* fix(wsl): bound file authorization's wait for a WSL home; no sync probe over an async one

Review follow-ups: a hung distro must not hold every file check for the async
probe's 60 s, so authorization waits at most 5 s for a mirrored root's home and
falls back while the probe keeps running and fills the cache. And a synchronous
getWslHome no longer starts its 5 s-bounded probe while an async one is waiting
out the same distro's boot.

* chore: prune the main-process sync launch ratchet (one fewer sync wsl.exe)
2026-10-24 13:13:55 -07:00
OrcaWinandOrcaWin a8cc0e8992 test: remove ineffective cases and repair persistence and platform coverage (#33243)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 13:04:10 -07:00
Neil e16662104a refactor(tasks): colocate source summary hook (#33242) 2026-10-24 12:48:59 -07:00
OrcaWinandOrcaWin ca310a5288 test: remove shallow checks and repair persistence and mobile oracles (#33239)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 12:31:21 -07:00
OrcaWinandOrcaWin f2446b92d7 test: remove ineffective checks and strengthen settings and notification coverage (#33235)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 12:15:15 -07:00
Neil 8733ec4033 refactor(persistence): colocate test fixtures and harness (#33233) 2026-10-24 11:56:22 -07:00