Files
workinf_Blender_Wasm/tools/web/coverage-baseline.mjs
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

66 lines
6.5 KiB
JavaScript

import assert from 'node:assert/strict';
import path from 'node:path';
import { digest, object, text } from './workflow-case-lib.mjs';
export const canonicalPath = (root, file) => path.relative(root, path.resolve(root, text(file, 'input path')));
export function caseEvidence(receipt, shaFile) {
object(receipt, 'case manifest'); assert.ok(Array.isArray(receipt.cases));
const cases = receipt.cases.map(item => {
object(item, 'case'); text(item.kind, 'case.kind');
assert.ok(item.inputs === undefined || Array.isArray(item.inputs)); assert.ok(item.artifacts === undefined || Array.isArray(item.artifacts));
const bindings = [...(item.inputs ?? []), ...(item.artifacts ?? [])];
const tests = item.result?.tests;
const observations = bindings.map(ref => { object(ref, 'case binding'); text(ref.path, 'case binding.path'); assert.ok(hash(ref.sha256)); const actualSha256 = shaFile(ref.path); assert.ok(actualSha256 === null || hash(actualSha256)); return { path: ref.path, expectedSha256: ref.sha256, actualSha256 }; });
const bound = Boolean(item.inputs?.length && item.artifacts?.length) && observations.every(ref => ref.actualSha256 === ref.expectedSha256);
const eligible = item.status === 'PASS' && item.process?.code === 0 && item.process.timedOut === false && item.process.cancelled === false && item.result?.passed === true && Number.isSafeInteger(tests) && tests > 0 && bound;
return { kind: item.kind, reportedTests: tests ?? null, bound, eligible, currentSelectedTests: eligible ? tests : 0, observations };
});
return { cases, acceptance: 'NOT_CLAIMED', remainingGates: receipt.remainingGates ?? [] };
}
const hash = value => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
function byId(values, label) {
assert.ok(Array.isArray(values), `${label}: expected array`);
const result = new Map();
for (const item of values) { object(item, label); const id = text(item.id, `${label}.id`); assert.ok(!result.has(id), `${label}: duplicate ${id}`); result.set(id, item); }
return result;
}
export function coverageBaseline(catalog, inventory, historical, shaFile) {
object(catalog, 'catalog'); object(inventory, 'inventory'); object(historical, 'integrity');
const routes = byId(catalog.entries, 'routes'), units = byId(inventory.units, 'units');
const ids = [...new Set([...routes.keys(), ...units.keys()])].sort();
assert.ok(hash(inventory.authority?.sourceTreeSha256), 'inventory source hash required');
assert.ok(Array.isArray(inventory.sourceFiles), 'sourceFiles must be an array');
const sources = inventory.sourceFiles.map(file => {
object(file, 'source'); text(file.path, 'source.path'); text(file.sourcePath, 'source.sourcePath'); assert.ok(hash(file.sha256));
const actualSha256 = shaFile(file.path); assert.ok(actualSha256 === null || hash(actualSha256));
return { ...file, actualSha256, status: actualSha256 === null ? 'MISSING' : actualSha256 === file.sha256 ? 'MATCH' : 'DRIFT' };
});
const sourceTreeSha256 = digest(sources.map(file => `${file.sourcePath}\0${file.actualSha256 ?? 'MISSING'}\n`).join(''));
const inventorySnapshotMatches = digest(sources.map(file => `${file.sourcePath}\0${file.sha256}\n`).join('')) === inventory.authority.sourceTreeSha256;
const references = new Map();
const inspect = reference => {
if (reference === undefined || reference === null) return { status: 'UNBOUND' };
object(reference, 'reference'); text(reference.path, 'reference.path'); assert.ok(hash(reference.sha256), 'reference SHA-256 required');
const key = `${reference.path}\0${reference.sha256}`;
if (!references.has(key)) { const actualSha256 = shaFile(reference.path); assert.ok(actualSha256 === null || hash(actualSha256)); references.set(key, { path: reference.path, expectedSha256: reference.sha256, actualSha256, status: actualSha256 === null ? 'MISSING' : actualSha256 === reference.sha256 ? 'MATCH' : 'DRIFT' }); }
return references.get(key);
};
assert.ok(Array.isArray(historical.references), 'historical references must be an array');
for (const entry of historical.references) inspect({ path: entry.path, sha256: entry.expectedSha256 });
const evidence = ids.map(id => {
const unit = units.get(id), binding = unit?.coverage, recorded = unit?.coverageStatus === 'VERIFIED';
const manifest = inspect(binding?.evidenceManifest), production = inspect(binding?.productionEvidence);
const referenceMatches = manifest.status === 'MATCH' && production.status === 'MATCH';
const sourceMatches = hash(binding?.sourceTreeSha256) && binding.sourceTreeSha256 === sourceTreeSha256;
const reasons = [!unit && 'NOT_IN_INVENTORY', !routes.has(id) && 'NOT_IN_CATALOG', !recorded && 'NO_RECORDED_PASS', !referenceMatches && 'EVIDENCE_REFERENCE_INVALID', !sourceMatches && 'SOURCE_VERSION_UNBOUND', !inventorySnapshotMatches && 'INVENTORY_SNAPSHOT_INVALID'].filter(Boolean);
return { id, recorded, referenceMatches, sourceMatches, eligible: reasons.length === 0, reasons };
});
const refs = [...references.values()].sort((a, b) => a.path.localeCompare(b.path) || a.expectedSha256.localeCompare(b.expectedSha256));
const count = (array, predicate) => array.filter(predicate).length;
const verifiedUnits = count(evidence, unit => unit.eligible), unitCount = ids.length;
const reconciliation = { catalogUnits: routes.size, inventoryUnits: units.size, denominator: unitCount, catalogOnly: ids.filter(id => !units.has(id)), inventoryOnly: ids.filter(id => !routes.has(id)) };
const integrity = { referencedArtifacts: refs.length, matchingArtifacts: count(refs, ref => ref.status === 'MATCH'), driftedArtifacts: count(refs, ref => ref.status === 'DRIFT'), missingArtifacts: count(refs, ref => ref.status === 'MISSING') };
const history = { recordedVerifiedUnits: count(evidence, unit => unit.recorded), referenceValidUnits: count(evidence, unit => unit.recorded && unit.referenceMatches), oldIntegrityObservedAt: historical.observedAt ?? null, oldDriftedArtifacts: historical.driftedArtifacts };
const source = { scope: 'declared inventory source files', inventoryTreeSha256: inventory.authority.sourceTreeSha256, currentScopeSha256: sourceTreeSha256, changedFiles: count(sources, file => file.status !== 'MATCH'), inventorySnapshotMatches: digest(sources.map(file => `${file.sourcePath}\0${file.sha256}\n`).join('')) === inventory.authority.sourceTreeSha256 };
return { schemaVersion: 1, unitCount, verifiedUnits, unverifiedUnits: unitCount - verifiedUnits, sourceTreeSha256, reconciliation, integrity, history, source, evidence, references: refs, sources };
}