34 lines
4.1 KiB
JavaScript
34 lines
4.1 KiB
JavaScript
import { createHash } from 'node:crypto'
|
|
import { readFile, stat } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-isomorphic-provenance-verification.json'), 'utf8'))
|
|
const fail = (message) => { throw new Error(`FreeCAD isomorphic provenance check failed: ${message}`) }
|
|
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.runtime !== 'node' || report.implementation !== 'freecad-linked' || report.productionWorkerLinked !== true || report.systemExact !== false) fail('report boundary is invalid')
|
|
const symmetric = report.cases?.symmetricCommon
|
|
if (symmetric?.status !== 'ambiguous' || symmetric.stable !== 0 || symmetric.ambiguous < 1 || symmetric.candidates < symmetric.ambiguous * 2 || symmetric.persistedCandidateSets !== true) fail('symmetric Common evidence is incomplete')
|
|
const mixed = report.cases?.mixedCut
|
|
if (mixed?.status !== 'ambiguous' || mixed.stable < 1 || mixed.ambiguous < 1 || mixed.elementMaps < 1) fail('mixed Cut evidence is incomplete')
|
|
const downstream = report.cases?.downstreamRotate
|
|
if (downstream?.status !== 'ambiguous' || downstream.ambiguous < 1 || !downstream.candidateObjects?.includes('cut-object') || !downstream.candidateObjects?.includes('cut-tool')) fail('cross-feature candidate propagation is incomplete')
|
|
const longChain = report.cases?.longChain
|
|
if (JSON.stringify(longChain?.operations) !== '["cut","rotate","fillet","mirrored","linear-pattern"]' || longChain.stages?.length !== 5 || longChain.persistedFinalEvidence !== true || longChain.referencedCandidateObjectsRetained !== true || longChain.stringHasherMonotonic !== true) fail('long-chain naming evidence is incomplete')
|
|
for (const [index, stage] of longChain.stages.entries()) {
|
|
if (stage.status !== 'ambiguous' || stage.stable < 1 || stage.ambiguous < 1 || stage.candidates < stage.ambiguous * 2 || (index > 0 && stage.persistedInputEvidence !== true)) fail(`long-chain stage ${String(stage.operation)} did not preserve stable and ambiguous provenance`)
|
|
if (index > 0 && (stage.referencedCandidateObjectsRetained !== true || stage.elementMaps < 1 || stage.referencedInputCandidateObjects?.some((objectId) => !stage.candidateObjects?.includes(objectId)))) fail(`long-chain stage ${String(stage.operation)} lost referenced provenance or ElementMap2 evidence`)
|
|
if (index > 1 && stage.stringHasherEntries < longChain.stages[index - 1].stringHasherEntries) fail(`long-chain stage ${String(stage.operation)} regressed StringHasher state`)
|
|
}
|
|
if (report.fcstdAmbiguityRejected !== true) fail('FCStd fail-closed evidence is absent')
|
|
if (JSON.stringify(report.fcstdStableLinkRoundtrip?.subElements) !== '["Face1"]' || report.fcstdStableLinkRoundtrip.inspectRewritePreserved !== true || report.fcstdStableLinkRoundtrip.nativeDesktopResaveCovered !== false) fail('FCStd stable LinkSub persistence boundary is incomplete')
|
|
for (const artifact of report.artifacts ?? []) {
|
|
const path = resolve(root, 'native/occt-history/dist', artifact.name)
|
|
const [content, bytes] = await Promise.all([readFile(path), stat(path).then(({ size }) => size)])
|
|
if (bytes !== artifact.bytes || createHash('sha256').update(content).digest('hex') !== artifact.sha256) fail(`report is stale for ${artifact.name}`)
|
|
}
|
|
if (report.artifacts?.length !== 3) fail('JS/WASM/DATA artifact lock is incomplete')
|
|
const harnessPath = resolve(root, report.harness?.path ?? '')
|
|
const [harnessContent, harnessBytes] = await Promise.all([readFile(harnessPath), stat(harnessPath).then(({ size }) => size)])
|
|
if (report.harness?.path !== 'scripts/run-freecad-isomorphic-provenance.ts' || harnessBytes !== report.harness.bytes || createHash('sha256').update(harnessContent).digest('hex') !== report.harness.sha256) fail('report is stale for its executable harness')
|
|
console.log(JSON.stringify({ status: 'freecad-isomorphic-provenance-pass', symmetricAmbiguities: symmetric.ambiguous, downstreamAmbiguities: downstream.ambiguous, longChainStages: longChain.stages.length, fcstdAmbiguityRejected: true, fcstdStableLinkRoundtrip: true, systemExact: false }, null, 2))
|