mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 00:02:56 +00:00
fix(runtime): retire the resolved-worktree snapshot when the repo catalog changes
listResolvedWorktrees is served from a 1s-TTL snapshot derived from store.getRepos(), but nothing retired it when the repo set changed. Adding an SSH repo through repos:addRemote therefore left it invisible for the rest of the TTL. ssh-skill-installation lost that race deterministically: notifySshRelayReady fires while ssh.connect is still running and warms the snapshot through refreshRestoredOrchestrationAuthority, ~60ms before the helper registers the remote repo. The worktree-scoped install lands ~500ms later, inside the window, so resolveSkillSshTarget matched zero worktrees and threw skill-install-workspace-not-found. Invalidate in notifyReposChangedForRemoteClients, the runtime funnel every repo IPC mutation already reaches, alongside the sibling worktree notifier that invalidates the same cache. Runtime-side repo mutations already invalidate at their own call sites. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -136,6 +136,9 @@ export class OrcaRuntimeWithNotifySshStateChanged extends OrcaRuntimeWithGetStat
|
||||
// clients need an explicit catalog invalidation; the local renderer already
|
||||
// got its own repos:changed and must not be re-notified (#11994).
|
||||
notifyReposChangedForRemoteClients(): void {
|
||||
// Why here: the resolved-worktree snapshot is derived from the repo list, so a repo
|
||||
// registered while a snapshot is warm stays invisible for the whole TTL.
|
||||
this.invalidateResolvedWorktreeCache()
|
||||
this.emitClientEvent({ type: 'reposChanged' })
|
||||
}
|
||||
|
||||
|
||||
@@ -289,6 +289,41 @@ describe('OrcaRuntimeService', () => {
|
||||
expect(reposChanged).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
// A repo added while a resolved snapshot was warm stayed invisible for the whole TTL, so a
|
||||
// worktree-scoped call landing in that window resolved nothing for the freshly added host.
|
||||
it('notifyReposChangedForRemoteClients retires the warm resolved-worktree snapshot', async () => {
|
||||
vi.mocked(listWorktrees).mockClear()
|
||||
vi.mocked(listWorktrees).mockResolvedValue([makeWorktreeInfo(TEST_WORKTREE_PATH)])
|
||||
const localRepo = store.getRepo(TEST_REPO_ID)!
|
||||
const lateRepo = {
|
||||
...localRepo,
|
||||
id: 'repo-registered-late',
|
||||
path: '/remote/repo',
|
||||
connectionId: 'ssh-late'
|
||||
}
|
||||
const provider = {
|
||||
listWorktrees: vi.fn().mockResolvedValue([makeWorktreeInfo('/remote/worktree')])
|
||||
}
|
||||
registerSshGitProvider('ssh-late', provider as never)
|
||||
let repos = [localRepo]
|
||||
const runtime = new OrcaRuntimeService({ ...store, getRepos: () => repos } as never)
|
||||
|
||||
try {
|
||||
const warm = await runtime.listManagedWorktrees()
|
||||
expect(warm.worktrees.map((worktree) => worktree.path)).not.toContain('/remote/worktree')
|
||||
|
||||
repos = [localRepo, lateRepo]
|
||||
runtime.notifyReposChangedForRemoteClients()
|
||||
|
||||
const afterRegistration = await runtime.listManagedWorktrees()
|
||||
expect(afterRegistration.worktrees.map((worktree) => worktree.path)).toContain(
|
||||
'/remote/worktree'
|
||||
)
|
||||
} finally {
|
||||
unregisterSshGitProvider('ssh-late')
|
||||
}
|
||||
})
|
||||
|
||||
it('persists changed worktree order once and emits targeted invalidations', () => {
|
||||
const firstId = `${TEST_REPO_ID}::/tmp/first`
|
||||
const secondId = `${TEST_REPO_ID}::/tmp/second`
|
||||
|
||||
Reference in New Issue
Block a user