feat: advance FreeCAD exact parity evidence
This commit is contained in:
@@ -3,15 +3,21 @@ import { resolve } from 'node:path'
|
||||
|
||||
const root = resolve(new URL('..', import.meta.url).pathname)
|
||||
const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
|
||||
const [sourceInventory, typeInventory, guiInventory, reference] = await Promise.all([
|
||||
const [sourceInventory, typeInventory, guiInventory, reference, guiReference, boundaries, exactPlan] = await Promise.all([
|
||||
load('config/freecad-source-inventory.json'),
|
||||
load('config/freecad-type-property-inventory.json'),
|
||||
load('config/freecad-gui-command-inventory.json'),
|
||||
load('.cache/freecad/reference-desktop.json'),
|
||||
load('.cache/freecad/reference-desktop-gui-commands.json'),
|
||||
load('config/freecad-oracle-boundaries.json'),
|
||||
load('config/freecad-web-exact-parity-plan.json'),
|
||||
])
|
||||
const fail = (message) => { throw new Error(`FreeCAD oracle coverage: ${message}`) }
|
||||
const lockedCommit = '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d'
|
||||
if (reference.schemaVersion !== 1 || reference.freecadVersion !== '1.1.1' || reference.gitCommit !== lockedCommit) fail('reference desktop report is not the locked FreeCAD 1.1.1 baseline.')
|
||||
if (guiReference.schemaVersion !== 1 || guiReference.freecadVersion !== '1.1.1' || guiReference.gitCommit !== lockedCommit || guiReference.guiUp !== true || guiReference.probeScope !== 'gui-commands') fail('GUI command report is not the dedicated locked FreeCAD 1.1.1 oracle.')
|
||||
if (guiReference.determinism?.schemaVersion !== 1 || guiReference.determinism.normalizedProcessFields?.join(',') !== 'pointer-address,uuid,freecad-document-cache-run') fail('GUI command report lacks deterministic process-field normalization.')
|
||||
if (boundaries.schemaVersion !== 1 || boundaries.baseline?.freecadVersion !== '1.1.1' || boundaries.baseline?.commit !== lockedCommit) fail('oracle boundary decisions do not match the locked baseline.')
|
||||
if (sourceInventory.moduleCount !== 34 || reference.moduleCount !== 34 || reference.modules?.length !== 34) fail('source and desktop oracle must both enumerate 34 modules.')
|
||||
if (typeInventory.moduleCount !== 34 || typeInventory.modules?.length !== 34) fail('type/property inventory must enumerate all 34 modules.')
|
||||
|
||||
@@ -26,17 +32,32 @@ for (const name of sourceModules.keys()) {
|
||||
const missingReferenceModules = [...referenceModules.keys()].filter((name) => !sourceModules.has(name))
|
||||
if (missingReferenceModules.length > 0) fail(`desktop oracle has unregistered modules: ${missingReferenceModules.join(', ')}.`)
|
||||
|
||||
const referenceCommands = new Map((reference.guiCommands?.commands ?? []).map((command) => [command.id, command]))
|
||||
const referenceCommands = new Map((guiReference.guiCommands?.commands ?? []).map((command) => [command.id, command]))
|
||||
const inventoryCommands = new Map(guiInventory.commands.map((command) => [command.id, command]))
|
||||
if (referenceCommands.size !== guiInventory.commandCount || inventoryCommands.size !== guiInventory.commandCount) fail('GUI command counts or IDs are not unique and aligned.')
|
||||
for (const [id, command] of inventoryCommands) {
|
||||
const referenceCommand = referenceCommands.get(id)
|
||||
if (!referenceCommand) fail(`GUI command ${id} is missing from the desktop oracle.`)
|
||||
if (!Array.isArray(referenceCommand.observations) || referenceCommand.observations.length === 0) fail(`GUI command ${id} has no desktop observations.`)
|
||||
if (referenceCommand.observations.some((observation) => !Object.hasOwn(observation, 'noDocument') || !Object.hasOwn(observation, 'documentNoSelection') || !Object.hasOwn(observation, 'selectedPartBox'))) fail(`GUI command ${id} lacks the complete document/selection state matrix.`)
|
||||
if (command.status !== 'runtime-probed') fail(`GUI command ${id} is not marked runtime-probed.`)
|
||||
for (const state of ['noDocument', 'documentNoSelection', 'selectedPartBox']) if (command.cases?.[state]?.observed !== true || !Array.isArray(command.cases[state].states) || command.cases[state].states.length === 0) fail(`GUI command ${id} inventory lacks ${state} evidence.`)
|
||||
}
|
||||
for (const id of referenceCommands.keys()) if (!inventoryCommands.has(id)) fail(`desktop GUI command ${id} is missing from the generated inventory.`)
|
||||
for (const command of guiInventory.sourceOnlyCommands) if (!command.reason || !Array.isArray(command.sourceModules)) fail(`source-only command ${command.id} lacks an explicit reason.`)
|
||||
const sourceOnlyCommands = new Map(guiInventory.sourceOnlyCommands.map((command) => [command.id, command]))
|
||||
const sourceOnlyCommandDecisions = new Map((boundaries.sourceOnlyCommands ?? []).map((command) => [command.id, command]))
|
||||
if (sourceOnlyCommandDecisions.size !== (boundaries.sourceOnlyCommands ?? []).length) fail('source-only command decisions contain duplicate IDs.')
|
||||
const expectedSourceOnlyCommandIds = [...sourceOnlyCommands.keys()].sort()
|
||||
const decidedSourceOnlyCommandIds = [...sourceOnlyCommandDecisions.keys()].sort()
|
||||
if (JSON.stringify(expectedSourceOnlyCommandIds) !== JSON.stringify(decidedSourceOnlyCommandIds)) fail(`source-only command decisions drifted: expected ${expectedSourceOnlyCommandIds.join(', ')}, received ${decidedSourceOnlyCommandIds.join(', ')}.`)
|
||||
const allowedCommandClassifications = new Set(['legacy-lazy-command', 'optional-addon-command-group', 'dynamic-command-template', 'optional-preference-command-group', 'optional-executable-command', 'context-lazy-command', 'source-template-command'])
|
||||
const exactTaskIds = new Set(exactPlan.programs.flatMap((program) => program.tasks).map((task) => task.id))
|
||||
for (const [id, decision] of sourceOnlyCommandDecisions) {
|
||||
const inventoryCommand = sourceOnlyCommands.get(id)
|
||||
if (!allowedCommandClassifications.has(decision.classification) || !decision.runtimeExpectation || !exactTaskIds.has(decision.exactTask) || !Array.isArray(decision.sourceEvidence) || decision.sourceEvidence.length === 0) fail(`source-only command ${id} lacks a complete classification.`)
|
||||
if (JSON.stringify(decision.sourceModules) !== JSON.stringify(inventoryCommand.sourceModules)) fail(`source-only command ${id} module ownership differs from the generated inventory.`)
|
||||
}
|
||||
|
||||
const runtimeObjects = reference.runtimeObjects
|
||||
if (!runtimeObjects?.available || runtimeObjects.candidateSource !== 'Document.supportedTypes' || !Array.isArray(runtimeObjects.types)) fail('desktop oracle is missing the complete Document.supportedTypes runtime inventory.')
|
||||
@@ -55,8 +76,16 @@ const runtimeObjectCount = runtimeObjects.availableCount
|
||||
const sourceUsageTypeIds = new Set((typeInventory.documentObjectUsages ?? []).map((record) => record.typeId))
|
||||
if (sourceUsageTypeIds.size === 0 || !Array.isArray(typeInventory.typeIdDeclarations)) fail('source TypeId declaration and addObject usage indexes are missing.')
|
||||
const sourceUsageMissingRuntime = [...sourceUsageTypeIds].filter((typeId) => !runtimeTypeIds.has(typeId)).sort()
|
||||
const expectedNotBuilt = reference.modules.filter((module) => module.runtimeStatus === 'not-built').map((module) => module.name).sort()
|
||||
const decidedNotBuilt = (boundaries.notBuiltModules ?? []).map((module) => module.name).sort()
|
||||
if (JSON.stringify(expectedNotBuilt) !== JSON.stringify(decidedNotBuilt)) fail(`not-built module decisions drifted: expected ${expectedNotBuilt.join(', ')}, received ${decidedNotBuilt.join(', ')}.`)
|
||||
for (const module of boundaries.notBuiltModules) if (!module.buildOption || !['proxy', 'browser-reimplementation', 'source-template'].includes(module.decision) || !module.exactTask || !module.boundary || !Array.isArray(module.sourceEvidence) || module.sourceEvidence.length === 0) fail(`not-built module ${module.name} lacks a complete decision.`)
|
||||
const decidedSourceOnlyTypeIds = (boundaries.sourceOnlyTypeIds ?? []).map((entry) => entry.typeId).sort()
|
||||
if (JSON.stringify(sourceUsageMissingRuntime) !== JSON.stringify(decidedSourceOnlyTypeIds)) fail(`source-only TypeId decisions drifted: expected ${sourceUsageMissingRuntime.join(', ')}, received ${decidedSourceOnlyTypeIds.join(', ')}.`)
|
||||
for (const entry of boundaries.sourceOnlyTypeIds) if (!entry.classification || !entry.runtimeExpectation || !Array.isArray(entry.sourceEvidence) || entry.sourceEvidence.length === 0) fail(`source-only TypeId ${entry.typeId} lacks a complete classification.`)
|
||||
const staticObjectCandidateCount = sourceUsageTypeIds.size
|
||||
const moduleStatusCounts = Object.fromEntries([...new Set(reference.modules.map((module) => module.runtimeStatus))].sort().map((status) => [status, reference.modules.filter((module) => module.runtimeStatus === status).length]))
|
||||
const sourceOnlyCommandClassificationCounts = Object.fromEntries([...allowedCommandClassifications].sort().map((classification) => [classification, boundaries.sourceOnlyCommands.filter((command) => command.classification === classification).length]).filter(([, count]) => count > 0))
|
||||
console.log(JSON.stringify({
|
||||
status: 'freecad-oracle-coverage-pass',
|
||||
baseline: { freecadVersion: reference.freecadVersion, commit: reference.gitCommit },
|
||||
@@ -65,6 +94,8 @@ console.log(JSON.stringify({
|
||||
guiCommands: guiInventory.commandCount,
|
||||
guiCommandsRuntimeAligned: true,
|
||||
sourceOnlyCommands: guiInventory.sourceOnlyCommands.length,
|
||||
sourceOnlyCommandsClassified: sourceOnlyCommandDecisions.size,
|
||||
sourceOnlyCommandClassificationCounts,
|
||||
registeredObjectTypeCount: runtimeObjects.candidateCount,
|
||||
runtimeObjectCount,
|
||||
unavailableObjectCount: runtimeObjects.unavailableCount,
|
||||
@@ -73,7 +104,10 @@ console.log(JSON.stringify({
|
||||
sourceDocumentObjectUsageCount: staticObjectCandidateCount,
|
||||
sourceUsageMissingRuntimeCount: sourceUsageMissingRuntime.length,
|
||||
sourceUsageMissingRuntime: sourceUsageMissingRuntime,
|
||||
notBuiltModuleDecisions: boundaries.notBuiltModules.map(({ name, decision, exactTask }) => ({ name, decision, exactTask })),
|
||||
sourceOnlyTypeIdDecisions: boundaries.sourceOnlyTypeIds.map(({ typeId, classification }) => ({ typeId, classification })),
|
||||
sourceOnlyCommandDecisions: boundaries.sourceOnlyCommands.map(({ id, classification, exactTask }) => ({ id, classification, exactTask })),
|
||||
staticObjectCandidateCount,
|
||||
exactPromotionReady: false,
|
||||
remaining: ['not-built modules require an explicit build/proxy decision', 'source-declared TypeId/property candidates outside the registered document-object set require a native API inventory'],
|
||||
remaining: ['task-scoped proxy/browser implementations remain open', 'runtime GUI task, failure, cancel and recovery states require per-command workflow oracles'],
|
||||
}, null, 2))
|
||||
|
||||
Reference in New Issue
Block a user