import { createHash } from 'node:crypto' import { readFile, stat } 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 load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse) const fail = (message) => { throw new Error(`FreeCAD recovered naming classification: ${message}`) } const [report, oracle, resave] = await Promise.all([ load('config/freecad-recovered-naming-classification.json'), load('config/freecad-composite-history-elementmap-oracle.json'), load('config/freecad-composite-history-resave-verification.json'), ]) if (report.schemaVersion !== 1 || report.baseline?.freecadVersion !== '1.1.1' || report.baseline.commit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.baseline.oracleId !== oracle.baselineId) fail('baseline is invalid.') const harnessPath = resolve(root, report.nativeProbe?.path ?? '') const [harnessContent, harnessBytes] = await Promise.all([readFile(harnessPath), stat(harnessPath).then(({ size }) => size)]) if (report.nativeProbe.path !== 'scripts/freecad-composite-history-elementmap-oracle.py' || report.nativeProbe.bytes !== harnessBytes || report.nativeProbe.sha256 !== createHash('sha256').update(harnessContent).digest('hex') || report.nativeProbe.independentRuns < 2) fail('native replay provenance is missing or stale.') if (!Array.isArray(report.classifications) || report.classifications.length !== recoveredNamingDecisions.length) fail('classification report does not contain the complete serial decision prefix.') let classifiedStages = 0 for (const [index, entry] of report.classifications.entries()) { const decision = recoveredNamingDecisions[index] if (!same({ taskId: entry.taskId, caseId: entry.caseId, classification: entry.classification, reasonCode: entry.reasonCode, rationale: entry.rationale }, decision) || entry.replayStable !== true || entry.implementationDefect !== (decision.classification === 'implementation_defect')) fail(`${decision.taskId} classification contract is invalid.`) const fixture = oracle.cases.find(({ id }) => id === entry.caseId) const resaveFixture = resave.cases.find(({ id }) => id === entry.caseId) if (!fixture || !resaveFixture) fail(`${entry.caseId} is absent from the locked native corpus.`) const expected = buildRecoveredNamingSnapshot(fixture, resaveFixture) validateRecoveredNamingSnapshot(expected) validateRecoveredNamingSnapshot(entry.reference) validateRecoveredNamingSnapshot(entry.replay) if (!same(entry.reference, expected) || !same(entry.replay, expected)) fail(`${entry.caseId} classification evidence is stale or the independent replay differs.`) classifiedStages += expected.driftOrdinals.length } const countClassification = (classification) => recoveredNamingDecisions.filter((entry) => entry.classification === classification).length const expectedSummary = { classifiedCases: recoveredNamingDecisions.length, classifiedStages, stableSemantics: countClassification('stable_semantics'), allowedEvolution: countClassification('allowed_evolution'), implementationDefects: countClassification('implementation_defect'), unknown: 0 } if (!same(report.summary, expectedSummary)) fail('classification summary is inconsistent.') const driftCases = oracle.cases.filter((candidate) => candidate.mutation.metrics.namingRestorationDriftStages > 0) const totalDriftStages = driftCases.reduce((sum, candidate) => sum + candidate.mutation.metrics.namingRestorationDriftStages, 0) console.log(JSON.stringify({ status: 'freecad-recovered-naming-classification-pass', completedTasks: report.classifications.map(({ taskId }) => taskId), cases: report.classifications.map(({ caseId, classification }) => ({ caseId, classification })), independentRuns: report.nativeProbe.independentRuns, classifiedCases: report.summary.classifiedCases, classifiedStages: report.summary.classifiedStages, remainingCases: driftCases.length - report.summary.classifiedCases, remainingStages: totalDriftStages - report.summary.classifiedStages, }, null, 2))