{"record":{"id":"e077d4344029b6ae","repo":"ruvnet/ruflo","slug":"reporoot-must-be-the-git-top-level-top","errorCode":null,"errorMessage":"repoRoot must be the git top-level: ${top}","messagePattern":"repoRoot must be the git top-level: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/worktrees/coordinator.ts","lineNumber":42,"sourceCode":"    encoding: 'utf8',\n    stdio: ['ignore', 'pipe', 'pipe'],\n    maxBuffer: 4 * 1024 * 1024,\n  }).trim();\n}\n\nfunction assertId(value: string, label: string): void {\n  if (!SAFE_ID.test(value)) throw new Error(`invalid ${label}: ${value}`);\n}\n\nexport class CodexWorktreeCoordinator {\n  readonly repoRoot: string;\n  readonly registryDir: string;\n  readonly worktreeBase: string;\n\n  constructor(repoRoot: string) {\n    this.repoRoot = resolve(repoRoot);\n    const top = git(this.repoRoot, ['rev-parse', '--show-toplevel']);\n    if (resolve(top) !== this.repoRoot) throw new Error(`repoRoot must be the git top-level: ${top}`);\n    this.registryDir = join(this.repoRoot, '.claude-flow', 'swarm', 'worktrees');\n    this.worktreeBase = join(dirname(this.repoRoot), '.ruflo-worktrees', basename(this.repoRoot));\n  }\n\n  prepare(\n    runId: string,\n    agents: Array<{ id: string; readOnly?: boolean }>,\n    options: { allowDirty?: boolean; baseRef?: string } = {},\n  ): WorktreeRunRecord {\n    assertId(runId, 'run id');\n    if (!agents.length) throw new Error('at least one agent is required');\n    const unique = new Set<string>();\n    for (const agent of agents) {\n      assertId(agent.id, 'agent id');\n      if (unique.has(agent.id)) throw new Error(`duplicate agent id: ${agent.id}`);\n      unique.add(agent.id);\n    }\n    const registryPath = this.registryPath(runId);","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/worktrees/coordinator.ts#L24-L60","documentation":"The CodexWorktreeCoordinator constructor resolves the passed repoRoot and requires it to equal (after resolve()) the `git rev-parse --show-toplevel` of that directory. Passing a subdirectory of the repository (or a path outside one) throws, because worktrees and the in-repo registry (.claude-flow/swarm/worktrees) are anchored to the top-level.","triggerScenarios":"new CodexWorktreeCoordinator(process.cwd()) while the process runs in a package subdirectory; constructing with a monorepo package path; passing a directory that is not a git repository at all (rev-parse then fails or returns a different root).","commonSituations":"Monorepos where CLIs start in v3/@claude-flow/codex instead of the repo root; scripts hardcoding relative package paths; running inside a linked worktree's subdirectory.","solutions":["Construct with the git top-level: execSync('git rev-parse --show-toplevel').trim()","Add a resolveRoot helper that walks up from cwd until .git is found, then pass that","Run the coordinator's entrypoint from the repository root"],"exampleFix":"// before\nnew CodexWorktreeCoordinator('v3/@claude-flow/codex'); // subdir -> throws\n// after\nimport { execSync } from 'node:child_process';\nconst root = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\nnew CodexWorktreeCoordinator(root);","handlingStrategy":"validation","validationCode":"import { execSync } from 'node:child_process';\nconst gitRoot = (p: string) =>\n  realpathSync(execSync('git rev-parse --show-toplevel', { cwd: p, encoding: 'utf8' }).trim());\nif (gitRoot(p) !== realpathSync(resolve(p))) throw new Error('not the git top-level');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Resolve the root once via rev-parse --show-toplevel and pass it to every git-anchored API","Write a walk-up-to-.git helper for CLI entrypoints started in subdirectories","Test coordinators from monorepo package directories"],"tags":["git","repository-root","worktrees","validation"],"backgroundTag":"wrong-repository-root","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}