74 lines
4.5 KiB
JavaScript
74 lines
4.5 KiB
JavaScript
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 }));
|