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))