286 lines
25 KiB
JavaScript
286 lines
25 KiB
JavaScript
import { readdir, readFile } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const load = async (file) => JSON.parse(await readFile(resolve(root, file), 'utf8'))
|
|
const fail = (message) => { throw new Error(`FreeCAD golden coverage: ${message}`) }
|
|
const lockedCommit = '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d'
|
|
|
|
const plan = await load('config/freecad-golden-coverage-plan.json')
|
|
if (plan.schemaVersion !== 1 || plan.baselineId !== 'freecad-1.1.1' || plan.sourceCommit !== lockedCommit) fail('coverage plan is not locked to FreeCAD 1.1.1.')
|
|
if (plan.parameterMutationPlan !== 'config/freecad-parameter-mutation-plan.json') fail('coverage plan must reference the fail-closed parameter mutation matrix.')
|
|
const parameterMutationPlan = await load(plan.parameterMutationPlan)
|
|
if (parameterMutationPlan.schemaVersion !== 1 || parameterMutationPlan.baselineId !== plan.baselineId || parameterMutationPlan.sourceCommit !== lockedCommit || parameterMutationPlan.failClosed !== true || !Array.isArray(parameterMutationPlan.dimensions) || parameterMutationPlan.dimensions.length !== 5) fail('parameter mutation plan is not locked to the same baseline or five-dimensional fail-closed contract.')
|
|
if (!Array.isArray(plan.dimensions) || new Set(plan.dimensions).size !== plan.dimensions.length) fail('dimensions must be a unique array.')
|
|
if (!Array.isArray(plan.families) || plan.families.length < 30) fail('coverage plan must enumerate every supported Part/PartDesign family.')
|
|
const familyIds = new Set()
|
|
for (const family of plan.families) {
|
|
if (!family || typeof family.id !== 'string' || familyIds.has(family.id)) fail(`invalid or duplicate family ${family?.id || '<unknown>'}.`)
|
|
if (!Array.isArray(family.operationTypes) || family.operationTypes.length === 0) fail(`${family.id} has no operation type mapping.`)
|
|
for (const field of ['stageTypeIds', 'roundtripIds', 'browserOperations']) if (!Array.isArray(family[field])) fail(`${family.id}.${field} must be an array.`)
|
|
if (family.oracleSuccessSources !== undefined && !Array.isArray(family.oracleSuccessSources)) fail(`${family.id}.oracleSuccessSources must be an array.`)
|
|
for (const source of family.oracleSuccessSources || []) if (typeof source?.file !== 'string' || !Array.isArray(source.ids) || source.ids.length === 0 || source.ids.some((id) => typeof id !== 'string')) fail(`${family.id} has an invalid oracle success source.`)
|
|
familyIds.add(family.id)
|
|
}
|
|
|
|
const oracleFiles = [...new Set(plan.families.flatMap((family) => (family.oracleSuccessSources || []).map((source) => source.file)))]
|
|
const oracleReports = new Map(await Promise.all(oracleFiles.map(async (file) => [file, await load(file)])))
|
|
const oracleSuccessCounts = new Map()
|
|
for (const family of plan.families) for (const source of family.oracleSuccessSources || []) {
|
|
const report = oracleReports.get(source.file)
|
|
if (report?.schemaVersion !== 1 || report.status !== 'pass' || report.freecadVersion !== '1.1.1' || report.gitCommit !== lockedCommit || !Array.isArray(report.cases)) fail(`${source.file} is not a locked FreeCAD oracle report.`)
|
|
for (const id of source.ids) {
|
|
const fixture = report.cases.find((candidate) => candidate.id === id)
|
|
if (!fixture || fixture.passed !== true || fixture.status !== 'Valid' || (fixture.typeId && fixture.typeId !== family.id)) fail(`${source.file}/${id} is not valid success evidence for ${family.id}.`)
|
|
oracleSuccessCounts.set(family.id, (oracleSuccessCounts.get(family.id) || 0) + 1)
|
|
}
|
|
}
|
|
|
|
const successManifest = await load(plan.successManifest)
|
|
const failureManifest = await load(plan.failureManifest)
|
|
const supplementalSuccessManifest = await load(plan.supplementalSuccessManifest)
|
|
const supplementalFailureManifest = await load(plan.supplementalFailureManifest)
|
|
const goldenVerification = await load('config/freecad-golden-verification.json')
|
|
const partdesignFailureOracle = await load(plan.partdesignFailureOracle)
|
|
const partdesignRevolutionGrooveOracle = await load(plan.partdesignRevolutionGrooveOracle)
|
|
const partdesignTransformOracle = await load(plan.partdesignTransformOracle)
|
|
const partBuildersOracle = await load(plan.partBuildersOracle)
|
|
if (successManifest.schemaVersion !== 1 || successManifest.baselineId !== plan.baselineId || !Array.isArray(successManifest.scenarios)) fail('success manifest baseline or shape is invalid.')
|
|
if (failureManifest.schemaVersion !== 1 || failureManifest.baselineId !== plan.baselineId || !Array.isArray(failureManifest.failures)) fail('failure manifest baseline or shape is invalid.')
|
|
if (supplementalSuccessManifest.schemaVersion !== 1 || supplementalSuccessManifest.baselineId !== plan.baselineId || !Array.isArray(supplementalSuccessManifest.scenarios)) fail('supplemental success manifest baseline or shape is invalid.')
|
|
if (supplementalFailureManifest.schemaVersion !== 1 || supplementalFailureManifest.baselineId !== plan.baselineId || !Array.isArray(supplementalFailureManifest.failures)) fail('supplemental failure manifest baseline or shape is invalid.')
|
|
if (goldenVerification.schemaVersion !== 1 || goldenVerification.baselineId !== plan.baselineId || goldenVerification.sourceCommit !== lockedCommit) fail('golden verification config is not locked to the same baseline.')
|
|
if (partdesignFailureOracle.schemaVersion !== 1 || partdesignFailureOracle.baselineId !== 'freecad-1.1.1-partdesign-failure-oracle' || partdesignFailureOracle.freecadVersion !== '1.1.1' || partdesignFailureOracle.gitCommit !== lockedCommit || partdesignFailureOracle.status !== 'pass' || !Array.isArray(partdesignFailureOracle.cases) || partdesignFailureOracle.summary?.rejected !== 13 || partdesignFailureOracle.summary?.acceptedEmpty !== 4) fail('PartDesign failure oracle baseline or tri-state summary is invalid.')
|
|
const partdesignFailureCounts = new Map(partdesignFailureOracle.cases.filter((fixture) => fixture.observed === 'rejected' && fixture.passed === true).map((fixture) => [fixture.typeId, 1]))
|
|
if (partdesignRevolutionGrooveOracle.schemaVersion !== 1 || partdesignRevolutionGrooveOracle.baselineId !== 'freecad-1.1.1-partdesign-revolution-groove-oracle' || partdesignRevolutionGrooveOracle.freecadVersion !== '1.1.1' || partdesignRevolutionGrooveOracle.gitCommit !== lockedCommit || partdesignRevolutionGrooveOracle.status !== 'pass' || partdesignRevolutionGrooveOracle.summary?.passed !== 2 || !Array.isArray(partdesignRevolutionGrooveOracle.cases)) fail('PartDesign Revolution/Groove oracle baseline or summary is invalid.')
|
|
const partdesignFeatureSuccessCounts = new Map(partdesignRevolutionGrooveOracle.cases.filter((fixture) => fixture.passed === true && fixture.shapeValid === true && fixture.solids === 1 && fixture.volume > 0).map((fixture) => [fixture.typeId, 1]))
|
|
if (partdesignTransformOracle.schemaVersion !== 1 || partdesignTransformOracle.baselineId !== 'freecad-1.1.1-partdesign-transform-oracle' || partdesignTransformOracle.freecadVersion !== '1.1.1' || partdesignTransformOracle.gitCommit !== lockedCommit || partdesignTransformOracle.status !== 'pass' || partdesignTransformOracle.summary?.successPassed !== 6 || partdesignTransformOracle.summary?.failurePassed !== 4 || partdesignTransformOracle.summary?.rejected !== 4 || partdesignTransformOracle.summary?.acceptedEmpty !== 0 || !Array.isArray(partdesignTransformOracle.cases)) fail('PartDesign transform oracle baseline or failure summary is invalid.')
|
|
const partdesignTransformFailureCounts = new Map(partdesignTransformOracle.cases.filter((fixture) => fixture.passed === true && fixture.observed === 'rejected' && fixture.state?.includes('Invalid')).map((fixture) => [fixture.typeId, 1]))
|
|
if (partBuildersOracle.schemaVersion !== 1 || partBuildersOracle.baselineId !== 'freecad-1.1.1-part-builders-oracle' || partBuildersOracle.freecadVersion !== '1.1.1' || partBuildersOracle.gitCommit !== lockedCommit || partBuildersOracle.status !== 'pass' || partBuildersOracle.summary?.successPassed !== 6 || partBuildersOracle.summary?.failurePassed !== 6 || partBuildersOracle.summary?.rejected !== 6 || partBuildersOracle.summary?.acceptedEmpty !== 0 || !Array.isArray(partBuildersOracle.successCases) || !Array.isArray(partBuildersOracle.failureCases)) fail('Part builders oracle baseline or summary is invalid.')
|
|
const partBuilderSuccessCounts = new Map(partBuildersOracle.successCases.filter((fixture) => fixture.passed === true && fixture.observed === 'success' && fixture.shapeValid === true && fixture.solids === 1).map((fixture) => [fixture.typeId, 1]))
|
|
const partBuilderFailureCounts = new Map(partBuildersOracle.failureCases.filter((fixture) => fixture.passed === true && fixture.observed === 'rejected' && fixture.shapeNull === true).map((fixture) => [fixture.typeId, 1]))
|
|
|
|
const countTopLevelOperations = (manifest, directory, entriesKey) => {
|
|
const counts = new Map()
|
|
return Promise.all(manifest[entriesKey].map(async (entry) => {
|
|
if (!entry || typeof entry.id !== 'string' || typeof entry.file !== 'string') fail(`invalid ${entriesKey} manifest entry.`)
|
|
const fixture = await load(`${directory}/${entry.file}`)
|
|
if (fixture.id !== entry.id || !fixture.operation || typeof fixture.operation.type !== 'string') fail(`${entry.id} has no operation payload.`)
|
|
const type = fixture.operation.type
|
|
counts.set(type, (counts.get(type) || 0) + 1)
|
|
return fixture
|
|
})).then((fixtures) => ({ fixtures, counts }))
|
|
}
|
|
|
|
const [{ fixtures: mainSuccessFixtures, counts: mainSuccessCounts }, { fixtures: mainFailureFixtures, counts: mainFailureCounts }, { fixtures: supplementalSuccessFixtures, counts: supplementalSuccessCounts }, { fixtures: supplementalFailureFixtures, counts: supplementalFailureCounts }] = await Promise.all([
|
|
countTopLevelOperations(successManifest, 'fixtures/freecad-golden', 'scenarios'),
|
|
countTopLevelOperations(failureManifest, 'fixtures/freecad-golden/failures', 'failures'),
|
|
countTopLevelOperations(supplementalSuccessManifest, 'fixtures/freecad-golden/feature-families', 'scenarios'),
|
|
countTopLevelOperations(supplementalFailureManifest, 'fixtures/freecad-golden/feature-families/failures', 'failures'),
|
|
])
|
|
const mergeCounts = (first, second) => {
|
|
const merged = new Map(first)
|
|
for (const [type, count] of second) merged.set(type, (merged.get(type) || 0) + count)
|
|
return merged
|
|
}
|
|
const successFixtures = [...mainSuccessFixtures, ...supplementalSuccessFixtures]
|
|
const failureFixtures = [...mainFailureFixtures, ...supplementalFailureFixtures]
|
|
const successCounts = mergeCounts(mainSuccessCounts, supplementalSuccessCounts)
|
|
const failureCounts = mergeCounts(mainFailureCounts, supplementalFailureCounts)
|
|
if (goldenVerification.successFixtures?.count !== mainSuccessFixtures.length || goldenVerification.failureFixtures?.count !== mainFailureFixtures.length || goldenVerification.supplementalFixtures?.successCount !== supplementalSuccessFixtures.length || goldenVerification.supplementalFixtures?.failureCount !== supplementalFailureFixtures.length) fail('golden verification fixture counts are stale.')
|
|
|
|
const composite = await load(plan.compositeOracle)
|
|
if (composite.schemaVersion !== 1 || composite.baselineId !== 'freecad-1.1.1-composite-history-elementmap2' || composite.freecadVersion !== '1.1.1' || composite.gitCommit !== lockedCommit || composite.status !== 'pass' || !Array.isArray(composite.cases)) fail('composite history oracle baseline is invalid.')
|
|
const stageCounts = new Map()
|
|
const elementMapCaseCounts = new Map()
|
|
let compositeStages = 0
|
|
let nativeEvidenceStages = 0
|
|
let finalShapeOnlyStages = 0
|
|
let opaquePreservedStages = 0
|
|
let ambiguousStages = 0
|
|
let missingEvidenceStages = 0
|
|
let nativeEvidenceValidatedStages = 0
|
|
let nativeMappedNameStages = 0
|
|
let nativeIndexedNameStages = 0
|
|
let indexedNameOnlyStages = 0
|
|
let privateTokenEvidenceRequiredStages = 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))$/
|
|
for (const fixture of composite.cases) {
|
|
if (fixture.status !== 'pass' || !Array.isArray(fixture.stages) || fixture.stages.length === 0) fail(`composite fixture ${fixture.id || '<unknown>'} is incomplete.`)
|
|
const hasElementMap = Object.keys(fixture.elementMapResources || {}).length > 0
|
|
for (const stage of fixture.stages) {
|
|
if (typeof stage.typeId !== 'string' || !stage.typeId) continue
|
|
compositeStages += 1
|
|
stageCounts.set(stage.typeId, (stageCounts.get(stage.typeId) || 0) + 1)
|
|
if (hasElementMap) elementMapCaseCounts.set(stage.typeId, (elementMapCaseCounts.get(stage.typeId) || 0) + 1)
|
|
if (stage.namingEvidenceStatus === 'native-evidence') nativeEvidenceStages += 1
|
|
else if (stage.namingEvidenceStatus === 'final-shape-only') finalShapeOnlyStages += 1
|
|
else if (stage.namingEvidenceStatus === 'opaque-preserved') opaquePreservedStages += 1
|
|
else if (stage.namingEvidenceStatus === 'ambiguous') ambiguousStages += 1
|
|
else missingEvidenceStages += 1
|
|
if (stage.nativeEvidence?.stageId === stage.name && stage.nativeEvidence?.resultObjectId === stage.name && stage.nativeEvidence?.status === stage.namingEvidenceStatus) nativeEvidenceValidatedStages += 1
|
|
if (stage.nativeEvidence?.mappedNameApiEntries > 0) nativeMappedNameStages += 1
|
|
if (stage.nativeEvidence?.indexedNameApiEntries > 0) nativeIndexedNameStages += 1
|
|
if (stage.nativeEvidence?.indexedNameOnly === true) indexedNameOnlyStages += 1
|
|
if (stage.nativeEvidence?.privateTokenEvidenceRequired === true) privateTokenEvidenceRequiredStages += 1
|
|
if (stage.nativeEvidence?.privateTokenEvidenceComplete === true) privateTokenEvidenceCompleteStages += 1
|
|
if (builderStageType.test(stage.typeId || '')) {
|
|
if (stage.nativeEvidence?.internalBuilderEvidence === true) internalBuilderEvidenceStages += 1
|
|
else if (stage.namingEvidenceStatus === 'native-evidence') internalBuilderEvidenceMissingStages += 1
|
|
}
|
|
}
|
|
}
|
|
let elementMapResources = 0
|
|
let stringHasherResources = 0
|
|
for (const fixture of composite.cases) {
|
|
elementMapResources += Object.keys(fixture.elementMapResources || {}).length
|
|
if (fixture.stringHasherResource?.text) stringHasherResources += 1
|
|
}
|
|
|
|
const roundtrip = await load(plan.fcstdRoundtrip)
|
|
if (roundtrip.schemaVersion !== 1 || roundtrip.baselineId !== plan.baselineId || roundtrip.freecadCommit !== lockedCommit || roundtrip.status !== 'verified' || !Array.isArray(roundtrip.scenarios)) fail('FCStd round-trip report baseline or shape is invalid.')
|
|
const roundtripPassIds = new Set(roundtrip.scenarios.filter((scenario) => scenario.status === 'pass' && (!scenario.differences || scenario.differences.length === 0)).map((scenario) => scenario.id))
|
|
|
|
const browserGolden = await load(plan.browserGolden)
|
|
const browserPrimitiveEvidence = await load(plan.browserPrimitiveEvidence)
|
|
if (browserGolden.schemaVersion !== 1 || browserGolden.baselineId !== plan.baselineId || browserGolden.freecadCommit !== lockedCommit || browserGolden.status !== 'pass' || !Array.isArray(browserGolden.reports)) fail('browser golden report baseline or shape is invalid.')
|
|
if (browserPrimitiveEvidence.schemaVersion !== 1 || browserPrimitiveEvidence.status !== 'pass' || browserPrimitiveEvidence.browserId !== 'chrome' || !Array.isArray(browserPrimitiveEvidence.operations) || browserPrimitiveEvidence.afterRelease?.shapeCount !== 0 || browserPrimitiveEvidence.afterRelease?.kernelReferenceCount !== 0) fail('browser primitive evidence is invalid or leaked resources.')
|
|
const browserOperationCounts = new Map()
|
|
for (const report of browserGolden.reports) {
|
|
if (report.status !== 'pass' || (report.differences || []).length !== 0 || typeof report.operation !== 'string') continue
|
|
browserOperationCounts.set(report.operation, (browserOperationCounts.get(report.operation) || 0) + 1)
|
|
}
|
|
for (const operation of ['ellipsoid', 'torus', 'prism', 'wedge']) {
|
|
const probe = browserGolden.productRecompute?.[operation]
|
|
if (probe?.quality?.structuralValid === true && probe.quality.isNull === false && probe.quality.structuralErrors === 0 && probe.quality.solids === 1 && probe.comparison?.status === 'pass' && (probe.comparison.differences || []).length === 0) {
|
|
browserOperationCounts.set(operation, (browserOperationCounts.get(operation) || 0) + 1)
|
|
}
|
|
}
|
|
for (const operation of browserPrimitiveEvidence.operations) {
|
|
if (typeof operation.name !== 'string' || (!(operation.volume > 0) && !(operation.length > 0))) continue
|
|
browserOperationCounts.set(operation.name, (browserOperationCounts.get(operation.name) || 0) + 1)
|
|
}
|
|
|
|
const historyFiles = (await readdir(resolve(root, 'config'))).filter((file) => file.startsWith('chrome-native-') && file.endsWith('-history-verification.json'))
|
|
const nativeOperationByFile = {
|
|
'chrome-native-history-verification.json': ['fuse', 'cut', 'common'],
|
|
'chrome-native-pad-history-verification.json': ['pad'],
|
|
'chrome-native-pocket-history-verification.json': ['pocket'],
|
|
'chrome-native-revolution-history-verification.json': ['revolution'],
|
|
'chrome-native-groove-history-verification.json': ['groove'],
|
|
'chrome-native-loft-history-verification.json': ['loft'],
|
|
'chrome-native-pipe-history-verification.json': ['pipe'],
|
|
'chrome-native-fillet-history-verification.json': ['fillet'],
|
|
'chrome-native-chamfer-history-verification.json': ['chamfer'],
|
|
'chrome-native-hole-history-verification.json': ['hole'],
|
|
'chrome-native-draft-history-verification.json': ['draft'],
|
|
'chrome-native-thickness-history-verification.json': ['thickness'],
|
|
'chrome-native-linear-pattern-history-verification.json': ['linear-pattern'],
|
|
'chrome-native-polar-pattern-history-verification.json': ['polar-pattern'],
|
|
'chrome-native-mirrored-history-verification.json': ['mirrored'],
|
|
'chrome-native-multi-transform-history-verification.json': ['multi-transform'],
|
|
}
|
|
const nativeOperations = new Map()
|
|
for (const file of historyFiles) {
|
|
const report = await load(`config/${file}`)
|
|
if (report.status !== 'pass' || report.nativeCapabilities?.availability !== 'available') continue
|
|
const history = report.history || {}
|
|
const recordCount = Number.isInteger(history.recordCount) ? history.recordCount : report.execution?.recordCount
|
|
if (!Number.isInteger(recordCount) || recordCount <= 0) continue
|
|
for (const operation of nativeOperationByFile[file] || []) nativeOperations.set(operation, (nativeOperations.get(operation) || 0) + 1)
|
|
}
|
|
const nativeAliases = new Map([
|
|
['partdesign-revolution', 'revolution'], ['partdesign-fillet', 'fillet'], ['partdesign-chamfer', 'chamfer'],
|
|
['additive-loft', 'loft'], ['subtractive-loft', 'loft'], ['additive-pipe', 'pipe'], ['subtractive-pipe', 'pipe'],
|
|
])
|
|
const nativeDimensionOperations = new Set(['fuse', 'cut', 'common', 'pad', 'pocket', 'revolution', 'groove', 'loft', 'pipe', 'fillet', 'chamfer', 'hole', 'draft', 'thickness', 'linear-pattern', 'polar-pattern', 'mirrored', 'multi-transform'])
|
|
|
|
const countFor = (family) => {
|
|
const operationTypes = new Set(family.operationTypes)
|
|
const nativeTypes = new Set([...operationTypes].map((type) => nativeAliases.get(type) || type))
|
|
const success = [...operationTypes].reduce((sum, type) => sum + (successCounts.get(type) || 0), 0) + (oracleSuccessCounts.get(family.id) || 0) + (partdesignFeatureSuccessCounts.get(family.id) || 0) + (partBuilderSuccessCounts.get(family.id) || 0)
|
|
const failure = [...operationTypes].reduce((sum, type) => sum + (failureCounts.get(type) || 0), 0) + (partdesignFailureCounts.get(family.id) || 0) + (partdesignTransformFailureCounts.get(family.id) || 0) + (partBuilderFailureCounts.get(family.id) || 0)
|
|
const native = [...nativeTypes].reduce((sum, type) => sum + (nativeOperations.get(type) || 0), 0)
|
|
const stage = family.stageTypeIds.reduce((sum, type) => sum + (stageCounts.get(type) || 0), 0)
|
|
const elementMap = family.stageTypeIds.reduce((sum, type) => sum + (elementMapCaseCounts.get(type) || 0), 0)
|
|
const roundtripCount = family.roundtripIds.filter((id) => roundtripPassIds.has(id)).length
|
|
const browser = family.browserOperations.reduce((sum, type) => sum + (browserOperationCounts.get(type) || 0), 0)
|
|
const required = ['success-fixture', 'failure-fixture']
|
|
if (family.roundtripIds.length > 0) required.push('fcstd-roundtrip')
|
|
if (family.stageTypeIds.length > 0) required.push('composite-stage', 'elementmap2-resource')
|
|
if ([...nativeTypes].some((type) => nativeDimensionOperations.has(type))) required.push('native-history')
|
|
if (family.browserOperations.length > 0) required.push('browser-replay')
|
|
const counts = {
|
|
'success-fixture': success,
|
|
'failure-fixture': failure,
|
|
'native-history': native,
|
|
'composite-stage': stage,
|
|
'elementmap2-resource': elementMap,
|
|
'fcstd-roundtrip': roundtripCount,
|
|
'browser-replay': browser,
|
|
}
|
|
const missing = required.filter((dimension) => counts[dimension] < 1)
|
|
return { id: family.id, required, counts, complete: missing.length === 0, missing }
|
|
}
|
|
|
|
const families = plan.families.map(countFor)
|
|
const completeFamilies = families.filter((family) => family.complete).length
|
|
const blockers = families.filter((family) => !family.complete).map((family) => ({ family: family.id, missing: family.missing }))
|
|
const globalBlockers = []
|
|
if (nativeEvidenceStages === 0) globalBlockers.push('composite oracle has no native-evidence stage; native topology identity remains unavailable')
|
|
if (nativeEvidenceValidatedStages !== nativeEvidenceStages) globalBlockers.push(`${nativeEvidenceStages - nativeEvidenceValidatedStages} native-evidence stages failed runtime evidence identity validation`)
|
|
if (nativeIndexedNameStages !== nativeEvidenceStages) globalBlockers.push(`${nativeEvidenceStages - nativeIndexedNameStages} native-evidence stages have no direct FreeCAD indexed-name identity`)
|
|
if (privateTokenEvidenceCompleteStages < privateTokenEvidenceRequiredStages) globalBlockers.push(`${privateTokenEvidenceRequiredStages - privateTokenEvidenceCompleteStages} native builder stages do not have complete private MappedName token evidence`)
|
|
if (internalBuilderEvidenceMissingStages > 0) globalBlockers.push(`${internalBuilderEvidenceMissingStages} composite builder stages have no native intermediate builder evidence; final geometry cannot reconstruct private intermediate history`)
|
|
globalBlockers.push('Production private naming ABI is available and non-unique isomorphic provenance is fail-closed; exhaustive cross-feature mutation, FCStd round-trip closure and complete native document/property semantics remain open')
|
|
if (missingEvidenceStages > 0) globalBlockers.push(`${missingEvidenceStages} composite stages have missing naming evidence status`)
|
|
if (elementMapResources === 0) globalBlockers.push('composite oracle has no ElementMap2 resources')
|
|
if (stringHasherResources === 0) globalBlockers.push('composite oracle has no StringHasher resources')
|
|
const sortedObject = (map) => Object.fromEntries([...map.entries()].sort(([left], [right]) => left.localeCompare(right)))
|
|
const detailed = process.argv.includes('--details')
|
|
const result = {
|
|
status: 'freecad-golden-coverage-pass',
|
|
baseline: { id: plan.baselineId, commit: plan.sourceCommit },
|
|
exactPromotionReady: false,
|
|
totals: {
|
|
successFixtures: successFixtures.length,
|
|
failureFixtures: failureFixtures.length,
|
|
primarySuccessFixtures: mainSuccessFixtures.length,
|
|
primaryFailureFixtures: mainFailureFixtures.length,
|
|
supplementalSuccessFixtures: supplementalSuccessFixtures.length,
|
|
supplementalFailureFixtures: supplementalFailureFixtures.length,
|
|
compositeCases: composite.cases.length,
|
|
compositeStages,
|
|
nativeEvidenceStages,
|
|
nativeEvidenceValidatedStages,
|
|
nativeIndexedNameStages,
|
|
nativeMappedNameStages,
|
|
indexedOnlyStages: indexedNameOnlyStages,
|
|
privateTokenEvidenceRequiredStages,
|
|
privateTokenEvidenceCompleteStages,
|
|
internalBuilderEvidenceStages,
|
|
internalBuilderEvidenceMissingStages,
|
|
finalShapeOnlyStages,
|
|
opaquePreservedStages,
|
|
ambiguousStages,
|
|
missingEvidenceStages,
|
|
elementMapResources,
|
|
stringHasherResources,
|
|
roundtripScenarios: roundtrip.scenarios.length,
|
|
roundtripPassScenarios: roundtripPassIds.size,
|
|
browserGoldenReports: browserGolden.reports.length,
|
|
browserPrimitiveOperations: browserPrimitiveEvidence.operations.length,
|
|
nativeOracleSuccessCases: [...oracleSuccessCounts.values(), ...partdesignFeatureSuccessCounts.values(), ...partBuilderSuccessCounts.values()].reduce((sum, count) => sum + count, 0),
|
|
nativeOracleFailureCases: [...partdesignFailureCounts.values(), ...partdesignTransformFailureCounts.values(), ...partBuilderFailureCounts.values()].reduce((sum, count) => sum + count, 0),
|
|
},
|
|
operationCounts: { success: sortedObject(successCounts), failure: sortedObject(failureCounts), oracleSuccess: sortedObject(new Map([...oracleSuccessCounts, ...partdesignFeatureSuccessCounts, ...partBuilderSuccessCounts])), oracleFailure: sortedObject(new Map([...partdesignFailureCounts, ...partdesignTransformFailureCounts, ...partBuilderFailureCounts])), nativeHistory: sortedObject(nativeOperations), browserReplay: sortedObject(browserOperationCounts) },
|
|
families: { total: families.length, complete: completeFamilies, incomplete: families.length - completeFamilies, blockerCount: blockers.length },
|
|
missingDimensions: Object.fromEntries([...new Set(blockers.flatMap(({ missing }) => missing))].sort().map((dimension) => [dimension, blockers.filter((family) => family.missing.includes(dimension)).length])),
|
|
blockers: globalBlockers,
|
|
}
|
|
if (detailed) result.familyBlockers = blockers
|
|
console.log(JSON.stringify(result, null, 2))
|