import { readFile } from 'node:fs/promises' 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([ 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'), ]) 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 (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.') const sourceModules = new Map(sourceInventory.modules.map((module) => [module.name, module])) const typeModules = new Map(typeInventory.modules.map((module) => [module.name, module])) const referenceModules = new Map(reference.modules.map((module) => [module.name, module])) for (const name of sourceModules.keys()) { if (!referenceModules.has(name) || !typeModules.has(name)) fail(`module ${name} is missing from one oracle inventory.`) const runtime = referenceModules.get(name) if (!runtime.runtimeStatus || typeof runtime.available !== 'boolean' || typeof runtime.importable !== 'boolean') fail(`module ${name} lacks explicit runtime status.`) } 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 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 (command.status !== 'runtime-probed') fail(`GUI command ${id} is not marked runtime-probed.`) } 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 runtimeObjectCount = reference.objects?.filter((object) => object.available && object.runtimeTypeId).length ?? 0 const staticObjectCandidateCount = typeInventory.modules.reduce((total, module) => total + module.objects.length, 0) const moduleStatusCounts = Object.fromEntries([...new Set(reference.modules.map((module) => module.runtimeStatus))].sort().map((status) => [status, reference.modules.filter((module) => module.runtimeStatus === status).length])) console.log(JSON.stringify({ status: 'freecad-oracle-coverage-pass', baseline: { freecadVersion: reference.freecadVersion, commit: reference.gitCommit }, modules: reference.moduleCount, moduleStatusCounts, guiCommands: guiInventory.commandCount, guiCommandsRuntimeAligned: true, sourceOnlyCommands: guiInventory.sourceOnlyCommands.length, runtimeObjectCount, staticObjectCandidateCount, exactPromotionReady: false, remaining: ['not-built modules require an explicit build/proxy decision', 'static TypeId/property candidates require per-object runtime probes'], }, null, 2))