import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' import { createElementMap2NameToken, elementMap2NameTokenToReference, elementMap2SemanticDigest, parseElementMap2, validateElementMap2, writeElementMap2 } from '../src/facade/elementMap2.ts' import { migrateStringHasherSchema, parseStringHasherTable, validateElementMap2StringHasherEvidence, validateStringHasherTable, writeStringHasherTable } from '../src/facade/stringHasher.ts' const root = resolve(new URL('..', import.meta.url).pathname) const load = async (path) => JSON.parse(await readFile(resolve(root, path), 'utf8')) const gate = await load('config/freecad-exact-history-elementmap-gate.json') const oracle = await load('config/freecad-composite-history-elementmap-oracle.json') const roundtrip = await load('config/freecad-fcstd-roundtrip-verification.json') if (oracle.baselineId !== gate.baselineId || oracle.freecadVersion !== gate.requiredFreecadVersion || oracle.gitCommit !== gate.requiredFreecadCommit || oracle.status !== 'pass' || oracle.summary?.cases !== gate.requiredCases || oracle.summary?.passed !== gate.requiredCases) throw new Error('Exact history/ElementMap2 gate baseline is not the locked FreeCAD 1.1.1 30-case report.') let wrongBindings = 0 let unexplainedRelations = 0 let roundtripNameDrift = 0 let elementMap2ParseFailures = 0 let elementMap2SemanticFailures = 0 let elementMap2TokenWriterFailures = 0 let stringHasherResources = 0 let stringHasherParseFailures = 0 let stringHasherSemanticFailures = 0 let stringHasherEvidenceFailures = 0 let namingEvidenceMissing = 0 let namingEvidenceBoundaryViolations = 0 let nativeEvidenceStages = 0 let nativeMappedNameStages = 0 let nativeIndexedNameStages = 0 let privateTokenEvidenceCompleteStages = 0 let internalBuilderEvidenceStages = 0 let internalBuilderEvidenceMissingStages = 0 const builderStageType = /^(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))$/ let resources = 0 for (const fixture of oracle.cases) { if (fixture.roundtripNameDrift !== 0) roundtripNameDrift += 1 const knownObjects = new Set(fixture.stages.map((stage) => stage.name)) for (const stage of fixture.stages) { 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.namingEvidenceStatus === 'native-evidence') nativeEvidenceStages += 1 if (stage.nativeEvidence?.mappedNameApiEntries > 0) nativeMappedNameStages += 1 if (stage.nativeEvidence?.indexedNameApiEntries > 0) nativeIndexedNameStages += 1 if (stage.nativeEvidence?.privateTokenEvidenceComplete === true) privateTokenEvidenceCompleteStages += 1 if (builderStageType.test(stage.typeId || '')) { if (stage.nativeEvidence?.internalBuilderEvidence === true) internalBuilderEvidenceStages += 1 else internalBuilderEvidenceMissingStages += 1 } const names = new Set() for (const entry of stage.names) { if (names.has(entry.name)) wrongBindings += 1 names.add(entry.name) for (const relation of entry.history || []) { if (!knownObjects.has(relation.object) || typeof relation.mappedName !== 'string') unexplainedRelations += 1 } } } let stringHasherTable if (fixture.stringHasherResource?.text !== null && fixture.stringHasherResource?.text !== undefined) { try { stringHasherTable = migrateStringHasherSchema(parseStringHasherTable(fixture.stringHasherResource.text)) const validation = validateStringHasherTable(stringHasherTable) if (!validation.valid) stringHasherParseFailures += 1 else { stringHasherResources += 1 const reparsed = migrateStringHasherSchema(parseStringHasherTable(writeStringHasherTable(stringHasherTable))) if (JSON.stringify(reparsed) !== JSON.stringify(stringHasherTable)) stringHasherSemanticFailures += 1 } } catch { stringHasherParseFailures += 1 stringHasherTable = undefined } } for (const resource of Object.values(fixture.elementMapResources || {})) { resources += 1 try { const document = parseElementMap2(resource.text) const validation = validateElementMap2(document) if (!validation.valid) elementMap2SemanticFailures += 1 const hasStringHasherEvidence = document.maps.some((map) => map.sections.some((section) => section.children.some((child) => child.stringIds.length > 0) || section.names.some((name) => name.tokens.some((token) => token.suffix.length > 1 || token.marker === '$')))) if (!stringHasherTable && hasStringHasherEvidence) stringHasherEvidenceFailures += 1 if (stringHasherTable) stringHasherEvidenceFailures += validateElementMap2StringHasherEvidence(document, stringHasherTable).length for (const map of document.maps) for (const section of map.sections) for (const entry of section.names) for (const token of entry.tokens) { try { const evidence = elementMap2NameTokenToReference(token, document.postfixes) if (createElementMap2NameToken(evidence, document.postfixes).raw !== token.raw) elementMap2TokenWriterFailures += 1 } catch { elementMap2TokenWriterFailures += 1 } } const reparsed = parseElementMap2(writeElementMap2(document)) if (reparsed.maps.length !== document.maps.length || reparsed.postfixes.length !== document.postfixes.length) elementMap2ParseFailures += 1 if (elementMap2SemanticDigest(reparsed) !== elementMap2SemanticDigest(document)) elementMap2SemanticFailures += 1 } catch { elementMap2ParseFailures += 1 } } } for (const scenario of roundtrip.scenarios || []) { if (scenario.status !== 'pass' || (scenario.differences || []).length !== 0) roundtripNameDrift += 1 } if (roundtrip.status !== 'verified' || wrongBindings !== gate.requirements.wrongBindings || unexplainedRelations !== gate.requirements.unexplainedRelations || roundtripNameDrift !== gate.requirements.roundtripNameDrift || elementMap2ParseFailures !== gate.requirements.elementMap2ParseFailures || elementMap2SemanticFailures !== gate.requirements.elementMap2SemanticFailures || elementMap2TokenWriterFailures !== gate.requirements.elementMap2TokenWriterFailures || stringHasherParseFailures !== gate.requirements.stringHasherParseFailures || stringHasherSemanticFailures !== gate.requirements.stringHasherSemanticFailures || stringHasherEvidenceFailures !== gate.requirements.stringHasherEvidenceFailures || namingEvidenceMissing !== gate.requirements.namingEvidenceMissing || namingEvidenceBoundaryViolations !== gate.requirements.namingEvidenceBoundaryViolations) throw new Error(`Exact gate failed: wrongBindings=${wrongBindings}, unexplainedRelations=${unexplainedRelations}, roundtripNameDrift=${roundtripNameDrift}, elementMap2ParseFailures=${elementMap2ParseFailures}, elementMap2SemanticFailures=${elementMap2SemanticFailures}, elementMap2TokenWriterFailures=${elementMap2TokenWriterFailures}, stringHasherParseFailures=${stringHasherParseFailures}, stringHasherSemanticFailures=${stringHasherSemanticFailures}, stringHasherEvidenceFailures=${stringHasherEvidenceFailures}, namingEvidenceMissing=${namingEvidenceMissing}, namingEvidenceBoundaryViolations=${namingEvidenceBoundaryViolations}.`) console.log(JSON.stringify({ status: 'freecad-exact-history-elementmap-gate-pass', exactPromotionReady: false, cases: oracle.summary.cases, resources, stringHasherResources, nativeEvidenceStages, nativeIndexedNameStages, nativeMappedNameStages, indexedOnlyStages: nativeIndexedNameStages - nativeMappedNameStages, privateTokenEvidenceCompleteStages, internalBuilderEvidenceStages, internalBuilderEvidenceMissingStages, wrongBindings, unexplainedRelations, roundtripNameDrift, elementMap2ParseFailures, elementMap2SemanticFailures, elementMap2TokenWriterFailures, stringHasherParseFailures, stringHasherSemanticFailures, stringHasherEvidenceFailures, namingEvidenceMissing, namingEvidenceBoundaryViolations }, null, 2))