Files
FreeCADWasmDev/tools/run_p6_object_contract_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

71 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
const docs = path.join(root, "docs", "p6");
const matrixPath = path.join(docs, "object-contract-matrix.json");
const matrix = JSON.parse(fs.readFileSync(matrixPath, "utf8"));
const gateNames = [
"geometry-family-gate.json", "cam-generic-family-gate.json", "part-generic-family-gate.json",
"fem-generic-family-gate.json", "mesh-geometry-family-gate.json", "fem-geometry-family-gate.json",
"partdesign-generic-family-gate.json", "robot-generic-family-gate.json",
"cam-geometry-family-gate.json", "import-geometry-family-gate.json",
"inspection-geometry-family-gate.json", "part-geometry-family-gate.json",
"partdesign-geometry-family-gate.json", "points-geometry-family-gate.json",
"robot-geometry-family-gate.json", "sandbox-geometry-family-gate.json",
"sketcher-geometry-family-gate.json", "surface-geometry-family-gate.json",
"measure-object-family-gate.json", "data-property-family-gate.json",
"document-graph-family-gate.json", "viewprovider-family-gate.json",
];
const gated = new Map();
const gatedByName = new Map();
const gateModules = {
"cam-generic-family-gate.json": "CAM", "part-generic-family-gate.json": "Part",
"fem-generic-family-gate.json": "Fem", "partdesign-generic-family-gate.json": "PartDesign",
"robot-generic-family-gate.json": "Robot",
};
for (const name of gateNames) {
const file = path.join(docs, name);
if (!fs.existsSync(file)) continue;
const gate = JSON.parse(fs.readFileSync(file, "utf8"));
for (const record of gate.records ?? []) {
if (record.id) gated.set(record.id, record);
if (record.name) {
const module = gateModules[name];
if (module) gatedByName.set(`${module}:${record.name}`, record);
for (const candidate of matrix.records.filter(item => item.name === record.name)) {
gatedByName.set(`${candidate.module}:${record.name}`, record);
}
}
}
}
const records = matrix.records.map((record) => {
const existing = gated.get(record.id) ?? gatedByName.get(`${record.module}:${record.name}`);
if (existing?.status) return { ...record, status: existing.status, detail: existing.detail ?? null, gate: existing.gate ?? null };
return {
...record,
status: "blocked",
detail: {
code: "native_fixture_required",
message: "No family-specific C-G native object fixture is registered yet",
firstError: true,
},
};
});
const counts = Object.fromEntries(["passed", "passed_boundary", "blocked"].map((status) => [status, records.filter((record) => record.status === status).length]));
const report = {
schema: "freecad-web-p6-object-contract-gate/v1",
taskId: "FW-P6-FPA-D",
result: records.length === matrix.records.length && counts.blocked > 0 ? "pass_with_blocked_records" : records.length === matrix.records.length ? "pass" : "fail",
coverage: { inventoryRecords: matrix.records.length, terminalRecords: records.filter((record) => ["passed", "passed_boundary", "blocked"].includes(record.status)).length, counts },
records,
source: { matrix: "docs/p6/object-contract-matrix.json", matrixSha256: crypto.createHash("sha256").update(fs.readFileSync(matrixPath)).digest("hex"), familyGates: gateNames.filter((name) => fs.existsSync(path.join(docs, name))) },
boundaries: ["blocked records retain source evidence but have no native fixture claim", "GUI-only view providers remain passed_boundary only when a family gate proves the boundary"],
};
fs.writeFileSync(path.join(docs, "object-contract-gate.json"), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(path.join(root, "docs", "freecad-p6-object-contract-d.log"), `result=${report.result}\ninventoryRecords=${report.coverage.inventoryRecords}\nterminalRecords=${report.coverage.terminalRecords}\npassed=${counts.passed}\npassed_boundary=${counts.passed_boundary}\nblocked=${counts.blocked}\n`);
console.log(JSON.stringify({ result: report.result, ...report.coverage }));
if (report.result === "fail") process.exitCode = 1;