The terminal gate's relay lister always resolved to null in production (no SSH PTY
provider is ever registered), so its relay and previous-relay branches, the legacy
relay route/router/routing, the previous-relay census and the once-connected
re-check could never run. The gate now takes only the host census; census-only
verdicts are pinned by a characterization test written before the change.
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
orcad now checks its flags with the same validator Electron serve and the
CLI use, so contradictory pairing flags fail with the same message instead
of one silently winning. The CLI launcher drops its duplicate recipe check
(the CLI handler already ran the shared validator).
Removes the daemon-init and cli/runtime/index re-export barrels and the
parsePtySessionId / OrcadInstanceLockError re-exports; callers and mocks
import the defining modules.
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Nothing mints an SSH-bridge credential since the relay client was removed
(#27214), so the 'ssh-bridge' caller kind, its host binders, result filter,
the CLI bridge-mode policy and the ORCA_SSH_BRIDGE_CREDENTIAL env read could
never run. The allowRemoteCliControl target field stays for older peers.
Co-authored-by: m4air <m4air@Mac.localdomain>
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>
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(ssh): remove the relay-only remote workspace sync so reconnect never sticks on pulling
* refactor(ssh): drop snapshot apply tokens and terminal finalization only the sync used
* test: drop leftover sync-status mock
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Compose typed file search and listing with narrow workspace/store dependencies and preserve the existing runtime command facade. Move the mobile inventory cache to its owner, remove four inheritance classes and six unchecked-file entries, and delete unused process bookkeeping while retaining producer-owned cancellation.
Preserve file access, provider capability fallbacks, host isolation, remote negotiation, budgets and cache lifetime. Independent audits verify moved method bodies, parameters, defaults and literals. Local tests and matched native Windows controls, typechecks, quality checks and CI qualification are recorded in the PR.
* feat(runtime): let a paired desktop register its phones as delegated mobile devices
A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of
its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the
desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(runtime): move the mobile device revoke primitive below dispatch
Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in
a layer under it as a pure move; this drops the abstract method seam. No behaviour change.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): keep a runtime grant until its delegated phones are revoked
A failed child cleanup now leaves the parent in place so a retry can still find the children, and
revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage,
shares credential minting, and drops the timing-dependent census.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(runtime): fail a delegated sync whose dropped phone cannot be revoked
Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors
before minting anything, so the desktop retries instead of believing the server matches its list.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* feat(mobile): relay a paired phone's server-workspace requests through the desktop
A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop
now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per
(phone, server), signed in as the phone's delegated device; replies pass through untouched except
stream ids. Hidden until the phone client ships: the relay capability is not advertised yet.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): state the desktop relay contract in its entry module
main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a
server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a
later request syncs it again.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): pin the relay's configured-server adapter
The production hosts adapter was only typechecked; this pins that it resolves configured servers
by exact id only (never a name or a removed server), fences by pairing revision and runtime
identity, and stops reporting retirements once unsubscribed.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile): tighten the desktop relay after elegance review
Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request
whose link ended while opening, passes failure codes through, relays only this desktop's own
phones, and drops the unused host-state reader until S2 needs one.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): bound relay frames and stop sticky relay verdicts
Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on
each new link so an in-place upgrade relays, answers a phone the server just declined without
syncing again, and ends a link whose mirrored capability update cannot be queued.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): run targeted desktop-owned calls on the desktop
The phone tags every call on a workspace screen with its execution host, so a
paired-desktop method carrying `runtime:<env>` now runs locally instead of
failing 'forbidden'. The Mac stays the only reader of the routing table.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(ci): run the remote-runtime WebSocket opener test on Node
Under the bun vitest project, `ws` resolves to Bun's built-in client, which
ignores maxPayload, so the frame-cap test hung. The opener only ships on
Node/Electron, so run its test in the Node runtime project like its siblings.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(mobile-relay): derive the phone allowlist from the routing table
Adding a phone method now takes one edit (its route). A server-targeted call on a
legacy non-E2EE socket is refused instead of running locally, and only
execution-host methods enter the relay.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The relay server deletion removed src/relay/ from the cross-host qualification
prefixes, but the scope tests merged alongside it still used src/relay/ paths
as their cross-host example. Use src/main/ssh/, which still carries that rule.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* 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>
* fix(files): a managed terminal links and opens a host file outside its workspace
A paired-server terminal checked out-of-workspace paths on this computer, so a
host file such as /tmp/screenshot.png never became a link, and a click was
refused. Link detection now asks the host the same question a click asks, with
the printing terminal's handle, and a granted file opens read-only through the
host's existing terminal-artifact grant.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(files): a restored server-granted tab is never this computer's file
Co-Authored-By: Claude <noreply@anthropic.com>
* test(e2e): keep the external-image journey's /tmp outside every workspace
The default folder workspace is /tmp, so the image resolved into it instead of
exercising a host file outside all workspaces.
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>
* ci(e2e): run the layout oracle on PRs that change layout writers
Co-Authored-By: Claude <noreply@anthropic.com>
* ci(e2e): narrow the layout oracle triggers to the modules that write layout
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(lint): delete the focus-routing helpers; bypassing owner routing fails lint
The last six renderer sites (Settings visibility defaults, preflight readiness,
Linear key dialog copy) mirror the default scope host and now say so through
defaultScopeHost. getActiveRuntimeTarget and settingsForRuntimeOwner are
deleted with their re-exports, the unused settings-fabrication helpers go with
them, and the owner-routing baseline is empty, so any new use fails lint. The
provider dispatch rationale is rewritten in neutral, shorter terms.
* test(preflight): keep the existing cast's rationale on the edited line
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* [CI] Reduce repeated test setup and remove source text checks
* [CI] Consolidate transcript tests and preserve fixture-only CI routing
* [CI] Retarget reliability gate to consolidated transcript suite
Combine related chat, worker-release, and filesystem tests while retaining their contracts. Replace fixed Codex terminal waits with observed readiness and hook completion. Keep runtime database contracts under Node.
Local measurements and all CI lifecycle costs are documented in PR #27297.
* refactor(sources): row-less sources and host-less surfaces name their host
Linear and Jira clients take a RowLessSource: a task-source context, an
explicit host, or defaultScopeSource(settings) when no Tasks source is
chosen, so raw settings no longer compile. defaultScopeHost joins
defaultCreationHost as the second sanctioned reader of the default-host
setting (both behind one private read, both ratchet seeds), and the status-bar
account switchers, settings navigation and the settings host picker's initial
default use it. Skill discovery routes by the owner id its install terminal
resolves, and the workspace-board Linear sync by the workspace owner.
* fix(lint): count the default scope source and assign-then-return readers
defaultScopeSource is a sanctioned read of the default host, so its call
sites count. A function that assigns a reader's result to a local and returns
that local is a reader too, which keeps the skill-discovery hook counted.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(runtime): owner transports, the creation default and a focus-read ratchet
Adds runtimeTargetForOwnerEnvironment, runtimeTargetForOwnerHostId and
runtimeTargetForWorkspaceOwner so routed call sites can get a transport from
the resource's owner, and defaultCreationHost as the one sanctioned reader of
the Active Server setting for creation flows with no source row.
The owner-routing ratchet gains a second per-file counter for reads of the
setting (member reads, the focus-routing helpers and defaultCreationHost), so
swapping a helper for a direct read can no longer lower a count. Baseline rows
may carry a trailing note that survives --prune.
* chore(lint): pin defaultCreationHost's own read in the focus-read baseline
* fix(lint): the focus-read ratchet counts shared focus helpers and destructuring reads
* fix(lint): discover functions that read the default host instead of hand-listing them
A hand list let a look-alike such as getAutomationListTarget replace
getActiveRuntimeTarget and lower both ratchets. Every exported top-level
function whose body reads the setting or calls a seed helper now counts as a
reader, and nested destructuring reads count too.
* fix(lint): readers are found through imports and returned values
Readers are keyed by defining file and resolved through imports and
re-exports, so a same-named function elsewhere is not mistaken for one, and
a function that returns a reader's result (a wrapper of a wrapper) is found
too. Components are not readers, and a declaration ends at the next
top-level statement.
* fix(lint): a reader called through a namespace or dynamic import still counts
* fix(lint): renamed, parenthesized and .then() dynamic imports bind readers too
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(review): PR reads and actions follow the repo owner, not a host-less task source
A GitHub task source without a runtime host used to spread
activeRuntimeEnvironmentId: null over the repo owner's settings, so
assignee, reviewer, label, status and merge cells for a runtime-owned
repo ran on this desktop. Every PR read and action site now routes
through getGitHubRepoRoutingSettings (renamed from the merge-only
helper), and the store's source-scoped routing defers to the repo owner
the same way. Focus is never a fallback.
Fixes#7623
* fix(review): a host-less source never inherits an ambiguous repo owner
Duplicate repo ids across hosts made the host-less source fallback pick a
server owner by focus (UI) or by first row (store), so a local item could
read or write on a paired server. The owner is now inherited only from
the single row with that id; ambiguity stays on the source's own host.
* test: drop stale repo-owner mocks from GitHub repository scripts
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* 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>
* fix(codex): keep running subagent groups in long chats
* fix(codex): remember superseded settled child turns
* fix(native-chat): retain running Claude subagent groups
* Keep repository detection off the main IPC thread
Share repository discovery decisions across synchronous compatibility callers and the existing asynchronous Git runner. Await discovery in project registration, nested imports, and folder upgrades, and recheck folder ownership after discovery completes.
* Make repository detection fixtures work on native Windows
* Await asynchronous Git checks in folder upgrade fixtures
* Prevent duplicate project registration after async discovery
* Check authoritative registration identity before renderer convergence
* Narrow repository responsiveness to detection and registration
* Keep one local project identity after concurrent registration
* Run registration SSH coverage in the existing Docker lane
* test(git): cover classifier failure stages and literal path identity
* test(git): use the current process host package
* test(repos): use current process host in registration fixture
* 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
* [CI] Consolidate renderer test modules and retire static scans
* [CI] Keep consolidated test within the existing line limit
* [CI] Refresh evidence for the preserved turn-duration gate