Files
FreeCADWasmDev/tools/run_p6_part_module_gate.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

78 lines
4.7 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const docs = path.join(root, "docs/p6");
const read = (file) => JSON.parse(fs.readFileSync(path.join(docs, file), "utf8"));
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(path.join(docs, file))).digest("hex");
const sourceRevision = execFileSync("git", ["-C", path.join(root, "freecad-source"), "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
function facadePass(file) {
const value = read(file);
return value.success?.kind === "result"
&& value.expectedError?.kind === "error"
&& value.duplicate?.error?.code === "duplicate_request"
&& value.cancelled?.error?.code === "cancelled";
}
function chromePass(file) {
const value = read(file);
return value.result === "pass"
&& value.mode === "headless-new"
&& value.viewport?.width === 1440
&& value.viewport?.height === 900
&& value.viewport?.deviceScaleFactor === 1
&& value.cases?.length === 5
&& value.cases.every((item) => item.status === "pass" && item.externalRequests.length === 0 && item.exceptions.length === 0);
}
const actionFiles = {
"import/export": ["part-action-02-kernel.json", "part-action-02-recovery.json", "part-action-02-facade.json", "part-action-02-chrome.json", "part-action-02-persistence.json"],
"inspect/source-inventory": ["part-action-03-kernel.json", "part-action-03-recovery.json", "part-action-03-facade.json", "part-action-03-chrome.json", "part-action-03-persistence.json"],
"python-api": ["part-action-04-kernel.json", "part-action-04-recovery.json", "part-action-04-facade.json", "part-action-04-chrome.json", "part-action-04-persistence.json"],
"save/reopen": ["part-action-05-kernel.json", "part-action-05-recovery.json", "part-action-05-facade.json", "part-action-05-chrome.json", "part-action-05-persistence.json"],
};
const actions = {
"create/recompute": { result: read("part-gate.json").status === "pass", artifact: "part-gate.json", sha256: sha256("part-gate.json"), evidence: ["part-gate.json"] },
};
for (const [action, files] of Object.entries(actionFiles)) {
const [kernel, recovery, facade, chrome, persistence] = files;
const pass = read(kernel).status === "pass"
&& read(recovery).status === "pass"
&& facadePass(facade)
&& chromePass(chrome)
&& (read(persistence).status === "pass" || read(persistence).result === "pass");
actions[action] = {
result: pass,
evidence: files.map((file) => ({ path: `docs/p6/${file}`, sha256: sha256(file) })),
};
}
const result = Object.values(actions).every((entry) => entry.result);
const report = {
schema: "freecad-web-p6-part-module-gate/v2",
taskId: "FW-P6-PRT-Y",
module: "Part",
result: result ? "pass" : "fail",
sourceRevision,
moduleStatusBeforeGate: "pending",
moduleStatusAfterGate: result ? "completed" : "pending",
eligibleForCompletion: result,
actions,
boundaries: [
"All five declared Part actions have independent C-G conclusions; create/recompute is covered by the existing first-slice gate.",
"BREP persistence is explicitly not applicable to import/export; inspect/source-inventory and python-api are read-only boundaries.",
"Part save/reopen is closed only for the deterministic Part::Box FCStd fixture; arbitrary paths, GUI workflows and cross-version migration remain out of scope.",
"The standalone PartBoxProbe smoke recovery remains explicit in FW-P6-PRT-ACT-05-G-R1 and does not change the DocumentProbe evidence.",
],
commands: ["node tools/run_p6_part_module_gate.mjs"],
};
fs.writeFileSync(path.join(docs, "part-module-gate.json"), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(path.join(docs, "part-module-gate.md"), `# Part module gate\n\n- Result: \`${report.result}\`\n- Source revision: \`${sourceRevision}\`\n- Declared actions: create/recompute, import/export, inspect/source-inventory, python-api, save/reopen\n- Part status after gate: \`${report.moduleStatusAfterGate}\`\n\nAll five declared actions have independent C-G evidence. Non-applicable persistence boundaries and the PartBoxProbe recovery are explicit.\n`);
fs.writeFileSync(path.join(root, "docs/freecad-p6-part-y-gate.log"), `${JSON.stringify({ result: report.result, actions: Object.fromEntries(Object.entries(actions).map(([key, value]) => [key, value.result])), moduleStatus: report.moduleStatusAfterGate })}\n`);
console.log(JSON.stringify({ result: report.result, actions: Object.keys(actions).length, moduleStatus: report.moduleStatusAfterGate }));
if (!result) process.exitCode = 1;