Commit Graph
809 Commits
Author SHA1 Message Date
Jinwoo Hong 24f5c57030 A chat started in a server workspace drawn from the server's layout stays where you opened it (switch-on builds only) (#32003)
* fix(L273-core): tabs this desktop created in a paired server's frame-drawn workspace survive the server's frames (switch-on)

A tab the server's layout never lists (a client-hosted browser tab today; any kind recorded the
same way) used to vanish on the server's next frame rebuild. This desktop now records such tabs by
server and workspace, in its localStorage only (never a session field nor sent), and the server's
frame builds keep them as drawn, inside the server's own groups:
- A held tab drawn in a group the server lacks moves to the nearest server group; it shows there
  only if the user was on it, and the user's group follows it. The group tree stays the server's.
- The record forgets an id once the server's frame lists it, on close, and on worktree removal,
  and prunes ids the restored session no longer has before the first frame after a reload.
- A held tab's drag moves it here only (no session.tabs.move); it offers no new split, and a
  server tab sharing its group counts only the server's tabs for split no-ops.
- Client-hosted staging records its tab (both ids) in layout-owner builds; restage takes the
  worktree from its caller instead of scanning the store.

* refactor(L273-core): the desktop-created record is inert with the switch off; clearer names

- With the switch off the record is never read, kept or written (a single gate in the record).
- The frame-drawn check is isDesktopCreatedTabInFrameDrawnServerWorkspace in
  frame-drawn-desktop-created-tabs.ts: "held" stays the frame hold's word.
- The held-tab placement drops its unread activeGroupId; the group follow derives it.

* wip(L270): a host advertising agentSession.create.tab-placement.v1 opens a created chat's tab where the creating client asked, in its own group and tab ids

* test(L270): a switch-off windowless host saves a chat opened with no client place at the end of the remembered active group, as before (test only)

* wip(L270): a desktop's chat in a frame-drawn server workspace stays put: the frames hold its provisional tab until the server lists it, and the create names its group and the server tab before it (layout-owner build only)

* test(L270): a switch-on serve saves a client's chat after the tab it named; the server-layout read moves to one e2e helper (test only)

* refactor(L270): the create's tab place keeps its own name, and the layout rule's comment names the client's place first

* refactor(L270): the create's tab fields live in their own schema file and the windowless save passes its chat through, so no file passes its line limit; the placement test types its mocks without assertions

* refactor(L270): the chat's place names server tabs through H5c's route, which also skips tabs the server's frames do not hold

* fix(L270): a chat opened while a reconnected server stream awaits its snapshot still names its group; the layout-owner e2e list runs the placement spec

* fix(L270): only a create in a frame-drawn server workspace awaits its tab place, so every other create keeps its timing (the eager History joiner after an early terminal close opens again)

* fix(native-chat): read a frame-drawn chat tab's place without a tick, so an eager History joiner's create is still sent

* test(native-chat): type the create's params so an async placement cannot compile; name the probe that fills the cache

* test: type the placement mock's original through a type import
2026-10-22 16:16:43 -04:00
Jinwoo Hong e710e89832 A paired desktop hands its editor tabs to the server before drawing a server workspace from the server's layout (switch-on builds only) (#32088)
* wip(h5c-1): server workspace layout intents go to the server's main; positions per stream

* wip(h5c-1): e2e without a cast

* fix(h5c-1): a routed move never moves here; routed callers read the server's stream; a restarted main starts its positions over

* fix(h5c-1): layout-apply-send back under its line limit

* test(h5c-1): a routed answer reaches the handler with the server's stream key

* Editor tabs main-first: the window sends each editor action to main and draws editor tabs only from main's frames (dark, layout-owner build only)

With ORCA_LAYOUT_OWNER_IS_MAIN on, editor open (preview replace, open to the side), reopen at
its position, close, close-all, promote, the resolved conflict placeholder, a restored-editor
move and a disk rename are layout commands the window sends before it draws anything. Window
content (open file records, drafts, buffers, the autosave gate) is written at once for an open,
and rekeyed in the same store update as the frame for a move or rename. Main publishes each
non-edit tab's view, keeps views in memory across applies and partition changes, stamps the
tab's time, folds same-file duplicates, and republishes a windowless host's session.tabs after
its own file open. The switch-off path is unchanged.

* fix(terminal): a CLI or phone close through main keeps a pinned tab, as the window-era close did

* feat(terminal): main applies phone closes, renames, pane titles, sleeps and agent-launch tabs itself, with or without a window (layout-owner build)

* fix(terminal): a tab close through main also ends the tab's dormant SSH relay session; agent-launch tabs main writes keep the window's focus rule, prompt and outcome (layout-owner build)

* feat(terminal): main starts a new tab's first terminal under the window's ids, and the pane attaches to it (layout-owner build)

* test(terminal): tests that counted the window's create calls or publications see main's start of a new tab

* fix(terminal): an account-switch restart binds only into a pane main's layout holds, with no window tree (layout-owner build, L197)

* test(terminal): the new-tab start tests hold no type assertions; the start's ledger row is L252

* fix(terminal): a new tab's pane attaches to any terminal main binds it to, and a failed start keeps the tab's startup

* test(terminal): a Claude account-switch restart's tests draw the pane main holds

* test(d3a-2): the buffer view index follows a rename main answers

* fix(terminal): a launch pane main takes back still tells the window why, which drops its hold and prompt; a rename through main answers the name it stored (layout-owner build)

* fix(terminal): a new tab another owner's terminal won attaches to it, and a failed start's startup never runs twice

* fix(terminal): main's pane close reads its stop result as a type only, so no runtime module loads before its base class

* fix(terminal): a phone's close through main drops the closed rows from what phones list at once, not at the window's next publish (layout-owner build)

* chore(terminal): mark the phone-close row drop interim (ledger L260)

* test(worktree-create): a new local tab's startup counts as run when main starts it (layout-owner build)

* test(worktree-create): record main's starts without a type assertion

* fix(terminal): a tab closed before main declines its start gets no startup back (layout-owner build)

* feat(h5d): hand desktop-only editor tabs to a layout-intents server before its frames draw the workspace

A paired desktop dropped an editor tab it opened in a server workspace (with its open file and
unsaved draft) on the server's next records frame once that workspace became frame-drawn, and
editor opens made after gating hit the same loss.

- openEditorTab is now on SERVER_ROUTED_LAYOUT_COMMANDS: the proposed tab id and group are kept,
  the file id becomes the server's own, and the record loses the desktop's runtimeEnvironmentId.
- Before a workspace gates, server-layout-frames hands its desktop-only editor tabs to the
  server's main (sendToServerMain), recomputes them on each answer, and gates only when all
  answered ok or tab_exists. Any other refusal or a failed call keeps it on the mirror for that
  connection; the next one retries. No timer, no hold store.

* fix(h5d): only an applied open counts as handed over; tab_exists keeps the tab on the mirror

* fix(terminal): the window's new tab starts in main when its own window asks over IPC, which labels itself a client (layout-owner build)

* feat(h5c-2): a paired desktop's writes to a server workspace its frames draw land in that server's main (layout-owner build)

- Routes setTabProps, setChatPane and non-terminal renameTab to the server's main as layout.apply;
  closes, starts, sleeps and terminal names keep the RPCs that main now applies (session.tabs.close,
  terminal.close, terminal.split, session.tabs.createTerminal, terminal.rename). A window-style
  closeTabs is never routed: main takes it as already stopped and stops nothing.
- A close the server refuses (a pinned tab) redraws the workspace from the server's frames, so the
  window's optimistic drop never outlives the answer; same for a refused pane close.
- A frame-drawn workspace keeps none of the mirror's ledgers (L240): no tab-id mappings, close or
  focus intents; they stay only for servers without layout.intents.v1 until the Phase 2 cutoff.
  Its pane-geometry push is skipped (it goes as intents); pane renames still push.
- e2e: paired-desktop-server-layout-writes covers close (layout, session.tabs, terminal stop),
  rename, split and new tab on a windowless switch-on serve.

* fix(h5c-2): an undelivered close of a frame-drawn tab is held for replay; a launch-agent clear stays off the server route

* refactor(h5c-2): the undelivered-close test moves to its own module, keeping the lifecycle file under its line cap

* fix(h5d): a tab closed while its hand-over was in flight is closed on the server before the workspace gates

* fix(h5d): an editor tab closed in a workspace a server's frames draw closes on that server by session.tabs.close; a tab kept on the mirror closes here only

* fix(h5d): a handed-over tab closed here before the gate is closed on the server first; a server-opened tab's close in a frame-drawn workspace is covered

* chore(h5d): a closed stream also closes the hand-over its hand-back started

* refactor(h5d): move the frame stream's routing inputs out of server-layout-frames to keep it under the line cap

Main's #31869 grew server-layout-frames past 300 lines once merged with H5d; the two helpers move unchanged.
2026-10-22 15:47:43 -04:00
Brennan Benson 199fc7b2c1 A Pi chat shows the turns made outside it (#31618)
* feat(native-chat): a Pi chat shows the turns made outside it

When a Pi chat's agent opens its conversation, Orca asks Pi for the entries
saved after the point the chat last accounted for (get_entries since that
entry id) and adds the terminal's turns once, in Pi's order, under the
"Continued outside this chat." divider. That point is saved on the
conversation handle after each chat turn and at each open. When the chat's
own unsaved work and the terminal's share a suffix that can't be split
(Orca died mid-turn), that suffix is skipped rather than duplicated, and
later terminal turns show normally. A Pi reply over the 8 MiB line limit now
fails only the optional read that asked for it instead of ending the chat.

* refactor(native-chat): share the catch-up open runner across agents

The Codex catch-up's agent-neutral parts move to one module: the open-time
envelope (its own deadline and cancellation, never gating the open), the
bounded staged batch, the single settlement commit that re-checks the
journal before writing, and the "Continued outside this chat." divider.
Codex keeps only how it reads its saved turns and what the chat already
holds. No behavior change; the other agents' catch-up builds on this
instead of copying it.

* refactor(pi): catch up through the shared catch-up runner instead of a copy

* chore: remove unrelated pnpm executable metadata

* refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks

* refactor(pi): read the chat's view through the runner's view()

* chore: remove regenerated pnpm executable metadata

* fix(native-chat): the catch-up deadline also bounds waiting for its commit to be written

A journal write barrier that never settled after the import was admitted held
the provider's open past the catch-up deadline. The wait now ends with the
deadline or the open's cancellation, like every other step.

* test(pi): check Pi's session entry ids and get_entries since against the pinned real Pi

* test: type the history catch-up sink fixture without assertions

* fix(native-chat): bound and revoke history preparation

* test: the runner's journal fake answers the cursor Pi's view reads

* feat(native-chat): validate expected saved items during history catch-up

* fix(pi): preserve outside history and optional read recovery

* test(pi): fail RPC smoke promptly when the child stops
2026-10-22 10:00:11 -07:00
Jinwoo Hong 4dd97f02d5 A paired desktop's tab moves, splits, resizes, props and renames in a server workspace go to the server's layout (switch-on builds only) (#31714)
* wip(h5c-1): server workspace layout intents go to the server's main; positions per stream

* wip(h5c-1): e2e without a cast

* fix(h5c-1): a routed move never moves here; routed callers read the server's stream; a restarted main starts its positions over

* fix(h5c-1): layout-apply-send back under its line limit

* test(h5c-1): a routed answer reaches the handler with the server's stream key

* fix(terminal): a CLI or phone close through main keeps a pinned tab, as the window-era close did

* feat(terminal): main applies phone closes, renames, pane titles, sleeps and agent-launch tabs itself, with or without a window (layout-owner build)

* fix(terminal): a tab close through main also ends the tab's dormant SSH relay session; agent-launch tabs main writes keep the window's focus rule, prompt and outcome (layout-owner build)

* feat(terminal): main starts a new tab's first terminal under the window's ids, and the pane attaches to it (layout-owner build)

* test(terminal): tests that counted the window's create calls or publications see main's start of a new tab

* fix(terminal): an account-switch restart binds only into a pane main's layout holds, with no window tree (layout-owner build, L197)

* test(terminal): the new-tab start tests hold no type assertions; the start's ledger row is L252

* fix(terminal): a new tab's pane attaches to any terminal main binds it to, and a failed start keeps the tab's startup

* test(terminal): a Claude account-switch restart's tests draw the pane main holds

* fix(terminal): a launch pane main takes back still tells the window why, which drops its hold and prompt; a rename through main answers the name it stored (layout-owner build)

* fix(terminal): a new tab another owner's terminal won attaches to it, and a failed start's startup never runs twice

* fix(terminal): main's pane close reads its stop result as a type only, so no runtime module loads before its base class

* fix(terminal): a phone's close through main drops the closed rows from what phones list at once, not at the window's next publish (layout-owner build)

* chore(terminal): mark the phone-close row drop interim (ledger L260)

* test(worktree-create): a new local tab's startup counts as run when main starts it (layout-owner build)

* test(worktree-create): record main's starts without a type assertion

* fix(terminal): a tab closed before main declines its start gets no startup back (layout-owner build)

* fix(terminal): the window's new tab starts in main when its own window asks over IPC, which labels itself a client (layout-owner build)

* feat(h5c-2): a paired desktop's writes to a server workspace its frames draw land in that server's main (layout-owner build)

- Routes setTabProps, setChatPane and non-terminal renameTab to the server's main as layout.apply;
  closes, starts, sleeps and terminal names keep the RPCs that main now applies (session.tabs.close,
  terminal.close, terminal.split, session.tabs.createTerminal, terminal.rename). A window-style
  closeTabs is never routed: main takes it as already stopped and stops nothing.
- A close the server refuses (a pinned tab) redraws the workspace from the server's frames, so the
  window's optimistic drop never outlives the answer; same for a refused pane close.
- A frame-drawn workspace keeps none of the mirror's ledgers (L240): no tab-id mappings, close or
  focus intents; they stay only for servers without layout.intents.v1 until the Phase 2 cutoff.
  Its pane-geometry push is skipped (it goes as intents); pane renames still push.
- e2e: paired-desktop-server-layout-writes covers close (layout, session.tabs, terminal stop),
  rename, split and new tab on a windowless switch-on serve.

* fix(h5c-2): an undelivered close of a frame-drawn tab is held for replay; a launch-agent clear stays off the server route

* refactor(h5c-2): the undelivered-close test moves to its own module, keeping the lifecycle file under its line cap

* fix(h5c): a routed tab move anchors on the server's tabs only, and never sends an anchor the server does not hold (layout-owner build)

* fix(h5c): every routed tab move is placed among the server's tabs at the routing point, skipping desktop-only tabs (layout-owner build)
2026-10-22 08:00:09 -04:00
Neil 4778133a1f ci: share the mobile bundle across SSH Windows host cells (#31883) 2026-10-22 01:01:05 -07:00
Neil e4959a6bed ci: canary a smaller WSL distro matrix for runtime changes (#31873) 2026-10-22 00:59:49 -07:00
Neil 2078db93ec ci: check dependency vulnerabilities on PRs and stable releases (#31858)
* ci: audit dependency security without gating releases

* ci: audit stable release tags instead of running daily

* ci: isolate release audit data and fix integration checks

* ci: require exact audit inputs and ignore candidate archive attributes
2026-10-22 00:43:07 -07:00
Neil 3d1b8e396e fix(ci): restrict manual candidate test workflows to read-only caches (#31887) 2026-10-22 00:19:38 -07:00
Neil ad4952e1da ci: retire completed relocation and pnpm cache experiments (#31843) 2026-10-21 22:37:06 -07:00
Neil 8b5c732e6e ci: skip relay integration for renderer-only PRs (#31842) 2026-10-21 22:31:54 -07:00
Neil 6c6ad7520f ci: vet adhoc source before build preparation (#31839) 2026-10-21 22:22:45 -07:00
Neil 019ff406f0 ci: deny cache writes from benchmark candidate code (#31838) 2026-10-21 22:20:30 -07:00
Neil f9c9cedad3 ci: declare read-only workflow token permissions (#31836) 2026-10-21 22:17:05 -07:00
Neil 0a7d6bb2d5 refactor(main): group ephemeral VM lifecycle modules (#31771) 2026-10-21 21:31:21 -07:00
Neil 307f0cdaee Move Relay deployment configuration into private cloud operations (#31792)
* refactor(cloud): move deployment configuration into private operations repo

* docs(cloud): clarify private operator ownership

* fix(ci): install pnpm through npm-compatible bootstrap

* fix(ci): avoid native downloader metadata timeout

* test(push): move deployment preflight contract into private repo

* fix(ci): bootstrap Node before installing pnpm in minimal containers

* fix(ci): pass Windows boundary test paths through the Node runner

* fix(ci): run Windows boundary Vitest with installed Node
2026-10-21 21:01:25 -07:00
Neil 8ba1c9ba09 fix(ci): restore cloud and main checks during pnpm registry outages (#31793)
* fix(ci): recover verified pnpm setup from registry metadata outages

* fix(ci): prepare native pnpm shims after fallback verification

* fix(ci): provide Node for pnpm fallback in bare containers
2026-10-21 20:25:36 -07:00
Neil 5c5f9a2362 Update cloud relay runtimes, providers and security checks (#31753)
* chore(cloud): update relay runtimes and security scanning

* docs(cloud): record Go binary advisory reachability

* chore(cloud): rerun security checks after resolving main conflict
2026-10-21 19:43:26 -07:00
Neil 95d9682b0c fix(ci): prepare historical VM rollback snapshots (#31775) 2026-10-21 19:40:42 -07:00
Neil bc60f69517 ci: fix protected-main downloads badge updates (#31747)
* ci: propose downloads badge updates through pull requests

* fix(i18n): align Portuguese CLI version message with current catalog

* test: provide locale in queued editor translation mock
2026-10-21 18:54:07 -07:00
Neil eb9d2f9a06 fix(security): update Node runtimes for October security releases (#31687)
* fix(security): update Node runtimes for October security releases

* fix(ci): compare normalized web file inventory without ordering artifacts
2026-10-21 17:06:28 -07:00
Brennan Benson afcb385c86 ci: install verified fish 4 directly for shell contracts (#31581)
* ci: install fish 4 from its GitHub release when the Launchpad PPA is down

* TEMP: skip the fish PPA to prove the GitHub release fallback (revert)

* Revert "TEMP: skip the fish PPA to prove the GitHub release fallback (revert)"

This reverts commit 58ba0c5074.

* ci: install pinned fish directly from its verified release

* ci: isolate standalone fish from runner resources
2026-10-21 14:05:32 -07:00
Neil 73930bedee Prepare server template assembly alongside qualification (#31422)
* Prepare template assembly during qualification and cache Intel downloads

* Limit early template qualification polling to fifteen seconds

* Verify Windows retains its download cache policy

* Drop Intel store restoration after measuring its overhead
2026-10-21 00:06:40 -07:00
Neil a04b2ebc33 fix(release): avoid API rate limits waiting for the mobile bundle (#31431)
* fix(release): replace mobile bundle API polling with job dependencies

* test(release): update qualification token permission expectations
2026-10-20 23:49:57 -07:00
Neil 309d71c003 Build adhoc Mac architectures in parallel (#31415)
* Parallelize adhoc Mac architecture packaging

* Include parallel Mac jobs in release timing reports

* Cover parallel Mac publication and failure gates

* Explicitly scope archive targets to each Mac architecture
2026-10-20 22:30:47 -07:00
Brennan Benson 2187f0b6b2 ci: one list for Windows boundary tests and one step for ratchets (#30643)
* ci: one list for Windows boundary tests and one step for ratchets

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

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

* fix(ci): preserve existing local lint ratchet scope
2026-10-20 22:13:36 -07:00
Neil 6964f819b9 Reuse qualified templates and batched signing for production releases (#31397) 2026-10-20 21:55:01 -07:00
Jinwoo HongandE2E Test b99552cabc fix(runtime): a phone reads and saves Markdown tabs on a headless server (#31101)
* fix(runtime): a phone reads and saves Markdown tabs on a headless server

With no window to relay through, markdown.readTab and markdown.saveTab answered
renderer_unavailable. Main now reads the tab's file (or its saved draft) from disk and
saves with a version compare plus the disk write precondition, in the shapes the
window's editor relay returns. With a window attached the relay is unchanged.

* Headless Markdown save: keep the draft's disk baseline, honour read-only tabs, re-find the record to clear

* Use the disk baseline only for a draft

* ci: run the phone Markdown spec's orcad case in the orca serve mode-switch job

* Headless Markdown: answer from disk only; a window's saved draft stays its view state

Clearing a saved draft wrote workspace layout from main, which no layout command carries.
Main now never shows or edits a draft; the desktop's restore check reports a disk change.

* Phone Markdown: one path through main and the disk, window or not

A paired server's workspace is read and saved on that server with files.read /
files.writeIfUnchanged, so it answers from its own disk and keeps the precondition.
The window relay (ui:mobileMarkdownRequest and the renderer bridge) is removed.

* e2e: a phone saves a Markdown tab the desktop holds unsaved edits in

The phone reads the disk with the tab marked dirty; its save lands on disk, and the
desktop keeps its draft and shows the changed-on-disk banner.

* Group the Markdown disk host's imports

* Drop an unneeded store cast from the dirty-readers test

* Phone Markdown: refuse a save on a server too old to check the disk; cover the untitled tab

A server before #30605 has no files.writeIfUnchanged, so the save is refused with the
existing update-required message rather than written unchecked.

* Phone Markdown: refuse a file an SSH host holds outside the workspace; pin the untitled answer

* Phone Markdown: untitled tabs stay read-only; older servers save like the desktop editor; a closed-source preview still reads

- The desktop lists an untitled document's tab with isUntitled (optional, additive); main answers
  it read-only with unsupported_untitled and refuses its save before touching the disk.
- A server without the files.write.disk-precondition capability gets the desktop editor's guarded
  save: compare from here, then a plain files.write, or a conflict. Every call of a save is pinned
  to the pairing it probed.
- A preview whose source tab was closed has no saved record; it reads its own file, read-only.

* e2e: read the untitled tab's answer without a cast

* Phone Markdown: pin a save's pairing before its first read

A save captures the server's pairing before it reads, and every call of the save, the read
included, is pinned to it, so a re-pair between the read and the write is refused.

* Test: with no window, a phone saves an existing untitled.md and never creates a deleted one

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 23:08:47 -04:00
Neil 71cf31bf69 Overlap native qualification setup with the mobile bundle build (#31336)
* Prepare server native lanes alongside the shared mobile bundle

* Expect read-only Actions access in template callers
2026-10-20 19:57:29 -07:00
Jinwoo Hong 065e6873f6 A paired desktop shows the held-terminal banner and Reopen for a server's held terminals (switch-on builds only) (#31275)
* A paired desktop shows a layout-owner server's held panes on the existing banner; Reopen asks the server (switch-on builds only)

The server's frames carry status 'held' on a pane a newer build's terminal service may still
run. The desktop now lists those panes on its existing newer-build banner, with the same wording
and states. Reopen calls the server's new `layout.reopenHeldPanes` under the server's ids: the
server stops its newer service, as the desktop's own Reopen does, and starts each pane still
held. The desktop starts nothing; the server's next frames clear the status and bind the pane.
A refusal or failure shows the banner's failed state.

Served and advertised (`layout.reopenHeldPanes.v1`) only by a layout-owner build, to paired
desktops (not phones). A server without the capability, or that publishes no status, shows no
banner, as today.

* test(layout): held server pane stays Reopening until the reattach frame, stays failed after a refusal, and shows nothing without the reopen capability

* fix(layout): a held pane's Reopen acts only on its own host and on main's published status (switch-on builds only)

- Reopen needs the clicked pane's key; a pane that already left the banner's store does nothing,
  so a server banner clicked as its frame drops the pane never stops this machine's newer service.
- Local versus server is decided by the pane's host, not by whether it has a release.
- The banner portal for a pane with no terminal handle exists only in a layout-owner build.
- The server reopens only panes its published layout shows held, the status the desktop saw.

* test(layout): Reopen ignores a pane rebound to a new terminal whose held entry is still stored

* test(layout): the dropped-pane Reopen test checks for the reopen method, since the view report shares the RPC client
2026-10-20 22:20:05 -04:00
Neil a308277f9a fix(deps): root and mobile resolve one zod, and CI fails if they drift (#31260)
* fix(deps): root and mobile resolve one zod, and CI fails if they drift

Mobile type-checks src/shared zod schemas. With mobile on zod 4.6.5 and the
root on 4.6.7, TypeScript treated them as unrelated packages, so wrapped
schemas inferred {} (tests/e2e/helpers/phone-feed-list.ts:154) whenever a
job installed mobile deps.

* fix(deps): guard every package mobile's tsconfig remaps, not only zod

* test(ci): pin the shared-dependency version check to the static phase
2026-10-20 19:12:29 -07:00
Neil be0961e911 Overlap adhoc release gates and cache qualified server templates (#31300)
* perf(ci): accelerate adhoc release builds and runner scheduling

* Overlap adhoc release preparation and reuse qualified build inputs

* Bind qualified template reuse to its embedded CLI source commit

* Use the available CPUs for native compilation in fast dev builds

* Verify qualified template cache provenance and daily invalidation

* Avoid incremental MSBuild tracking for fresh fast-release builds

* Right-size Intel macOS and Windows ARM release qualification

* Route Intel qualification through the standard larger macOS label

* Overlap Windows preparation and initialize the server native compiler once

* Test server compiler invocation through its process-runner interface

* Align adhoc signing contract with its larger packaging runner

* Recognize the native cache fingerprint as batch-file metadata
2026-10-20 18:45:13 -07:00
Neil c35ff3559d perf(ci): accelerate adhoc release builds and runner scheduling (#31297) 2026-10-20 17:51:19 -07:00
NeilandClaude d6bc6cb3b3 fix(process-host): let a slow Windows taskkill finish instead of orphaning the grandchild (#29501)
* fix(process-host): let a slow Windows taskkill finish instead of orphaning the grandchild

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

* fix(process-host): abandon taskkill shortly after the root exits so it cannot walk a reissued pid

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

* test(process-host): log the taskkill outcome and caller settle time under load

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

* ci: register the late-taskkill Windows test in the package-windows path list

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

* fix(process-host): release the git admission slot on root close again; cap the background taskkill at 30 s

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-20 17:30:38 -07:00
Jinwoo Hong a9310986cd A CLI or phone file on a layout-owner server opens in the tab group a paired desktop shows (switch-on builds only) (#31264)
* feat(layout-owner): a CLI or phone file on a layout-owner server opens in the group a paired desktop shows

Each paired desktop reports, through a new layout.reportView (served only by layout.intents.v1
servers, never to phones), the group it shows in each workspace the server's frames draw. The
server keeps reports per connection beside the window's, opens files in the latest one's group,
and drops a connection's reports when it closes, falling back to the saved view.

* test(e2e): a CLI file open on a switch-on server lands in the group a paired desktop shows

* fix(layout-owner): layout.reportView refuses a caller with no connection, as sidebarInputs.reportView does

* feat(layout-owner): a desktop's re-sent view after a reconnect never outranks another desktop's live report

layout.reportView takes an optional refresh flag. The desktop's first report of each workspace
after a stream snapshot carries it; the server ranks such entries below every live report, while
they still replace the connection's own earlier entries and beat the saved view.

* feat(layout-owner): a paired desktop re-sends its view, as a refresh, when its control connection to the server comes up on a new socket

The server drops a connection's view reports when it closes, and the layout stream can stay up
across that, so the desktop watches the shared control diagnostics for a new socket.

* fix(layout-owner): a desktop's refresh keeps its own live rank; stop reporting to a server without layout.reportView

A same-connection resubscribe re-sends its view as a refresh, which demoted the connection's
own live report below an older desktop's. Ranks are now an explicit live/refresh tier. A pre-PR
switch-on server answers method_not_found; the desktop then stops sending for that stream.

* fix(layout-owner): a group the server's own frames move is re-sent as a refresh, never a live report

The snapshot resend read the store before the sync applied the snapshot, so the flush's group
change reached the store subscription as the user's. Writes made by the frame sync now report as
refreshes, and the snapshot resend runs after the flush.

* test(layout-owner): a change frame's group move goes out as a refresh; the frame-write mark survives nested writes
2026-10-20 19:57:07 -04:00
Neil 49e6107ebe ci: stop running WSL host tests for UI-only changes (#31243) 2026-10-20 15:32:46 -07:00
Neil 32343cc703 test(skills): synchronize Windows rename contention (#31237) 2026-10-20 15:15:59 -07:00
NeilandClaude 706ea954e8 ci(wsl): run the WSL orcad lane on Debian 12 and Fedora 41 too (#29821)
Matrix the lane over distro families. Debian 12 and Fedora 41 rootfs are exported from their
container images (systemd off only, since the images ship no systemd). The lane test now makes the
systemd distro optional and writes its login-env export to the file a login bash actually reads
(~/.bash_profile on Fedora and Arch), which made new distros fail for the wrong reason.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-20 14:57:27 -07:00
Neil 5b861bf4b6 perf(ci): reuse verified Windows builds and trim release history transfer (#31227) 2026-10-20 14:47:02 -07:00
Neil 12bf32836b feat(ci): ratchet raw repo connectionId host checks; migrate the cheap ones (#31096)
* feat(ci): ratchet raw repo connectionId host reads; one drop-in resolver

Codex review 6 class fix (#4/#6/#7): host decisions read off a repo row's
legacy connectionId miss rows that name SSH only as executionHostId.

- getRepoRoutingConnectionId (shared/execution-host.ts): drop-in for a raw
  repo.connectionId routing check that also finds executionHostId-only SSH.
- check-repo-host-read-ratchet: per-file count of raw repo connectionId
  reads in src/main, src/shared, src/cli; counts may only shrink.
- Migrated 66 cheap reads: runtime GitHub/GitLab commands, PR/MR head ref
  fetches, preserved-branch cleanup, ref queries, base-ref IPC, worktree
  preparation, trash sweep, scan cache TTL, usage metadata.

* fix(ipc): PR base IPC routes executionHostId-only SSH rows; ratchet is a stated lower bound

Review follow-up (Opus on #31096):
- worktrees:resolvePrBase decided git/gh routing on the raw connectionId while
  its head-ref fetches already used the resolver; one resolved connectionId
  now drives all of it (test fails on main).
- getRepoRoutingConnectionId's contract is stated exactly: never local when the
  raw field is set; on a contradictory ssh row executionHostId wins.
- The ratchet regex no longer matches report.connectionId; header and docs
  say it is a lower bound.
- Regression tests for a runtime GitHub and GitLab command site.

* test(ci): pin the repo host-read ratchet step to the static phase

* fix(ci): repo host-read ratchet skips verbatim field copies into projected rows

A copy of connectionId into a projected row decides nothing; migrating it to
the resolver would change what the projection publishes. The sidebar feed
catalog's copy (added after the baseline) and two older projections no longer
count; baseline pruned to 91.
2026-10-20 14:00:32 -07:00
b8a5c8f910 A paired desktop draws a layout-owner server's workspaces from its layout stream (switch-on builds only) (#31181)
* review: shorten a test comment; the methods file header names layout.apply

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

* wip(h5b): a lost remote layout stream reopens with the local backoff

* wip(h5b): server frame id translation (pending the binding handle)

* wip(h5b): frame syncs take a source; the mirror gates frame-drawn server workspaces and indexes handles; ids translate with session.tabs handles

* wip(h5b): one layout stream per capable paired server, handing workspaces between frames and the mirror

* wip(h5b): tests for server frames; layout.getRemoteSnapshots returns only paired servers (L18)

* wip(h5b): re-gate a server workspace when its routing arrives after the snapshot; e2e hook and spec

* wip(h5b): late-owner test; e2e spec on the switch-on leg

* wip(h5b): server streams follow every paired capable server, not the focused one; a known-kind guard without a cast

* wip(h5b): server-frames tests read real paired-server state; no casts

* fix(h5b): a handed-back workspace is the mirror's alone; frozen frames draw only gated workspaces

* fix(h5b): a workspace rejoining the gate takes the full build

---------

Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-20 14:09:35 -04:00
Jinwoo HongandE2E Test a18b44bfc0 ci: the manual switch-on leg runs the headless server specs (#31108)
* ci: the manual switch-on leg runs the headless serve, paired-host and serve-restart specs

With H1's switch-on server template, these exercise main as the layout owner with no window.
Spec paths are exact: the changed-spec job greps each file, so prefixes only work for ssh-orcad-.

* ci: keep the Windows serve job out of the switch-on leg; it builds switch-off

* ci: the switch-on leg also runs the server-workspace tab composition and serve-restart binding specs

---------

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 09:05:10 -04:00
Jinwoo HongandE2E Test 444064105c build(orcad): a dark test template opt-in, marked so packaging refuses it (#31097)
build-orcad-template.mjs takes --dark-test-template to let ORCA_LAYOUT_OWNER=main
through for test servers; the template and out/orcad carry the switch marker, and
packaging refuses a marked template. The e2e template steps build a switch-on server
when the workflow runs with layout_owner=main.

Co-authored-by: E2E Test <e2e@test.local>
2026-10-20 07:22:34 -04:00
Neil cf6bbd30da perf(release): overlap Windows builds and gate signing on Mac success (#31032) 2026-10-20 00:21:25 -07:00
Neil 16fa2a844f fix(release): keep Mac qualification runs out of the release build's concurrency group (#31031) 2026-10-19 23:30:17 -07:00
Jinwoo Hong f884b9ff05 ci(relay): run cell-flags and reserve-watch on the paid runner pool (#31024)
Both sat 10-20 min in the shared ubuntu-latest queue on 10-19/20, leaving
reserve cells unwatched between segments. The rollout workflows already
run on this pool.
2026-10-20 02:07:56 -04:00
Neil 84a4526508 Overlap macOS notarization with serial architecture staging (#31006) 2026-10-19 22:31:50 -07:00
Neil 67eb94a918 perf(release): overlap server checks and share the mobile bundle (#30990)
* perf(release): overlap server qualification and reuse mobile bundle

* fix(ci): preserve draft suppression for independent compatibility checks

* test(release): update template download cache policy contract
2026-10-19 21:29:11 -07:00
Neil 972d6dcb0a Speed up macOS updates by avoiding background installer throttling (#30944)
* fix(updater): avoid macOS background installer throttling

* fix(updater): harden installer scheduling build and diagnostics
2026-10-19 20:41:54 -07:00
Neil b6eee3e981 fix(ssh): download the pinned Node on the host when this computer can't reach it (#30945)
* fix(ssh): let the host download the pinned Node when this computer can't

* fix(ssh): keep both host download tools inside one time budget

* fix(ssh): use timeout for wget only where it runs as expected
2026-10-19 19:56:11 -07:00
Neil 28d9f959de Run selected SSH tests instead of the whole suite on PRs (#30928)
* fix(ci): run only selected managed SSH specs on pull requests

* fix(ci): retain full SSH coverage for routed shared helpers
2026-10-19 18:21:53 -07:00