Files
Web_FreeCAD_Bitbybit/scripts/run-freecad-recovered-naming-classification.mjs
wangdequan 83cbc80971
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
feat: advance TSN drift and ordered pair evidence
2026-08-15 04:44:23 -04:00

117 lines
6.4 KiB
JavaScript

import { spawnSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { existsSync } from 'node:fs'
import { readFile, stat, writeFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { buildRecoveredNamingSnapshot, recoveredNamingDecisions, same, validateRecoveredNamingSnapshot } from './freecad-recovered-naming-classification.mjs'
const root = resolve(new URL('..', import.meta.url).pathname)
const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
const pythonPath = resolve(root, '.cache/freecad/sysroot/usr/lib/python3/dist-packages')
const harnessPath = resolve(root, 'scripts/freecad-composite-history-elementmap-oracle.py')
const oraclePath = resolve(root, 'config/freecad-composite-history-elementmap-oracle.json')
const resavePath = resolve(root, 'config/freecad-composite-history-resave-verification.json')
const outputPath = resolve(root, 'config/freecad-recovered-naming-classification.json')
const fail = (message) => { throw new Error(`FreeCAD recovered naming classification: ${message}`) }
const collectDifferences = (reference, replay, path = '$', differences = []) => {
if (same(reference, replay)) return differences
if (Array.isArray(reference) && Array.isArray(replay)) {
for (let index = 0; index < Math.max(reference.length, replay.length); index += 1) collectDifferences(reference[index], replay[index], `${path}[${index}]`, differences)
return differences
}
if (reference && replay && typeof reference === 'object' && typeof replay === 'object') {
for (const key of new Set([...Object.keys(reference), ...Object.keys(replay)])) collectDifferences(reference[key], replay[key], `${path}.${key}`, differences)
return differences
}
differences.push({ path, reference, replay })
return differences
}
if (!existsSync(executable)) fail(`native executable is missing: ${executable}`)
const [reference, referenceResave, harnessContent, harnessBytes] = await Promise.all([
readFile(oraclePath, 'utf8').then(JSON.parse),
readFile(resavePath, 'utf8').then(JSON.parse),
readFile(harnessPath),
stat(harnessPath).then(({ size }) => size),
])
const execution = spawnSync(executable, ['--python-path', pythonPath, harnessPath], {
cwd: root,
encoding: 'utf8',
timeout: 180_000,
maxBuffer: 80 * 1024 * 1024,
env: {
...process.env,
PYTHONPATH: `${pythonPath}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`,
LD_LIBRARY_PATH: `${resolve(root, '.cache/freecad/sysroot/usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}`,
MATPLOTLIBRC: resolve(root, '.cache/freecad/sysroot/etc/matplotlibrc'),
MPLBACKEND: 'Agg',
},
})
const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
const marker = 'FREECAD_COMPOSITE_HISTORY_ELEMENTMAP_RESULT='
const markerIndex = output.indexOf(marker)
const payload = markerIndex >= 0 ? output.slice(markerIndex + marker.length).match(/\{.*\}/s)?.[0] : undefined
if (execution.error || execution.status !== 0 || !payload) fail(`native replay exited with ${execution.status}: ${execution.error?.message || output.trim()}`)
const replay = JSON.parse(payload)
if (reference.freecadVersion !== '1.1.1' || reference.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || replay.freecadVersion !== reference.freecadVersion || replay.gitCommit !== reference.gitCommit || replay.status !== 'pass') fail('native baseline or replay status is invalid.')
const classifications = recoveredNamingDecisions.map((decision) => {
const referenceFixture = reference.cases.find(({ id }) => id === decision.caseId)
const referenceResaveFixture = referenceResave.cases.find(({ id }) => id === decision.caseId)
const replayFixture = replay.cases.find(({ id }) => id === decision.caseId)
if (![referenceFixture, referenceResaveFixture, replayFixture].every(Boolean)) fail(`${decision.caseId} is missing from reference or replay evidence.`)
const referenceSnapshot = buildRecoveredNamingSnapshot(referenceFixture, referenceResaveFixture)
const replaySnapshot = buildRecoveredNamingSnapshot(replayFixture, {
stageCorrelations: replayFixture.stageCorrelations,
roundtripNameDrift: replayFixture.roundtripNameDrift,
resaveNameDrift: replayFixture.resaveNameDrift,
nativeDesktopResaveCovered: replayFixture.nativeDesktopResaveCovered,
})
validateRecoveredNamingSnapshot(referenceSnapshot)
validateRecoveredNamingSnapshot(replaySnapshot)
if (!same(referenceSnapshot, replaySnapshot)) fail(`${decision.caseId} fingerprints differ between the locked reference and independent native replay: ${JSON.stringify(collectDifferences(referenceSnapshot, replaySnapshot))}`)
return {
...decision,
reference: referenceSnapshot,
replay: replaySnapshot,
replayStable: true,
implementationDefect: decision.classification === 'implementation_defect',
}
})
const countClassification = (classification) => classifications.filter((entry) => entry.classification === classification).length
const classifiedStages = classifications.reduce((sum, entry) => sum + entry.reference.driftOrdinals.length, 0)
const report = {
schemaVersion: 1,
baseline: {
freecadVersion: reference.freecadVersion,
commit: reference.gitCommit,
oracleId: reference.baselineId,
},
generatedBy: './npmw run probe:freecad-recovered-naming-classification',
checkedBy: './npmw run check:freecad-recovered-naming-classification',
nativeProbe: {
path: 'scripts/freecad-composite-history-elementmap-oracle.py',
bytes: harnessBytes,
sha256: createHash('sha256').update(harnessContent).digest('hex'),
independentRuns: 2,
},
classifications,
summary: {
classifiedCases: classifications.length,
classifiedStages,
stableSemantics: countClassification('stable_semantics'),
allowedEvolution: countClassification('allowed_evolution'),
implementationDefects: countClassification('implementation_defect'),
unknown: 0,
},
}
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
console.log(JSON.stringify({
status: 'freecad-recovered-naming-classification-generated',
cases: report.classifications.map(({ taskId, caseId, classification, reference: snapshot }) => ({ taskId, caseId, classification, driftOrdinals: snapshot.driftOrdinals })),
independentRuns: report.nativeProbe.independentRuns,
output: 'config/freecad-recovered-naming-classification.json',
}, null, 2))