import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const task = process.argv[2]; const output = path.resolve(process.argv[3] ?? ""); const contracts = { "M15-02C": { mode: "LOCAL_EXACT", nextTask: "M15-02D", required: ["desktopFixture", "wasmFixture", "sameFixture"] }, "M15-02D": { mode: "LOCAL_EQUIVALENT", nextTask: "M15-02E", required: ["mainEvidence", "saveReopenEvidence"] }, "M15-02E": { mode: "SERVER_EXACT", nextTask: "M15-02F", required: ["jobEvidence", "cancelEvidence", "isolationEvidence", "resultBindingEvidence"] }, "M15-02F": { mode: "UNKNOWN_DATA_PRESERVATION", nextTask: "M15-03A", required: ["unknownDataFixture", "saveReloadEvidence", "bytePreservationEvidence"] }, }; if (!contracts[task] || !output) throw new Error("usage: node generate-blender-contract-audit.mjs M15-02C OUTPUT.json"); const contract = contracts[task]; const mapPath = path.join(root, "tests/golden/M15-02A/blender-parity-map.json"); const map = JSON.parse(fs.readFileSync(mapPath, "utf8")); const claims = map.entries.filter((entry) => entry.implementationClass === contract.mode); const blocked = claims.filter((entry) => contract.required.some((field) => !entry[field])); const ready = claims.filter((entry) => !blocked.includes(entry)); const audit = { schemaVersion: 1, task, operation: "BLENDER_PARITY_CONTRACT_AUDIT", source: { path: "tests/golden/M15-02A/blender-parity-map.json", sha256: crypto.createHash("sha256").update(fs.readFileSync(mapPath)).digest("hex") }, contract: { mode: contract.mode, requiredEvidence: contract.required, failClosed: true }, summary: { inventoried: map.entries.length, applicable: claims.length, blocked: blocked.length, ready: ready.length, notApplicable: map.entries.length - claims.length }, blockedIds: blocked.map((entry) => entry.id).sort((a, b) => a < b ? -1 : a > b ? 1 : 0), readyIds: ready.map((entry) => entry.id).sort((a, b) => a < b ? -1 : a > b ? 1 : 0), interpretation: claims.length === 0 ? "NO_CLAIMS_DECLARED" : "MISSING_EVIDENCE_BLOCKS_CLAIM", nextTask: contract.nextTask, }; fs.mkdirSync(path.dirname(output), { recursive: true }); fs.writeFileSync(output, `${JSON.stringify(audit, null, 2)}\n`); process.stdout.write(`blender-contract-audit-generated task=${task} mode=${contract.mode} applicable=${claims.length} blocked=${blocked.length} output=${output}\n`);