mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 00:02:35 +00:00
* fix(worktrees): stop terminals after external deletion * fix(worktrees): request teardown per caller and revalidate uncached Two defects let the original fix silently strand PTYs: - teardown rode the scan's coalescing promise, so any caller that joined an in-flight scan purged its renderer state without ever asking for a sweep; it now runs per caller against its own known-id snapshot, deduped on the request it actually produces so fan-out still shares one host sweep. - the runtime's authoritative recheck was served from the 30s worktree-scan cache, which can still list a directory git already dropped. The renderer purges either way, so a stale miss leaked those processes permanently. Co-authored-by: Orca <help@stably.ai> * perf(worktrees): enumerate the host once per teardown sweep An agent cleaning up N workspaces made killAllProcessesForWorktree issue one full provider enumeration per missing worktree: O(N) relay round-trips carrying O(N^2) rows. At 30 worktrees over an 80ms-RTT SSH link that is 30 scans and ~1.3s of stalled teardown; it scales linearly from there. Share one point-in-time process list across the sweep — every worktree in it is already known-missing, so a single snapshot answers all of them. A failed scan is never shared: it falls back to a per-caller scan so one transient relay error cannot suppress the sweep for the whole batch. Pinned requirePhysicalStop:false since that path re-lists after shutdown and must not read a pre-shutdown snapshot. Co-authored-by: Orca <help@stably.ai> * test(worktrees): pin the disconnected-SSH no-teardown invariant main's new directSshAuthority gate bails before any refresh when an SSH target is not connected. That is exactly the #10562 safety rule — "host unreachable" must never be read as "worktree deleted" — so pin it: a disconnected target issues no teardown RPC and keeps its renderer state. Co-authored-by: Orca <help@stably.ai> * fix(worktrees): keep selector grammar intact when scoping by connection resolveRepoSelectorForConnection matched the selector as a bare repo id, so an explicit connection identity silently changed the grammar: `path:` and `name:` selectors resolved to repo_not_found on that path alone, losing the whole sweep. A connection identity should only *narrow* the candidate set. Extract the selector matching both paths now share, and stop re-resolving an already-resolved repo: teardown rescanned via `id:<repo.id>`, which throws selector_ambiguous when an id is duplicated across hosts even though the caller's own selector was unambiguous. Reported as a P2 by Greptile (as redundant work); it is load-bearing. Co-authored-by: Orca <help@stably.ai> * fix(worktrees): keep the shared snapshot out of provider internals The snapshot proxy passed itself as the Reflect.get receiver, so prototype methods invoked through it ran with `this` bound to the proxy. A provider whose own shutdown() re-read state via `this.listProcesses()` would then silently get this sweep's cached snapshot instead of the live host — batching leaking past the calls it was built for. Bind non-listProcesses members to the target so only the sweep's own calls share the snapshot. No shipped provider does this today; the point is that adding one must not quietly change teardown semantics. Raised by Greptile as an undocumented implicit constraint; closed structurally rather than by comment. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>