The journal SQL writer and reader entries run on worker threads. Their
dependencies are grandfathered in the SQLite baseline as worker-only, the
same way existing worker dependencies are; none run SQL on Electron main.
Prune baseline rows the branch no longer needs.
Carry the terminal identity worker alongside chat SQL workers and adopt provider aliases at the committed SQL cache boundary. Keep worker release and automation consumers on their relocated owners, preserving asynchronous journal reads and authority checks.
Retain main test deletions; none of the 47 deleted tests had branch-owned changes. Port incoming ACP fixtures to the asynchronous resume contract.
Validation: the standard merge hook, whole-merge lint, tc:node, e2e typecheck ratchet and changed-quality passed in gate825. All 120 tests in 14 owning files passed on Node and configured Bun. O1 diagnostics are isolated on their separate local branch and absent from this merge.
Carry retained Codex input intent, admission safety, account attribution and
idle confirmation through the SQL journal owners. Preserve main relocations
and deletions and the SQL queue, history and custody assertions.
Suppress publication of unchanged SQL command acknowledgments so empty queue
claims cannot renew a chat's idle clock. This SQL mismatch predates this sync;
main's new idle-retirement coverage exposed it. Mail custody fixtures now hold
native admission and use real edit leases without altering automatic delivery.
Validation: direct Node through tc-gate; merge hook, basic lint, tc:node,
e2e typecheck and pinned-main changed quality pass. All 148 owning tests in
25 files pass on both Node and configured Bun. O1 diagnostics remain excluded.
* feat(computer): type into a named text field without focusing it on Linux and macOS
* fix(computer): report unconfirmed Linux text readback and cover element typing
* test(computer): send an element with benchmark text only for targeted typing
* test(computer): pick gedit's editor, not its hidden overview map, in Linux e2e
* docs(computer): confirm focus before the unindexed type-text fallback
* fix(computer): report unreadable element text readback as readback_unsupported
* Give terminal agents a lasting Orca Agent ID
Terminal agents now get an Orca ID (orca_session_id:<agent>_<uuid>) derived
from their own conversation id on the host that runs them, so it stays the
same across worktree sleep/wake, app restart and a hand-run resume in any
pane. The terminal pane menu gets Copy Agent ID beside Copy Session ID, and
worktree ps --json agent rows carry orcaSessionId. Messaging a terminal
agent by that ID is refused with a message naming its pane until terminal
delivery lands.
* Name terminal agents at row admission and fix review round 1
- Perf: a row's Agent ID is resolved once, when its provider key changes at
admission (legacy rows via the status adapter's prepareRow hook, tmux rows at
canonical commit) and carried on the row as runtime-only agentIdentity.
Snapshots, publications and internal notifications only copy it; no key
serialization, cache lookup or database access per walk. Drops the pane FIFO
cache.
- Refusal: the identity service keeps a reverse index id -> rows, kept exact by
the adapter's rowChanged hook; a terminal refusal reads only matching panes
and names only confirmed running ones (no resume seeds, exited or
unconfirmed restored rows).
- Retry: a read-only identity file is lookup-only and never mints; any store
failure backs off all SQLite work for 30 s; a Copy click retries once.
- Menu: Copy Agent ID shows for a live pane only when its host stamped an ID,
and for a sleeping pane only when this app owns it on macOS/Linux.
- SSH: a sleeping record keeps its connection; a foreign (non-WSL) one resolves
no ID.
- Renderer keeps an existing ID only for the same agent, owner and provider key
including the Pi transcript locator.
- tmux-selected terminal rows carry their ID at every snapshot seam and in Copy
resolution; structured chat rows stay excluded.
* Keep Agent ID work out of notifications and reach Copy wherever a host can answer
- Internal status/provider notifications walk rows without Agent ID work:
canonical (tmux-selected) rows are decorated only for outputs that carry
the id (snapshots, live emit, ps, Copy). The tmux publish body is now
publishTmuxSelectedStatus so tests drive the production path.
- A row this host can name but has not yet (failed or pending link) is
published with orcaSessionId: null; older hosts omit the field. The
renderer offers Copy Agent ID on a live row whenever the field is present,
and the click retries through terminal.agentId.
- Hosts advertise terminal.agent-id.v1. A sleeping pane offers Copy Agent ID
when its owner is this app on macOS/Linux, or a remote owner that advertises
the capability on a known non-Windows platform, and its record names no
foreign connection.
- "Running in pane" places require that this runtime observed the row; a
saved status, Done included, is history.
* Guard the pane menu subscription and stop offering Copy where it can never succeed
- A settled read-only miss (no link in a newer build's identity file) no
longer publishes orcaSessionId; only a resolved id or a pending failed link
does, so Copy Agent ID is offered only where a click can succeed.
- Render contract for the per-pane menu selection: another pane's or host's
status change re-renders no unaffected pane; a pane's own stamp flips its
availability.
* Move terminal agent identity SQL into a worker thread
Main's blocking-SQLite ratchet forbids the synchronous identity store, so
(per the decider ruling) agent-identities.db is now owned by one lazy
worker-thread entry per hook-server owner, desktop and orcad alike.
- The schema, open/version handling, mint/link transaction and read-only
behaviour move unchanged into agent-identity-worker-entry.ts; main keeps
only the key type, path and an async client over WorkerThreadRequestQueue.
- Admission stays synchronous: a new key is published pending (null), one
request per exact key is shared by every row and click, and the committed
answer re-stamps the current rows still pending on that key (legacy and
tmux-selected) as identity-only replays. Replies are fenced by store
generation, key and current subscription; failures back off 30 s and a
Copy click retries once.
- terminal.agentId and the terminal-ID refusal are async: Copy waits for the
pending lookup and revalidates the pane; send/task routing resolves the
terminal recipient (awaiting the store's has() on an index miss) before
any synchronous routing or effects.
- The renderer treats the host's row naming a conversation as authoritative
for its id, so a settled miss clears a pending null, while ID-less local
updates keep it.
- Packaged for desktop and orcad; registered as a worker entry, no baseline
growth.
* Own identity demand per key, publish settled ids as projections, recheck authority
Implements the second decider ruling for the identity worker:
- One owner-level demand table replaces the per-key promise backlog and the
settled-row scan: each exact key a current row or caller needs has one
record (identity, places, waiters); the pump sends the worker one operation
at a time, only for keys still needed, so a healthy burst completes and
detached keys are never sent. Operation deadlines start at dispatch; a
caller's wait expiring never drops a row's demand.
- One failure gate for lookups and membership: after a failure nothing is
sent for 30 s except one coalesced Copy attempt; a single owner wake
retries while rows still wait, and cancels with the last demand or close.
- The retirement barrier outlives a client: a replaced worker must exit
before another starts; a different store path is a different authority.
- Settlement re-stamps the stored row and publishes a projection only: window
readers get the row without its hook event (no agent-state record) and
session.tabs is woken by a row touch; no enriched subscriber, plugin event
or freshness heartbeat runs again.
- Copy and terminal membership check the owner generation after their waits.
A known chat is decided by the chat store alone; send, peer mail, reply and
assignee admission resolve the terminal recipient before reading the facts
they act on, recheck the sender after the wait, and group send reselects
its audience and mailboxes after each wait before writing.
* Scope identity answers to their store authority, expire Copy grants with callers, index provider aliases
- One synchronized authority token before Copy/reverse fast paths, compared after their awaits;
answers carry their authority and an ended store turns rows, tmux subjects and the reverse
index pending together. Status path changes sync the authority outside admission.
- A failed operation reschedules the pump so a queued click gets its attempt; grants belong to
live waiters and a click joining a running attempt earns none.
- Provider-native ids resolve through a commit-maintained alias index at the record store.
- Group fan-out no longer classifies authoritative candidates; @idle rereads statuses with
current candidates before the final synchronous selection.
- Hook-server unit fixtures use an in-memory identity client; a real worker load/stop failure
check covers the worker boundary.
* Pick a shared provider alias's owner among its owners only
The alias index keeps the store's listing order (readable, then unreadable) as derived ranks, updated from the draft's appended keys on commit, so a duplicate alias no longer walks the history per lookup.
* Ask a remote pane's host for its Agent ID by the host's own pane key
Copy Agent ID on a web-terminal surface sent the client's mirrored pane key, which the execution host does not know, so it answered null.
* Keep focus where the user moved, recover a missed identity re-stamp, ack peer-mail replays first
Copy Agent ID refocuses its pane only when focus is unclaimed or still in the pane; a carried pending row takes its key's settled answer; an already-delivered peer-mail retry is acknowledged before recipient preparation.
* Chat UI settings: install an agent's chat runtime from its row
* Keep the install line while Orca installs over an external copy or can't read status
* Keep Removing… visible in the Chat UI install line
* Chat UI and new chats: show Antigravity sign-in as the next step after install
* Record NativeChatStructuredSession's cleared React Compiler bailout
* Keep the Chat UI row install-only; open sign-in on the chat's host and skip chats on the host's agy
* fix(settings): drop nested cards from Worktree hooks
The orca.yaml status, its parse-error callout and the local-scripts notice drew bordered panels inside the Worktree hooks card. Render them as flat rows; code blocks and inputs keep their borders.
* fix(settings): show healthy orca.yaml status as a collapsed row
Loaded and missing orca.yaml now render as a disclosure row under Command source instead of a tinted card inside the Worktree hooks card; the script preview or template is collapsed by default. Parse-error and update-available keep their warning card. Advanced content is indented to line up with its label. Restores the local-scripts notice card from main.
* chore(i18n): drop orca.yaml status strings the new row no longer uses
* fix(settings): keep orca.yaml row inside design-system lint rules
Keep main owner relocations and deliberate test deletions, carry SQL
journal readers to their surviving owners, and preserve the native chat
title generation ruling. Move SQL publication, startup failure and
durable shutdown assertions into the surviving owning suites.
Validation: standard merge hook, full Node typecheck, code quality,
anti-slop, flaky and e2e typecheck gates pass; owning suites pass on
Node and the configured Bun entry (23 files,119 tests each).
O1 diagnostics work remains uncommitted and excluded from this merge.
Preserve main's removal of model-report picker overrides while keeping SQL option writes, finite message steering inputs and tracked idle confirmation. Adopt the incoming owner relocation and deliberate test retirements; the new option freshness regression uses the surviving fixture.
Audited all 11 incoming commits from the prior main tip, pinned at 81d2b0b240. The standard merge hook and all static gates pass, and 33 owning cases pass on direct Node and the configured Bun entry. O1 diagnostics work remains excluded from this merge; no further main movement is folded in.
Preserve the SQL journal authority while adopting main's consumer relocations, account holds, context usage and deliberate API/test removals. Move retained SQL regression assertions to surviving owners and keep the retired conversation title owner deleted.
The P7 costs remain small measured overhead: warm work lookup p50 rises by 0.167 us (3.542 to 3.709 us; ratio 1.047, 60/80 pairs slower), and empty / two subscribers / changed writer acknowledgment p50 rises 5.5% (0.259459 to 0.273833 ms; paired interval excludes parity) on a writer path P7 does not change. Completion improves from 1.546834 to 0.657125 ms.
The 232-commit consumer audit and local validation receipts are recorded in the lane evidence. O1 diagnostics work remains excluded from this merge.
* fix(mobile): let the chat transcript scroll by touch on the phone's web pages
Gesture handler 3 (Expo 58 upgrade, #37935) gives a GestureDetector's view
touch-action: none on web by default. The chat transcript's pinch-to-zoom
detector sits on its scroller, so in the phone's WebView pages the browser
refused every touch pan and no chat could scroll. Gesture handler 2 left it
at auto.
The row list now wraps its own scroller in MobileNativeChatPinchZoom, which
keeps touch-action: auto. A browser render check pins the scroller's
touch-action under the detector.
* fix(mobile): keep the transcript pinch on its text size, not page zoom
touch-action: auto let a two-finger pinch on the transcript zoom the whole
page in the phone's web pages, so the transcript's text-size pinch never ran
and a zoomed page stopped the composer lifting over the keyboard.
pan-x pan-y keeps one-finger scrolling (and sideways code blocks) with the
browser and hands the pinch to the detector.
The render check now pans the transcript with one finger and pinches it
with two, asserting it scrolls, the text scale changes and the page stays
at scale 1.
* Antigravity settings: flatten into agy terminal and Chats cards
Replace the nested Antigravity card with two flat settings cards. Saved agy
accounts become Codex-style rows (click to select, Active badge, Remove), the
current login gets an inline Save, and usage refreshes from the active row.
Chats lists Sign in with (Enterprise fields inline), Account status with its
action, then Chat runtime.
* Use primitive variants for Antigravity row buttons and badge
* Antigravity settings: speak usage values and keep the /logout step
* perf(worktrees): give each repository's worktree list one owner
The sidebar and the runtime catalog each kept their own copy of a
repository's `git worktree list` result and their own staleness rule, so
the same change was often listed twice. One owner per repository, host
and Git runtime now holds the observation, its flight and invalidation;
each reader keeps its accepted age (sidebar 5 s, runtime 30 s plus the
admin-file verifier up to the 5-minute ceiling) and shares the scan.
Settled rows now go stale on Git-level mutations too, overtaken readers
follow one trailing scan, store hygiene uses the facts captured before
the scan that produced the rows (once per observation), and a cooled-
down watcher failure defers its catalog refresh instead of dropping it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): fence the catalog owner by runtime, single flight and hygiene claims
Sidebar reads age from scan start, so runtime verifications no longer
stretch them; probes only certify rows fingerprinted before their scan.
One flight per owner, with a single trailing scan after invalidation.
Switching a repo's Git runtime retires the old owner, and a reader whose
owner was retired mid-read returns non-authoritative rows. Only pruning
readers claim hygiene, failed scans keep the evidence gate's no-retry
claim, overflow invalidates at once, and the stopped-distro fallback
serves only current rows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): let only the live route own a repository's catalog
The catalog owner now asks the live registration which path and Git
runtime are current instead of trusting whichever request arrives last.
A request admitted for a replaced route runs no Git, retires nothing and
answers non-authoritatively; liveness is rechecked before a flight
starts and when it publishes. Route-changing writes retire obsolete
owners after they land. Adapters re-resolve the route after the WSL
admission check, a mismatching verification probe becomes the rescan's
fingerprint, and runtime scans share the strict true-empty-aware reader.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): bound live-route checks and recheck authority before pruning
Live-route checks read the repo registry once per synchronous pass
instead of hydrating every repo per check. WSL discovery answers that
change a repo's Git route retire obsolete owners like other route
writes, and desktop and headless pruning recheck the owner's authority
for the observation after their own awaits.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): revision the live-route snapshot and carry owner authority to publication
Route checks use one registry and project pass per route-input revision,
which repo, project-membership and runtime-setting writes advance
synchronously; WSL discovery answers retire obsolete owners at once.
Repair-state and SSH routes never acquire local Git authority. Metadata
pruning rechecks owner authority after its path probes, fleet lineage
claims require a current observation, and final desktop and runtime
listings stay non-authoritative once the owner has retired their route.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep remote-owned repos out of local Git routes
Registrations owned by another host never get a local Git route, WSL
discovery notifies after the distro list is written and isolates its
listeners, and tests cover idle retirement on route-input and discovery
changes plus pruning that loses owner authority mid-helper.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): cover catalog route authority at its production seams
Real repo-removal writer revokes the route at once, and desktop and
runtime listings never publish owner rows as authoritative after their
route was retired.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): prove catalog authority at the pruning helper and Git conversion writer
The runtime listing loses route authority during its own metadata prune
and stays non-authoritative; a real held path probe deletes nothing once
the owner retires the route; a folder converted to Git is admitted as a
live route as soon as the write lands.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): keep strict true-empty listing behind the mockable lister
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(worktrees): start each worktree.ps case from an unobserved catalog owner
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* build(typecheck): list the WSL discovery change module in the web project
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): withdraw catalog authority from a replaced observation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>