import fs from "node:fs"; import path from "node:path"; import crypto from "node:crypto"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const abs = (file) => path.join(root, file); const readJson = (file) => JSON.parse(fs.readFileSync(abs(file), "utf8")); const sha = (file) => crypto.createHash("sha256").update(fs.readFileSync(abs(file))).digest("hex"); const writeJson = (file, value) => fs.writeFileSync(abs(file), `${JSON.stringify(value, null, 2)}\n`); const bindingsPath = "tests/golden/WBV2/dynamic-coverage-bindings.json"; const inventoryPath = "tests/golden/WBV2/dynamic-inventory.json"; const coverageManifestPath = "tests/golden/WBV2/dynamic-coverage-manifest.json"; const inventoryManifestPath = "tests/golden/WBV2/dynamic-inventory-manifest.json"; const auditPath = "tests/golden/WBV2/dynamic-coverage-audit.json"; const repairPath = "tests/golden/WBV2/macro-binding-repair.json"; const bindings = readJson(bindingsPath); const inventory = readJson(inventoryPath); const macroTasks = new Set(); const index = readJson("tests/golden/WBV2/task-index.json"); const plan = readJson(index.source.path); for (const task of plan.tasks ?? []) { if (String(task.module?.moduleId ?? "").endsWith("_MACRO_BATCH")) macroTasks.add(task.id); } const removed = bindings.filter((binding) => macroTasks.has(binding.task) || String(binding.capabilityId ?? "").endsWith("_MACRO_BATCH")); const kept = bindings.filter((binding) => !macroTasks.has(binding.task) && !String(binding.capabilityId ?? "").endsWith("_MACRO_BATCH")); const removedIds = new Set(removed.map((binding) => binding.unitId)); const updatedUnits = inventory.units.map((unit) => { if (!removedIds.has(unit.id)) return unit; const { coverage, ...withoutCoverage } = unit; return { ...withoutCoverage, coverageStatus: "UNVERIFIED" }; }); const verifiedUnits = updatedUnits.filter((unit) => unit.coverageStatus === "VERIFIED").length; const updatedInventory = { ...inventory, units: updatedUnits, coverage: { ...inventory.coverage, verifiedUnits, unverifiedUnits: inventory.summary.unitCount - verifiedUnits }, coverageLedger: kept, }; writeJson(bindingsPath, kept); writeJson(inventoryPath, updatedInventory); const refresh = (file) => { if (!fs.existsSync(abs(file))) return; const value = readJson(file); if (file === coverageManifestPath) { value.artifacts.inventory.sha256 = sha(inventoryPath); value.artifacts.bindings.sha256 = sha(bindingsPath); value.coverage = { ...value.coverage, verifiedUnits, unverifiedUnits: inventory.summary.unitCount - verifiedUnits, boundUnitDelta: -removed.length }; } else if (file === inventoryManifestPath) { value.artifacts.inventory.sha256 = sha(inventoryPath); value.inventory = { ...value.inventory, verifiedUnits, unverifiedUnits: inventory.summary.unitCount - verifiedUnits, boundCaseDelta: -removed.length }; } else { const coverageByKind = {}; const coverageByFamily = {}; for (const unit of updatedUnits) { const kind = coverageByKind[unit.kind] ??= { verified: 0, unverified: 0 }; const family = coverageByFamily[unit.family] ??= { verified: 0, unverified: 0 }; const key = unit.coverageStatus === "VERIFIED" ? "verified" : "unverified"; kind[key] += 1; family[key] += 1; } value.dynamicInventory = { ...(value.dynamicInventory ?? {}), afterSha256: sha(inventoryPath), verifiedUnits, unverifiedUnits: inventory.summary.unitCount - verifiedUnits }; value.coverageByKind = coverageByKind; value.coverageByFamily = coverageByFamily; value.explicitBindings = kept; } writeJson(file, value); }; refresh(coverageManifestPath); refresh(inventoryManifestPath); refresh(auditPath); writeJson(repairPath, { schemaVersion: 1, operation: "REMOVE_FALSE_BROAD_MACRO_BINDINGS", removedBindingCount: removed.length, removedUnitIds: [...removedIds], verifiedUnits, unverifiedUnits: inventory.summary.unitCount - verifiedUnits, rule: "macro names require an exact native command gate; family substring matches are forbidden", }); process.stdout.write(`macro-binding-repair removed=${removed.length} verified=${verifiedUnits} unverified=${inventory.summary.unitCount - verifiedUnits}\n`);