Commit Graph
1355 Commits
Author SHA1 Message Date
Neil d0c8bee2e8 refactor(runtime): check session host queries and terminal focus (#28367) 2026-10-14 22:42:18 -07:00
Neil acd415a414 test: consolidate orchestration mailbox suites (#29002)
* test: consolidate orchestration mailbox suites

* test: update mailbox reliability gate paths

* test: preserve mailbox reliability history
2026-10-15 05:33:37 +00:00
Neil 0ec009a877 refactor(runtime): check workspace host, file and persistence boundaries (#27945) 2026-10-14 22:23:57 -07:00
Neil 8cfa1bbed3 test: consolidate contract suites and trim duplicate cases (#28988)
Group isolated contract suites behind explicit entrypoints to reduce repeated Vitest startup, consolidate RPC coverage without changing cases, and remove duplicate script assertions and child processes.
2026-10-14 21:50:26 -07:00
Neil 9623805c40 test: restore terminal stop routing coverage (#28971) 2026-10-14 20:55:19 -07:00
Neil f5d5c5c404 test: share setup across related leaf suites (#28956) 2026-10-14 20:34:07 -07:00
Neil d717798055 refactor(runtime): check terminal stop and workspace admission (#27782) 2026-10-14 20:28:35 -07:00
Neil bcaed523cd ci: contain persistent transforms and trim model overhead (#28948) 2026-10-14 20:22:13 -07:00
Brennan Benson 3bfb1149da Native chat: one current-work projection for every host reader, death recorded first (2/5, rebuild of #26867) (#28103)
* feat(native-chat): record which agent run wrote each journal item

Each journal item row, subagent roster entry and background-task entry now
records ownerFence: the lease fence of the agent run whose output produced it.
Optional, persisted, no row version bump and no migration. The journal's
itemFence() returns it, so the turn-verdict rules and per-run provider activity
read the producing run rather than the writer's fence.

* feat(native-chat): one current-work projection for every host reader; record death first

The host decides what work is current in one place
(structured-agent-session-current-work.ts) from the lease, the exit this host
saw, the runtimes it replaced, its live child and each item's provenance.
Every host reader (queue, Stop, prompt answers, commands, status feed, idle
sweep, teardown snapshot, history pages and frames, orchestration mailbox)
reads it, so a dead agent's leftovers stop reading Working or holding the queue
even when their cleanup write failed. The journal's activeTurnId()/liveTurnScope()
become the raw runningTurn().

On exit the host records the child's end first and signals generationEnded,
which republishes frames, the status feed and settlement waits and wakes the
queue drain with no journal row. Pages and frames carry optional working and
actionablePromptIds fields.

* refactor(native-chat): keep structuredAgentSessionHandsOver file-local until a caller needs it

Nothing outside the projection calls it in this part; Part 5 exports it for the start-failure row.

* test(native-chat): fewer provenance tests: fold reopen and unstated-owner checks into the main case

Drops the epoch-replacement case (Part 2 rewind test reads the same values through the same rebuild) and the corrupt-value case (no version writes one).

* test(native-chat): drop current-work mirrors: the viewKey unit case and the still-live rewind case

The flow cases keep every outcome: dead leftovers read as nothing, Stop has nothing to stop, a waiting card drains, a live owner publishes as before, and a rewind never promotes dead work.

* test(native-chat): an unstamped roster entry from an older build reads as its row provenance

Moved here from Part 4 so it sits beside stampJournalEntryProvenance.

* test(native-chat): build the older-build roster row inline, after main removed its body helper

Main #27947 writes one row per subagent and deleted journal-subagent-group-body; a roster row from an older build is still what this case reads.

* refactor(native-chat): provenance is per item only; drop per-entry stamping

Main's #27947 writes one saved row per subagent, and each background task already has its own row,
so an item's own ownerFence names the run of every entry a producer still writes. Per-entry stamps
inside a row mattered only for the shared roster rows saved before #27947, and no run writes into
those any more, so the row's own provenance settles them correctly. Removes the entry stamping,
the optional ownerFence on roster entries and background tasks, and their tests.

* fix(native-chat): an item's provenance is set when it is created; revisions keep it

A newer run's provider can revise an item an older run created: Pi's start and an ACP
session/load both report context usage onto the newest saved turn. Restamping that turn with
the newer run let the newer run's exit settle the older run's unverifiable turn as interrupted.
Only creation states an owner now, as main's itemFences rule did.

* test(native-chat): keep the provenance test below the production lines it covers
2026-10-14 20:19:23 -07:00
Neil 767f0dd89f ci: skip packaging for verified unit harnesses (#28934) 2026-10-14 19:56:27 -07:00
Neil d371e2d8b6 ci: reduce runner occupancy in release and contract jobs (#28930) 2026-10-14 19:53:57 -07:00
6837a4c180 fix(ssh): a relay-era desktop can join the server another desktop on the same login runs (#28886)
* fix(ssh): a relay-era desktop can join the server another desktop on the same login runs

A desktop that still had relay-era projects on a host whose managed server
another Orca version ran could never connect: its migration skipped the
running-version adoption, so it deferred on a terminal count only a paired
server can give. Once the versions matched it failed instead because the
server already held the same repository path, added by the other desktop.

The migration now pairs with the running server like a fresh desktop does,
before any upload. Projects the server already holds at the same host path
are linked to the server's project instead of imported twice: the journal
is re-cut without them, under the same migration id, only after the server
proves it holds nothing of the earlier manifest. A conflict the server will
keep refusing now says what conflicts and how to get past it.

* fix(ssh): never link a project a bridged relay terminal still runs in

The bridge reattaches a relay pane only into its own tab, which a linked
project never sends to the server, so linking would leave that terminal
running out of sight. Refuse the link instead, naming the project and how
to end the terminals.

* test(ssh): run the link test on Node's SQLite runtime and read the host's activation record

* test(ssh): establish desktop B's profile before seeding its relay-era state

* test(ssh): connect desktop B's host explicitly in the shared-login spec

* fix(ssh): say what a linked project left behind, and never blame it for an unowned relay terminal

A project this desktop moved that the host's server already held is linked to the
server's copy, so its tabs and automations from this desktop stay behind. The
connect now carries those linked projects and the SSH host card says so, with the
automation count, instead of dropping them silently.

A bridged relay terminal of unknown or removed project still blocks a link, but its
guidance no longer names the linked project as where it runs. Both terminal
messages now offer removing the project from this desktop as the way out.

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

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:48:25 -07:00
Neil f5c50642de refactor(runtime): check terminal creation and splitting (#27708) 2026-10-14 19:29:25 -07:00
Neil 1bdbc23648 refactor(runtime): check session graphs and tab lifecycle (#27694) 2026-10-14 19:11:03 -07:00
Jinwoo Hong 7cf09809f6 fix(mobile): group by Project with the desktop sidebar's own code (#28850)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters

A phone created through a desktop now records whom it acted for, so the
desktop's Hide other-client workspaces filter keeps the user's own phone
workspaces (locally and on servers) and hides only other people's.
The desktop relay marks each server row its sidebar would hide, and the
rows its own browser or chat tabs keep awake; the phone applies both.

* test(e2e): dedupe sidebar row ids in the visibility spec

* fix(mobile): address review: owner naming, old-server relayed phones, scope trim

* refactor: move the sidebar's project section grouping into shared

The project sections the desktop sidebar draws under Group by Project are
built from the project/setup projection, the per-host catalog fold, the
endpoint host stamp, and the section labelling and ordering rules. Move the
pure parts of each into src/shared (no behaviour change) so the paired phone
can call the same code instead of guessing the grouping.

* fix(mobile): group by Project with the desktop sidebar's own sections

The phone grouped Project sections by repo display name, so it merged
checkouts the desktop keeps apart (several local stablyai/orca checkouts,
or a server's copy) and split what the desktop merges. The phone now reads
each host's repo.list, project.list and projectHostSetup.list (the desktop's
own and each server's through the relay), folds them with the renderer's
shared catalog fold and projection merge, and builds sections with the
desktop's shared grouping, labelling and ordering code. Keys, labels and
Recent order equal the sidebar's; a host that predates the project lists
gets the repo-derived projection, as the desktop does.

Header colour and icon come from the section's anchor repo and rows from
their own repo by id, as on the desktop. The repo filter lists every
catalog repo by id per host instead of merging same-named repos. Group-by
labels read Project and None. project.list and projectHostSetup.list are
routed for paired phones (read-only, workspace permission).

* refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag

A phone now records its owner as the creator (this host for a directly
paired phone, the relaying desktop for a delegated one), so the shared rule
is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved
browser/chat tabs into hasHostSidebarActivity, so the phone's status dot
follows too, and local worktree.ps counts the same signals. The old-server
fallback is removed.

* test(e2e): poll the phone and the sidebar together in the visibility spec

* fix(mobile): one Project-section builder for desktop and phone, with the desktop's repo order

Review fixes. The sidebar's buildOrderedGroups now calls the shared
buildProjectSections for Group by Project (notice and pending repos are
optional inputs), so desktop and phone run one function. The phone ranks
manual header order by its folded catalog order, which is each host's
repo.list order with the desktop's own repos first, as the renderer loads
them; only the desktop's cross-host manualRepoOrder (pairing-local) is
unknown to it. The projection selector is now a memo over the shared
resolver. A transient project-list failure keeps that host's last catalog;
only forbidden or method_not_found falls back to the repo-derived
projection. Catalog rows are checked on every member grouping reads, and
the parity test answers the renderer's real catalog fetch, includes a
server's own project rows, and fixes Manual and Recent orders that differ.

* fix(mobile): use the repo-derived catalog until a host's project lists first answer

* fix(mobile): the desktop serves its sidebar's Project catalog instead of the phone fetching it

The phone fetched repo.list, project.list and projectHostSetup.list from
the desktop and every relay-ready server each minute and folded them
itself, so rows from offline or older servers fell back to repo sections
and keys moved on cold start.

The desktop renderer now publishes the inputs its own Project headers use
(state.repos in sidebar order, the projection selector's projects and
setups, projectOrderBy) on the runtime window graph it already sends to
main, only when they change. Main keeps the last one and answers it on a
new paired-desktop read, mobileRelay.projectSections. The phone reads it
with its repo metadata and groups with the shared builder; a desktop that
predates the read (forbidden or method_not_found) leaves the phone grouping
rows by repo from the desktop's repo.list, and a transient failure keeps
the last catalog. The payload carries only grouping and label fields, no
hooks, credentials or uploaded icons. The phone's own catalog fetching and
its project.list/projectHostSetup.list routes are removed.

* fix(mobile): keep uploaded repo icons on the phone, and isolate the snapshot tests

* test(e2e): the desktop window serves its sidebar's Project catalog to the phone

* chore(i18n): regenerate the runtime English catalog (drift already on main)

* fix(mobile): read the Project catalog as its own request, outside the recorded repo-metadata flow

The extra read inside the repo-metadata refresh stalled its recorded RPC
sequence. It now runs beside it with its own throttle. The repo-metadata
goldens are re-recorded for the intended state change: the name-keyed
colour, icon and id maps became desktopRepos.

* fix(mobile): fetch the Project catalog at once when the screen moves to another host

The refresh window is now kept per client, so a host screen reused for another desktop doesn't wait out the previous host's 60s window before showing its Project headers.
2026-10-14 22:04:41 -04:00
Neil 8df0f67c82 ci: skip redundant PR platform warmers (#28917) 2026-10-14 18:48:13 -07:00
NeilandClaude d1bb7fec80 [CI] Reduce repeated test imports and fixture overhead (#28627)
Co-authored-by: Claude <claude@anthropic.com>
2026-10-14 18:23:35 -07:00
Brennan Benson e2fa611916 feat(native-chat): chat scratch folders expire after 30 days unused (#28394)
* fix(files): never write through a dangling symlink

A path that doesn't exist yet was authorized by walking up to the nearest
existing folder. A dangling symlink fails realpath with ENOENT, so the walk
stepped over it and approved the link's own path; the write then followed
the link and created its target outside the allowed folders. The walk now
continues from the link's target, and returns that real destination.

* refactor(native-chat): rename the chat visuals folder to the chat scratch folder

The per-chat folder agents write into is the chat's scratch space, not only
a visuals folder. Its root becomes <state>/chat-scratch/<hash> (the hash is
unchanged) and the agent variable becomes ORCA_CHAT_SCRATCH_DIR. The old
ORCA_CHAT_VISUALS_DIR is never set and is still stripped from what every
agent child inherits, Pi included. The visuals skill names the new variable
and says the folder is temporary.

* test(native-chat): keep the delivery test to the rename

The launch parity snapshot already pins that every agent drops the
pre-rename variable.

* feat(native-chat): chat scratch folders expire after 30 days unused

A chat's scratch folder was removed only when its record was gone or its
local workspace was proven deleted, so the folder of a long-idle chat
lived forever.

The sweep now expires a chat's folder once its newest journal row and
the newest entry anywhere in the folder (complete, no-follow scan) are
older than 30 days, unless the host holds a running agent, a delivery or
an open dispatch for the chat. The final recheck and a rename to
<hash>.expired run in the chat's session lane and then a per-folder lock
that Orca scratch saves (desktop IPC and host RPC) also take; the delete
runs outside both. A save that settles after the age scan marks that
expiry stale. Leftover .expired folders and the pre-rename
native-chat-visuals root are removed.

The workspace-removed rule and its catalog verdict are deleted.

* fix(files): refuse any dangling symlink in the missing-path walk

Following a dangling link by hand resolved relative targets against the
lexical parent, not the link's real folder: through a folder link the walk
could loop forever or authorize the wrong file. Refuse instead: realpath
can't say where a dangling link leads, but a write would follow it.

* test(files): drop the loop case that duplicated the dangling-link refusal

* refactor(native-chat): trim the scratch expiry and test the real live-work veto

- Remove the other-profile catalog options (running profile id, never-written
  profiles) that only the deleted workspace verdict used; history cleanup's
  reader is unchanged.
- The sweep keeps its original remove-and-warn block; an unused chat's
  folder is parked by one function. Drop the unused `now` option and the
  candidate identity check.
- The live-agent case runs against the host's real runWhileExecutionIdle.

* style(native-chat): keep the session host within its line limit after the main sync

* fix(native-chat): every scratch save and every overlapping sweep share the folder lock

Binary (base64) runtime saves now take the scratch folder lock like text saves, and each folder
keeps a set of expiry candidates so one sweep finishing no longer hides a save from another.
2026-10-14 18:22:39 -07:00
Neil f23fa8ca76 Prepare Windows natives with scoped compiler settings and measure servicing (#28881)
* Scope Windows native compiler setup and measure ARM inbox preparation

* Add a controlled Windows native preparation comparison

* Test native preparation through an injected process runner

* Guard reviewed Windows native batch runners
2026-10-14 18:18:24 -07:00
Jinwoo Hong 3b11b7ac94 fix(mobile): phone server rows follow the desktop's other-client and sleeping filters (#28819)
* fix(mobile): phone server rows follow the desktop's other-client and sleeping filters

A phone created through a desktop now records whom it acted for, so the
desktop's Hide other-client workspaces filter keeps the user's own phone
workspaces (locally and on servers) and hides only other people's.
The desktop relay marks each server row its sidebar would hide, and the
rows its own browser or chat tabs keep awake; the phone applies both.

* test(e2e): dedupe sidebar row ids in the visibility spec

* fix(mobile): address review: owner naming, old-server relayed phones, scope trim

* refactor(mobile): record a phone's owner as creator and fold desktop tab activity into the host flag

A phone now records its owner as the creator (this host for a directly
paired phone, the relaying desktop for a delegated one), so the shared rule
is unchanged and onBehalfOf is gone. The relay ORs the desktop's saved
browser/chat tabs into hasHostSidebarActivity, so the phone's status dot
follows too, and local worktree.ps counts the same signals. The old-server
fallback is removed.

* test(e2e): poll the phone and the sidebar together in the visibility spec
2026-10-14 21:18:01 -04:00
Neil 222b780276 Reuse real launcher builds and guard unused runtime RPC imports (#28905) 2026-10-14 18:17:14 -07:00
Kelvin Amoaba 0905ad4139 feat(native-chat): copy chat selections when Copy on Select is on (#28737)
* feat(native-chat): copy chat selections when Copy on Select is on

The terminal's Copy on Select setting now also copies text selected in a
native chat transcript, at the point the Add to chat offer already treats
a selection as finished. It writes the same text right-click Copy does,
skips the composer and other fields, and leaves the setting key as is.

* fix(i18n): translate the Copy on Select description and search keywords

Settings and Settings search showed English to es, fr, ja, ko and zh users
after the description moved to new keys. Extend the old terminal-only
translations to cover chat, and translate the new highlight and chat search
keywords.
2026-10-15 01:16:13 +00:00
Kelvin Amoaba 6d519f7835 fix(mobile): keep content clear of the side insets in landscape (#28749)
In landscape the iPhone sensor housing and Android's side nav bar are left/right
insets, which no screen or over-the-air page padded for.

Fixes #21190
2026-10-15 00:57:56 +00:00
Neil 39815a086f Reduce CI import, frame-capture, teardown, and shard costs (#28879)
* Avoid unused module imports and trim cosmetic test coverage

* Capture hidden popup frames and attach CDP before guest creation

* Overlap relay test projects within the existing worker budget

* Consolidate native preparation and add an opt-in transform cache comparison

* Release inherited debugger sockets during final E2E teardown

* Refresh unit scheduling weights from all five qualified ARM shards

* Avoid retaining unasserted RPC call history in the delivery model

* Use the supported unconditional background wait syntax

* Overlap unconditional output upload with native preparation

* Verify relay integration stays enabled for ordinary unit callers
2026-10-14 17:10:55 -07:00
Jinjing 232f6048e7 Remove obsolete Hermes cron documentation and translations (#28877) 2026-10-14 16:29:28 -07:00
Neil 6607c54984 perf(ci): cut redundant tests and reuse expensive setup (#28669)
* perf(ci): reduce test overhead and reuse expensive build work

* test(ci): keep worker startup outside failure-case deadlines

* test: isolate push fixture advisory locks

* test(ci): preserve fixed drain retry allowance

* Align CI contracts with current triggers and guide content

* Stabilize consolidated reveal scenarios and retain behavioral CI contracts

* Fix upstream lint blockers while retaining behavior and assertions

* Classify global Jira preferences in the WSL migration inventory

* Align Jira test fixtures with startup and build safety checks

* Include package-test routing dependency in CI checkouts and fixtures

* Keep native stream classes intact in transcript dependency tests
2026-10-14 16:14:27 -07:00
Brennan Benson 850e71e672 Resume in New CLI for OpenCode and OMP chats (#28075)
* feat(chat): Resume in New CLI for OpenCode and OMP chats

OpenCode's and OMP's CLIs have no copy flag, but their chat protocol copies a
conversation (ACP session/fork). For a fork of one of their chats, the chat's
host now has a short-lived agent, launched exactly as the chat's own, copy the
conversation; the terminal then opens the copy with a plain resume. The chat
keeps its own conversation, so the CLI is never a second writer on it.

The copy travels on the existing aiVault.prepareSessionResume fork request as
an optional forkedSessionId; a host that predates it answers without one, and
the client asks for an Orca update instead of resuming the original.

* fix(chat): open an OpenCode 2.x chat's CLI copy with opencode, not opencode2

The copy kept the row's agent, so Resume in New CLI ran `opencode2 --standalone --session <copy>`,
which OpenCode 2.x doesn't install. The chat's host now answers the copy with the agent whose CLI
opens it: the chat's own.

* fix(chat): type the copied conversation's agent as a history agent

* fix: preserve independent chat copies through CLI launch

* test: keep only the tests that pin user-visible behavior

* fix(chat): validate OpenCode copies on their execution host

* test(chat): expect copied OMP forks after main integration
2026-10-14 16:06:50 -07:00
Neil 7244bf3e6f Reclaim idle worktree space automatically on dev startup (#28864)
* Run safe worktree cleanup on dev and add interactive pnpm reclaim

* Rename storage cleanup command to reclaim-space

* Keep real stream constructors in transcript dependency tests

* Detach cleanup output and align historical cache previews
2026-10-14 15:47:02 -07:00
Jinjing ebd4f00453 Skip reindexing unchanged transcripts on cold session list reads (#28798)
* Skip reindexing unchanged transcripts on cold session list reads

A cold session-list read re-parsed every self-contained transcript and rewrote its search rows, even when nothing had changed. That wasted work and churned the index on every list.

- When a transcript's stat (mtime, size, inode, device) matches a complete index entry, the list read now skips the rewrite. Agents with sidecar metadata, unknown sizes, or partial or stale entries still reread.
- Test fixtures that replay a "changed" transcript now pass a new mtime so they still exercise the replace path.
- New tests cover unchanged skips, changed size/mtime/file identity, sidecar refresh, due/failed repair, and append.
- Dependent-transcript and Codex title tests check that cold rereads keep search and title data current.

* Make the self-contained transcript check exhaustive over every agent

- Replace the hand-maintained `Set` of self-contained agents with a
  `Record<AiVaultAgent, boolean>`, so adding an agent type fails typecheck
  until someone decides whether its transcript is self-contained.
- Classification is unchanged: the agents that were in the set are `true`,
  and the rest are `false` with a short note on the sidecar or metadata
  source they depend on.
- Make the replace-generation tests derive the second mtime from the first
  candidate instead of a hard-coded timestamp, so they stay valid if the
  default fixture mtime changes.

* Guard file-stat reuse with a self-contained transcript test

Indexing skips reparsing unchanged transcripts for agents marked self-contained. That is only safe if their parsers read nothing but the transcript itself, and nothing enforced it. A parser that started reading a sidecar or dependency file could serve stale index entries.

- Export SELF_CONTAINED_TRANSCRIPT_AGENTS so the test checks the real list
- Add a test that parses each self-contained agent's fixture, both direct and cached, and fails if any file other than the transcript is read
- Register the test in the SQLite runtime-file include list

* Require stored file identity before skipping a transcript rewrite

- Rows with a NULL dev or ino were treated as covered, so a file replaced with the same size and mtime could be skipped and never reindexed.
- Such rows now count as uncovered, so the rewrite runs and records the identity.
- The identity is read through the shared `fileIdentity` helper instead of the raw file fields.

* Add regression test for session rows indexed without file identity

A session row indexed before file identity (dev/ino) was recorded was never reread once the stat began supplying one, so it could keep stale search content. This test covers that case: the first read with identity replaces the row, and a second read with the same identity leaves it alone.
2026-10-14 14:37:57 -07:00
Neil cc03142b9c feat(wsl-mover): background writers step aside for a project a WSL move is taking (D1b-5c-3) (#28835)
* feat(wsl-mover): background writers back off a project a WSL move is taking (D1b-5c-3)

* fix(wsl-mover): re-check before renames, quiet owned automations, stable folder ids (D1b-5c-3)

* test(wsl-mover): seed journals one phase at a time and advance existing moves (D1b-5c-3)

* fix(wsl-mover): a branch rename that already ran settles even when the move skips the folder (D1b-5c-3)
2026-10-14 14:13:51 -07:00
Neil 8f83f6b076 feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d) (#28820)
* feat(wsl-mover): pause a moving project's terminals through Sleep and prove they exited (D1b-5d)

* fix(wsl-mover): gate again under the fence, swap journal phases atomically, keep sessions frozen through commit (D1b-5d)

* test(wsl-mover): the fence e2e walks the journal one phase at a time, as a move writes it (D1b-5d)
2026-10-14 13:07:50 -07:00
Jinwoo Hong d09605ee8e ci: run the Electron browser probes serially, out of the unit shards (#28799)
The three popup compatibility probes ran in the parallel unit shards and
timed out at their 14s in-process deadline when other files starved the
runner. Move them into the package job's serial Electron lifecycle step,
and stop the six probes already in that step from also running in the
shards.

Claude-Session: p2-headless
2026-10-14 15:35:39 -04:00
Neil b0308f24ef feat(wsl-mover): refuse edits to a project a WSL move is taking (D1b-5c-2) (#28761)
* feat(wsl-mover): refuse edits to a project a WSL move is taking, and drop its background bookkeeping quietly (D1b-5c-2)

* fix(wsl-mover): hide only local rows of a moved project; the host's own row keeps its id (D1b-5c-2)

* fix(wsl-mover): refuse a user's unread toggle, fence branch deletes, host setups and issue commands; settings unfreeze once the host owns a repo (D1b-5c-2)

* test(wsl-mover): the fence e2e refuses edits to moving projects and drops their bookkeeping quietly (D1b-5c-2)

* refactor(wsl-mover): name the update type the fence inspects (D1b-5c-2)

* test(wsl-mover): run the fence tests that open a real store in the Node runtime (D1b-5c-2)

* fix(wsl-mover): a host setup's own repo stays frozen once the host owns it; e2e pins the quiet drop (D1b-5c-2)
2026-10-14 12:13:28 -07:00
Jinjing 8644ade7fc Remove Hermes automation support (#28519)
* Remove external cron history from the automation UI

* Simplify the automation editor

* Remove Hermes cron discovery and management
2026-10-14 11:36:43 -07:00
Jinjing 52d9dd2fd7 Keep CI failure excerpts bounded and strip malformed ANSI controls linearly (#28671)
* Bound and sanitize CI failure excerpts with linear ANSI stripping

* Fix excerpt regression checks and reduce bounded marker overhead

* Require signal proof for benchmark timeouts and load consumer fixtures portably

* Validate benchmark timeout framing and run required cases with a pinned CI baseline

* Strip dense generic ANSI escapes with bounded forward scanning

* Use the standard UTF8 encoder for byte-cap test oracles

* Gate CI log performance suites behind a dedicated job

The wall-clock performance suites run about 1k child workloads up to 63 MiB, so they were sharing runners with unit tests and could fail on noise. They now run only in their own `check_log_performance` job, opted in with `ORCA_CHECK_LOG_PERF=1`.

- Remove the pinned-baseline checkout and `ORCA_CHECK_LOG_BASELINE_DIR`. The baseline comparison is now a manual step that reads the pinned ref from local Git history.
- Run the current-implementation checks with `compareBaseline: false`, which skips baseline builds and audits.

* Inline check-log fixture and drop dedicated perf CI job

- Replace the captured failure-log JSON with a synthetic log built by `createCheckLogFixture()`, so no scraped job output or provenance metadata is kept in the repo.
- Remove the `check_log_performance` CI job. The log performance suites now run only when `ORCA_CHECK_LOG_PERF=1` is set, as a manual benchmark.
- Update the benchmark scripts and tests to use the new fixture helper and drop the `fixturePath` parameter.
2026-10-14 11:22:40 -07:00
Jinwoo Hong 3e25fe70f6 fix(unread): a pane is read when it is on screen; closing a waiting tab clears its dot (#28637)
* test(unread): characterize today's workspace and pane unread transitions

Pins, against the real store, what every unread trigger does today: the workspace
flag per host row, the three tab/pane marker maps, persistence calls and payloads,
lastActivityAt and sortEpoch bumps, the Dock count and the floating launcher dot.
Events are replayed through one driver file so a later rewiring of who writes the
state changes only the driver, never these expectations.

* refactor(unread): one workspace-attention module owns every unread transition

Every write to the workspace unread flag and the tab/pane/completion markers now
goes through src/renderer/src/store/slices/workspace-attention: store operations
named by the triggering event (bell, agent attention, pane input, viewed-agent
auto-ack, manual mark read/unread) and pure transitions folded into other slices'
set() calls (activation, tab activation, close, sleep, hibernate, retire, rekey,
reconcile, mirror removal, purge, acknowledge). Behaviour, storage, readers,
persistence and wire format are unchanged; the characterization suites pass
without edits to their expectations.

* refactor(unread): review fixes for the workspace-attention module

- Ratchets: moved terminal-tab-attention baseline entries to terminal-tab-appearance; split
  the characterization setup/observables into an exempt fixture so the driver holds only events.
- Removed the test-only writer seam; mocked-store tests assert the event they fire.
- The slice owns the experimental pane gate for completions too; groupAttentionEnabled is gone
  from the attention request and write.
- Collapsed marker removals to removeTabsAttention / removePanesAttention /
  removeCompletionAttention / removeTabMarkers / movePaneAttention; tab activation uses
  omitRecordKeys.
- Mark read/unread is passed in like other store actions; readers of the workspace dot go
  through isWorkspaceUnread.

* feat(unread): on screen means read; the workspace flag follows its markers

While Orca's window is visible and focused, the tabs on screen (active tab
of the active workspace, plus an open floating panel) are read: every tab,
pane and completion marker of each. One scan (the auto-ack hook, now
startViewedAttentionScan) replaces the keydown, pointerdown, tab
activation, group focus, workspace activation and auto-ack marker clears.

After a read, close, sleep, hibernate, retire or purge that removed a
marker, a workspace's flag is cleared once no tab or completion marker
remains on a live tab; a marker-less flag (Mark as unread, restart,
another client) is read when its workspace comes on screen. Mark as read
removes every marker in the workspace. Reads and removals dismiss the
affected panes' OS notifications in one IPC call.

* fix(unread): a bell or completion already on screen leaves no marker or flag write

Marking now asks the scan's own on-screen rule (isTabOnScreen: the window is
visible and focused and the tab is a scan target), so an on-screen bell no
longer persists the flag true then false. A completion in any split pane of
the tab on screen is viewed, matching the read. A superseded turn's
completion-marker removal settles the flag and dismisses like other removals.

* fix(unread): read the screen across hosts, groups and non-agent views; mark only for an absent user

- Arriving at a workspace in its own view settles its flag even when it shows an
  editor, browser or nothing; Settings and other views read nothing.
- A bell or completion on screen skips marking only when the user is confirmed
  present under the scan's own presence gate (input, or main reporting the
  desktop active within the shared away threshold); otherwise it marks and the
  gated scan reads it when the user returns.
- The active workspace's host is part of the screen, so switching same-id folder
  rows between hosts is an arrival on the new host.
- Removal settles lower every same-id folder row and persist each host.
- Opening and closing the floating panel both rescan.
- Every visible tab group's active tab is on screen, not only the focused one.
- One pane-key parser, one marker-tab enumeration, one removal wrapper
  (settleAttentionRemovedBy), one remove-and-dismiss; the hook is now
  useReadOnScreenAttention over on-screen-attention-scan.ts, and terminal
  keystrokes reach presence through a capture-phase window keydown.

* fix(unread): presence only from renderer input; host-exact folder flags; subjectless completions follow the screen

- Main's 'not away' answers only the scan that asked; presence is stamped
  only by trusted input (wheel included), so it can't outlive the threshold.
- isWorkspaceViewed uses the on-screen rule (workspace view showing, focused
  window, user present), so a subjectless completion marks under Settings or
  while away.
- Folder flag writes resolve the row's host like updateFolderWorkspace does
  (project-group fallback), so the row flipped and the owner persisted match.
- A runtime host purge dismisses but doesn't settle flags of surviving rows.
- DESKTOP_AWAY_SECONDS is the shared threshold's neutral name.

* test(unread): folder flag persists name the row's host

* test(unread): drop casts from the folder persistence seeds

* fix(unread): point the reliability gate and e2e fixture at the renamed on-screen hook

* refactor(unread): drop reshapes the follow-up deletes; keep the flag functions module-internal

Reverts the reconcile/mirror/activation/focus/group call sites and the isWorkspaceUnread
selector to main, removes removeTabMarkers, removeCompletionAttention and the activation
read helpers, and drives tests through public attention actions.

* test(unread): read the Dock badge flag down by putting its tab on screen

* fix(unread): keep the on-input read action removed after merging main
2026-10-14 14:10:00 -04:00
Brennan Benson 81ed8c0feb feat(runtime-files): the host serves a chat's scratch folder as a file root (#28397)
* fix(files): never write through a dangling symlink

A path that doesn't exist yet was authorized by walking up to the nearest
existing folder. A dangling symlink fails realpath with ENOENT, so the walk
stepped over it and approved the link's own path; the write then followed
the link and created its target outside the allowed folders. The walk now
continues from the link's target, and returns that real destination.

* refactor(native-chat): rename the chat visuals folder to the chat scratch folder

The per-chat folder agents write into is the chat's scratch space, not only
a visuals folder. Its root becomes <state>/chat-scratch/<hash> (the hash is
unchanged) and the agent variable becomes ORCA_CHAT_SCRATCH_DIR. The old
ORCA_CHAT_VISUALS_DIR is never set and is still stripped from what every
agent child inherits, Pi included. The visuals skill names the new variable
and says the folder is temporary.

* test(native-chat): keep the delivery test to the rename

The launch parity snapshot already pins that every agent drops the
pre-rename variable.

* fix(files): refuse any dangling symlink in the missing-path walk

Following a dangling link by hand resolved relative targets against the
lexical parent, not the link's real folder: through a folder link the walk
could loop forever or authorize the wrong file. Refuse instead: realpath
can't say where a dangling link leads, but a write would follow it.

* feat(runtime-files): the host serves a chat's scratch folder as a file root

File commands accept `path:<this host's chat-scratch root>/<32 hex>` and
resolve it only when it names this host's own folder, at the file-commands
seam so the selector never reaches chat placement. They authorize against
the project roots plus the chat-scratch root, refuse files.open/openDiff
for the folder, and agentSession.readVisual's ok results carry the file's
absolute path so a client can build the selector.

* test(runtime-files): name the readVisual helper's parameter type

* test(files): drop the loop case that duplicated the dangling-link refusal

* test(runtime-files): run the readVisual RPC test, which opens a real SQLite store, under Node

* test(runtime-files): put the same-suffix folder inside the authorized root so only the selector refuses it

* test(runtime-files): wait for the stores to close before removing the state folder
2026-10-14 10:49:52 -07:00
NeilandClaude cc0217fe96 test(ci): bound the Windows OEM process-read test's cold PowerShell start (#28564)
* test(ci): give the Windows OEM process-read test the PowerShell cold-start watchdog

The runner's first Windows PowerShell 5.1 start has reached 48 s on packaged CI; the
test's 30 s cap failed package (windows) 7 times in ~1,000 runs. Match the 75 s
watchdog nsis-process-check already uses for the same cold start.

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

* test(ci): keep the OEM marker alive past a cold read, and bound the PowerShell read

A blocking spawnSync can't be interrupted by the test timeout, and the marker child
exited at 30 s, so a 48 s cold read could miss it. The reader now has its own 60 s bound.

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

* fix(bench): keep idle-CPU cleanup running when the final process read fails

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

* fix(bench): kill the app tree when the final process read fails

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-14 08:13:53 -07:00
Neil dd4d0fa51f Repair Jira integration fixtures and verification contracts (#28743)
## ELI5

Keep the test suite working with the Jira features that have just landed. There is no visible app change.

## What Changed

The host-picker test now expects the host-qualified selection the parent already accepts. The startup fixture provides Electron's protocol handler and verifies that ready installs the Jira image scheme. Jira edit tests use exported wrappers with module-local setters that are cleared on unmount, preserving the issue/source switch tests without the banned mutable function export.

The WSL storage inventory classifies Jira list columns, linked-view choice and prompt templates as device preferences, matching their keys and the existing Linear policy. Skill-guide tests check the actual generated discovery projection rather than assuming it is shorter than every full guide or uses one fixed phrase. The cache test follows the headless workflow's deliberate removal of its push trigger; cache-warmer checks remain intact.

## Why

Final CI found six stale integration assumptions after the Jira batch. Updating the fixtures and inventory preserves the landed behavior and the underlying checks. Changing production code, padding the Jira guide, or restoring an intentionally removed workflow trigger would change the product to satisfy incorrect test assumptions.

## Linked Issue

Follow-up to #28622, #28631 and #28592. The existing headless trigger policy was introduced in #28664.

## Visual Proof

N/A — tests, test fixtures and storage inventory declarations only; no visual or interaction change.

## Testing

- [x] Automated tests updated.
- [x] 196 focused tests across nine files pass on macOS, covering every failed shard assertion, fixture consumers and the headless workflow policy.
- [x] Node and web typechecks pass; all seven changed-code quality stages, formatting and diff checks pass.
- Reproduced five failing assertions in startup/export/storage tests and the skill projection failure before the repairs. The host-picker owner also reproduced the stale selection failure before correcting it.
- Final full CI will run on this PR. The prior Ubuntu 22.04 persistence job crashed in the native watcher build before persistence tests ran; this change does not claim a Linux release, live Jira or real SSH pass.

## Review

Independent source review found no issues; all six reviewed file hashes and the complete diff match committed head `aa703eb50147ba95646cb82f10b6d86b4f890fc4`. Risk: low. No authentication, network, protocol implementation, workflow, renderer behavior, remote messages or migration rewrite logic changes.

## Agent skill upstream boundary

- [x] Not applicable; no upstream skill-installer source or artifacts copied.

## Notes

The three storage keys contain device-level preferences, without workspace paths or identifiers. Tests preserve host disambiguation and issue/source switching. Platform, SSH and provider behavior remains in the existing implementations.

## Checklist

- [x] Small and focused.
- [x] Explained the before/after, mechanism and choice of approach.
- [x] Visual proof marked N/A with a reason.
- [x] Self-reviewed for correctness and cleanup.
- [x] Cross-platform and SSH impact considered.
- [x] Focused tests, types, formatting and quality checks pass; full CI covers the broader build.
2026-10-14 06:37:44 -07:00
Neil a0c46b405f feat(jira): load attachment images outside issue Markdown (#28631)
Load Jira attachment images separately from issue Markdown so descriptions and comments stay small while screenshots load lazily and open in the existing lightbox. Downloads begin when an image mounts, capture its owning host, require negotiated image support, and use bounded authenticated byte streams and caches. Older clients retain their inline-image handlers.

Keep legacy and cancellable downloads in separate in-flight domains so cancelling a new image reader cannot abort a legacy reader; completed bytes share the bounded cache. Preserve the landed assignee, board, transition/comment queue and request lifetimes. Move unchanged project metadata IPC handlers into their domain module and tighten two inherited focus-check ceilings without changing behavior.

Validation:410 tests/54 suites; all changed-source quality stages, lint, format, routing and contract checks; exact final node/web types; independent source review;48/48 fresh hidden Electron checks with226 stable source hashes and nine inspected screenshots. Synthetic transports exercise production clients and UI. Current CI static checks, mobile bundle, wire compatibility, Linux/Windows packaging and Windows serve pass; five test shards were pending at review.

Extends the existing attachment work credited to Frank (@lifefloating) and Brennan Benson in PR #8938; existing contribution credits and media remain in this PR.
2026-10-14 05:33:30 -07:00
7ac2dcc7d4 feat(cli): let agents read and update Jira tickets safely (#28592)
Expose host-owned verified Jira reads and single-attempt writes through the CLI. Preserve site ambiguity checks, old-host capability refusal, atomic label changes, and uncertainty after failed writes.

Refs #11986 and #26055.

Co-authored-by: Dan Cieslak <dcieslak19973@users.noreply.github.com>
Co-authored-by: kokbee <kok2209@gmail.com>
2026-10-14 03:03:50 -07:00
Neil f31aada5b4 Require a unique full PR gate before merging (#28632)
* Require exact-commit PR checks before agent merges

* Give full PR checks a unique required status context

* Validate required context ownership on every pull request
2026-10-14 01:25:28 -07:00
Jinwoo Hong d3b17ca9f4 fix(runtime): save a windowless host's chat tabs in its layout (#28354)
* fix(runtime): save a windowless host's chat tabs in its layout

Claude-Session: p2-headless

* fix(runtime): remove a chat's saved tab on the index's hidden notice, which every close reaches

Claude-Session: p2-headless

* fix(runtime): a chat whose record is gone loses its saved tab at startup

Claude-Session: p2-headless

* test: run the windowless chat-layout host test in the Node runtime project

Claude-Session: p2-headless

* fix(runtime): remove a chat's saved tab from whichever partition still holds it

Claude-Session: p2-headless

* refactor(runtime): list saved chat tabs across partitions in the layout module

Claude-Session: p2-headless

* refactor(runtime): read saved chat tabs straight from the store's partitions

Claude-Session: p2-headless

* fix(chat-tabs): save a windowless chat tab under its own tab id

Review pass: the saved tab reuses the id status and attention address, the close
checks for a window before scanning partitions, and the host tests seed readable
records with unreadable, other-host and no-index cases.

Claude-Session: p2-headless

* test(chat-tabs): a window opening a windowless-saved chat reuses its tab

Claude-Session: p2-headless

* test(chat-tabs): check the saved tab with the lookup a window reuses

The window's existing-tab lookup moves to the shared projection so the
windowless-layout test exercises it without importing the renderer.

Claude-Session: p2-headless

* test(chat-tabs): assert the reused tab's identity directly

Drops the shared lookup added for the test; two functions already carry that name.

Claude-Session: p2-headless

* fix(chat-tabs): leave an unstamped chat tab in an SSH partition to that host

Claude-Session: p2-headless

* fix(chat-tabs): count an unstamped chat tab as this host's only in the local partition

A desktop mirrors a remote server's chats into that server's runtime partition
without a host stamp; the startup sweep must not delete them.

Claude-Session: p2-headless

* refactor(chat-tabs): one chat tab writer, one startup match, local partition only

Chats run only on local hosts, so their saved tabs live in the local partition:
the cross-partition listing and its writer go. Open and close share one layout
load, the startup fill and sweep become one function beside the index seed, and
the tab filter is the existing savedSessionId.

Claude-Session: p2-headless

* fix(chat-tabs): restore and sweep saved Claude chat tabs like any structured chat

The Claude skip predates Claude structured chats (#18519, #18560); an id the host
has no record of is already dropped by the readable restorer.

Claude-Session: p2-headless
2026-10-14 04:15:36 -04:00
Neil 078f8f314f refactor: check terminal output methods and organize modules (#27606) 2026-10-14 01:14:12 -07:00
Neil f894e257e9 ci: stop daily and post-merge headless-server checks (#28664) 2026-10-14 01:13:59 -07:00
Neil 9c72b3c30f refactor: check headless session tab methods (#27584) 2026-10-14 01:05:31 -07:00
Neil b84d371a9f refactor(runtime): compose typed browser command owners (#27576) 2026-10-14 01:02:56 -07:00
Neil 230b39c279 refactor(runtime): organize file search and terminal artifacts by domain (#27571) 2026-10-14 01:01:37 -07:00
Neil ae8842306b chore(lint): flag Enter handling on raw text fields that skips the IME guard (#28609)
Adds an oxlint plugin rule that reports Enter-key handlers on raw input,
textarea, and contentEditable elements (and keydown listeners on text-field
receivers) unless the handler reaches the shared IME guard. Also routes the
automation run history window listener through isImeOwnedKeyboardEvent, whose
previous nativeEvent check never fired for native events.
2026-10-14 00:55:26 -07:00
Brennan Benson 76d1c68ab5 Resume in New Native Chat for OpenCode (#28072)
* feat(chat): Resume in New Native Chat for OpenCode terminal conversations

An OpenCode conversation started in the terminal now offers "Resume in New Native Chat" in its
Session History row menu and its tab's right-click menu, as Claude, Codex, Grok and OMP do: a new
OpenCode chat takes it over, showing its history and continuing it. OpenCode 2.x rows
(`opencode2`) and 1.x rows both open an OpenCode chat.

Two pieces kept OpenCode off the shared adoption path. Its account is a locator, not a folder, so
the transcript import now works over the account the chat pins, whatever its kind; the folder
agents wrap their lookups in directoryTranscriptImport, unchanged. Its conversations live in a
database, so the journal import reads the found database with the reader the OpenCode transcript
view already uses. OpenCode refuses to reopen a session from another folder, so the workspace rule
covers it too.

* test(chat): Pi is now the agent with no transcript import

* fix(chat): find an OpenCode 2.x terminal tab's Session History row

The pane reports `opencode` while Session History lists the conversation as `opencode2`, so the pane
header's and tab menu's Resume in New Native Chat found no row. Match by the chat agent that takes the
conversation over.

* Fix OpenCode chat adoption environment and pane eligibility

* test(chat): adapt parent adoption fixtures to account identities

* test: keep only the tests that pin user-visible behavior

* refactor(chat): map a Session History row to its chat agent where the chat starts

Both Resume in New Native Chat callers repeated the row-to-chat agent lookup. The launch now
takes the row agent and maps it once, so the Session History actions file stays unchanged.
2026-10-14 00:25:08 -07:00