import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { execFileSync } from "node:child_process"; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); const docs = path.join(root, "docs/p6"); const readJson = file => JSON.parse(fs.readFileSync(path.join(docs, file), "utf8")); const readCard = file => fs.readFileSync(path.join(root, "docs/tasks/p6/robot", file), "utf8"); const firstSlice = fs.readFileSync(path.join(root, "docs/p6/robot-gate.md"), "utf8"); const action02 = readJson("robot-action-02-persistence.json"); const action04 = readJson("robot-action-04-save-reopen.json"); const action03Card = readCard("FW-P6-ROB-ACT-03-G.md"); const action04Card = readCard("FW-P6-ROB-ACT-04-G.md"); const revision = execFileSync("git", ["-C", path.join(root, "freecad-source"), "rev-parse", "HEAD"], { encoding: "utf8" }).trim(); const actions = [ { id: "FW-P6-ROB-ACT-01", action: "create/recompute", result: /Result:\s*`pass`/.test(firstSlice) ? "pass" : "fail", evidence: ["docs/p6/robot-gate.md", "docs/p6/robot-kernel.json"] }, { id: "FW-P6-ROB-ACT-02", action: "inspect/source-inventory", result: action02.result, applicable: action02.applicable, evidence: ["docs/p6/robot-action-02-persistence.json"] }, { id: "FW-P6-ROB-ACT-03", action: "python-api", result: /结论:`pass_boundary`/.test(action03Card) ? "pass_boundary" : "fail", applicable: false, evidence: ["docs/tasks/p6/robot/FW-P6-ROB-ACT-03-G.md", "docs/p6/robot-action-03-persistence.json"] }, { id: "FW-P6-ROB-ACT-04", action: "save/reopen", result: action04.result, applicable: action04.applicable, evidence: ["docs/p6/robot-action-04-save-reopen.json"] }, ]; const result = actions.every(action => action.result === "pass" || action.result === "pass_boundary"); const summary = { module: "Robot", result: result ? "completed" : "pending", actions: actions.map(({ id, action, result: status, applicable }) => ({ id, action, result: status, ...(applicable === undefined ? {} : { applicable }) })), revision, sourceChanges: ["src/Mod/Robot/App/RobotObject.cpp::RobotObject::Save", "src/Mod/Robot/App/RobotObject.cpp::RobotObject::Restore", "src/WebAssembly/FreeCADCoreProbe/RobotRuntimeProbeFixed.cpp"], boundaries: ["inspect/source-inventory and python-api are read-only and explicitly pass_boundary for persistence.", "save/reopen is limited to the fixed local FCStd fixture; RobotGui and cross-version migration remain out of scope."], evidenceSha256: crypto.createHash("sha256").update(JSON.stringify({ actions, revision })).digest("hex") }; fs.writeFileSync(path.join(docs, "robot-module-gate.json"), `${JSON.stringify({ schema: "freecad-web-p6-robot-module-gate/v1", taskId: "FW-P6-ROB-Y", ...summary }, null, 2)}\n`); const markdown = [ "# Robot module gate", "", `- Result: ${summary.result}`, `- Revision: ${revision}`, `- Actions: create/recompute ${actions[0].result}; inspect/source-inventory ${actions[1].result}; python-api ${actions[2].result}; save/reopen ${actions[3].result}.`, "- Evidence: docs/p6/robot-module-gate.json, docs/p6/robot-action-02-persistence.json, docs/p6/robot-action-04-save-reopen.json.", "- Boundaries: inspect/source-inventory and python-api remain read-only pass-boundary actions; fixed local FCStd only for persistence; RobotGui, arbitrary external paths and cross-version migration are excluded.", "", ].join("\n"); fs.writeFileSync(path.join(docs, "robot-module-gate.md"), markdown); console.log(JSON.stringify({ result: summary.result, actionCount: actions.length, evidenceSha256: summary.evidenceSha256 })); if (!result) process.exitCode = 1;