Summary
EDGE_TYPE_ALIASES maps documented_by to documents, and normalizeGraph rewrites the edge type without swapping source and target:
return { ...edge, type: edgeAliases[edge.type] };
documented_by and documents are converses, so the rewrite silently reverses the edge. This is the same hazard the table already documents for a different alias:
// Note: "implemented_by" is intentionally NOT aliased to "implements" —
// it inverts edge direction (see commit fd0df15). The LLM should use
// "implements" with correct source/target instead.
The rule is right; documented_by slipped past it.
Version
main at b77e3e8c, @understand-anything/core 0.1.0, built with pnpm build, Node 26.
Reproduction
import { normalizeGraph } from "./dist/schema.js";
const graph = {
kind: "codebase",
nodes: [
{ id: "auth.ts", name: "auth.ts", type: "file" },
{ id: "auth.md", name: "auth.md", type: "document" },
{ id: "handler.ts", name: "handler.ts", type: "file" },
{ id: "queue", name: "queue", type: "service" },
],
edges: [
{ source: "auth.ts", target: "auth.md", type: "documented_by" },
{ source: "handler.ts", target: "queue", type: "triggers_on" },
{ source: "auth.ts", target: "auth.md", type: "implemented_by" }, // control
],
};
for (const e of normalizeGraph(graph).edges) {
console.log(`${e.source} --${e.type}--> ${e.target}`);
}
Actual
auth.ts --documents--> auth.md
handler.ts --triggers--> queue
auth.ts --implemented_by--> auth.md
The first edge said auth.ts is documented by auth.md. After normalisation it says auth.ts documents auth.md, so the source file now documents its own documentation. The third line is the control: implemented_by is left alone exactly as the comment describes, which shows the safeguard works and this case was simply missed.
Why it is hard to notice downstream
Nothing errors, no issue is recorded in autoFixGraph, and the edge count is unchanged. The graph stays schema-valid because documents is a canonical type. Only the arrow is backwards, so any view answering "what documents this file", or any traversal that follows documents in one direction, returns the opposite of the truth for these edges. A reader has no way to tell an inverted edge from a correct one by looking at the output.
A second one I am less sure about
triggers_on maps to triggers in the same table, and the reproduction above shows handler.ts --triggers--> queue. If triggers_on is meant as "this handler fires in response to that queue", it is inverted for the same reason. If it is meant as "this triggers on that target", it is fine. I could not tell which reading you intend from the surrounding code, so I am raising it as a question rather than asserting it is wrong.
Not filed as a bug, but noticed while reading
Several node aliases are lossy in a way that may be deliberate: interface and struct both collapse to class, and migration, database and view all collapse to table. That is a modelling decision rather than a defect, so I have not filed it, but a knowledge graph that cannot distinguish a view from a table will answer some questions confidently and wrongly, and it seemed worth mentioning while I was in here.
Summary
EDGE_TYPE_ALIASESmapsdocumented_bytodocuments, andnormalizeGraphrewrites the edge type without swappingsourceandtarget:documented_byanddocumentsare converses, so the rewrite silently reverses the edge. This is the same hazard the table already documents for a different alias:The rule is right;
documented_byslipped past it.Version
mainat b77e3e8c,@understand-anything/core0.1.0, built withpnpm build, Node 26.Reproduction
Actual
The first edge said auth.ts is documented by auth.md. After normalisation it says auth.ts documents auth.md, so the source file now documents its own documentation. The third line is the control:
implemented_byis left alone exactly as the comment describes, which shows the safeguard works and this case was simply missed.Why it is hard to notice downstream
Nothing errors, no issue is recorded in
autoFixGraph, and the edge count is unchanged. The graph stays schema-valid becausedocumentsis a canonical type. Only the arrow is backwards, so any view answering "what documents this file", or any traversal that followsdocumentsin one direction, returns the opposite of the truth for these edges. A reader has no way to tell an inverted edge from a correct one by looking at the output.A second one I am less sure about
triggers_onmaps totriggersin the same table, and the reproduction above showshandler.ts --triggers--> queue. Iftriggers_onis meant as "this handler fires in response to that queue", it is inverted for the same reason. If it is meant as "this triggers on that target", it is fine. I could not tell which reading you intend from the surrounding code, so I am raising it as a question rather than asserting it is wrong.Not filed as a bug, but noticed while reading
Several node aliases are lossy in a way that may be deliberate:
interfaceandstructboth collapse toclass, andmigration,databaseandviewall collapse totable. That is a modelling decision rather than a defect, so I have not filed it, but a knowledge graph that cannot distinguish a view from a table will answer some questions confidently and wrongly, and it seemed worth mentioning while I was in here.