mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(worktree): skip retirement backfill on every non-local host (#15023)
The backfill guard tested repo.connectionId, but a runtime-owned repo carries executionHostId with no connectionId, so it read as local. The scan then walked this machine's workspace and agent-transcript directories and filed the result under the runtime host's namespace — retiring names never used there while missing the ones that were. Guard on the execution host id instead. Ongoing retirement was already correct for these repos; only the one-time historical seed was wrong.
This commit is contained in:
@@ -336,4 +336,60 @@ describe('ensureRetiredWorktreeNamesBackfilled', () => {
|
||||
|
||||
expect(merged).toEqual([])
|
||||
})
|
||||
|
||||
it('skips a runtime-owned repo, which has no connectionId but is still not local', async () => {
|
||||
// Why: a `connectionId` check calls this repo local, so the scan reads THIS machine's
|
||||
// directories and files them under the runtime's namespace — retiring names never used there
|
||||
// while missing the ones that were. The host id is the only reliable local test.
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-retirement-runtime-'))
|
||||
const workspaceRoot = join(root, 'workspaces')
|
||||
await mkdir(join(workspaceRoot, FIRST), { recursive: true })
|
||||
const merged: string[] = []
|
||||
const store = {
|
||||
mergeRetiredWorktreeNames: (_repoId: string, names: Iterable<string>) => {
|
||||
merged.push(...names)
|
||||
return true
|
||||
}
|
||||
}
|
||||
const runtimeRepo = {
|
||||
...makeRepo('repo-runtime', '/repos/runtime'),
|
||||
executionHostId: 'runtime:env-1'
|
||||
} as Repo
|
||||
|
||||
try {
|
||||
const collisionKey = await ensureRetiredWorktreeNamesBackfilled(store, runtimeRepo, {
|
||||
workspaceDir: workspaceRoot,
|
||||
nestWorkspaces: false
|
||||
})
|
||||
|
||||
expect(merged).toEqual([])
|
||||
expect(collisionKey).toBeNull()
|
||||
} finally {
|
||||
await rm(root, { force: true, recursive: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('still backfills a plain local repo, so the skip is scoped to non-local hosts', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-retirement-local-'))
|
||||
const workspaceRoot = join(root, 'workspaces')
|
||||
await mkdir(join(workspaceRoot, FIRST), { recursive: true })
|
||||
const merged: string[] = []
|
||||
const store = {
|
||||
mergeRetiredWorktreeNames: (_repoId: string, names: Iterable<string>) => {
|
||||
merged.push(...names)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await ensureRetiredWorktreeNamesBackfilled(store, makeRepo('repo-local', '/repos/local'), {
|
||||
workspaceDir: workspaceRoot,
|
||||
nestWorkspaces: false
|
||||
})
|
||||
|
||||
expect(merged).toEqual([FIRST])
|
||||
} finally {
|
||||
await rm(root, { force: true, recursive: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readdir } from 'node:fs/promises'
|
||||
import { homedir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { getRepoExecutionHostId } from '../shared/execution-host'
|
||||
import { getRepoExecutionHostId, LOCAL_EXECUTION_HOST_ID } from '../shared/execution-host'
|
||||
import {
|
||||
creatureNameTier,
|
||||
EMPTY_RETIRED_NAME_REGISTRY,
|
||||
@@ -239,8 +239,13 @@ export async function ensureRetiredWorktreeNamesBackfilled(
|
||||
settings: RetirementPathSettings
|
||||
): Promise<string | null> {
|
||||
// Remote workspaces keep their agent state on the execution host, which this scan cannot see, so
|
||||
// a re-added SSH repo does not recover its retirements the way a local one does.
|
||||
if (isFolderRepo(repo) || repo.connectionId) {
|
||||
// a re-added remote repo does not recover its retirements the way a local one does.
|
||||
//
|
||||
// Why the host id and not `connectionId`: a runtime-owned repo carries `executionHostId` with no
|
||||
// `connectionId`, so a connectionId check calls it local, scans THIS machine's directories, and
|
||||
// files the result under the runtime's namespace — retiring names never used there while missing
|
||||
// the ones that were.
|
||||
if (isFolderRepo(repo) || getRepoExecutionHostId(repo) !== LOCAL_EXECUTION_HOST_ID) {
|
||||
return null
|
||||
}
|
||||
const probePath = await computeWorktreePathAsync(
|
||||
|
||||
Reference in New Issue
Block a user