{"record":{"id":"a1dec23ef58900c0","repo":"ruvnet/ruflo","slug":"repository-changed-while-capturing-exact-source-st","errorCode":null,"errorMessage":"repository changed while capturing exact source state","messagePattern":"repository changed while capturing exact source state","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/repository-state.ts","lineNumber":426,"sourceCode":"  const requested = repoPath instanceof URL ? fileURLToPath(repoPath) : repoPath;\n  const repoRoot = realpathSync(resolve(requested));\n  const top = realpathSync(gitText(repoRoot, ['rev-parse', '--show-toplevel']));\n  if (top !== repoRoot) throw new Error(`repoPath must be the Git top-level: ${top}`);\n\n  const cleanStatus = (): Buffer => gitBuffer(repoRoot, [\n    'status',\n    '--porcelain=v1',\n    '-z',\n    '--untracked-files=all',\n    '--ignore-submodules=none',\n  ]);\n  const initialStatus = cleanStatus();\n  if (initialStatus.length === 0) {\n    repositoryPaths(repoRoot, false);\n    const baseCommit = gitText(repoRoot, ['rev-parse', '--verify', 'HEAD']);\n    const treeId = gitText(repoRoot, ['rev-parse', '--verify', 'HEAD^{tree}']);\n    if (cleanStatus().length !== 0 || gitText(repoRoot, ['rev-parse', '--verify', 'HEAD']) !== baseCommit) {\n      throw new Error('repository changed while capturing exact source state');\n    }\n    const repository = repositoryIdentity(repoRoot);\n    const identity = {\n      version: 1,\n      scope: 'git-visible',\n      repositoryId: repository.repositoryId,\n      commit: baseCommit,\n      tree: treeId,\n    };\n    return {\n      version: 1,\n      scope: 'git-visible',\n      kind: 'clean',\n      clean: true,\n      repository,\n      baseCommit,\n      treeId,\n      sourceStateId: digest(canonicalJson(identity)),","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/repository-state.ts#L408-L444","documentation":"On the clean-worktree fast path, the collector reads `git status --porcelain=v1 -z --untracked-files=all --ignore-submodules=none` and HEAD twice, around its tree-id reads. If the second status is non-empty or HEAD moved, the repository mutated during capture and the error aborts rather than binding a sourceStateId to a state that no longer exists.","triggerScenarios":"Anything touching the repo between the two reads: a concurrent `git fetch` updating refs, a hook or editor committing, an auto-formatter writing files, another agent session in the same worktree.","commonSituations":"Snapshotting while a CI checkout step, IDE auto-fetch, or another agent runs in the same clone; release tooling capturing evidence while a background job commits.","solutions":["Ensure no other git writers run against the worktree during capture and retry","Disable IDE auto-fetch and background git hooks for the capture window","Prefer capturing from a clean checkout dedicated to the capture"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const headA = gitText(root, ['rev-parse', 'HEAD']);\nconst statusA = gitText(root, ['status', '--porcelain=v1', '-z']);\nconst headB = gitText(root, ['rev-parse', 'HEAD']);\nconst still = headA === headB && statusA === gitText(root, ['status', '--porcelain=v1', '-z']);","typeGuard":null,"tryCatchPattern":"try { return capture(root); }\ncatch (e) {\n  if (/repository changed while capturing/.test(String(e)) && quiesced) return capture(root);\n  throw e;\n}","preventionTips":["Run captures in a checkout dedicated to the snapshot","Disable IDE auto-fetch and background git hooks during capture","Serialize git writers with a lock if multiple tools share the clone"],"tags":["race-condition","git","harness"],"backgroundTag":"concurrent-modification-detected","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}