#!/usr/bin/env node import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const p6 = path.join(root, "docs/p6"); const read = (name) => JSON.parse(fs.readFileSync(path.join(p6, name), "utf8")); const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); const matrix = read("workbench-command-matrix.json"); const native = read("workbench-command-native-smoke.json"); const facade = read("workbench-command-facade.json"); const chrome = read("workbench-command-chrome.json"); const cases = Object.fromEntries((chrome.cases ?? []).map((item) => [item.id, item])); const requiredCases = ["workbench-ready", "workbench-action", "workbench-expected-error", "workbench-cancel", "workbench-reset", "workbench-repeat"]; const externalRequests = (chrome.cases ?? []).flatMap((item) => item.externalRequests ?? []); const exceptions = (chrome.cases ?? []).flatMap((item) => item.exceptions ?? []); const result = matrix.schema === "freecad-web-workbench-command-matrix/v1" && matrix.moduleCount === 32 && matrix.guiModuleCount === 22 && matrix.recordCount === 778 && matrix.dispositionCounts.native_handler === 138 && matrix.dispositionCounts.restricted === 640 && matrix.dispositionCounts.not_implemented === 0 && native.result === "pass" && native.response.count === 778 && facade.result === "pass" && facade.trace?.duplicate?.error?.code === "duplicate_request" && facade.trace?.expectedError?.error?.code === "invalid_request" && facade.trace?.cancelled?.error?.code === "cancelled" && chrome.result === "pass" && requiredCases.every((id) => cases[id]?.status === "pass") && cases["workbench-action"]?.state?.stage === "success" && cases["workbench-action"]?.state?.matrix?.records === 778 && cases["workbench-expected-error"]?.state?.response?.error?.code === "invalid_request" && cases["workbench-cancel"]?.state?.response?.error?.code === "cancelled" && cases["workbench-reset"]?.state?.stage === "reset" && cases["workbench-repeat"]?.state?.stage === "success" && externalRequests.length === 0 && exceptions.length === 0; const report = { schema: "freecad-web-p6-workbench-command-gate/v1", taskId: "FW-P6-FPA-G", result: result ? "pass" : "fail", coverage: { modules: matrix.moduleCount, guiModules: matrix.guiModuleCount, records: matrix.recordCount, dispositions: matrix.dispositionCounts }, artifacts: { matrix: { path: "docs/p6/workbench-command-matrix.json", sha256: sha256(path.join(p6, "workbench-command-matrix.json")) }, native: { path: "docs/p6/workbench-command-native-smoke.json", sha256: sha256(path.join(p6, "workbench-command-native-smoke.json")) }, facade: { path: "docs/p6/workbench-command-facade.json", sha256: sha256(path.join(p6, "workbench-command-facade.json")) }, chrome: { path: "docs/p6/workbench-command-chrome.json", sha256: sha256(path.join(p6, "workbench-command-chrome.json")) }, screenshot: chrome.evidence?.screenshot ?? null, screenshotSha256: chrome.evidence?.screenshotSha256 ?? null, wasm: { path: "web/public/kernel/FreeCADWebWorkbenchCommandProbe.wasm", sha256: sha256(path.join(root, "web/public/kernel/FreeCADWebWorkbenchCommandProbe.wasm")) }, javascript: { path: "web/public/kernel/FreeCADWebWorkbenchCommandProbe.js", sha256: sha256(path.join(root, "web/public/kernel/FreeCADWebWorkbenchCommandProbe.js")) }, }, browser: { version: chrome.chrome?.version ?? null, mode: chrome.mode ?? null, viewport: chrome.viewport ?? null }, cases: requiredCases.map((id) => ({ id, status: cases[id]?.status ?? "missing", stage: cases[id]?.state?.stage ?? null, error: cases[id]?.state?.response?.error?.code ?? null })), boundaries: [ "Desktop Gui/Qt commands are explicit restricted records until a portable Web workflow is backed by its own native route.", "Web command records prove registry routing and dispatcher disposition; route-specific module gates remain authoritative for command behavior.", "Task panels and resources are inventoried and surfaced as source entries; Qt-only interaction remains a declared boundary.", ], }; fs.writeFileSync(path.join(p6, "workbench-command-gate.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(root, "docs/freecad-p6-workbench-command-gate.log"), `${JSON.stringify({ result: report.result, records: matrix.recordCount, native: matrix.dispositionCounts.native_handler, restricted: matrix.dispositionCounts.restricted, externalRequests: externalRequests.length, exceptions: exceptions.length })}\n`); console.log(JSON.stringify({ result: report.result, records: matrix.recordCount, native: matrix.dispositionCounts.native_handler, restricted: matrix.dispositionCounts.restricted, externalRequests: externalRequests.length, exceptions: exceptions.length })); if (!result) process.exitCode = 1;