mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
Production crash diagnostics measured ~128 `git worktree list` execs/min (9,400 in one 80-minute session, ~16% of wall-clock in git subprocesses): the resolved-worktree scan fans out over every registered repo on a 30s cache TTL, and most registered repos on the affected installs were agent-CLI scratch repos (~/.codex-tmp capsules, vendor imports, skill checkouts) that need no freshness. Classify agent-scratch repo roots with a curated shared matcher and stamp their scan-cache entries with a 5-minute TTL instead of 30s. Orca-driven mutations still bypass the TTL via the per-repo generation bump, so only passive pickup of external changes slows for scratch repos. Expected steady-state reduction on the measured install: ~82% fewer git spawns.
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
import { normalizeRuntimePathForComparison } from './cross-platform-path'
|
|
|
|
/** Why: agent CLIs reserve these repo-root paths for scratch; broader matches
|
|
* can hide legitimate user worktrees (#9388). */
|
|
const AGENT_SCRATCH_PATH_PREFIXES: readonly (readonly string[])[] = [
|
|
['.claude', 'worktrees'],
|
|
['.gsd-workspaces']
|
|
]
|
|
|
|
export type AgentScratchWorktreePathMatcher = (worktreePath: string) => boolean
|
|
|
|
export function createAgentScratchWorktreePathMatcher(
|
|
checkoutPaths: readonly string[]
|
|
): AgentScratchWorktreePathMatcher {
|
|
const checkoutPathKeys = new Set(checkoutPaths.map(normalizeRuntimePathForComparison))
|
|
return (worktreePath) => {
|
|
const segments = normalizeRuntimePathForComparison(worktreePath).split('/')
|
|
for (const prefix of AGENT_SCRATCH_PATH_PREFIXES) {
|
|
for (let index = 0; index + prefix.length < segments.length; index += 1) {
|
|
if (!prefix.every((segment, offset) => segments[index + offset] === segment)) {
|
|
continue
|
|
}
|
|
const checkoutPath = segments.slice(0, index).join('/')
|
|
// Why: splitting strips the separator from filesystem roots, but normalized checkout keys retain it.
|
|
const checkoutPathKey = /^[a-z]:$/i.test(checkoutPath)
|
|
? `${checkoutPath}/`
|
|
: checkoutPath || '/'
|
|
if (checkoutPathKeys.has(checkoutPathKey)) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
export function isAgentScratchWorktreePath(repoPath: string, worktreePath: string): boolean {
|
|
return createAgentScratchWorktreePathMatcher([repoPath])(worktreePath)
|
|
}
|
|
|
|
/** Why: agent CLIs also mint whole scratch *repos* under these containers; a
|
|
* repo registered at such a root is agent-internal, not a user project (#9388). */
|
|
const AGENT_SCRATCH_REPO_ROOT_SEGMENTS: readonly (readonly string[])[] = [
|
|
['.codex-tmp'],
|
|
['.codex', 'vendor_imports'],
|
|
['.claude', 'skills'],
|
|
...AGENT_SCRATCH_PATH_PREFIXES
|
|
]
|
|
|
|
export function isAgentScratchRepoRootPath(repoPath: string): boolean {
|
|
const segments = normalizeRuntimePathForComparison(repoPath).split('/')
|
|
for (const marker of AGENT_SCRATCH_REPO_ROOT_SEGMENTS) {
|
|
// Why: match the marker anywhere above the repo root (the repo lives at or
|
|
// under the scratch container), unlike worktree matching which anchors to a
|
|
// registered checkout path.
|
|
for (let index = 0; index + marker.length <= segments.length; index += 1) {
|
|
if (marker.every((segment, offset) => segments[index + offset] === segment)) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|