Files
Web_FreeCAD_Bitbybit/scripts/check-freecad-web-exact-promotion.mjs

61 lines
4.1 KiB
JavaScript

import { access, 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 [plan, compatibility, platformCoverage, sourceInventory, reportSchema] = await Promise.all([
load('config/freecad-web-exact-parity-plan.json'),
load('config/compatibility-matrix.json'),
load('config/platform-module-coverage.json'),
load('config/freecad-source-inventory.json'),
load('config/freecad-web-exact-promotion-report.schema.json'),
])
const fail = (message) => { throw new Error(`FreeCAD Web exact promotion: ${message}`) }
const tasks = plan.programs.flatMap((program) => program.tasks)
const finalTask = tasks.find((task) => task.id === plan.promotionGate.finalTask)
const requiredMetrics = plan.promotionGate.requiredZeroMetrics
const requiredEvidence = plan.promotionGate.requiredEvidenceClasses
if (reportSchema.$schema !== 'https://json-schema.org/draft/2020-12/schema' || reportSchema.properties?.completedTasks?.const !== 52 || reportSchema.properties?.exactModules?.const !== 34) fail('promotion report schema is missing the locked 52/52 and 34/34 contract.')
if (plan.claim?.exact !== true) {
if (!finalTask) fail('EX-REL-01 is missing from the exact-parity plan.')
if (finalTask?.status === 'completed') fail('EX-REL-01 cannot be completed while claim.exact is false.')
if (compatibility.systemExactEvaluation?.exact !== false) fail('compatibility matrix must remain exact=false while promotion is incomplete.')
console.log(JSON.stringify({
status: 'freecad-web-exact-promotion-fail-closed',
systemExact: false,
finalTask: finalTask?.status ?? 'missing',
completedTasks: tasks.filter((task) => task.status === 'completed').length,
requiredTasks: tasks.length,
exactModules: platformCoverage.modules.filter((module) => module.level === 'exact').length,
requiredExactModules: plan.promotionGate.requiredExactModules,
report: 'not-required-until-EX-REL-01-is-requested',
}, null, 2))
process.exit(0)
}
if (!finalTask || finalTask.id !== 'EX-REL-01' || finalTask.status !== 'completed' || finalTask.exactBlockedBy.length !== 0) fail('EX-REL-01 must be completed without blockers before exact can be claimed.')
if (tasks.some((task) => task.status !== 'completed')) fail('all exact-parity tasks must be completed before promotion.')
if (compatibility.systemExactEvaluation?.exact !== true || compatibility.systemExactEvaluation.featureExactCount !== plan.promotionGate.requiredExactModules) fail('compatibility matrix exact evaluation is not fully promoted.')
const exactModules = platformCoverage.modules.filter((module) => module.level === 'exact').map((module) => module.name).sort()
const expectedModules = sourceInventory.modules.map((module) => module.name).sort()
if (JSON.stringify(exactModules) !== JSON.stringify(expectedModules)) fail(`exact module set is incomplete: ${exactModules.length}/${expectedModules.length}.`)
if (compatibility.systemExactEvaluation.blockers?.length !== 0) fail('system exact blockers must be empty at promotion time.')
const reportPath = 'config/freecad-web-exact-promotion-report.json'
await access(resolve(root, reportPath)).catch(() => fail(`${reportPath} is required for EX-REL-01.`))
const report = await load(reportPath)
if (report.schemaVersion !== 1 || report.taskId !== 'EX-REL-01') fail('promotion report schema or task ID is invalid.')
if (report.completedTasks !== tasks.length || report.exactModules !== expectedModules.length) fail('promotion report does not confirm 52/52 tasks and 34/34 exact modules.')
for (const metric of requiredMetrics) if (report.metrics?.[metric] !== 0) fail(`promotion metric ${metric} must be zero.`)
for (const evidence of requiredEvidence) if (!report.evidenceClasses?.includes(evidence)) fail(`promotion report is missing evidence class ${evidence}.`)
console.log(JSON.stringify({
status: 'freecad-web-exact-promotion-pass',
systemExact: true,
completedTasks: report.completedTasks,
exactModules: report.exactModules,
zeroMetrics: requiredMetrics,
evidenceClasses: requiredEvidence,
}, null, 2))