Removes seven modules that only their own tests import, dead orcad exports
and protocol constants, spells orcad's configuration exit code once, reuses
the shared errorMessage helper, and drops links to the deleted
docs/reference folder.
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The relay client is gone, so the three relay-ladder cells and the
@orcad-cli-relay-kept matrix case could not run. Delete them, their
last helper (ssh-test-target-connection), a stale path filter and the
unused build:relay step; rename the runner to invoke-windows-host-cells.ps1.
Co-authored-by: m4air <m4air@Mac.localdomain>
Fold the empty owner-routing baseline into the focus-read ratchet. The three
deleted focus-routing helpers stay banned as a forbidden-name list.
Co-authored-by: m4air <m4air@Mac.localdomain>
* ci: drop the relay Windows process-tree job from desktop packaging
* ci: refuse relay-era tags in a directly dispatched mac release build
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(relay): director reaches Cloud SQL through a proxy sidecar (staging), gated event-loop stall alert
On Cloud Run's built-in /cloudsql socket a connect() blocks the director's whole
event loop until the connector's upstream dial ends, 10 s per attempt while the
instance starts. Staging froze both directors for 134 s on 2026-10-11 and
production four directors for 40-50 s on 2026-09-28.
relay_director_cloud_sql_proxy_sidecar runs the Cloud SQL Auth Proxy beside the
director on 127.0.0.1:5432, as the GCE cells already do, with its own TCP
database URL secret. relay_event_loop_stall_alert adds the event_loop_ms_max
metric and a 5 s stall alert. Both default off, so production plans unchanged;
staging turns both on.
* fix(relay): adopt a reviewed Terraform director shape on deploy; counter-based stall alert; cheaper probed sidecar
- deploy-relay-blue-green: --shape-baseline-revision names the 0% revision a Terraform
apply created; the deploy preserves that shape instead of the serving one. It must be the
latest created, unserved revision on the serving image. Wired into the staging and
production director workflows behind ADOPT_RELAY_DIRECTOR_TERRAFORM_SHAPE.
- Event-loop stall alert is now a gated counter metric on eventLoopDelayMsMax>=5000.
- Sidecar gets a /liveness probe and 0.25 vCPU (Cloud Run rejects only a total under 1 vCPU
with concurrency above 1; checked with validateOnly against the API).
- staging.tfvars pins the serving director's concurrency 1000 and timeout 3600 s, so the
adopted apply shape does not fall to the 80 / 30 s defaults.
* fix(relay): shape adoption refuses identity, topology, pruning and image changes; GOMAXPROCS=1 on the sidecar
parseArguments now refuses --shape-baseline-revision with a runtime identity bootstrap, a
director topology change or revision pruning, so every caller is covered, and the deploy
refuses a shape baseline whose image differs from the serving one. Both director workflows
also refuse the combinations up front. The proxy sidecar runs with GOMAXPROCS=1 to match its
0.25 vCPU quota. The long-hold alert comment is back above its resource.
One hosted runner tops out near 2,048 connections to one cell (4 shards each stalled at 512 with
control_open_timeout), short of the 2,840 the Asia flip-back test needs.
The Asia load runner is registered to the old repository, so asia-east2 load could not run.
Where the load comes from does not change what c4 does with it.
* perf(ci): skip unnecessary alias scans and compare preflight compilers
* perf(ci): overlap measured Bun typechecking with static checks
* perf(ci): use measured Bun lint runtime and strengthen compiler parity checks
* wip: delete the SSH relay server and its build, assets and tests
* wip: drop main/relay plugin conformance; WSL reader lookup test
* fix: drop the emptied relay bench; prune baselines
* test: keep the native halves of tests that also covered the relay
* chore(ci): drop shard timings for the deleted relay server tests
* fix: keep the directory listing budget that darwin terminal names now import
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* wip: connect fails for hosts the managed server can't serve; remove relay session, deploy and relay UI
* wip: delete relay-only client modules left unreachable, prune their tests and gates
* wip: format
* wip: exhaustive unserved message
* wip: drop relay attestation test for the deleted ownership proof
* fix: prune stale baselines; type the test transport stub
* fix(ssh): census proves leftover relay leases; name the remedy for older terminals; translate every connect-failure surface
* test(ssh): leftover leases defer to the host census
* test(e2e): remove relay-path SSH e2e specs and their CI lanes
* test(e2e): drop the auto-convert case that built its relay era through a live relay
* test(e2e): mark relay-era browser and catalog journeys fixme until they seed a profile
* test(e2e): seed relay-era browser and catalog journeys from a saved profile
The three journeys built their old-version state through a connect without an orcad template, which now refuses the host. They now start from a seeded relay-era profile, so the first connect converts the host as an upgrading user's would.
* chore(ci): drop shard timings for the deleted relay tests
* test(e2e): carry a relay-era login cookie across the browser conversion; own every launch
* test(ci): drop the deleted SSH layout oracle from its route
* test(e2e): open the retained browser after conversion, retrying a pre-connect card
* test(e2e): bind the relay-era browser partition before seeding its cookie
A relay-era build always recorded the partition binding before a page stored
data in it. Seeding the cookie into an unbound partition made the binding store
refuse it (browser_route_partition_binding_store_invalid), so the retained tab
showed the generic routing card and Retry could never clear it.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
* test(e2e): cover SSH terminal and workspace journeys on a managed orcad host
* test(e2e): port the provisioned-root recipe journey; read ports from the host scan
* test(e2e): fix the provisioned-root spec's catalog refresh call
* test(e2e): wait for the shell prompt, open file links directly, and mark forward restore fixme
* test(e2e): record launches for teardown, match the prompt robustly, park the link test
* test(ci): list the journeys job in the workflow permission matrix
* test(e2e): run the terminal image-link journey again
* test(e2e): park the external-path link journey on its product gap
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Staging auth mints relay load principals for one workflow file only, so the step-5
staging run's load lives as a mode of that workflow. Load runs take their own
concurrency group and no Cloud SQL lease, so deploys and switch writes can run
under load. Origins, region, principals and sharding are pinned by the workflow.
* fix(relay): the cell switch tool changes only the named switches and checks every key on read-back
A cell resets a key missing from its switch file to its default, so the tool now reads the
current object and changes only what --set names. The read-back compares every known key and
fails when the cell reports ignoredKeys.
* fix(relay): the cell switch tool refuses a switch the cell's image does not support
It reads supportedFlags from runtime-status (#27057). An image from before that field takes only
readinessLocal and ticketCheck off/shadow, so ticketCheck=enforce cannot void an old cell's object.
* fix(relay): the switch tool validates the whole object, refuses voided files, and records admitMode in Postgres
Every key and value of the object to be written is checked against what the image accepts (an
image without the list: only the keys it applies, ticketCheck off/shadow). A file the cell did
not apply is refused. admitMode=reserve needs a typed RESERVE <cell>; Postgres records reserve
before the file is written, and db only after the cell reports every control leased again.
* feat(relay): the switch tool knows reregisterInFlight (1-16), the flip-back pace
* fix(relay): the switch tool closes the cache gap before reserve, undoes a refused write, and has a break-glass db flip
After recording reserve in Postgres it waits past every director's 5 s cache and records it
again (re-running the open-flow check) before writing the file. A file write refused outright
(412 or 4xx) puts Postgres back to db; after a 5xx it stays reserve. An object the cell did not
apply is repaired when the new object passes the image check. Break glass (typed BREAK GLASS
<cell>) records db for a dead or unreachable reserve cell without its read-back.
* fix(relay): a director deploy refuses a traffic move across reserve-mode support while any cell is reserve
* fix(relay): the flip-back wait scales with the cell's controls and pace, and says how to finish
* fix(relay): the switch tool refuses to re-arm a tripped cell, breaks glass only on a silent one, and undoes reserve if the second post is refused
* feat(relay): director deploys set reserve placement, and refuse turning it off while any cell is reserve
* fix(relay): Terraform keeps the deploy-owned placement and shadow cells, and the deploy guard reads placement as built and refuses dropping a reserve cell from the shadow list
* fix(relay): director deploys take --admin-audience alone, name a drifted deploy identity, and carry the shadow seat-feed cells
Staging passes --admin-audience for the reserve guard without durable rehome
verification, which the parser refused. Only the generation and control
origin now travel together, and they still need the audience.
The guard decodes the caller from its token and refuses, by name, a revision
whose ORCA_RELAY_DEPLOY_SERVICE_ACCOUNT is another account, instead of
reading into a 401.
--shadow-seat-feed-cells (preserve, all, or a cell list) is a workflow input
on both director deploys; preserve keeps the serving value. A contract test
parses every workflow's real argv for both deploy scripts.
* fix(relay): the flag tool paces a flip back from the pool size the cell reports
An image from before the field falls back to the deployed sizes.
* refactor(runtime): route adapter, split fences, ownership table, combined ratchet
Adds callHostRoute, which calls a host-owned method on the host a HostRoute
names and reports a reply whose host identity moved mid-call as unverifiable
instead of applying it. Host fences are split: an identity fence (pairing,
runtime connection generation, the route's own SSH target) for mutations, and
the catalog data fence built on top of it for snapshot reads, so unrelated
catalog or SSH-state churn no longer moves a mutation's fence.
HOST_OPERATION_OWNERSHIP classifies every runtime RPC namespace. A new
ratchet counts getActiveRuntimeTarget( + legacyRouteFromSettings( +
settingsForRuntimeOwner( together per renderer file (227). One exemplar, the
folder workspace review-note write, moves onto the adapter.
* fix(runtime): fence the folder note write on its server; refuse aliased ratchet helpers
The folder catalog row lives on the paired server, so its write is fenced on
that server's identity, not on the SSH target the folder names. The ratchet
now counts spaced calls and value uses, and refuses an aliased import or
re-export that would hide calls from the count.
* test(ci): pin the owner-routing ratchet step to the static phase
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(wsl): move the WSL guest bundle sources out of src/relay
The WSL hook relay and WSL browser network relay are built from src/relay but are
not part of the SSH relay. Move them, and the 46 relay modules the hook relay
bundles, to src/wsl-guest so the SSH relay can be deleted on its own. Pure
renames; the built bundles are byte-identical.
Adds a boundary test so nothing outside src/relay imports it except relay tests.
* fix(ci): exempt renamed files' existing lint debt in the changed-lines gate
* test(relay): keep the cast suppression on the wrapped line
* test(relay): only parse files that mention relay in the import boundary check
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* Verify pinned transcript reads and stream Codex usage aggregation
* Resume Claude usage scans with compact token checkpoints
* Harden transcript snapshots and reject incomplete discovery
* Keep Codex race fixtures inside their temporary data home
* Verify pinned transcript rotation with portable file moves
* test: keep Claude usage mock registration in test suites
* Compress saved Claude usage without dropping history
* Keep Claude cache writes bound to their original request
* Register Claude contracts without conflicting with chat contracts
* Assert unchanged Claude sources skip the actual cache writer
Reduce import-scanner work by omitting unused AST parents and the duplicate traversal. Run the mandatory process-host import check alongside static work and explicitly join it before CI can succeed.
Update both workflow phase/enforcement contracts and migrate the newer benchmark to the public subprocess package API. Keep the original package build path, manifests, runtime, test selection, and shard count. Independent native review is clean; validation and timing evidence are in the PR.
* Pair native chat tool results in linear time with provider identity
* Preserve orphan tool output and independent disclosure identity
* Keep mobile tool detail attached to its command after row changes
* Keep named mobile results open across copied history refreshes
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging.
Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan.
Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews.
Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
* feat(relay): one-cell flag write workflow with dry run, compare-and-swap and read-back
Operate Relay Production Cell Flags writes cells/<cellId>.json for one
cell: it checks the origin answers as that cell with a flag channel,
refuses a stale expected generation, writes with ifGenerationMatch,
stamps the actor and run on the object, and waits up to 15 s for the
cell's runtime status to report the new generation applied. The deploy
Workload Identity condition admits the new workflow.
* fix(relay): flag workflow retries a transient 5xx, shows the plan before the generation check, and names the failure
Runtime-status reads go through the shared one-retry admin helper, and a
failed read-back poll no longer ends the wait early. The plan line is
logged before the generation check, a failure is logged as one JSON line
the job summary carries, and the object and summary name the triggering
actor.
* Revert "Revert Claude per-account folders (#24434, #26405) before release (#26638)"
This reverts commit 9ca9b4e36b.
* Move the terminal daemon protocol to 44 for the re-landed claude function
v43 daemons (the revert) lack the claude account function, so new terminals must not reuse them. v42 and v43 stay attachable.
* Move the local build compatibility contract to daemon protocol 44
* Point the old-terminal test at daemon protocol 44
* Claude accounts re-land: never sign anyone out, keep proxies working, accounts mirror ~/.claude (#26769)
* Run a Claude account on System default until it has its own login
An account saved before per-account folders has no login in its folder. While System default (~/.claude, or the user's own CLAUDE_CONFIG_DIR) is signed in to the same email, the router sends every launch there and writes that choice into the pointer file, so nobody is logged out by the update. Once the account signs in to its own folder, the next launch uses it. The WSL router follows the same rule with the guest's ~/.claude.
* Refresh each Claude account folder from ~/.claude before every launch
~/.claude (or the user's own CLAUDE_CONFIG_DIR) is now the master copy. Each account folder gets its whole settings.json, including the proxy address and its key, and every .claude.json key except the login, Claude's account caches and install ids, so first-run questions are not asked again. Every other top-level entry is linked (folders) or copied (files), except Claude's per-folder daemon, jobs, live sessions, login files and throwaways. sessions is no longer shared, and an earlier link is undone.
The refresh runs before every Claude Orca starts, for each new terminal and on account switch. Orca never writes back to ~/.claude. The ledger that tracked selective sharing is gone.
* Route chat auth and unselected-account usage through the Claude router
Chats decided whether to drop inherited Anthropic auth from the selection alone, so an account running on System default lost the user's own key. They now ask the router, as terminals, AI commit messages, automations and usage already do. Usage for an unselected account is read where its launches would run. A test reads every entry point through the fallback and fails if a launch path outside the router reads the Claude selection or builds an account folder. The router's two launch errors move to their own file to keep it under the line limit.
* Show Claude account sign-in prompts only when an account cannot run
With the fallback, an account System default is signed in to already works, so:
- The one-time "Finish setting up your Claude accounts" toast is gone.
- Accounts System default covers no longer need a sign-in: no Sign in in the status bar menu or Settings, and they can be selected.
- The old-terminal banner shows only when claude in that terminal would run a different account than the selected one, and asks again after a switch. Its wording is unchanged.
The retired toast's saved flag stays in the paired-client schema so an older client's write is still accepted. The email comparison moves beside the login reader to keep the router under the line limit.
* Keep the user's shell Anthropic auth on Claude account launches
A signed-in account's launches used to drop ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_OAUTH_TOKEN from the inherited environment while ANTHROPIC_BASE_URL passed through, so a proxy set in the shell got the subscription login and answered 401. Host launches on an account now keep the shell's auth with its address, as System default does. Chats still name the account kind in a failed sign-in, now from the router rather than from whether auth was dropped.
* Share from ~/.claude only what Claude itself would seed into a new folder
The deny list missed much of Claude's runtime state, so tokens, locks and live files such as .session_ingress_token, server-sessions.json and remote-control were copied or linked over each account's own every launch. The list now mirrors the one Claude uses when it seeds a fresh config folder from ~/.claude: its runtime entries, every hidden entry, daemon files and agent memory. teams, ide, downloads and scratch are on it, so they stay per folder.
* Merge trusted folders and MCP servers into an account instead of replacing them
Each refresh replaced the account's projects and mcpServers with ~/.claude's, so folder trust and allowed tools recorded only in the account were lost, and a refresh landing between Orca's folder-trust write and Claude's start brought the trust dialog back. Both are now merged per folder path or server name: ~/.claude wins for entries it has, the account keeps the rest, and a folder trusted in the account stays trusted. Both writers take Claude's lock on the file, so the order no longer matters.
* Require the same organization before System default stands in for an account
The fallback compared emails only, so one email saved for two organizations made both accounts run on whichever organization System default was signed in to. When the saved account and System default's login both name an organization, it must now match too, on the host and in WSL, and the old-terminal banner uses the same comparison. The saved-account lookup the three checks repeated is now one helper.
* Treat v42 terminals as having the claude account function
v42 daemons shipped the same claude function and pointer path as v44, so claude in those terminals already follows the selected account. The old-terminal banner now shows only for daemons before v42 and for v43, the revert, which dropped the function.
* Fail the routing test when a new module resolves the claude binary
The census only caught code that read the Claude selection, not a new launcher that runs claude without asking the router. A second check now lists every module that resolves the claude binary or loads the Agent SDK, each with why it is routed, and fails on any new one, or on a listed one that no longer matches.
* Skip the account refresh on launches that run on System default
A launch that falls back to System default never uses the account folder, so starting a setup for it on every launch was wasted work. Account switches still set the folder up for a later sign-in.
* Give a terminal opened before the login shell's env only the account pointer
At startup, a pane could be routed before the login shell reported the user's own CLAUDE_CONFIG_DIR, so the fallback compared against ~/.claude.json instead. Until that env arrives a pane gets only the pointer, and the pointer is rewritten once it does, so claude typed there reads the right folder. The recent claude --version answer moves beside the version probe to keep the router under the line limit.
* Drop the chat-only Anthropic auth stripping and override refusal
Chats no longer drop the shell's Anthropic auth on any account, so the chat launch's override refusal and the policy's stripAuthEnv could only be reached from test fixtures. The structured auth policy is now just which login a failed sign-in names. Terminals keep their refusal, which still guards WSL launches; the failure reason stays in the shared vocabulary for older peers.
* Publish a new account state file whole
The first .claude.json write went straight to the target, so a crash mid-write could leave Claude a truncated state file. It is now written to a staged file and linked into place without replacing one Claude created meanwhile.
* Read an unselected WSL account's usage where the WSL router would run it
Usage for an unselected WSL account read its saved folder directly, so an account the guest's ~/.claude covers showed no usage, and the read bypassed the router. It now asks the WSL router, after the same check that the distro is already running.
* Keep the shell's Anthropic auth when the claude function runs an account
The claude shell function (POSIX, fish and PowerShell) unset ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, AWS_BEARER_TOKEN_BEDROCK and auth-like ANTHROPIC_CUSTOM_HEADERS before running Claude in an account folder, so a proxy set in the shell lost its key while keeping its address. It now only sets CLAUDE_CONFIG_DIR and Orca's marker, as on System default. Protocol 44 is unreleased, so no new bump; the shell-wrapper snapshots are regenerated.
* Never drop the user's Anthropic auth from a Claude launch
Orca's launches stripped ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, AWS_BEARER_TOKEN_BEDROCK and auth-like ANTHROPIC_CUSTOM_HEADERS for WSL terminals, terminal splits and through the preparation's stripAuthEnv flag, and refused a launch whose agent env set them. If the user's shell overrides the login, that is their setup: every launch now keeps it, on accounts, System default and WSL alike. The flag, the strip paths and the override refusal are gone; the refusal's reason stays in the shared vocabulary so older peers still decode it.
Orca's own process env and the claude --version probe still drop these variables: they are Orca's, not the user's launch.
* Leave out of an account's state everything Claude resets when it signs out
The .claude.json copy left out the login and keys matching cache patterns, but still carried account state such as additionalModelOptionsAnsweredAt, artifactRosterDenied, lastSeenOrgDefaultUpdatedAt and the subscription notices. The copy now leaves out the exact keys Claude resets on logout, beside the install ids, Console API key and first-token date; the cache patterns stay for account caches logout does not reset. Onboarding is still copied.
* Judge an old terminal's Claude banner by the pane's own runtime
The banner compared every old pane with the host selection and showed whenever only a WSL account was selected, so old host panes showed it with nothing to say and old WSL panes were judged by the host's account. The pane now names its runtime: a host pane is compared with the host selection, a WSL pane with its distro's selection through the WSL router, and a pane whose runtime is unknown, or with no account selected for it, stays hidden.
* Leave an account's settings file alone when only Orca's hooks differ from the default home's
* Reread a WSL account's Claude state only when the file changes
* Move tests merged from main onto the account-only Claude auth policy
* Keep terminal workspace root links on their execution host
* Resolve terminal root-link source host from the owner, not a missing connection id
A direct SSH pane whose worktree id also exists on the desktop has no
connectionId (ambiguous owner), which was read as local and offered the
desktop workspace. Resolve the source via the owner-route resolver and
skip the root shortcut when the source host is unknown.
* Resolve terminal file links against the source owner's host
A same-id SSH workspace left connectionId unset, so a rejected root link,
the default-app row and Shift-click fell through to this machine's files.
The terminal file context now names the resolved direct SSH host and
refuses file access when no owner can be determined.
* Keep an ambiguous terminal link owner unresolved instead of local
The connection lookup ignores detected worktree rows, so a registered local
row could override an owner the full resolver found ambiguous (registered
local, detected SSH) and send link stat/open to this machine. Only a missing
route may now defer to the connection lookup.
* Re-resolve a held chat link's owner before rechecking it
Native chat rechecks held link targets when a turn ends. A target resolved
while its owner was local kept that context after a same-id worktree was
detected on an SSH host, so the recheck still asked this machine. Each
existence ask now re-resolves the workspace owner and refuses when it is
unresolved or names a different host.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* Restore remote explorer updates through the recovered app watcher
* Keep the remote Explorer watched when it is shown as a fallback tab
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
This reverts commit 46460d49c7 (#26171).
Pause the Node 24 requirement so existing SSH and WSL users are not
surprised by a host-runtime upgrade requirement in the next release.
This is a precautionary rollout hold requested to avoid near-term user
disruption, not a cancellation of the Node 24 transition.
Restore the prior Node 18 host minimum and companion build targets.
The previously shipped bundled Node 24 runtime and compatibility launcher
remain in place. Preserve the original change in a separate draft PR for
reintroduction after compatibility and rollout validation.
* Guard desktop open actions with the target workspace owner
* Wait for the folder catalog only in the folder menu exercise
* Resolve the row owner only while its Open in menu is open
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* feat(runtime): default to bundled Node 24 and require modern hosts
* Update SSH deployment expectations for Node 24 guidance
* Expect Node 24 in Windows runtime installation guidance
* Preserve editor file identity during SSH host conversion
* Keep a dirty in-session editor tab on its file record during SSH conversion
A file opened over the relay in the current session carries route provenance,
so the no-host-evidence check repointed its tab to a fresh clean record and
hid the unsaved draft. Keep any retained record with no runtime owner instead.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* Distinguish remote browser service failures from server outages
* Use the shared browser_unavailable code in the stream failure classifier
---------
Co-authored-by: m4air <m4air@Mac.localdomain>