Repository navigation
Direct-SSH snapshot tabs are silently dropped when the worktree catalog resolves their paths late #12902
Description
Summary
When a direct-SSH remote workspace snapshot is applied, any snapshot worktree path the local worktree catalog cannot resolve yet is silently dropped and never retried. The tabs under that path — and any sleeping agent sessions bound to them — stay missing until the next full reconnect. To the user this looks like their remote agent session vanishing: activating the worktree shows a brand-new empty terminal instead of the restored session.
Environment
- Orca 1.4.174 (also reproduced on 1.4.171), macOS (darwin arm64)
- Direct SSH workspace to a Linux host, claude agent running in a remote worktree tab
What happens
applyDirectSshRemoteWorkspaceSnapshot (src/renderer/src/hooks/remote-workspace-snapshot-apply.ts) imports the pulled snapshot through importRemoteWorkspaceSession with resolveWorktreeId: uniqueWorktreeIdByPath(worktreeIds). In importRemoteWorkspaceSession (src/shared/remote-workspace-session-projection.ts):
for (const [worktreePath, tabs] of Object.entries(remote.tabsByWorktreePath ?? {})) {
const worktreeId = resolvePath(worktreePath)
if (!worktreeId) {
continue // <-- tabs for this path are dropped, no retry ever happens
}
...
}The resolver is built from the worktree catalog at apply time. For remote hosts the catalog populates asynchronously — in practice a remote worktree often only resolves after the user activates it in the sidebar. The snapshot apply, however, runs once on connect/reconnect. So the ordering is:
- SSH connects, snapshot is pulled and applied. The catalog does not know the remote worktree path yet → its tabs are dropped from the import.
- The sleeping-agent wake sweep finds its resume record but logs
tab <id> ... not present in any worktree (likely pre-hydration); skippingforever — the tab never appears. - The user clicks the worktree. The catalog now resolves the path, but nothing re-runs the import. The worktree hydrates with no tabs, Orca creates a fresh empty terminal tab.
- A subsequent workspace export pushes that tabless state back to the host, so the snapshot itself loses the tabs — the session loss becomes durable.
Reproduction
- On a direct-SSH workspace, run an agent (e.g.
claude) in a terminal tab of a remote worktree; let the workspace snapshot sync. - Quit Orca. Kill the remote relay daemon on the host (simulates relay restart/update — same thing happens after an app update deploys a new relay).
- Relaunch Orca and, before activating anything, let SSH reconnect and the snapshot apply run (catalog is still cold).
- Activate the worktree: it comes up with a new empty terminal; the snapshot tabs and the agent resume record never fire.
Expected
Snapshot tabs whose worktree path resolves later should still be hydrated — the import should be retried (bounded) as the catalog fills in, so restored sessions attach to their original tabs instead of being lost.
Fix
PR incoming from this account: it appends a bounded deferred-hydration watcher to applyDirectSshRemoteWorkspaceSnapshot that re-imports late-resolving paths scoped to just those worktrees, additively (never removing live tabs) and without moving user focus. Running patched locally; recovers the exact scenario above.
Related: #12722, #12704, #12751 (adjacent restore/merge issues in the same flow).
Activity
AmethystLiang commentedon Aug 6, 2026
thanks for reporting. looking into it
fix(ssh): retain remote sessions across late catalogs, path collision…
fix(remote): stop a colliding path key, a dead conflict state, and a …
nwparker commentedon Sep 7, 2026
Resolved by merged PR #17948. Snapshot application now reports unplaced worktree paths; target sync waits for catalog placement and pulls a fresh host snapshot when those paths become resolvable. Deferred retries are bounded, preserve conflict/non-authoritative state on failures, and permit converging placement. Reviewed the full issue and comment, and re-ran the deferred-placement-retry and target-sync regression suites successfully. Closing as completed.
fix(remote): stop a colliding path key, a dead conflict state, and a …