import assert from "node:assert/strict"; 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 source = path.join(root, "freecad-source"); const evidencePaths = { A: "import-source-boundary.json", B: "import-runtime.json", C: "import-kernel.json", D: "import-recovery.json", E: "import-facade.json", F: "import-chrome.json", G: "import-persistence.json", }; const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex"); const evidence = Object.fromEntries(Object.entries(evidencePaths).map(([card, file]) => { const bytes = fs.readFileSync(path.join(docs, file)); return [card, { path: `docs/p6/${file}`, sha256: sha256(bytes), data: JSON.parse(bytes) }]; })); assert.equal(evidence.A.data.result, "pass_probe_only"); for (const card of ["B", "C", "D", "E", "F"]) assert.equal(evidence[card].data.result, "pass"); assert.equal(evidence.G.data.result, "pass_boundary"); assert.equal(evidence.C.data.deterministic, true); assert.equal(evidence.D.data.statePreserved, true); assert.equal(evidence.E.data.sequencing.maxActive, 1); assert.equal(evidence.E.data.schemaChecks.noNativePointers, true); assert.equal(evidence.F.data.network.externalRequestCount, 0); assert.equal(evidence.F.data.runtimeExceptionCount, 0); assert.equal(evidence.G.data.replayDeterministic, true); assert.equal(evidence.G.data.persistence.applicability, "not_applicable_current_action"); const sourceInputs = Object.entries(evidence.A.data.inputs).map(([file, expectedSha256]) => { const actualSha256 = sha256(fs.readFileSync(path.join(source, file))); assert.equal(actualSha256, expectedSha256, `${file} changed after the source boundary probe`); return { file, expectedSha256, actualSha256, unchanged: true }; }); const report = { schema: "freecad-web-p6-import-gate/v1", module: "Import", slice: "import/step", result: "pass_first_slice_only", taskRange: "FW-P6-IMP-A..FW-P6-IMP-G", sourceRevision: { boundary: evidence.A.data.sourceRevision, current: execFileSync("git", ["-C", source, "rev-parse", "HEAD"], { encoding: "utf8" }).trim(), importInputsUnchanged: sourceInputs.every((input) => input.unchanged), inputs: sourceInputs, }, fixture: evidence.C.data.input, kernelSummary: evidence.C.data.first.summary, stateSha256: evidence.D.data.validSnapshotSha256, chrome: { version: evidence.F.data.chrome.version, mode: evidence.F.data.mode, viewport: evidence.F.data.viewport, externalRequests: evidence.F.data.network.externalRequestCount, runtimeExceptions: evidence.F.data.runtimeExceptionCount, screenshotSha256: evidence.F.data.evidence.screenshotSha256, }, evidence: Object.fromEntries(Object.entries(evidence).map(([card, item]) => [card, { path: item.path, sha256: item.sha256, result: item.data.result }])), covered: ["Import App target initialization", "Import.StepShape registration", "STEP import through ReaderStep and ImportOCAFExt", "deterministic summary", "structured errors and recovery", "typed Facade/Worker", "Google Chrome replay", "resource manifest and fresh-runtime replay"], pending: ["create/recompute action chain", "inspect/source-inventory action chain", "python-api action chain", "save/reopen action chain", "STEP export", "IGES/glTF/DXF/IFC/OBJ format coverage"], moduleStatus: "pending", conclusion: "The source-backed STEP import first slice is complete; the Import module remains pending until all declared action chains pass.", }; fs.writeFileSync(path.join(docs, "import-gate.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(docs, "import-gate.md"), `# Import 首切片 gate\n\n- 结果:\`pass_first_slice_only\`\n- 首切片:\`import/step\`\n- 内核摘要:32 objects / 5 parts / 5 solids / volume 104939.95608486432\n- 状态 SHA-256:\`${report.stateSha256}\`\n- Chrome:${report.chrome.version},${report.chrome.mode},${report.chrome.viewport.width}x${report.chrome.viewport.height},外部请求 0,运行时异常 0\n- 源码复核:A 卡记录的 ${sourceInputs.length} 个 Import 输入 hash 在当前 revision 仍全部一致\n- 模块状态:\`pending\`;本 gate 只关闭 STEP import 首切片\n\n## 未覆盖\n\n${report.pending.map((item) => `- ${item}`).join("\n")}\n`); console.log(JSON.stringify({ result: report.result, evidence: Object.keys(evidence).length, sourceInputs: sourceInputs.length, moduleStatus: report.moduleStatus }));