78 lines
4.7 KiB
JavaScript
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;
|