Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 55 additions & 20 deletions web-viewer-sample/scripts/capture-design-system-reference.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import http from "node:http";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
planOriginRebaseline,
runGuardedOriginRebaseline,
} from "./design-system-rebaseline-authority.mjs";

const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, "..", "..");
Expand Down Expand Up @@ -212,7 +216,15 @@ async function loadPlaywright() {
}
}

async function captureBaselines() {
async function captureBaselines({ captureScreens, preservedScreens }) {
if (preservedScreens.length > 0) {
console.log(
`[design-reference] preserving canonical product-surface baselines: ${preservedScreens
.map((screen) => screen.id)
.join(", ")}`,
);
}

const { chromium } = await loadPlaywright();
const playwrightPackage = JSON.parse(
await readFile(path.join(repoRoot, "web-viewer-sample", "node_modules", "@playwright", "test", "package.json"), "utf8"),
Expand Down Expand Up @@ -246,7 +258,7 @@ async function captureBaselines() {
colorScheme: manifest.fidelity_contract.color_scheme,
});
try {
for (const screen of manifest.screens) {
for (const screen of captureScreens) {
Comment thread
monkey1sai marked this conversation as resolved.
const page = await context.newPage();
await page.addInitScript(() => {
const fixedNow = Date.parse("2026-07-14T00:00:00+08:00");
Expand Down Expand Up @@ -321,6 +333,10 @@ async function captureBaselines() {
await browser.close();
await server.close();
}
return {
capturedScreenCount: captureScreens.length,
preservedScreenCount: preservedScreens.length,
};
}

if (!(await pathExists(sourceRoot))) {
Expand All @@ -335,25 +351,44 @@ if (rebaseline) {
manifest.fidelity_contract.platform + ".",
);
}
manifest.source.files = actualSourceFiles;
manifest.source.snapshot_sha256 = canonicalFileDigest(actualSourceFiles);
await captureBaselines();
manifest.source.captured_at_utc = new Date().toISOString();
const baselineDescriptors = [];
for (const screen of manifest.screens) {
for (const viewport of manifest.fidelity_contract.viewports) {
const baseline = screen.baselines[viewport.id];
baselineDescriptors.push({
path: baseline.path,
bytes: (await stat(resolveBaselinePath(baseline.path))).size,
sha256: baseline.sha256,
});
}
}
manifest.baseline_snapshot_sha256 = canonicalFileDigest(baselineDescriptors);
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
const rebaselinePlan = planOriginRebaseline(manifest.screens);
const captureResult = await runGuardedOriginRebaseline({
preservedScreens: rebaselinePlan.preservedScreens,
viewportIds: manifest.fidelity_contract.viewports.map(
(viewport) => viewport.id,
),
readDigest: async (relativePath) =>
sha256(await readFile(resolveBaselinePath(relativePath))),
captureBaselines: () => captureBaselines(rebaselinePlan),
commitManifest: async () => {
manifest.source.files = actualSourceFiles;
manifest.source.snapshot_sha256 = canonicalFileDigest(actualSourceFiles);
manifest.source.captured_at_utc = new Date().toISOString();
const baselineDescriptors = [];
for (const screen of manifest.screens) {
for (const viewport of manifest.fidelity_contract.viewports) {
const baseline = screen.baselines[viewport.id];
baselineDescriptors.push({
path: baseline.path,
bytes: (await stat(resolveBaselinePath(baseline.path))).size,
sha256: baseline.sha256,
});
}
}
manifest.baseline_snapshot_sha256 = canonicalFileDigest(
baselineDescriptors,
);
await writeFile(
manifestPath,
`${JSON.stringify(manifest, null, 2)}\n`,
"utf8",
);
},
});
console.log(
`[design-reference] rebaseline complete: ${manifest.screens.length} screens x ${manifest.fidelity_contract.viewports.length} viewports`,
`[design-reference] rebaseline complete: ${captureResult.capturedScreenCount} origin screens captured, ` +
`${captureResult.preservedScreenCount} product screens preserved x ` +
`${manifest.fidelity_contract.viewports.length} viewports`,
);
} else {
const drift = compareSourceFiles(manifest.source.files, actualSourceFiles);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const AUTHORING_ORIGIN = "authoring_origin";
const CANONICAL_PRODUCT_SURFACE = "canonical_product_surface";

/**
* Select only screens whose baseline authority is the approved authoring
* origin. Legacy screens without a provenance object retain that authority;
* once provenance is explicit, an unknown or missing authority fails closed.
*/
export function planOriginRebaseline(screens) {
const captureScreens = [];
const preservedScreens = [];

for (const screen of screens) {
if (!Object.hasOwn(screen, "baseline_provenance")) {
captureScreens.push(screen);
continue;
}

const authority = screen.baseline_provenance?.authority;
if (authority === AUTHORING_ORIGIN) {
captureScreens.push(screen);
continue;
}
if (authority === CANONICAL_PRODUCT_SURFACE) {
preservedScreens.push(screen);
continue;
}

const authorityLabel =
typeof authority === "string" && authority.length > 0
? authority
: "missing";
throw new Error(
`Unsupported baseline provenance authority '${authorityLabel}' for ${screen.id}.`,
);
}

return { captureScreens, preservedScreens };
}

/**
* Fail closed when a baseline excluded from origin re-capture no longer
* matches the digest pinned by its owning product surface.
*/
export async function verifyPreservedBaselineIntegrity(
preservedScreens,
viewportIds,
readDigest,
) {
for (const screen of preservedScreens) {
for (const viewportId of viewportIds) {
const baseline = screen.baselines?.[viewportId];
if (!baseline) {
throw new Error(
`Missing ${viewportId} preserved baseline slot for ${screen.id}.`,
);
}
const digest = await readDigest(baseline.path);
if (digest !== baseline.sha256) {
throw new Error(`Preserved baseline hash mismatch: ${baseline.path}`);
}
}
}
}

/**
* Keep the integrity preflight and both write phases on one testable path.
* A rejected preflight must prevent both screenshot and manifest writes.
*/
export async function runGuardedOriginRebaseline({
preservedScreens,
viewportIds,
readDigest,
captureBaselines,
commitManifest,
}) {
await verifyPreservedBaselineIntegrity(
preservedScreens,
viewportIds,
readDigest,
);
const captureResult = await captureBaselines();
await commitManifest();
return captureResult;
}
Loading
Loading