feat: prepare FreeCAD private naming worker linkage
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled

This commit is contained in:
2026-08-11 23:16:22 -04:00
parent aa607451ad
commit 97967041e2
30 changed files with 505 additions and 84 deletions

View File

@@ -1,6 +1,7 @@
import { createHash } from 'node:crypto'
import { access, readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
const root = resolve(new URL('..', import.meta.url).pathname)
const artifactNames = ['bitbybit-occt-history.js', 'bitbybit-occt-history.wasm']
@@ -27,4 +28,56 @@ const workerClient = await readFile(resolve(root, 'src/facade/nativeHistoryWorke
if (!workerClient.includes("'/native/occt-history/bitbybit-occt-history.js'")) throw new Error('Native history Worker client URL does not match the published artifact.')
const files = []
for (const name of artifactNames) files.push({ name, sha256: hash(await readFile(resolve(publicRoot, name))) })
console.log(JSON.stringify({ status: 'artifact-pass', files }, null, 2))
let namingAbi = { availability: 'unavailable', callbacks: [] }
const factoryModule = await import(pathToFileURL(resolve(distRoot, 'bitbybit-occt-history.js')).href)
const factory = factoryModule.default
if (typeof factory !== 'function') throw new TypeError('OCCT history artifact default export is not a factory.')
const nativeModule = await factory({ locateFile: (fileName) => resolve(distRoot, fileName.split('/').at(-1)) })
const callbacks = ['freecadNamingAbiVersion', 'freecadNamingCapabilitiesJson', 'freecadNamingEvidenceJson'].filter((name) => typeof nativeModule[name] === 'function')
if (callbacks.length !== 0 && callbacks.length !== 3) throw new Error(`FreeCAD naming ABI is partially exported: ${callbacks.join(', ')}.`)
if (callbacks.length === 3) {
const abiVersion = nativeModule.freecadNamingAbiVersion()
const descriptor = JSON.parse(nativeModule.freecadNamingCapabilitiesJson())
if (abiVersion !== 1 || descriptor.schemaVersion !== 1 || descriptor.freecadVersion !== '1.1.1' || descriptor.sourceCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || descriptor.mappedNameRef !== true || descriptor.stringHasher !== true || descriptor.elementMap2 !== true || !Array.isArray(descriptor.operations) || !descriptor.operations.includes('cut')) throw new Error('FreeCAD naming ABI descriptor is not the locked v1 contract with Cut support.')
let object
let tool
let evidence
try {
object = nativeModule.makeBox(10, 10, 10)
tool = nativeModule.makeBox(5, 5, 5)
const objectStep = nativeModule.shapeToStep(object)
const toolStep = nativeModule.shapeToStep(tool)
const history = nativeModule.booleanHistoryFromStep(objectStep, toolStep, 'cut')
const request = {
schemaVersion: 1,
requestId: 'artifact-private-naming-probe',
documentId: 'artifact-probe-document',
documentVersion: 1,
operationId: 'artifact-probe-cut',
operation: 'cut',
stageId: 'artifact-probe:stage:0',
resultObjectId: 'artifact-probe:result',
resultObjectTag: 3,
inputs: [
{ inputId: 'object', objectId: 'artifact-probe:object', role: 'object', step: objectStep, objectTag: 1 },
{ inputId: 'tool', objectId: 'artifact-probe:tool', role: 'tool', step: toolStep, objectTag: 2 },
],
stages: [{ stageId: 'artifact-probe:stage:0', operation: 'cut', inputIds: ['object', 'tool'], ordinal: 0 }],
resultStep: history.resultStep,
resultBrep: history.resultBrep,
history,
}
evidence = JSON.parse(nativeModule.freecadNamingEvidenceJson(JSON.stringify(request)))
} finally {
object?.delete?.()
tool?.delete?.()
}
if (!evidence || evidence.schemaVersion !== 1 || evidence.stageId !== 'artifact-probe:stage:0' || evidence.resultObjectId !== 'artifact-probe:result' || !['native-evidence', 'ambiguous'].includes(evidence.status) || !Array.isArray(evidence.mappedNames) || evidence.mappedNames.length === 0 || !evidence.stringHasher || !evidence.elementMap2) throw new Error('FreeCAD naming ABI did not return complete stage-bound Cut evidence.')
namingAbi = { availability: 'available', callbacks, abiVersion, descriptor, evidenceProbe: { operation: 'cut', status: evidence.status, mappedNames: evidence.mappedNames.length, stringHasher: true, elementMap2: true } }
}
const abiContract = JSON.parse(await readFile(resolve(root, 'config/freecad-sketcher-partdesign-abi-contract.json'), 'utf8'))
const expectedImplementation = abiContract.privateNamingAbi?.shippedWorkerImplementation
if (expectedImplementation === 'not-linked' && namingAbi.availability !== 'unavailable') throw new Error('OCCT history artifact exports FreeCAD naming callbacks but the shipped-worker contract still says not-linked.')
if (expectedImplementation === 'freecad-linked' && namingAbi.availability !== 'available') throw new Error('Shipped-worker contract claims FreeCAD linkage but the artifact ABI probe is unavailable.')
if (!['not-linked', 'freecad-linked'].includes(expectedImplementation)) throw new Error('Shipped-worker FreeCAD naming implementation status is invalid.')
console.log(JSON.stringify({ status: 'artifact-pass', files, namingAbi, exactBoundary: namingAbi.availability === 'available' ? 'probe-only; FreeCAD evidence still requires locked descriptor and response validation' : 'occt-only; EX-TSN-02 remains in_progress and systemExact=false' }, null, 2))