// Every caller that answers "can this evidence key be rendered" and "which // Asset does it render" must give the same answer, because they are one // decision (`findResolvableAsset`). Three fix rounds each rebuilt one of them // as a separate expression that agreed with the others only on the inputs // that round's examples used, so this file does not test examples: it // cross-products adversarial Asset lists (duplicate `assetKey`s, mixed // `managementStatus`, null/empty `publicPath`, both array orders, the legacy // static key shadowed by a real Asset) against a set of keys and asserts the // agreement itself, through production entry points only. // // Against fix round 3's code this reported 53 of 144 combinations disagreeing. // // The callers covered are the mock adapter's projection gate and descriptor // resolver, `validateWorkingCopy`, the shared pixel resolver, and -- added in // the final fix wave -- `instant-preview.tsx`'s own gate and descriptor // resolver. The last pair is byte-parallel to the adapter's (they live in // different layers: `presentation/` may not import `adapters/`), and is the // pair three earlier fix rounds regressed. Its one sanctioned divergence is // the legacy static key's `assetId`, which the assertions below pin rather // than ignore. Mutating either half of that pair reports 96 and 64 of 144 // combinations disagreeing respectively. import assert from "node:assert/strict"; import { test } from "vitest"; import type { Asset } from "../../../src/features/tech-log/contracts/studio/contract.ts"; import { projectWorkingCopy } from "../../../src/features/tech-log/adapters/mock/project-public-render-model.ts"; import { validateWorkingCopy } from "../../../src/features/tech-log/adapters/mock/validate-working-copy.ts"; import { isSupportedEvidenceKey } from "../../../src/features/tech-log/adapters/static/evidence-assets.ts"; import { createAssetCatalogResolver } from "../../../src/features/tech-log/presentation/shared/public-render/asset-resolvers.ts"; import { instantPreviewEvidenceKeyGate, resolveInstantPreviewAssetDescriptor, } from "../../../src/features/tech-log/presentation/studio/components/instant-preview.tsx"; import { ContentFormatError } from "../../../src/features/tech-log/domain/content-format/parse-case-content.ts"; const LEGACY = "fetch-strategy-boundary"; function asset(overrides: Partial): Asset { return { id: "00000000-0000-4000-8000-000000000001", assetKey: "k", kind: "IMAGE", mediaType: "image/svg+xml", originalFilename: "k.svg", byteSize: 1, width: null, height: null, altText: null, decorative: false, managementStatus: "READY", publicPath: "/media/k.svg", usageCount: 0, version: 1, createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", ...overrides, } as Asset; } const id = (n: number) => `00000000-0000-4000-8000-00000000000${n}`; const lists: Array<{ label: string; assets: Asset[] }> = [ { label: "empty", assets: [] }, { label: "single READY", assets: [asset({ id: id(1) })] }, { label: "single READY null path", assets: [asset({ id: id(1), publicPath: null })] }, { label: "single READY empty path", assets: [asset({ id: id(1), publicPath: "" })] }, { label: "single QUARANTINED", assets: [asset({ id: id(1), managementStatus: "QUARANTINED" })] }, { label: "single REJECTED", assets: [asset({ id: id(1), managementStatus: "REJECTED" })] }, { label: "single ARCHIVED", assets: [asset({ id: id(1), managementStatus: "ARCHIVED" })] }, { label: "dup: null path first, real second", assets: [asset({ id: id(1), publicPath: null }), asset({ id: id(2), publicPath: "/media/k-2.svg", updatedAt: "2026-08-14T00:00:01.000Z" })], }, { label: "dup: empty path first, real second", assets: [asset({ id: id(1), publicPath: "" }), asset({ id: id(2), publicPath: "/media/k-2.svg", updatedAt: "2026-08-14T00:00:01.000Z" })], }, { label: "dup: QUARANTINED newest, READY older", assets: [asset({ id: id(1), managementStatus: "QUARANTINED", updatedAt: "2026-08-14T00:00:09.000Z" }), asset({ id: id(2) })], }, { label: "dup: two resolvable, different paths", assets: [ asset({ id: id(1), publicPath: "/media/k-1.svg", updatedAt: "2026-08-14T00:00:00.000Z" }), asset({ id: id(2), publicPath: "/media/k-2.svg", updatedAt: "2026-08-14T00:00:01.000Z" }), ], }, { label: "dup: identical updatedAt (id tiebreak)", assets: [asset({ id: id(3), publicPath: "/media/k-3.svg" }), asset({ id: id(2), publicPath: "/media/k-2.svg" })], }, { label: "dup: decorative split", assets: [ asset({ id: id(1), publicPath: "/media/k-1.svg", decorative: true, updatedAt: "2026-08-14T00:00:02.000Z" }), asset({ id: id(2), publicPath: "/media/k-2.svg", decorative: false, updatedAt: "2026-08-14T00:00:01.000Z" }), ], }, { label: "decoy: other key owns /media/k.svg", assets: [asset({ id: id(1), assetKey: "other", publicPath: "/media/k.svg" })], }, { label: "legacy key backed by a real READY asset", assets: [asset({ id: id(1), assetKey: LEGACY, publicPath: "/media/legacy-override.svg" })], }, { label: "legacy key backed by a READY asset with null path", assets: [asset({ id: id(1), assetKey: LEGACY, publicPath: null })], }, { label: "legacy key: null-path first, real second", assets: [ asset({ id: id(1), assetKey: LEGACY, publicPath: null }), asset({ id: id(2), assetKey: LEGACY, publicPath: "/media/legacy-override.svg", updatedAt: "2026-08-14T00:00:01.000Z" }), ], }, { label: "three-way dup with mixed status and paths", assets: [ asset({ id: id(1), publicPath: "" }), asset({ id: id(2), managementStatus: "QUARANTINED", updatedAt: "2026-08-14T00:00:05.000Z" }), asset({ id: id(3), publicPath: "/media/k-3.svg", updatedAt: "2026-08-14T00:00:02.000Z" }), asset({ id: id(4), publicPath: "/media/k-4.svg", updatedAt: "2026-08-14T00:00:01.000Z" }), ], }, ]; const keys = ["k", "other", LEGACY, "missing"]; const catalog = [ { id: "topic", type: "TOPIC", label: "T", publicPath: "/t", dependencyRevision: "r1" }, { id: "evidence-row", type: "EVIDENCE", label: "some-label", publicPath: "/cases/x", dependencyRevision: "r1" }, { id: "legacy-row", type: "EVIDENCE", label: LEGACY, publicPath: `/media/${LEGACY}.svg`, dependencyRevision: "r1" }, ] as never[]; function draft(key: string, alt: string) { return { kind: "CASE", title: "t", slug: "s", summary: "s", topicId: "topic", projectId: null, relations: [], problem: "p", conclusion: "c", environment: "e", reproduction: "r", lastVerifiedOn: "2026-08-14", bodyMarkdown: `:::evidence key="${key}" alt="${alt}" caption="c" zoom="false"\n:::`, } as never; } type Descriptor = { assetId: string; assetKey: string; publicPath: string; decorative: boolean }; function project(key: string, alt: string, assets: readonly Asset[]) { try { const model = projectWorkingCopy(draft(key, alt), catalog, { mode: "PREVIEW", publishedAt: null }, assets) as { bodyBlocks: Array<{ type: string; asset?: Descriptor }>; }; const figure = model.bodyBlocks.find((block) => block.type === "EVIDENCE_FIGURE"); return { accepted: true as const, descriptor: figure!.asset! }; } catch (error) { return { accepted: false as const, error }; } } function validate(key: string, alt: string, assets: readonly Asset[]) { return validateWorkingCopy( { ...(draft(key, alt) as object), id: "11111111-1111-4111-8111-111111111111", version: 1, updatedAt: "2026-08-14T00:00:00.000Z" } as never, { now: new Date("2026-08-14T01:00:00.000Z"), validationId: "v", dependencyRevision: "r1", catalog, documents: [], assets }, ); } test("gate, projection resolver, pixel resolver and validation never disagree", () => { let checked = 0; const problems: string[] = []; const check = (condition: boolean, message: string) => { if (!condition) problems.push(message); return condition; }; const cases = lists.flatMap(({ label, assets }) => [ { label, assets }, { label: `${label} (reversed)`, assets: [...assets].reverse() }, ]); for (const { label, assets } of cases) { for (const key of keys) { checked += 1; const where = `${label} / ${key}`; const projected = project(key, "a", assets); const report = validate(key, "a", assets); const unsupported = report.issues.some((issue) => issue.code === "EVIDENCE_UNSUPPORTED"); const pixels = createAssetCatalogResolver(assets)(key); // 1. Validation and the preview projection accept exactly the same keys. check(!unsupported === projected.accepted, `${where}: validation and projection disagree`); // 1b. Instant Preview's gate is a fourth caller of the same decision, // living in `presentation/` (which may not import `adapters/`) and // therefore carrying its own copy of the legacy-key predicate. It // must accept exactly the same keys, in either array order. const previewAccepts = instantPreviewEvidenceKeyGate(assets)(key); check(previewAccepts === projected.accepted, `${where}: Instant Preview gate and the projection disagree`); check( instantPreviewEvidenceKeyGate([...assets].reverse())(key) === previewAccepts, `${where}: order-dependent Instant Preview gate`, ); if (!projected.accepted) { check(projected.error instanceof ContentFormatError, `${where}: expected a gate rejection, got ${String(projected.error)}`); check( projected.error instanceof ContentFormatError && /supported local evidence key not found/.test(projected.error.issues[0]!.detail), `${where}: rejection is not the gate's`, ); continue; } // 2. Nothing the gate accepts may render as an empty path. check(projected.descriptor.publicPath !== "", `${where}: accepted a key that renders as an empty publicPath`); // 3. The block descriptor and the pixels are the same asset. check(pixels.src === projected.descriptor.publicPath, `${where}: descriptor (${projected.descriptor.publicPath}) and pixels (${pixels.src}) disagree`); // 4. The descriptor names an asset that really is in the list and really // is renderable (or the legacy static asset, which is in no list). const named = assets.find((candidate) => candidate.id === projected.descriptor.assetId); if (named) { check(named.assetKey === key, `${where}: descriptor names an asset with a different assetKey`); check(named.managementStatus === "READY", `${where}: descriptor names a non-READY asset`); check(Boolean(named.publicPath), `${where}: descriptor names an asset with no publicPath`); } else { check(isSupportedEvidenceKey(key), `${where}: descriptor names an asset that is not in the list`); } // 5. Reversing the array changes nothing anywhere. const reversed = [...assets].reverse(); const reProjected = project(key, "a", reversed); if (check(reProjected.accepted, `${where}: order-dependent acceptance`) && reProjected.accepted) { check( JSON.stringify(reProjected.descriptor) === JSON.stringify(projected.descriptor), `${where}: order-dependent descriptor (${projected.descriptor.publicPath} vs ${reProjected.descriptor.publicPath})`, ); } check(createAssetCatalogResolver(reversed)(key).src === pixels.src, `${where}: order-dependent pixels`); // 6. The alt rule is judged against the asset that actually renders. const emptyAlt = validate(key, "", assets); const altRequired = emptyAlt.issues.some((issue) => issue.code === "EVIDENCE_ALT_REQUIRED"); check(altRequired === !projected.descriptor.decorative, `${where}: alt rule judged a different asset than the one rendered`); // 7. Instant Preview's own pair agrees with the mock adapter's. The two // are byte-parallel expressions in different layers (`presentation/` // may not import `adapters/`), and the pair regressed in three // separate fix rounds, so the agreement is asserted rather than // assumed. The single sanctioned divergence is the legacy static // key's `assetId`: with no Asset backing it, Instant Preview returns // a fixed literal where the adapter derives one from the static // registry. Everything else -- acceptance, path, dimensions, // mediaType, decorative -- must match exactly, and the divergence is // pinned so a future change cannot widen it unnoticed. const previewDescriptor = resolveInstantPreviewAssetDescriptor(assets)(key); const { assetId: previewAssetId, ...previewRest } = previewDescriptor; const { assetId: mockAssetId, ...mockRest } = projected.descriptor as unknown as Record & { assetId: string }; check( JSON.stringify(previewRest) === JSON.stringify(mockRest), `${where}: Instant Preview descriptor differs from the adapter's beyond assetId (${JSON.stringify(previewRest)} vs ${JSON.stringify(mockRest)})`, ); check( /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(previewAssetId), `${where}: Instant Preview assetId is not a stable UUID (${previewAssetId})`, ); check( (previewAssetId === mockAssetId) === Boolean(assets.some((candidate) => candidate.assetKey === key && candidate.id === mockAssetId)), `${where}: assetId agreement is not confined to the legacy fallback`, ); // 8. Instant Preview's descriptor and the pixels it renders are one asset. check( pixels.src === previewDescriptor.publicPath, `${where}: Instant Preview descriptor (${previewDescriptor.publicPath}) and pixels (${pixels.src}) disagree`, ); // 9. Order independence holds for Instant Preview's resolver too. check( JSON.stringify(resolveInstantPreviewAssetDescriptor([...assets].reverse())(key)) === JSON.stringify(previewDescriptor), `${where}: order-dependent Instant Preview descriptor`, ); } } assert.ok(checked >= 100, `only ${checked} combinations checked`); assert.deepEqual(problems, [], `${problems.length} of ${checked} combinations disagree:\n${problems.join("\n")}`); });