Summary
merge-batch-graphs.py drops cross-batch edges whose endpoint carries a different node-type prefix than the node that was actually emitted. The only signal is a line on stderr, so a run looks successful while the graph quietly comes out with fewer relationships than it should.
Why it happens
Each file-analyzer subagent chooses the ID prefix for the nodes it owns (file: / config: / document: / pipeline: / table: / schema: / endpoint:). When batch A emits an edge pointing at a file that batch B classified, A has to guess that prefix — batchImportData and neighborMap only resolve project-internal code imports, so doc→pipeline, doc→config and config→code references have no ground truth.
Step 6 then compares IDs by exact equality:
if src not in node_ids or tgt not in node_ids:
unfixable.append(f"Edge {src} → {tgt} ({etype}): dropped, missing ...")
continue
Code files almost always end up as file:, so the loss lands entirely on the non-code node types — which is where documents, triggers, configures and depends_on live.
Reproduction
Two batches in .understand-anything/intermediate/:
$ python merge-batch-graphs.py <projectRoot>
Input: 2 nodes, 1 edges
Could not fix (1 issues — needs agent review):
- Edge document:docs/d.md → document:wf/a.md (documents): dropped, missing target
Output: 2 nodes, 0 edges
The relationship is real and both endpoints exist. Only the prefix guess was wrong.
Impact
On a 205-file project (Next.js CRM, 46 SQL migrations, 17 workflow docs, docs tree), a single run had 25 edges about to be dropped this way. 21 of them were documents edges from the four root docs to the workflow files — the entire "which document describes which automation" map. Exactly one of the 26 reported drops was legitimate (a path referenced in a doc that no longer exists on disk).
Three more projects showed 2, 7 and 35 affected edges in the same run.
Note on the existing mechanism
The Could not fix (N issues — needs agent review) report implies the Phase 3 assemble-reviewer agent is meant to catch this. That agent is optional and LLM-priced, and this particular repair is fully deterministic — resolving the ID against the node set while ignoring the prefix, and only when the match is unambiguous.
A fix is proposed in #624 (with tests). Happy to adjust the approach if you'd prefer it handled elsewhere in the pipeline — e.g. having compute-batches.mjs publish an authoritative path → nodeType map so analyzers never have to guess.
Summary
merge-batch-graphs.pydrops cross-batch edges whose endpoint carries a different node-type prefix than the node that was actually emitted. The only signal is a line on stderr, so a run looks successful while the graph quietly comes out with fewer relationships than it should.Why it happens
Each
file-analyzersubagent chooses the ID prefix for the nodes it owns (file:/config:/document:/pipeline:/table:/schema:/endpoint:). When batch A emits an edge pointing at a file that batch B classified, A has to guess that prefix —batchImportDataandneighborMaponly resolve project-internal code imports, so doc→pipeline, doc→config and config→code references have no ground truth.Step 6 then compares IDs by exact equality:
Code files almost always end up as
file:, so the loss lands entirely on the non-code node types — which is wheredocuments,triggers,configuresanddepends_onlive.Reproduction
Two batches in
.understand-anything/intermediate/:The relationship is real and both endpoints exist. Only the prefix guess was wrong.
Impact
On a 205-file project (Next.js CRM, 46 SQL migrations, 17 workflow docs, docs tree), a single run had 25 edges about to be dropped this way. 21 of them were
documentsedges from the four root docs to the workflow files — the entire "which document describes which automation" map. Exactly one of the 26 reported drops was legitimate (a path referenced in a doc that no longer exists on disk).Three more projects showed 2, 7 and 35 affected edges in the same run.
Note on the existing mechanism
The
Could not fix (N issues — needs agent review)report implies the Phase 3assemble-revieweragent is meant to catch this. That agent is optional and LLM-priced, and this particular repair is fully deterministic — resolving the ID against the node set while ignoring the prefix, and only when the match is unambiguous.A fix is proposed in #624 (with tests). Happy to adjust the approach if you'd prefer it handled elsewhere in the pipeline — e.g. having
compute-batches.mjspublish an authoritativepath → nodeTypemap so analyzers never have to guess.