Files
Web_FreeCAD_Bitbybit/scripts/check-freecad-composite-history-elementmap-oracle.mjs

78 lines
7.6 KiB
JavaScript

import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { elementMap2SemanticDigest, parseElementMap2, validateElementMap2, writeElementMap2 } from '../src/facade/elementMap2.ts'
import { validateNativeNamingEvidence } from '../src/facade/nativeNamingEvidence.ts'
import { migrateStringHasherSchema, parseStringHasherTable, validateStringHasherTable, writeStringHasherTable } from '../src/facade/stringHasher.ts'
const root = resolve(new URL('..', import.meta.url).pathname)
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-composite-history-elementmap-oracle.json'), 'utf8'))
if (report.schemaVersion !== 1 || report.baselineId !== 'freecad-1.1.1-composite-history-elementmap2' || report.freecadVersion !== '1.1.1' || report.status !== 'pass' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') throw new Error('FreeCAD composite history ElementMap2 oracle baseline is invalid.')
if (report.summary?.cases !== 30 || report.summary?.passed !== 30 || report.summary?.failed !== 0 || !Array.isArray(report.cases) || report.cases.length !== 30) throw new Error('FreeCAD composite oracle must contain exactly 30 passing cases.')
let parsedResources = 0
let parsedStringHasherResources = 0
let namingEvidenceStages = 0
let namingEvidenceMissing = 0
let namingEvidenceBoundaryViolations = 0
let nativeEvidenceValidatedStages = 0
let nativeMappedNameStages = 0
let nativeIndexedNameStages = 0
let privateTokenEvidenceCompleteStages = 0
let internalBuilderEvidenceStages = 0
let internalBuilderEvidenceMissingStages = 0
for (const fixture of report.cases) {
if (fixture.status !== 'pass' || !fixture.finalObject || !fixture.stages?.length) throw new Error(`FreeCAD composite fixture ${fixture.id} has no valid feature history stages.`)
if (fixture.roundtripNameDrift !== 0) throw new Error(`FreeCAD composite fixture ${fixture.id} changed mapped names after FreeCAD reopen.`)
const names = new Set()
let historyStages = 0
for (const stage of fixture.stages) {
if (!stage.shape?.valid || !stage.names?.length) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has invalid topology.`)
namingEvidenceStages += 1
if (!['final-shape-only', 'opaque-preserved', 'native-evidence', 'ambiguous', 'missing'].includes(stage.namingEvidenceStatus)) namingEvidenceMissing += 1
if (stage.namingEvidenceStatus === 'opaque-preserved' && !fixture.stringHasherResource?.text) namingEvidenceBoundaryViolations += 1
if (!stage.nativeEvidence || stage.nativeEvidence.stageId !== stage.name || stage.nativeEvidence.resultObjectId !== stage.name) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has incomplete native naming evidence identity.`)
if (stage.nativeEvidence.status !== stage.namingEvidenceStatus) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has inconsistent naming evidence status.`)
const nativeEvidence = { ...stage.nativeEvidence }
if (fixture.stringHasherResource?.text) nativeEvidence.stringHasher = migrateStringHasherSchema(parseStringHasherTable(fixture.stringHasherResource.text))
const nativeValidation = validateNativeNamingEvidence(nativeEvidence)
if (!nativeValidation.valid) throw new Error(`FreeCAD native naming evidence validation failed for ${fixture.id}/${stage.name}: ${nativeValidation.issues[0].path}: ${nativeValidation.issues[0].message}`)
nativeEvidenceValidatedStages += 1
if (stage.nativeEvidence.mappedNameApiEntries > 0) nativeMappedNameStages += 1
if (stage.nativeEvidence.indexedNameApiEntries > 0) nativeIndexedNameStages += 1
if (stage.nativeEvidence.privateTokenEvidenceComplete === true) {
if (stage.nativeEvidence.mappedNameApiEntries !== stage.nativeEvidence.mappedNames.length) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} claims incomplete private token evidence as complete.`)
privateTokenEvidenceCompleteStages += 1
}
if (stage.nativeEvidence.status === 'native-evidence' && stage.nativeEvidence.internalBuilderEvidence === true) internalBuilderEvidenceStages += 1
else if (stage.nativeEvidence.status === 'native-evidence' && stage.nativeEvidence.internalBuilderEvidence !== true && /^(Part::(Fuse|Cut|Common|Extrusion|Revolution|Loft|Sweep|Fillet|Chamfer)|PartDesign::(Pad|Pocket|Revolution|Groove|AdditiveLoft|SubtractiveLoft|AdditivePipe|SubtractivePipe|Fillet|Chamfer|Draft|Thickness|Mirrored|MultiTransform|LinearPattern|PolarPattern|Hole))$/.test(stage.typeId || '')) internalBuilderEvidenceMissingStages += 1
const stageHasHistory = stage.names.some((entry) => Array.isArray(entry.history) && entry.history.length > 0)
if (stageHasHistory) historyStages += 1
for (const entry of stage.names) {
if (names.has(`${stage.name}:${entry.name}`)) throw new Error(`Duplicate ElementMap name ${fixture.id}/${stage.name}/${entry.name}.`)
names.add(`${stage.name}:${entry.name}`)
}
}
if (historyStages === 0) throw new Error(`Missing FreeCAD getElementHistory chain for ${fixture.id}.`)
if (fixture.stringHasherResource?.text !== null && fixture.stringHasherResource?.text !== undefined) {
const table = migrateStringHasherSchema(parseStringHasherTable(fixture.stringHasherResource.text))
const validation = validateStringHasherTable(table)
if (!validation.valid) throw new Error(`StringHasher semantic validation failed for ${fixture.id}: ${validation.issues[0].path}: ${validation.issues[0].message}`)
const reparsed = migrateStringHasherSchema(parseStringHasherTable(writeStringHasherTable(table)))
if (JSON.stringify(reparsed) !== JSON.stringify(table)) throw new Error(`StringHasher semantic roundtrip changed native naming evidence for ${fixture.id}.`)
parsedStringHasherResources += 1
}
for (const [path, resource] of Object.entries(fixture.elementMapResources || {})) {
const document = parseElementMap2(resource.text)
const validation = validateElementMap2(document)
if (!validation.valid) throw new Error(`ElementMap2 semantic validation failed for ${fixture.id}/${path}: ${validation.issues[0].path}: ${validation.issues[0].message}`)
const canonical = writeElementMap2(document)
const reparsed = parseElementMap2(canonical)
if (reparsed.maps.length !== document.maps.length || reparsed.postfixes.length !== document.postfixes.length) throw new Error(`ElementMap2 schema roundtrip changed counts for ${fixture.id}/${path}.`)
if (elementMap2SemanticDigest(reparsed) !== elementMap2SemanticDigest(document)) throw new Error(`ElementMap2 semantic roundtrip changed native naming history for ${fixture.id}/${path}.`)
parsedResources += 1
}
}
if (parsedResources === 0) throw new Error('FreeCAD composite oracle did not capture any ElementMap2 resources.')
if (nativeIndexedNameStages !== namingEvidenceStages) throw new Error(`FreeCAD composite oracle must retain direct indexed-name evidence for every stage, found ${nativeIndexedNameStages}/${namingEvidenceStages}.`)
if (namingEvidenceMissing !== 0 || namingEvidenceBoundaryViolations !== 0) throw new Error(`FreeCAD naming evidence boundary is incomplete: missing=${namingEvidenceMissing}, violations=${namingEvidenceBoundaryViolations}.`)
console.log(JSON.stringify({ status: 'freecad-composite-history-elementmap-pass', cases: report.summary.cases, parsedResources, parsedStringHasherResources, namingEvidenceStages, nativeEvidenceValidatedStages, nativeIndexedNameStages, nativeMappedNameStages, indexedOnlyStages: nativeIndexedNameStages - nativeMappedNameStages, privateTokenEvidenceCompleteStages, namingEvidenceMissing, namingEvidenceBoundaryViolations, internalBuilderEvidenceStages, internalBuilderEvidenceMissingStages }, null, 2))