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>
* fix(orcad): install agent status hooks when a managed host starts
* fix(orcad): read login-profile Grok/Kiro homes before installing hooks
* refactor(agent-hooks): run the login-shell home probe through process-host
---------
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>
* refactor(worktrees): worktree create, listing, removal and repo hook/ref reads route by their owner
Worktree create, prefetch, detected listings and missing-terminal teardown,
preserved-branch cleanup and removal follow the repo or workspace owner.
Repo hook and base-ref reads take the repo's host id instead of settings.
Composer, settings panes and the setup guide pass the selected repo's host.
* test(hooks): build the duplicate-row state without a cast
---------
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
* refactor(git): git calls route by the worktree's owner
RuntimeGitContext carries the owner's transport (target) instead of a
settings object, so raw focus settings no longer compile at a git call.
Source control, the checks panel, diff loaders, fork sync, lineage,
worktree metadata, review push targets and diff-comment persistence derive
it from the repo or worktree rows (resolveOwner) or a captured owner.
Editor-slice remote actions take an explicit runtimeTarget and otherwise
resolve the worktree owner; an unresolved owner is refused, never sent to
the focused server. Generation preferences ride a separate prefs field.
* fix(work-items): one owner row routes direct-launch preflight, PR start point and create
* fix(review): PR dialog effects key on the host, not the target object
A fresh target literal per render re-ran base-ref discovery forever and ran the
test worker out of memory.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
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.
Catalog fetches, path-status probes, project and group mutations, nested
scans and imports now take the owner's host explicitly instead of falling
back to the Active Server setting. A new project with no picked host still
goes to the default host, through defaultCreationHost.
Co-authored-by: m4air <m4air@Mac.localdomain>
* 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>
getRuntimeRepoTarget takes the transport its caller already routed by
instead of reading the Active Server setting (with a raw focus default).
Work-item, comment, check and review-thread requests get it from
getGitHubSourceTarget (task source host, else the repo's owner); PR
lookups from the repo row; PR refreshes from the candidate's owner.
Hosted-review fetches route by hostedReviewRepoOwnerTarget. An unknown
repo stays on this computer instead of following the focused server.
Cache keys are unchanged. Deletes settingsForHostedReviewRepoOwner,
settingsForGitHubFocusedRepoOwner and getGitHubWorkItemSourceSettings.
Co-authored-by: m4air <m4air@Mac.localdomain>
Agent note sends, background agent and terminal launches, structured chat
reveal and activation, and the tab strip's Windows shell probe take their
transport from the workspace owner's environment instead of building
owner-shaped settings for getActiveRuntimeTarget. The automation observer and
the Codex selection lane read the owner from the PTY id alone, so an
ownerless remote id is never assigned the focused server. Local
advertised-URL events always rescan this computer, even when a server is
the default host.
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(terminals): terminal input and inspection route by the PTY's owner
A remote PTY id already names the server that owns it. inspect, confirm,
send, verified send and the data subscription took settings only to fall
back to the focused server for an id without an owner segment, so an old
ownerless id could reach a server that does not own it. They now route by
the id alone: an ownerless remote id is never dialed and never confirmed
locally.
Dropping the parameter removes the settings plumbing the native chat,
agent draft paste and follow-up paths carried only for this, including the
raw focused-settings default in sendBracketedPasteToAgent, and deletes
getSettingsForAgentTabRuntimeOwner.
* test(e2e): native chat SSH delivery and remote completion follow the narrower terminal signatures
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* refactor(files): file reads and edits route by the file's owner
The runtime file clients took a settings object and turned its Active Server
field into a connection, so any caller that passed raw settings silently read
or wrote on the focused server. They now take `target: RuntimeClientTarget`,
and every caller derives it from the owner: the tab's stamped owner, else its
worktree's owner (getRuntimeTargetForFileOwner / getRuntimeTargetForWorktreeOwner),
the file-explorer operation owner, or an already captured owner id.
Behavior: single-host (local-only) users see no change. An editor tab opened
without an owner stamp, a terminal link with no PTY owner, an OS file drop and
a notebook link now go to the workspace's owner instead of whichever server is
focused when the read happens.
Removes getEditorFileDropSettingsForWorktree and the native-chat settings clone
cache; settingsForWorktreeOperationRoute loses its editor caller.
* fix(files): file owner targets come from the rows, never the focused server
getRuntimeTargetForWorktreeOwner/getRuntimeTargetForFileOwner move to
lib/file-owner-runtime-target.ts and resolve through the focus-free
resolveOwner. A workspace with no row yet stays on this app (tabs and drops
stamp a server owner when one exists); rows that disagree return null, and
callers with an error path refuse via requireRuntimeTargetForFileOwner.
* test: keep the editor content-state fixture within the line limit
* fix(files): an unresolved document owner blocks local image reads
A null owner target became runtimeContext undefined, which image readers take
as a plain local file, so a server-owned document's images were read from this
machine. It is now runtimeContext null, the readers' do-not-load sentinel, in
the rich editor and the preview, and prewarm skips it.
* fix(files): read the image node view's storage through a named type
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
GitHub PR and issue dialogs, task-page cells, Project boards, slug metadata,
the repo slug index, hosted-review actions, GitLab MR base resolution and the
Settings API-budget panels now carry a RuntimeClientTarget derived from the
repo row, the row-less task source, the board row's cache key or the Settings
host scope, instead of settings-shaped objects resolved through
getActiveRuntimeTarget. A repo row with no owner stamp is local; an untracked
Project row edits on the host that loaded it.
Owner-routing ratchet 209 -> 165; focus-read ratchet 401 -> 352.
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(sources): GitHub Projects boards read from the task page's source host
The Projects board, its project picker and unmatched-row edits picked their
host from the Active Server setting on their own, while the Tasks header
named the selected repositories' hosts. The board now takes the same
row-less source host that Linear and Jira use, converted through
getTaskSourceRuntimeSettings, and the header names that host in Projects
mode. fetchProjectViewTable takes the source explicitly instead of reading
settings. The ownership table marks the four board-level GitHub Projects
reads as scope-picked.
* fix(github-projects): an open unmatched-row dialog keeps writing to its row's host
The dialog took the board's live source, so a source change while it was
open sent its comment and field edits to a different host than the one the
row was loaded from. It now routes by the source encoded in its row's cache
key, like the row field edits already do.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* feat(settings): host-owned settings panes choose their host explicitly
Settings gets a Host menu (this computer or a saved server). Provider
accounts, agent detection and terminal capabilities follow it instead of
the Active Server setting, which is only the menu's starting value. A
server that is no longer saved reads unavailable and never falls back to
another host. The provider-account client now takes the owner target
instead of settings.
* chore(i18n): regenerate runtime-required English catalog
* fix(accounts): Codex restart notices follow the host the switch wrote
A Settings account switch on the chosen host flagged panes on the focused
host instead. markLiveCodexSessionsForRestart and its lane matcher now take
the owner target explicitly; Settings passes the picker's host, the status
bar its focused target, and local sign-in this computer.
---------
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>
* fix(review): GitLab reads and actions run on the repo's own host; failed reads show as errors
GitLab task lists, the item dialog, right-sidebar MR actions and the Checks
panel now pick their host from the repo or workspace row: a server-owned
repo asks its server over runtime RPC, a local or SSH repo uses this
computer's glab. The focused Active Server is never consulted. The runtime
arm reuses the web client's gl-over-RPC table, now shared.
A failed checks or comments read in the Checks panel renders as an error
that names the machine that answered, instead of 'No checks configured'.
* fix(review): keep the server for SSH workspaces behind it; pin the MR host for post-launch thread resolves
An SSH workspace reached through a paired server keeps ssh:<target> as its
host and names the server separately; its GitLab credentials live on that
server, so the Checks panel and job-log tab now route there.
A comment-resolution launch now records the MR's credential host when it is
queued, so the post-launch resolve no longer looks the repo up by a bare id
that two hosts can share.
---------
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
Only Codex had a WSL fallback when a transcript is looked up by session id,
so Claude running inside WSL with no hook path was searched only on the
Windows profile and chat showed "Transcript unavailable". Every id lookup
now searches this host's roots first and, only on a miss, each running
distro's roots for the same agent, through the shared per-root WSL scan.
One layout table names how each agent files a session (claude, codex, grok,
omp) for both host and WSL lookups.
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
* feat(native-chat): offer Codex Ultrafast as a speed choice
Codex lists Ultrafast as a third service tier for eligible models and
accounts (GPT-6.1 Sol). Codex chats saved Fast as on/off, which cannot
hold a third tier. The Codex speed is now a Standard/Fast/Ultrafast
choice built from the tiers the model lists; the tier id is still
resolved from the catalog at each send. Older clients keep their Fast
toggle: the host still reports Fast on/off and accepts it as a speed.
* refactor(native-chat): save the Codex tier id instead of a speed name
The chat now keeps the tier id Codex lists (serviceTier: default,
priority, ultrafast) and sends it as-is on turn/start; Codex drops a
tier the model no longer lists with its own warning. This removes the
saved per-model tier map, the send-time lookup and the reconcile code.
The unused fastModeTierByModel catalog field is deleted. Older clients
still get Fast on/off, derived from the model's tier named Fast. Also
drops the pnpm-lock.yaml churn the first commit picked up.
* test(pi): drop the removed fastModeTierByModel catalog field
* fix(native-chat): review round 1 for Codex service tiers
- Accept an older client's fastMode write through the Codex adapter; it
was refused before reaching the tier alias.
- Bump the saved model catalog schema so catalogs from before
serviceTiers re-list instead of offering a Fast toggle with no tier.
- Keep only the reported tier on the record, not the Fast it implies.
- Drop a reported tier, not only a picked one, when the next model does
not list it.
- Localize Standard inside the speed choice only.
- Return the options answer directly instead of a one-field wrapper.
* fix(native-chat): review round 2 for Codex service tiers
Keep a tier Codex reports for the thread (it accepts unlisted ones such
as flex); read the legacy Fast tier beside an empty serviceTiers list.
* fix(native-chat): review round 3 for Codex service tiers
A reported tier the old model listed and the new one does not resets to
Standard on a model switch, and the reported tier itself stays pickable.
* fix(native-chat): keep a re-picked reported Codex tier
The thread's reported tier is never reset by the unlisted-tier drop, so a
re-picked Flex survives the next read, a model switch, and the turn.
* refactor(native-chat): one rule for which Codex tier a model offers
Standard, a tier the model lists, or the thread's reported tier that no
model lists. The setter and the unlisted-tier reset share it, replacing
the separate reported-tier and model-switch guards.
* test(native-chat): drop the removed fastModeTierByModel field from main's new catalog test
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.