diff --git a/src/domain/sourceToBundleComparison.ts b/src/domain/sourceToBundleComparison.ts index df6965c0f..64155fccd 100644 --- a/src/domain/sourceToBundleComparison.ts +++ b/src/domain/sourceToBundleComparison.ts @@ -163,7 +163,7 @@ const compareMappings = ( omittedCandidateReferences += omittedCandidates; const classification = classifyMapping( retainedCandidates, - omittedEvaluations === 0, + omittedCandidates === 0, projection.absenceComplete, ); const semantic = { diff --git a/src/domain/webCaptureDiff.ts b/src/domain/webCaptureDiff.ts index e3810b061..ca267d88c 100644 --- a/src/domain/webCaptureDiff.ts +++ b/src/domain/webCaptureDiff.ts @@ -39,6 +39,13 @@ const dimensionSchema = z.object({ omitted_changes: z.number().int().min(0), reason: z.string().nullable(), }); +const legacyUnknownDimension = { + status: "unknown" as const, + total_changes: 0, + changes: [], + omitted_changes: 0, + reason: "Dimension was not recorded by this version 1 capture diff.", +}; /** Completeness-aware changes across stable browser evidence dimensions. */ export const webCaptureDiffSchema = z.object({ @@ -53,8 +60,8 @@ export const webCaptureDiffSchema = z.object({ network: dimensionSchema, metadata: dimensionSchema, webmcp: dimensionSchema, - accessibility: dimensionSchema, - storage: dimensionSchema, + accessibility: dimensionSchema.default(legacyUnknownDimension), + storage: dimensionSchema.default(legacyUnknownDimension), }), limitations: z.array(z.string()), }); diff --git a/tests/sourceToBundleComparison.test.ts b/tests/sourceToBundleComparison.test.ts index 5a0a25485..12840a26d 100644 --- a/tests/sourceToBundleComparison.test.ts +++ b/tests/sourceToBundleComparison.test.ts @@ -116,6 +116,23 @@ describe("historical source to bundle comparison", () => { expect(result.coverage.status).toBe("partial"); }); + it("keeps exact mappings unknown when candidate output is truncated", () => { + const result = compareSourceToBundle({ + reference: historicalGraph("complete"), + application: { + evidenceId: EVIDENCE_ID, + rootArtifactSha256: HASH.artifact, + graph: applicationGraph("complete"), + }, + limits: { ...limits(), max_candidate_nodes: 1 }, + }); + + expect(item(result, "src/duplicated.ts").status).toBe("unknown"); + expect( + item(result, "src/duplicated.ts").omitted_candidates, + ).toBeGreaterThan(0); + }); + it("reports the candidate frontier instead of deciding past a limit", () => { const result = compareSourceToBundle({ reference: historicalGraph("complete", ["src/split.ts"]), diff --git a/tests/webCaptureDiff.test.ts b/tests/webCaptureDiff.test.ts index d3b110ae7..37f7c9a48 100644 --- a/tests/webCaptureDiff.test.ts +++ b/tests/webCaptureDiff.test.ts @@ -5,6 +5,7 @@ import { inspectWebPageInputSchema } from "../src/domain/browserObservation.js"; import { compareWebCaptures, compareWebCapturesInputSchema, + webCaptureDiffSchema, } from "../src/domain/webCaptureDiff.js"; import { startFakeCdpBrowser, @@ -59,6 +60,17 @@ describe("web capture diff", () => { status: "unknown", total_changes: 0, }); + const { + accessibility: _accessibility, + storage: _storage, + ...legacy + } = result.dimensions; + const parsedLegacy = webCaptureDiffSchema.parse({ + ...result, + dimensions: legacy, + }); + expect(parsedLegacy.dimensions.accessibility.status).toBe("unknown"); + expect(parsedLegacy.dimensions.storage.status).toBe("unknown"); }); it("does not claim unchanged when a relevant section is incomplete", async () => {