Repository navigation
fix(remote): stop a disclosure list latching the mirror completeness gate - #18619
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (9)
Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review. 📝 WalkthroughWalkthroughThe change adds Merge Risk: 🟡 Moderate · up to This change allows peer-runtime omissions to settle terminal inventories, but a malformed covered host identifier can still make an incomplete empty inventory authoritative, potentially hiding or pruning remote terminal metadata. Resolve this validation gap before merge. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Linked Issues checkExplanation The changes directly address the reported remote terminal inventory hydration failure by treating peer-runtime omissions as disclosure rather than missing coverage. The context does not show implementation for all linked issue objectives, especially worktree/project inventory behavior and UI-created tab routing.
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
A single-commit fix for #18595: a leftover runtime: session partition in workspaceSessionsByHostId was making every terminal.list / worktree.list answer with a permanently non-empty hostScope.omittedHostIds, which the renderer reads as unverifiable and parks remote panes on forever.
listKnownExecutionHostIdsnarrowing — session-partition host ids whose parsed kind isruntimeare no longer added to the "known" set, so a bare pairing partition stops appearing as an omission.- Four focused tests — two revert-tests (unscoped and worktree-scoped listings drop the phantom omission) and two guards (a mirrored row still omits its
runtimehost; an SSH partition still omits).
I traced the change against every consumer of listKnownExecutionHostIds and the omittedHostIds field and found the narrowing consistent and safe:
- Repos (
getRepoExecutionHostId), folder workspaces, resolved worktrees, and terminal rows with aruntime:executionHostIdall still disclose a paired runtime through the loops inbuildTerminalListHostScopeand theincludeConfiguredHostsbranch, so only the evidence-free partition is dropped — the original #14973 justification is preserved. queriedHostIdsis built only fromlistProcessesWithHostScopeFromRuntimeController(local + registered SSH providers) andrefreshPtyWorktreeRecordsWithControllerInventory(adds onlyssh:hosts), so it never contains aruntime:id; the untouchedcoveredfilter atorca-runtime-get-runtime-id.ts:171remains inert and no host is made to look covered.isCompleteSessionTabsPtyCensusalready excludedruntime:hosts from its completeness check, so its behavior is unchanged (and now more consistent) under the narrowing.- The new assertions are exact (
toEqual([])/toEqual(['runtime:env-7'])), and the revert/guard split in the PR description matches what the tests actually pin.
Wire compatibility is handled correctly: the frame shape is unchanged and the host publishes different content, which can only turn a phantom unverifiable into a real verdict for old clients.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
One new commit since the prior pullfrog review (8dbbfcd → 2ab22ca): a documentation-only update to the resolveOmittedHostScopeSelectors JSDoc in src/cli/omitted-host-scope-selectors.ts.
- Doc comment corrected for the #18595 narrowing — the provenance list now reads "repos, folder workspaces, mirrored rows, and resolved worktrees" instead of "…and workspace sessions", and a new paragraph explains the distinction the fix introduced: content-bearing runtime hosts stay named as gaps, while a bare
workspaceSessionsByHostIdpartition is not.
The comment is accurate against the code: a runtime: host still enters omittedHostIds through repos (getRepoExecutionHostId), folder workspaces, resolved worktrees, and mirrored terminal rows in buildTerminalListHostScope, and only the evidence-free session partition is dropped. No behavioral surface changed.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
One new commit since the prior pullfrog review (2ab22ca → 6e3a1c9): two tests added to src/main/runtime/terminal-list-execution-host-scope.test.ts that close the last open coverage gap.
- Worktree-listing host scope now exercised through the real pipeline —
listManagedWorktrees()reacheslistKnownExecutionHostIdsviamanagedWorktreeQueries.listKnownHostIds(not the injected dependencyworktree-list-host-scope.test.tsuses), so these tests pin the actual sourcing rule on the second surface the fix governs. drops a partition-only paired runtime from the worktree listing scope, keeping SSH— asserts aruntime:env-7partition contributes nothing whilessh:box-9(and the honestlocalgap from zero rows) stays named.keeps a repo-known paired runtime in the worktree listing scope— asserts a repo carryingexecutionHostId: 'runtime:env-9'still surfaces that host, guarding against over-broadening.
Both assertions are exact (toEqual), and I traced each through listKnownExecutionHostIds → buildWorktreeListingPage → buildWorktreeListingHostScope: reverting the kind !== 'runtime' filter re-admits runtime:env-7 and fails both, so they are genuine revert-tests, not coverage theatre. Verified locally — the file's 25 tests pass.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
…gate `hostScope.omittedHostIds` was doing two jobs with opposite requirements. As disclosure it must over-name: `omitted-host-scope-selectors.ts` deliberately keeps ids for servers that are no longer paired so a caller can still see the gap, and `docs/reference/ssh-execution-boundary.md` requires a listing to name what it did not cover. As a completeness gate it must name only coverage that was owed and not delivered, or it latches. It latched. `workspaceSessionsByHostId` keeps a partition for every runtime a machine has ever paired with and nothing prunes it, and a mirrored `remote:` row names its peer too — so any client that has ever paired outward publishes a permanently non-empty `omittedHostIds`. `probeHostLiveTerminals` read that as `unverifiable`, `markHostSessionMirrorHydrated` never fired, and panes parked on `parkUntilHostSessionMirrorHydrates` never drained. `hostScopeCensusIsComplete` gives the gate its own answer and leaves the disclosure list alone. A `runtime:` host is never owed coverage by the runtime answering: a paired runtime is a peer with its own control plane reached with `--environment`, and there is no paired-runtime PTY provider for this runtime to have queried. Two other branches stay load-bearing — an absent scope is a host too old to claim one, and a listing that covered no host proves nothing. No wire change: the host publishes byte-identical content and only the client's reading moves, so this reaches the reporter by updating their client alone rather than waiting for their remote. That also avoids a new field's fallback rule, where "absent means complete" would recreate the bug with the polarity flipped. `queried-host-kinds.test.ts` pins the invariant the predicate rests on at its source, because the consolidation moving the SSH path onto orcad is the change most likely to introduce a runtime-backed PTY provider and quietly invalidate it. Fixes #18595
6e3a1c9 to
f49bc28
Compare
…invariant claim The readiness review found the second gate unpinned: reverting `web-session-terminal-orphan-recovery-inventory.ts` alone to the pre-PR expression left the whole renderer suite green, because every existing fixture passes `omittedHostIds: []`. The commit claimed two gates and proved one. Four cases now drive `resolveTerminalOrphanInventory` through a non-empty scope. Reverting that gate alone fails the peer-runtime case. Note the absent-scope case deletes the key rather than passing `undefined`, because `listResult` substitutes its default for `undefined` — routing through the fixture there silently tests the default instead. `queried-host-kinds.test.ts` also claimed more than it caught: a runtime-backed transport registered under an SSH connection id reports as `ssh:` and passes, which is the shape the orcad consolidation is expected to take. It pins the spelling this function emits, which is what the gate keys on, and now says so.
There was a problem hiding this comment.
Important
The PR description still documents the reverted host-side fix, not the client-side gate that actually ships — it should be rewritten before merge.
Reviewed changes
This commit rewrites the fix from the previously reviewed host-side approach (narrowing listKnownExecutionHostIds so the host stops publishing phantom runtime: omissions) to a client-side completeness gate. The host now publishes omittedHostIds exactly as before; the renderer decides what counts as incomplete.
- Added
hostScopeCensusIsCompleteinsrc/shared/runtime-listing-host-scope.ts— a census is complete only when a scope is present, at least one host was covered, and every omitted host is aruntime:peer (disclosure) rather than anssh:/localgap. - Wired it into the two renderer readers —
host-live-terminal-probe.tsandweb-session-terminal-orphan-recovery-inventory.tsnow call the gate instead ofomittedHostIds.length > 0, so a leftoverruntime:partition no longer forcesunverifiable. - Added tests — a source-invariant test (
queried-host-kinds.test.ts) pins that the PTY inventory only ever nameslocal/sshhosts, plus probe-level and unit tests that fail without the gate.
⚠️ PR description documents the reverted approach
The prose below the (auto-regenerated) LoC table still describes the earlier host-side fix: "The change" says listKnownExecutionHostIds no longer admits a partition-only runtime: host, and "Testing" points at six tests in terminal-list-execution-host-scope.test.ts. None of that is in this diff — orca-runtime-get-runtime-id.ts and that test file are unchanged from base. The implementation is now entirely client-side, so the "Wire compatibility" and "Hardware verification" sections are also inverted: the host publishes the same content as before, and only updated clients change behavior. Rewrite the description around hostScopeCensusIsComplete so a reviewer is not misled about where the fix lives and whether an old client is fixed.
Technical details
# Stale PR description vs. client-side implementation
## Affected sites
- PR #18619 description ("The change", "Testing", "Wire compatibility", "Hardware verification") — documents the reverted `listKnownExecutionHostIds` narrowing.
## Required outcome
- Rewrite the description to describe `src/shared/runtime-listing-host-scope.ts#hostScopeCensusIsComplete` and its two renderer call sites, and correct the wire-compat claim (host content unchanged; fix is client-only).
## Open questions for the human
- Is a client-only rollout acceptable (old clients keep the bug until they update), or does the fix also need the host-side narrowing the earlier commits had?DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
One new commit since the prior pullfrog review (f49bc28 → 1e3c6e9): the orphan-recovery gate is now pinned with its own test file, the invariant guard's claim is narrowed to what it actually proves, and the PR description was rewritten to match the client-side approach.
- Added
web-session-terminal-orphan-recovery-host-scope-gate.test.ts— four tests driverecoverWebSessionTerminalOrphansBeforeApplytwice (pruning needs two consecutive authoritative inventories) and pin the second consumer ofhostScopeCensusIsComplete: a peer-only omission prunes the ghost binding, while an SSH gap, a covered-nothing listing, and an absent scope all retain it. - Narrowed the
queried-host-kinds.test.tsinvariant claim — the comment now spells out what the test does and does not catch (a runtime-backed transport registered under an SSH id still reportsssh:), and the assertion hardened to a UUID-shaped connection id that must still come backssh:. - Rewrote the PR body — "Root cause", "Wire compatibility" (now correctly "no wire change"), hardware verification, and the testing table all describe the client-side gate, resolving the stale-description note from the prior review.
The new gate test is a genuine revert test, not coverage theatre: tracing resolveTerminalOrphanInventory → confirmSurfaceInventoryAbsence, a peer-runtime-only omittedHostIds is the only shape that reaches the remove path, so reverting the gate would turn "prunes a twice-absent binding" into a retained binding and fail. I ran both changed test files locally — 6 tests pass.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
|
@coderabbitai review Note for reviewers: this PR was force-pushed and rebuilt. Earlier reviews covered an abandoned host-side approach ( |
|
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Team
Run ID: 3d198d40-aaee-4b5b-be88-e7e9eb2ad424
📒 Files selected for processing (7)
src/main/ipc/pty/runtime/queried-host-kinds.test.tssrc/renderer/src/runtime/host-live-terminal-probe.tssrc/renderer/src/runtime/host-session-mirror-empty-inventory-settle.test.tssrc/renderer/src/runtime/web-session-terminal-orphan-recovery-host-scope-gate.test.tssrc/renderer/src/runtime/web-session-terminal-orphan-recovery-inventory.tssrc/shared/runtime-listing-host-scope.test.tssrc/shared/runtime-listing-host-scope.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
| return false | ||
| } | ||
| // A listing that covered no host at all proves nothing, whatever it omitted. | ||
| if (scope.hostIds.length === 0) { |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Validate covered host identifiers before declaring the census complete.
hostIds.length accepts an unparseable covered host as proof of coverage. For example, { hostIds: ['runtime:'], omittedHostIds: [] } returns true. The probe validates only that hostIds is an array, so it can convert this malformed inventory into none instead of unverifiable.
Reject the scope unless every hostIds entry parses successfully. Add a matching regression test.
Proposed fix
- if (scope.hostIds.length === 0) {
+ if (
+ scope.hostIds.length === 0 ||
+ scope.hostIds.some((hostId) => parseExecutionHostId(hostId) === null)
+ ) {
return false
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (scope.hostIds.length === 0) { | |
| if ( | |
| scope.hostIds.length === 0 || | |
| scope.hostIds.some((hostId) => parseExecutionHostId(hostId) === null) | |
| ) { |
CodeRabbit found a real asymmetry: the predicate refused an omitted host id it
could not parse, but accepted an unparseable *covered* id as proof of coverage.
`isTerminalListResult` validates only that `hostIds` is an array, so
`{hostIds: ['runtime:'], omittedHostIds: ['runtime:env-7']}` was `unverifiable`
before this PR and would have become `complete` after it.
Taken as "at least one legible covered host" rather than the suggested "every id
parses". A host that later gains a kind this client cannot parse would otherwise
report an incomplete census forever — which is this bug in a new coat, and the
failure mode the predicate exists to prevent.
The check exposed four tests publishing `hostIds: ['remote-runtime']`, a bare
environment id that `parseExecutionHostId` rejects. No host emits that: a runtime
answering `terminal.list` names the execution hosts it covered, which is `local`
— verified against a live paired runtime. Those fixtures are corrected to the
shape the wire actually carries, which is why the assertions move.
|
@coderabbitai review |
|
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
One new commit since the prior pullfrog review (1e3c6e9 → 2797c6f) that tightens branch 2 of the completeness gate and fixes a latent fixture bug the tightening exposed.
hostScopeCensusIsCompletenow rejects a coverage claim with no legible covered host — branch 2 changed fromscope.hostIds.length === 0to!scope.hostIds.some(parseExecutionHostId), so a listing whose covered set is entirely unreadable ids (['runtime:'], garbage) is no longer believed on the strength of an empty or all-runtime:omission list. Deliberately "at least one legible" rather than "all legible", so a host that later gains a kind this client cannot parse does not report an incomplete census forever — the same latch this predicate exists to stop, in a new coat.- Corrected the orphan-recovery regression fixture default scope — the default
hostScopenamed the environment id (remote-runtime) as a covered host, which is not a legible execution-host id; the new branch would have rejected it. It now nameslocal, matching what a live host actually reports, with matching'local'literal updates in the sibling settle/pending-handle/gate tests. - Added two legibility unit tests —
refuses a coverage claim with no legible host in it(a genuine revert test: with branch 2 reverted to the length check,['runtime:']parses tonulland would still return complete) andaccepts a coverage claim carrying one legible host beside an unreadable one(pins the "at least one" choice against an over-tightening toevery).
I verified the changed test files locally (52 tests pass), and the covered-side leniency / omitted-side strictness split is sound: an unparseable covered id is irrelevant to completeness, while an unparseable omitted id still correctly reads as a gap.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
…gate (stablyai#18619) * fix(remote): stop a disclosure list latching the mirror completeness gate `hostScope.omittedHostIds` was doing two jobs with opposite requirements. As disclosure it must over-name: `omitted-host-scope-selectors.ts` deliberately keeps ids for servers that are no longer paired so a caller can still see the gap, and `docs/reference/ssh-execution-boundary.md` requires a listing to name what it did not cover. As a completeness gate it must name only coverage that was owed and not delivered, or it latches. It latched. `workspaceSessionsByHostId` keeps a partition for every runtime a machine has ever paired with and nothing prunes it, and a mirrored `remote:` row names its peer too — so any client that has ever paired outward publishes a permanently non-empty `omittedHostIds`. `probeHostLiveTerminals` read that as `unverifiable`, `markHostSessionMirrorHydrated` never fired, and panes parked on `parkUntilHostSessionMirrorHydrates` never drained. `hostScopeCensusIsComplete` gives the gate its own answer and leaves the disclosure list alone. A `runtime:` host is never owed coverage by the runtime answering: a paired runtime is a peer with its own control plane reached with `--environment`, and there is no paired-runtime PTY provider for this runtime to have queried. Two other branches stay load-bearing — an absent scope is a host too old to claim one, and a listing that covered no host proves nothing. No wire change: the host publishes byte-identical content and only the client's reading moves, so this reaches the reporter by updating their client alone rather than waiting for their remote. That also avoids a new field's fallback rule, where "absent means complete" would recreate the bug with the polarity flipped. `queried-host-kinds.test.ts` pins the invariant the predicate rests on at its source, because the consolidation moving the SSH path onto orcad is the change most likely to introduce a runtime-backed PTY provider and quietly invalidate it. Fixes stablyai#18595 * test(remote): pin the orphan-recovery host-scope gate and narrow the invariant claim The readiness review found the second gate unpinned: reverting `web-session-terminal-orphan-recovery-inventory.ts` alone to the pre-PR expression left the whole renderer suite green, because every existing fixture passes `omittedHostIds: []`. The commit claimed two gates and proved one. Four cases now drive `resolveTerminalOrphanInventory` through a non-empty scope. Reverting that gate alone fails the peer-runtime case. Note the absent-scope case deletes the key rather than passing `undefined`, because `listResult` substitutes its default for `undefined` — routing through the fixture there silently tests the default instead. `queried-host-kinds.test.ts` also claimed more than it caught: a runtime-backed transport registered under an SSH connection id reports as `ssh:` and passes, which is the shape the orcad consolidation is expected to take. It pins the spelling this function emits, which is what the gate keys on, and now says so. * fix(remote): require a legible covered host before believing a census CodeRabbit found a real asymmetry: the predicate refused an omitted host id it could not parse, but accepted an unparseable *covered* id as proof of coverage. `isTerminalListResult` validates only that `hostIds` is an array, so `{hostIds: ['runtime:'], omittedHostIds: ['runtime:env-7']}` was `unverifiable` before this PR and would have become `complete` after it. Taken as "at least one legible covered host" rather than the suggested "every id parses". A host that later gains a kind this client cannot parse would otherwise report an incomplete census forever — which is this bug in a new coat, and the failure mode the predicate exists to prevent. The check exposed four tests publishing `hostIds: ['remote-runtime']`, a bare environment id that `parseExecutionHostId` rejects. No host emits that: a runtime answering `terminal.list` names the execution hosts it covered, which is `local` — verified against a live paired runtime. Those fixtures are corrected to the shape the wire actually carries, which is why the assertions move.

Fixes #18595.
Root cause: one field, two jobs, opposite requirements
hostScope.omittedHostIdsserves two purposes that pull in opposite directions.As disclosure, it must over-name.
src/cli/omitted-host-scope-selectors.ts:33-42deliberately keeps ids for servers that are no longer paired, becausedocs/reference/ssh-execution-boundary.mdrequires a listing to name its gaps, and an agent completing a partial listing needs to see them.As a completeness gate, it must name only coverage that was owed and not delivered — otherwise it latches.
It latched. Two sources put a peer runtime in that list permanently:
workspaceSessionsByHostIdkeeps a partition for every runtime a machine has ever paired with, and nothing prunes it (session-host-partitions.ts:70-80).remote:<env>@@handlerow contributesterminal.executionHostId(orca-runtime-get-runtime-id.ts:133-137).Neither can ever be
covered, because aruntime:host can never enterqueriedHostIds— there is no paired-runtime PTY provider (src/main/ipc/pty/provider/registry.ts:18-23,src/main/ipc/pty/runtime/inventory-operations.ts:24-52). So any client that has ever paired outward publishes a permanently non-emptyomittedHostIds,probeHostLiveTerminalsreturnsunverifiableforever,markHostSessionMirrorHydratednever fires, and panes parked onparkUntilHostSessionMirrorHydratesnever drain. Blank remote tabs.The change
hostScopeCensusIsComplete()insrc/shared/runtime-listing-host-scope.tsgives the gate its own answer and leaves the disclosure list untouched. Three branches, all load-bearing:scope === undefined→ not complete. A host too old to publish a scope cannot claim one; absence of the claim is never the claim. This stays first.hostIds.length === 0→ not complete. A listing that answered for no host proves nothing.hostIdscarries no idparseExecutionHostIdcan read → not complete. An unreadable coverage claim is not a claim.kind === 'runtime'.Branch 3 is the fix. A paired runtime is a peer with its own control plane, reached with
--environment; this runtime never queried it and never owed it coverage, so its presence in the list is disclosure rather than a gap. This mirrors a rule the host already applies to itself —isCompleteSessionTabsPtyCensusdiscountsruntimekinds identically (orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts:188-197).Applied at the two gates:
host-live-terminal-probe.tsandweb-session-terminal-orphan-recovery-inventory.ts. Deliberately not applied atworktree-live-terminal-surface-owners.ts:118— that is a worktree-scoped listing where every other host includingssh:is omitted by design, so the predicate would null its owner index permanently on any machine with an SSH target.Branch 2 is defence in depth, not a live path:
listKnownExecutionHostIdsalways seedslocal, so a real scope withhostIds: []also omitslocaland is refused by branch 3 anyway. It exists so a scope that covered nothing can never read complete if that stops holding.Review finding: covered ids were trusted without being read
CodeRabbit found a real asymmetry introduced by the first version of this gate. The predicate refused an omitted id it could not parse, but accepted an unparseable covered id as proof of coverage —
isTerminalListResultvalidates only thathostIdsis an array. So{hostIds: ['runtime:'], omittedHostIds: ['runtime:env-7']}wasunverifiablebefore this PR and would have becomecompleteafter it. Narrow, but a new exposure created by the fix.Taken as at least one legible covered host, not the suggested "every id parses". The strict form is a forward-compatibility trap: a host that later gains a host kind this client cannot parse would report an incomplete census forever — which is this bug in a new coat, and exactly the failure mode the predicate exists to prevent. A test pins that distinction.
Why four pre-existing tests changed
Adding the check turned four tests red, and they are corrected rather than accommodated. They published
hostScope: { hostIds: ['remote-runtime'] }— a bare environment id thatparseExecutionHostIdrejects.No host emits that shape. A runtime answering
terminal.listnames the execution hosts it covered, which islocal; verified against a live paired runtime (see the hardware section above, where the real wire answer ishostIds: ["local"]). The fixtures asserted a wire shape that does not exist, which is why the assertions move."I changed pre-existing tests so my change would pass" deserves scrutiny, so the reasoning is stated here rather than left implicit. Two of the four resolved from one line in the shared fixture (
web-session-terminal-orphan-recovery-regression-fixtures.ts); the other two were inline literals inweb-session-terminal-pending-handle-recovery.test.ts.Wire compatibility: no wire change
Rules 1, 2 and 3 of
docs/reference/remote-wire-compatibility.mdare not engaged. Nosrc/mainproduction file is touched; the host publishes byte-identical content and only the client's reading moves.hostScope)hostScope &&, recovery reads it as unverifiableBeing client-side is the point: it reaches the reporter by updating their M1 alone, without waiting for their remote to update. Mixed versions are the normal state. It also avoids a new field's fallback rule, where "absent means complete" would recreate this bug with the polarity flipped.
Hardware verification, on the reporter's shape
The real headless runtime (
out/orcad/orcad.js) built from this branch, seeded with the reporter's own environment id from the issue, paired to the real CLI over the real RPC path:That is the shape reported in the issue. Feeding that live wire answer to the real
probeHostLiveTerminalsfrom each tree — the host output is identical either side, so this isolates exactly what moved:Also measured: this defect does not require a stale pairing or an aged profile. A brand-new client profile has its paired runtimes in
workspaceSessionsByHostIdafter the first connect, so it fires on first use.Version check on the reporter's host
Worth recording, because it decides whether this reaches them.
hostScopeonterminal.list— the field this gate reads — shipped in v1.4.187; the reporter runs 1.4.197, and a 1.4.195 remote here publishes it. Confirmed on hardware.hostScopeonworktree.listis a different, unreleased path (#18417, added 2026-09-03, in no release tag). A 1.4.195 remote correctly omits it, which is whyorca worktree list --environment <remote>returns nohostScopetoday. That is not a defect and not what the gate reads.Testing
reads a peer runtime as disclosure, not as coverage this host owedsettles on a peer runtime it disclosed but never owed coverage forreports a live terminal through a peer-runtime disclosureprunes a twice-absent binding when the only omission is a peer runtimerefuses a listing that covered no host at allReverting each branch independently: branch 3 → 3 failures; branch 2 → 1 failure; gate 2 alone → 1 failure. That last one matters: a readiness review caught that the first version of this PR left gate 2 unpinned — reverting it alone kept the entire renderer suite green, because every pre-existing fixture passes
omittedHostIds: []. The commit claimed two gates and proved one.One fixture trap worth naming:
listResultsubstitutes its default scope when handedundefined, so the absent-scope case deletes the key instead. Routing through the fixture there silently tests the default.pnpm tccleansrc/renderer+src/cli+src/shared— 4273 files, 37279 passedsrc/main— 3093 passed; 5 failures under full-suite load (node-pty-master-fd-retirement,ssh-remote-commands) that pass 26/26 in isolation, plusstructured-tui-transcript-catchupwhich is known-broken on main. None touch this change.oxlintexit 0 on all changed files,oxfmtapplied, nomax-linesdisableDeliberately not addressed
CodeRabbit's pre-merge checks pass 4 of 5. The failure is Docstring Coverage at 50% against an 80% threshold, over four analyzed functions. Overridden: the production function (
hostScopeCensusIsComplete) carries a full docstring, the remainder are test helpers, andAGENTS.mdasks for concise non-obvious comments rather than blanket ones. Meeting the threshold would mean adding boilerplate that violates the repo's own style rule to satisfy a bot heuristic.Not covered
workspaceSessionsByHostIdon unpair. This makes the entry harmless rather than absent. Separate follow-up.worktree listreturning[]on a pure-remote client was investigated separately and is not a second defect: four independent guards prevent aruntime:-hosted repo from entering a client store, and the symptom reproduces exactly on a fresh client with zero runtime repos. It is this bug plus having no locally registered repos.