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>
66 lines
6.5 KiB
JavaScript
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 };
|
|
}
|