fix(artifacts): compare source map target identity

Fixes #6706
This commit is contained in:
morluto
2026-10-23 21:03:10 +08:00
parent 8520d425d0
commit 131c1f1219
2 changed files with 50 additions and 0 deletions
+7
View File
@@ -538,6 +538,11 @@ const relationsByPath = (
? null
: (occurrences.get(occurrence.parent_occurrence_id)?.logical_path ??
null),
// Containment roots are path-normalized to avoid graph-wide deltas, but
// a source map's target is a semantic content identity, not its parent
// directory. Preserve that identity in the relation comparison.
mapped_artifact_id:
edge.relation === "maps-source" ? edge.parent_artifact_id : null,
child_artifact_id: occurrence.artifact_id,
relation: edge.relation,
logical_path: occurrence.logical_path,
@@ -556,6 +561,8 @@ const relationsByPath = (
interface RelationProjection {
/** Logical parent is stable across graph-root ID changes. */
readonly parent_logical_path: string | null;
/** Content identity is material for a maps-source edge. */
readonly mapped_artifact_id: string | null;
readonly child_artifact_id: string;
readonly relation: ArtifactInventoryResult["edges"][number]["relation"];
readonly logical_path: string | null;
@@ -571,6 +571,49 @@ describe("artifact comparison completeness", () => {
});
describe("artifact source-map relationship coverage", () => {
it("reports when an unchanged map points to changed source content", async () => {
const root = await createTestTempDirectory("rea-map-target-change-");
const leftPath = join(root, "left");
const rightPath = join(root, "right");
await Promise.all([mkdir(leftPath), mkdir(rightPath)]);
await Promise.all([
writeFile(join(leftPath, "main.js"), "/* first source */\n"),
writeFile(join(rightPath, "main.js"), "/* second source */\n"),
writeFile(
join(leftPath, "main.js.map"),
'{"version":3,"sources":[],"names":[],"mappings":""}',
),
writeFile(
join(rightPath, "main.js.map"),
'{"version":3,"sources":[],"names":[],"mappings":""}',
),
]);
const [left, right] = await Promise.all([
observe(leftPath),
observe(rightPath),
]);
const comparison = compareArtifacts(left, right);
const leftInventory = parseArtifactInventoryEvidence(left).inventory;
const rightInventory = parseArtifactInventoryEvidence(right).inventory;
const leftMap = leftInventory.occurrences.find(
({ logical_path }) => logical_path === "main.js.map",
);
const rightMap = rightInventory.occurrences.find(
({ logical_path }) => logical_path === "main.js.map",
);
expect(leftMap?.artifact_id).toBe(rightMap?.artifact_id);
expect(comparison.changes).toContainEqual(
expect.objectContaining({
logical_path: "main.js.map",
classification: "changed",
dimensions: ["relations"],
left_artifact_id: leftMap?.artifact_id,
right_artifact_id: rightMap?.artifact_id,
}),
);
});
it("preserves unknown source-map relationships for duplicate ZIP paths", async () => {
const root = await createTestTempDirectory("rea-duplicate-source-map-");
const path = join(root, "duplicate-source-map.zip");