{"record":{"id":"e4c44646f4e35781","repo":"Egonex-AI/Understand-Anything","slug":"retry-batches-do-not-cover-exactly-the-affected-fi","errorCode":null,"errorMessage":"Retry batches do not cover exactly the affected files","messagePattern":"Retry batches do not cover exactly the affected files","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"understand-anything-plugin/skills/understand/prepare-symbol-retry.mjs","lineNumber":51,"sourceCode":"  const report = await validateIncrementalSymbols(projectRoot, { intermediateDir });\n  if (report.ok || report.unresolvedFiles.length === 0) {\n    throw new Error('No unresolved symbol files eligible for a targeted retry; inspect the symbol report');\n  }\n  const paths = new Set(report.unresolvedFiles);\n  const changedFilesPath = join(intermediateDir, 'incremental-symbol-retry-files.json');\n  const batchesPath = join(intermediateDir, 'incremental-symbol-retry-batches.json');\n  atomicWriteJson(changedFilesPath, [...paths]);\n  const skillDir = dirname(fileURLToPath(import.meta.url));\n  const batching = spawnSync(process.execPath, [\n    join(skillDir, 'compute-batches.mjs'), projectRoot,\n    `--changed-files=${changedFilesPath}`, `--output=${batchesPath}`,\n  ], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });\n  if (batching.stderr) process.stderr.write(batching.stderr);\n  if (batching.status !== 0) throw new Error(`Retry batching failed: ${batching.error ?? batching.status}`);\n  const batches = readJson(batchesPath).batches;\n  const scheduled = batches.flatMap(batch => batch.files.map(file => file.path));\n  if (JSON.stringify([...scheduled].sort()) !== JSON.stringify([...paths].sort())) {\n    throw new Error('Retry batches do not cover exactly the affected files');\n  }\n  for (const batch of batches) {\n    if (!Number.isInteger(batch.batchIndex) || batch.batchIndex < 1) throw new Error('Invalid retry batch index');\n    const files = new Set(batch.files.map(file => file.path));\n    batch.previousSymbols = baseline.files.filter(file => files.has(file.filePath)).flatMap(file =>\n      file.nodes.filter(symbolKind).map(node => {\n        const parents = new Set(file.edges.filter(edge => edge.type === 'contains' && edge.target === node.id)\n          .map(edge => edge.source));\n        const owners = file.nodes.filter(parent => parent.type === 'class' && parents.has(parent.id))\n          .map(parent => parent.name);\n        return {\n          id: node.id, name: node.name, type: node.type, filePath: node.filePath,\n          ...(node.lineRange ? { lineRange: node.lineRange } : {}),\n          ...(owners.length ? { owners } : {}),\n        };\n      }),\n    );\n    batch.missingSymbols = report.files.filter(file => files.has(file.filePath));","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/Egonex-AI/Understand-Anything/blob/07edf82a04371b6f69779b067bdc8a1a8753a9db/understand-anything-plugin/skills/understand/prepare-symbol-retry.mjs#L33-L69","documentation":"After batching, the script asserts that the set of file paths scheduled across all returned batches is exactly equal (as sorted JSON arrays) to the set of unresolved retry paths. Any mismatch — missing files, extra files, or duplicates — means batching did not round-trip correctly, so it throws to prevent a partial retry from being recorded.","triggerScenarios":"compute-batches.mjs returns batches that drop some changed files, include files not requested, or duplicate a path — typically due to a batching bug, ignore-filter divergence inside compute-batches, or a stale/corrupt batches output JSON.","commonSituations":"compute-batches silently filtered files through its own ignore rules; a version mismatch between the prepare script and compute-batches.mjs; manually edited batches output from a previous run; paths normalized differently (relative vs absolute) on one side.","solutions":["Diff the retry file list (incremental-symbol-retry-files.json) against the produced batches JSON to find which paths are missing or extra.","Delete the stale incremental-symbol-retry-batches.json and re-run the script so batching recomputes.","Check compute-batches.mjs's ignore/filter logic for rules that drop requested files; align it with the retry path list.","Ensure both sides normalize paths identically (relative to projectRoot) before comparing."],"exampleFix":"// before: batching dropped an ignored file\nscheduled = [a.js, b.js]; paths = [a.js, b.js, c.ts] → throw\n// after: whitelist retry files in compute-batches ignore config, or re-run\ndelete .ua/intermediate/incremental-symbol-retry-batches.json\nnode prepare-symbol-retry.mjs .","handlingStrategy":"validation","validationCode":"const requested = new Set(retryPaths);\nconst scheduled = new Set(batches.flatMap(b => b.files.map(f => f.path)));\nconst missing = [...requested].filter(p => !scheduled.has(p));\nconst extra = [...scheduled].filter(p => !requested.has(p));\nif (missing.length || extra.length) {\n  throw new Error(`Batch coverage mismatch; missing=${missing} extra=${extra}`);\n}","typeGuard":"const coversExactly = (batches, paths) => {\n  const s = new Set(batches.flatMap(b => b.files.map(f => f.path)));\n  return s.size === paths.length && paths.every(p => s.has(p));\n};","tryCatchPattern":"try {\n  await runRetry(projectRoot);\n} catch (e) {\n  if (e.message.includes('Retry batches do not cover exactly')) {\n    // delete stale batches output and re-run so batching recomputes\n  } else throw e;\n}","preventionTips":["Delete stale incremental-symbol-retry-batches.json before re-running retries.","Keep prepare-symbol-retry.mjs and compute-batches.mjs at the same plugin version.","Normalize paths identically (relative to projectRoot) on both sides of the comparison.","Add a unit test asserting batch coverage equals the requested file set."],"tags":["batching","invariant-violation","retry","coverage"],"backgroundTag":"internal-invariant-violation","analyzedSha":"07edf82a04371b6f69779b067bdc8a1a8753a9db","analyzedAt":"2026-09-07T23:20:10.829Z","contentChangedAt":"2026-09-07T23:20:10.829Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}