import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const projectRoot = resolve(repoRoot, "web-rtcp-5axis-xyzbc-trt-sim-plan"); const nativePath = process.argv[2] || resolve(projectRoot, "working/evidence/native-xyzbc-trt-evidence.json"); const webPath = process.argv[3] || resolve(projectRoot, "working/evidence/web-xyzbc-trt-evidence.json"); const outputPath = process.argv[4] || resolve(projectRoot, "working/evidence/compare-xyzbc-trt-evidence.json"); const nativeEvidence = JSON.parse(await readFile(nativePath, "utf8")); const webEvidence = JSON.parse(await readFile(webPath, "utf8")); const checks = [ check("profile", "native axis mask is XYZBC", nativeEvidence.coverage?.axisProfile === true, { nativeAxisMask: nativeEvidence.after?.axisMask ?? nativeEvidence.before?.axisMask, }), check("profile", "web default profile is xyzbc-trt", webEvidence.coverage?.profileDefaultXyzbc === true, { webProfile: webEvidence.profile?.id, }), check("ini", "native loaded xyzbc switchkins program", nativeEvidence.coverage?.xyzbcProgramOpen === true, { nativeFile: nativeEvidence.after?.file ?? nativeEvidence.before?.file, }), check("ini", "web INI parse is ready", webEvidence.coverage?.iniReady === true, { webIniReady: webEvidence.ini?.ready, }), check("switchkins", "native switchkins pin readable", nativeEvidence.coverage?.switchkinsPinReadable === true, { nativePin: nativeEvidence.hal?.pins?.["motion.switchkins-type"], }), check("switchkins", "web remap files staged", webEvidence.coverage?.remapsStaged === true, { remaps: webEvidence.ini?.remaps, }), check("program", "web default xyzbc_switchkins program staged", webEvidence.coverage?.defaultProgramStaged === true, { selected: webEvidence.opfsStaging?.selectedProgram, }), check("tool-table", "web tool table staged", webEvidence.coverage?.toolTableStaged === true, { toolTable: webEvidence.ini?.toolTable, }), check("pyvcp", "web PyVCP XML staged", webEvidence.coverage?.pyvcpXmlStaged === true, { pyvcpXmlPath: webEvidence.profile?.pyvcpXmlPath, }), check("positions", "native joint feedback readable", nativeEvidence.coverage?.jointFeedbackReadable === true, { joints: nativeEvidence.after?.jointActualPosition, }), check("wasm", "required WASM artifacts available", webEvidence.coverage?.wasmArtifactsReady === true, { missing: webEvidence.wasm?.missing, }), check("parameters", "parameter file staged", webEvidence.coverage?.parameterFileStaged === true, { parameterFile: webEvidence.ini?.parameterFile, }), ]; const failed = checks.filter((item) => item.status !== "pass"); const blockers = [ ...(nativeEvidence.status === "blocked" ? nativeEvidence.blocker ? [nativeEvidence.blocker] : ["native-blocked"] : []), ...(webEvidence.blockers || []).map((blocker) => blocker.id), ]; const report = { apiName: "xyzbc-trt-native-web-evidence-comparison", status: failed.length === 0 ? "pass" : "fail", comparedAt: new Date().toISOString(), nativePath, webPath, summary: { checkCount: checks.length, passCount: checks.length - failed.length, failCount: failed.length, blockers, nativeStatus: nativeEvidence.status, webStatus: webEvidence.status, }, checks, requiredImprovements: failed.map((item) => ({ category: item.category, requirement: item.requirement, evidence: item.evidence, })), semanticBoundary: "native_linuxcnc_vs_web_opfs_wasm_xyzbc_trt_evidence_comparison", }; await mkdir(dirname(outputPath), { recursive: true }); await writeFile(outputPath, JSON.stringify(report, null, 2) + "\n", "utf8"); console.log(`compare_xyzbc_trt_evidence=${outputPath}`); if (failed.length > 0) { console.log(`compare_xyzbc_trt_status=fail fail_count=${failed.length}`); } else { console.log("compare_xyzbc_trt_status=pass"); } function check(category, requirement, passed, evidence = {}) { return { category, requirement, status: passed ? "pass" : "fail", evidence, }; }