feat: advance FreeCAD parity verification and runtime boundaries
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled

This commit is contained in:
2026-08-11 02:14:45 -04:00
parent 26ea9b57d7
commit 2be18a511e
31 changed files with 451 additions and 57 deletions

View File

@@ -0,0 +1,13 @@
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const report = JSON.parse(await readFile(resolve(root, 'config/browser-matrix-verification.json'), 'utf8'))
const expected = ['firefox', 'webkit']
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.browsers?.length !== expected.length) throw new Error('Browser matrix report is incomplete.')
for (const engine of expected) {
const entry = report.browsers.find((candidate) => candidate.engine === engine)
const persistencePass = engine === 'firefox' ? entry?.opfs === true && entry?.persistence === 'opfs' : entry && ['opfs', 'memory-fallback'].includes(entry.persistence)
if (!entry || entry.status !== 'pass' || entry.pageErrors?.length !== 0 || entry.shell !== true || entry.workspace !== true || entry.crossOriginIsolated !== true || entry.sharedArrayBuffer !== true || !persistencePass || entry.workbench !== 'Part Design' || entry.canvas?.geometrySource !== 'bitbybit-occt' || entry.canvas.width < 100 || entry.canvas.height < 100 || entry.screenshotBytes < 10_000) throw new Error(`Browser matrix ${engine} evidence is incomplete.`)
}
console.log(JSON.stringify({ status: 'browser-matrix-pass', browsers: report.browsers.map(({ engine, userAgent, persistence, canvas }) => ({ engine, userAgent, persistence, canvas })) }, null, 2))

View File

@@ -21,7 +21,8 @@ async function walk(relative = '') {
}
const violations = []
for (const file of await walk()) {
const sourceFiles = await walk()
for (const file of sourceFiles) {
if (allowDirectRuntime.has(file)) continue
const content = await readFile(new URL(file, sourceRoot), 'utf8')
for (const pattern of forbidden) {
@@ -30,9 +31,18 @@ for (const file of await walk()) {
}
}
const productionFacade = await readFile(new URL('facade/productionFacade.ts', sourceRoot), 'utf8')
const application = await readFile(new URL('App.tsx', sourceRoot), 'utf8')
const runtimeProfile = await readFile(new URL('facade/runtimeProfile.ts', sourceRoot), 'utf8')
if (/from\s+["']\.\/mockFacade["']/.test(productionFacade)) violations.push('facade/productionFacade.ts: production entry imports mockFacade directly')
if (/createMockFacade|facade\/mockFacade/.test(application)) violations.push('App.tsx: production application references the mock Facade')
for (const declaration of ['bitbybit-occt', 'optional-worker', 'not-exposed', 'sqlite-opfs-with-memory-fallback', 'three-webgl2']) {
if (!runtimeProfile.includes(declaration)) violations.push(`facade/runtimeProfile.ts: missing explicit runtime boundary '${declaration}'`)
}
if (violations.length) {
console.error('Facade-only boundary failed:')
violations.forEach((violation) => console.error(`- ${violation}`))
process.exit(1)
}
console.log(`Facade-only boundary passed (${(await walk()).length} TypeScript source files checked).`)
console.log(`Facade-only boundary passed (${sourceFiles.length} TypeScript source files checked).`)

View File

@@ -11,7 +11,8 @@ const golden = await load('config/chrome-fcstd-golden-verification.json')
const secondary = await load('config/chrome-secondary-formats-verification.json')
const fail = (message) => { throw new Error(`FCStd closure: ${message}`) }
if (native.status !== 'verified' || native.baselineId !== 'freecad-1.1.1' || native.freecadCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || native.scenarioCount !== 22 || native.scenarios?.length !== 22 || native.unknownDifferencesFail !== true || JSON.stringify(native.directions) !== JSON.stringify(['freecad-web-freecad', 'web-freecad-web'])) fail('native round-trip baseline or direction matrix is incomplete.')
const nativeScenarioCount = native.scenarioCount
if (native.status !== 'verified' || native.baselineId !== 'freecad-1.1.1' || native.freecadCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || !Number.isSafeInteger(nativeScenarioCount) || nativeScenarioCount < 23 || native.scenarios?.length !== nativeScenarioCount || native.unknownDifferencesFail !== true || JSON.stringify(native.directions) !== JSON.stringify(['freecad-web-freecad', 'web-freecad-web'])) fail('native round-trip baseline or direction matrix is incomplete.')
if (native.scenarios.some((scenario) => scenario.status !== 'pass' || scenario.differences?.length !== 0 || scenario.domains?.length === 0 || scenario.evidence === undefined)) fail('native round-trip contains an unknown difference or missing evidence.')
const proxy = native.scenarios.find((scenario) => scenario.id === 'proxy-byte-preservation')?.evidence
if (!proxy || proxy.byteIdentical !== true || proxy.sourceArchiveBytes !== proxy.preservedArchiveBytes || proxy.sourceSha256 !== proxy.preservedSha256 || proxy.proxyObjectCount !== 8 || proxy.resavedProxyObjectCount !== 8) fail('unknown/proxy archive bytes were not preserved.')
@@ -28,4 +29,4 @@ if (secondary.status !== 'pass' || secondary.formats?.descriptors !== formats.le
const hardening = spawnSync('./npmw', ['run', 'check:fcstd-hardening'], { cwd: root, encoding: 'utf8', timeout: 120_000 })
if (hardening.status !== 0) fail(`FC-11 hardening suite failed: ${(hardening.stdout || '') + (hardening.stderr || '')}`)
console.log(JSON.stringify({ status: 'fcstd-closure-pass', nativeScenarios: native.scenarioCount, semanticObjects: semantic.objectNames.length, goldenModels: golden.scenarioCount, formatMatrix: secondary.formats.matrix, chromeDirections: chromeRoundTrip.directions, hardening: 'pass' }, null, 2))
console.log(JSON.stringify({ status: 'fcstd-closure-pass', nativeScenarios: nativeScenarioCount, semanticObjects: semantic.objectNames.length, goldenModels: golden.scenarioCount, formatMatrix: secondary.formats.matrix, chromeDirections: chromeRoundTrip.directions, hardening: 'pass' }, null, 2))

View File

@@ -6,7 +6,7 @@ const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
const [
fcstdFuzz, geometryFuzz, sketchFuzz, successFixtures, failureFixtures, supplementalSuccessFixtures, supplementalFailureFixtures,
sketchOracle, partdesignBase, partdesignLoft, partdesignDressup, partdesignTransform, partdesignFailures, partdesignRevolutionGroove, partBuilders,
fcstdRoundTrip, app, performance, fault, opfsMigration, security, qa08, addon, script,
fcstdRoundTrip, app, browserMatrix, performance, fault, opfsMigration, security, qa08, addon, script,
] = await Promise.all([
load('config/qa04-fcstd-fuzz-verification.json'),
load('config/qa04-geometry-fuzz-verification.json'),
@@ -25,6 +25,7 @@ const [
load('config/freecad-part-builders-oracle.json'),
load('config/freecad-fcstd-roundtrip-verification.json'),
load('config/chrome-app-e2e-verification.json'),
load('config/browser-matrix-verification.json'),
load('config/chrome-performance-verification.json'),
load('config/chrome-fault-injection-verification.json'),
load('config/chrome-opfs-migration-verification.json'),
@@ -46,10 +47,11 @@ if (partdesignCases !== 23 || [partdesignBase, partdesignLoft, partdesignDressup
if (partdesignFailures.status !== 'pass' || partdesignFailures.freecadVersion !== '1.1.1' || partdesignFailures.summary?.cases !== 17 || partdesignFailures.summary.passed !== 17 || partdesignFailures.summary.rejected !== 13 || partdesignFailures.summary.acceptedEmpty !== 4 || partdesignFailures.summary.accepted !== 0) throw new Error('QA-02 PartDesign failure oracle evidence is incomplete.')
if (partdesignRevolutionGroove.status !== 'pass' || partdesignRevolutionGroove.freecadVersion !== '1.1.1' || partdesignRevolutionGroove.summary?.cases !== 2 || partdesignRevolutionGroove.summary.passed !== 2 || partdesignRevolutionGroove.cases?.some((fixture) => fixture.shapeNull !== false || fixture.shapeValid !== true || fixture.solids !== 1)) throw new Error('QA-02 PartDesign Revolution/Groove oracle evidence is incomplete.')
if (partBuilders.status !== 'pass' || partBuilders.freecadVersion !== '1.1.1' || partBuilders.summary?.successCases !== 6 || partBuilders.summary.successPassed !== 6 || partBuilders.summary.failureCases !== 6 || partBuilders.summary.failurePassed !== 6 || partBuilders.summary.rejected !== 6 || partBuilders.summary.acceptedEmpty !== 0) throw new Error('QA-02 Part builders oracle evidence is incomplete.')
if (fcstdRoundTrip.status !== 'verified' || fcstdRoundTrip.scenarioCount !== 22 || fcstdRoundTrip.scenarios?.some((scenario) => scenario.status !== 'pass' || scenario.differences?.length !== 0)) throw new Error('QA-02 FCStd oracle evidence is incomplete.')
if (fcstdRoundTrip.status !== 'verified' || !Number.isSafeInteger(fcstdRoundTrip.scenarioCount) || fcstdRoundTrip.scenarioCount < 23 || fcstdRoundTrip.scenarios?.length !== fcstdRoundTrip.scenarioCount || fcstdRoundTrip.scenarios?.some((scenario) => scenario.status !== 'pass' || scenario.differences?.length !== 0)) throw new Error('QA-02 FCStd oracle evidence is incomplete.')
const requiredAppWorkflows = ['start-to-workspace', 'freecad-menus', 'command-search-dialog', 'preferences-about-dialogs', 'model-tree-context-menu', 'workbench-change', 'create-sketch-command', 'loft-section-task', 'drawer-collapse', 'cam-workbench-job-flow', 'opfs-save-reopen', 'import-format-selection', 'export-format-selection']
if (app.status !== 'pass' || requiredAppWorkflows.some((workflow) => !app.workflows?.includes(workflow)) || app.pageErrors?.length !== 0 || app.desktop?.kernelPreviewSource !== 'bitbybit-occt' || app.desktop.canvas?.uniqueColors < 4 || app.desktop?.commandGroups < 4 || app.desktop?.selectionView !== true || app.desktop?.statusBar !== true || app.uiParity?.commandDialog?.modal !== true || app.uiParity?.preferencesDialog?.modal !== true || app.uiParity?.aboutDialog?.modal !== true || app.uiParity?.contextMenu?.items?.length !== 4 || app.uiParity?.camMenu?.items !== 60 || app.cam?.ui?.generated !== true || app.cam.ui.toolbarCommands !== 60 || app.cam.ui.toolpathPoints < 5 || app.mobile?.viewport?.width !== 390 || Object.keys(app.screenshots ?? {}).length !== 3) throw new Error('QA-03 production Chrome E2E evidence is incomplete.')
if (browserMatrix.status !== 'pass' || browserMatrix.browsers?.length !== 2 || !['firefox', 'webkit'].every((engine) => browserMatrix.browsers.some((entry) => entry.engine === engine && entry.status === 'pass' && entry.pageErrors?.length === 0 && entry.canvas?.geometrySource === 'bitbybit-occt'))) throw new Error('QA-03 non-Chrome browser matrix evidence is incomplete.')
if (fcstdFuzz.status !== 'fcstd-parser-fuzz-pass' || fcstdFuzz.cases !== 1000 || fcstdFuzz.counts?.accepted + fcstdFuzz.counts?.rejected !== 1000 || Object.keys(fcstdFuzz.categories ?? {}).length !== 13 || fcstdFuzz.timing?.p95Ms >= 50) throw new Error('QA-04 FCStd fuzz evidence is incomplete.')
if (geometryFuzz.status !== 'geometry-input-fuzz-pass' || geometryFuzz.cases !== 2000 || geometryFuzz.counts?.accepted + geometryFuzz.counts?.rejected !== 2000 || Object.keys(geometryFuzz.categories ?? {}).length !== 30 || geometryFuzz.timing?.p95Ms >= 10) throw new Error('QA-04 geometry fuzz evidence is incomplete.')
@@ -66,7 +68,8 @@ console.log(JSON.stringify({
tasks: 8,
unitSuites: testFiles.length,
unitTests: testCases,
freecad: { successFixtures: 105, failureFixtures: 56, primarySuccessFixtures: 100, primaryFailureFixtures: 51, supplementalSuccessFixtures: 5, supplementalFailureFixtures: 5, sketchConstraints: 19, partdesignCases, partdesignFailureCases: partdesignFailures.summary.cases, partdesignRejectedFailures: partdesignFailures.summary.rejected, partdesignAcceptedEmpty: partdesignFailures.summary.acceptedEmpty, partdesignRevolutionGrooveCases: partdesignRevolutionGroove.summary.cases, partBuilderSuccessCases: partBuilders.summary.successCases, partBuilderFailureCases: partBuilders.summary.failureCases, fcstdRoundTrips: 22 },
freecad: { successFixtures: 105, failureFixtures: 56, primarySuccessFixtures: 100, primaryFailureFixtures: 51, supplementalSuccessFixtures: 5, supplementalFailureFixtures: 5, sketchConstraints: 19, partdesignCases, partdesignFailureCases: partdesignFailures.summary.cases, partdesignRejectedFailures: partdesignFailures.summary.rejected, partdesignAcceptedEmpty: partdesignFailures.summary.acceptedEmpty, partdesignRevolutionGrooveCases: partdesignRevolutionGroove.summary.cases, partBuilderSuccessCases: partBuilders.summary.successCases, partBuilderFailureCases: partBuilders.summary.failureCases, fcstdRoundTrips: fcstdRoundTrip.scenarioCount },
fuzz: { fcstd: fcstdFuzz.cases, geometry: geometryFuzz.cases, solverModels: sketchFuzz.models },
chrome: { workflows: app.workflows.length, screenshots: Object.keys(app.screenshots).length, accessibilityNodes: app.screenReader.nodes },
browsers: Object.fromEntries(browserMatrix.browsers.map((entry) => [entry.engine, { status: entry.status, persistence: entry.persistence, geometrySource: entry.canvas.geometrySource }])),
}, null, 2))

View File

@@ -0,0 +1,90 @@
import { createReadStream, existsSync } from 'node:fs'
import { writeFile } from 'node:fs/promises'
import { createServer } from 'node:http'
import { extname, normalize, resolve } from 'node:path'
import { firefox, webkit } from '../cnc_wams_gpt6/linuxcnc-master/web/node_modules/playwright/index.mjs'
const root = resolve(import.meta.dirname, '..')
const dist = resolve(root, 'dist')
const reportPath = resolve(root, 'config/browser-matrix-verification.json')
if (!existsSync(resolve(dist, 'index.html'))) throw new Error('Browser matrix requires a production build in dist/. Run npm run build first.')
const contentTypes = {
'.css': 'text/css; charset=utf-8',
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.wasm': 'application/wasm',
}
const server = createServer((request, response) => {
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
response.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
const requestPath = decodeURIComponent((request.url || '/').split('?')[0])
const candidate = requestPath === '/' ? resolve(dist, 'index.html') : normalize(resolve(dist, `.${requestPath}`))
const file = candidate.startsWith(dist) && existsSync(candidate) ? candidate : resolve(dist, 'index.html')
response.setHeader('Content-Type', contentTypes[extname(file)] || 'application/octet-stream')
createReadStream(file).on('error', (error) => { response.writeHead(500); response.end(String(error)) }).pipe(response)
})
await new Promise((resolveServer, rejectServer) => {
server.once('error', rejectServer)
server.listen(0, '127.0.0.1', resolveServer)
})
const baseUrl = `http://127.0.0.1:${server.address().port}`
const requested = process.argv.find((argument) => argument.startsWith('--browser='))?.slice('--browser='.length)
const engines = requested ? requested.split(',') : ['firefox', 'webkit']
const browserTypes = { firefox, webkit }
const executablePaths = { firefox: process.env.FIREFOX_BIN, webkit: process.env.WEBKIT_BIN }
const results = []
try {
for (const engine of engines) {
const browserType = browserTypes[engine]
if (!browserType) throw new Error(`Unsupported browser matrix engine: ${engine}`)
const browser = await browserType.launch({ headless: true, ...(executablePaths[engine] ? { executablePath: executablePaths[engine] } : {}) })
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } })
const pageErrors = []
page.on('pageerror', (error) => pageErrors.push(error.message))
try {
await page.goto(`${baseUrl}/workspace/pump-housing`, { waitUntil: 'domcontentloaded', timeout: 60_000 })
await page.waitForSelector('.app-shell', { timeout: 30_000 })
await page.waitForFunction(() => document.querySelector('.three-viewport-host canvas')?.dataset.geometrySource === 'bitbybit-occt', undefined, { timeout: 120_000 })
const state = await page.evaluate(async () => {
const canvas = document.querySelector('.three-viewport-host canvas')
let opfs = false
try { opfs = Boolean(await navigator.storage?.getDirectory?.()) } catch { opfs = false }
return {
userAgent: navigator.userAgent,
crossOriginIsolated,
sharedArrayBuffer: typeof SharedArrayBuffer === 'function',
opfs,
persistence: opfs ? 'opfs' : 'memory-fallback',
shell: Boolean(document.querySelector('.app-shell')),
workspace: Boolean(document.querySelector('.workspace-page')),
workbench: document.querySelector('select[aria-label="Workbench"]')?.value || '',
canvas: {
width: canvas?.width || 0,
height: canvas?.height || 0,
geometrySource: canvas?.dataset.geometrySource || '',
},
}
})
const screenshot = await page.screenshot({ type: 'png' })
const persistencePass = engine === 'firefox' ? state.opfs && state.persistence === 'opfs' : ['opfs', 'memory-fallback'].includes(state.persistence)
const pass = pageErrors.length === 0 && state.shell && state.workspace && state.crossOriginIsolated && state.sharedArrayBuffer && persistencePass && state.workbench === 'Part Design' && state.canvas.width >= 100 && state.canvas.height >= 100 && state.canvas.geometrySource === 'bitbybit-occt' && screenshot.byteLength > 10_000
results.push({ engine, status: pass ? 'pass' : 'fail', ...state, screenshotBytes: screenshot.byteLength, pageErrors })
} catch (error) {
results.push({ engine, status: 'fail', pageErrors: [...pageErrors, error instanceof Error ? error.message : String(error)] })
} finally {
await browser.close()
}
}
} finally {
await new Promise((resolveClose) => server.close(resolveClose))
}
const report = { schemaVersion: 1, generatedAt: new Date().toISOString(), status: results.every((entry) => entry.status === 'pass') ? 'pass' : 'fail', browsers: results }
await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`)
console.log(JSON.stringify(report, null, 2))
if (report.status !== 'pass') throw new Error('Non-Chrome browser matrix failed.')

View File

@@ -13,6 +13,15 @@ const validLanes = new Set(['all', 'chrome', 'oracle', 'wasm'])
if (!validLanes.has(requestedLane)) {
throw new Error(`Unknown lane "${requestedLane}". Expected one of: ${[...validLanes].join(', ')}`)
}
const phaseArg = process.argv.find((arg) => arg.startsWith('--phase='))
const requestedPhase = phaseArg ? phaseArg.slice('--phase='.length) : 'all'
const validPhases = new Set(['all', 'execute', 'check'])
if (!validPhases.has(requestedPhase)) {
throw new Error(`Unknown phase "${requestedPhase}". Expected one of: ${[...validPhases].join(', ')}`)
}
if (requestedPhase !== 'all' && requestedLane !== 'chrome') {
throw new Error(`Phase "${requestedPhase}" is currently supported only for the chrome lane.`)
}
const chromeTests = Object.keys(scripts)
.filter((name) => name.startsWith('test:chrome-'))
@@ -92,6 +101,17 @@ const lanes = {
}
const selectedLanes = requestedLane === 'all' ? ['oracle', 'wasm', 'chrome'] : [requestedLane]
const chromeExecution = ['build', ...chromeTests]
const chromeChecks = [
...chromeTests
.map((name) => `check:${name.slice('test:'.length)}`)
.filter((name) => scripts[name]),
'check:freecad-tsn-stage-evidence',
]
const commandsFor = (lane) => {
if (lane !== 'chrome' || requestedPhase === 'all') return lanes[lane]
return requestedPhase === 'execute' ? chromeExecution : chromeChecks
}
const results = []
function runScript(name, laneEnvironment = {}) {
@@ -118,12 +138,13 @@ try {
FREECAD_REFERENCE_CMD: resolve(root, '.cache/freecad/install-desktop/bin/FreeCAD'),
}
: {}
for (const name of lanes[lane]) runScript(name, laneEnvironment)
for (const name of commandsFor(lane)) runScript(name, laneEnvironment)
}
} finally {
const outputDir = resolve(root, '.cache', 'ci')
mkdirSync(outputDir, { recursive: true })
writeFileSync(resolve(outputDir, 'real-verification.json'), `${JSON.stringify({ generatedAt: new Date().toISOString(), lanes: selectedLanes, results }, null, 2)}\n`)
const summaryName = requestedPhase === 'all' ? 'real-verification.json' : `real-verification-${requestedPhase}.json`
writeFileSync(resolve(outputDir, summaryName), `${JSON.stringify({ generatedAt: new Date().toISOString(), lanes: selectedLanes, phase: requestedPhase, results }, null, 2)}\n`)
}
console.log(`\n[real-verification] completed ${results.length} commands across ${selectedLanes.join(', ')}.`)
console.log(`\n[real-verification] completed ${results.length} ${requestedPhase} commands across ${selectedLanes.join(', ')}.`)