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 allowUnrunnable = process.argv.includes("--allow-unrunnable"); const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/"); const read = (relativePath) => fs.readFileSync(path.join(root, relativePath), "utf8"); const json = (relativePath) => JSON.parse(read(relativePath)); const sha256 = (bytes) => { return crypto.createHash("sha256").update(bytes).digest("hex"); }; function parseQueue() { const source = read("docs/EXECUTION_QUEUE.md"); const currentTask = source.match(/\|\s*当前任务\s*\|\s*`([^`]+)`/u)?.[1]; const parentManifest = source.match(/\|\s*parent manifest\s*\|\s*`([^`]+)`/iu)?.[1]; const coverageTask = source.match(/\|\s*legacy coverage checkpoint\s*\|\s*`([^`]+)`/iu)?.[1] ?? currentTask; const coverageParentManifest = source.match(/\|\s*legacy coverage parent\s*\|\s*`([^`]+)`/iu)?.[1] ?? (currentTask.startsWith("M") ? parentManifest : null); if (!currentTask || !parentManifest || !coverageTask || !coverageParentManifest) throw new Error("queue pointer is incomplete"); return { currentTask, parentManifest, coverageTask, coverageParentManifest }; } function addIssue(issues, code, detail) { issues.push({ code, detail }); } const issues = []; let queue = null; let parent = null; let index = null; let catalog = []; let plan = null; let matrix = null; let completed = []; try { queue = parseQueue(); const parentPath = path.join(root, queue.parentManifest); if (!fs.existsSync(parentPath)) addIssue(issues, "PARENT_MANIFEST_MISSING", queue.parentManifest); else { parent = JSON.parse(fs.readFileSync(parentPath, "utf8")); if (parent.nextTask !== queue.currentTask) addIssue(issues, "PARENT_NEXT_TASK_MISMATCH", `${parent.nextTask ?? "NONE"}!=${queue.currentTask}`); } const capabilityIndex = queue.currentTask.startsWith("WBV2-"); index = json(capabilityIndex ? "tests/golden/WBV2/task-index.json" : "tests/golden/M15-03A/task-index.json"); if (index.activeTask !== queue.currentTask) addIssue(issues, "INDEX_ACTIVE_TASK_MISMATCH", `${index.activeTask}!=${queue.currentTask}`); const catalogPath = path.join(root, index.catalog.path); if (!fs.existsSync(catalogPath)) addIssue(issues, "CATALOG_MISSING", index.catalog.path); else { catalog = fs.readFileSync(catalogPath, "utf8").trimEnd().split("\n").filter(Boolean).map((line) => JSON.parse(line)); const active = catalog.filter((entry) => entry.state === "active"); if (active.length !== 1) addIssue(issues, "ACTIVE_TASK_COUNT", `count=${active.length}`); else if (active[0].id !== queue.currentTask) addIssue(issues, "CATALOG_ACTIVE_TASK_MISMATCH", `${active[0].id}!=${queue.currentTask}`); } plan = json("tests/golden/M15-03A/next-task-plan.json"); matrix = json("docs/BLENDER_WASM_CAPABILITY_MATRIX_TEMPLATE.json"); completed = json("tests/golden/M15-03A/completed-gap-tasks.json"); } catch (error) { addIssue(issues, "CONTROL_PLANE_READ_ERROR", error instanceof Error ? error.message : String(error)); } const active = catalog.find((entry) => entry.state === "active") ?? null; let legacyCoverage = null; try { const legacyIndex = json("tests/golden/M15-03A/task-index.json"); const legacyCatalog = fs.readFileSync(path.join(root, legacyIndex.catalog.path), "utf8").trimEnd().split("\n").filter(Boolean).map((line) => JSON.parse(line)); legacyCoverage = legacyCatalog.find((entry) => entry.id === queue.coverageTask) ?? null; if (!legacyCoverage) addIssue(issues, "LEGACY_COVERAGE_TASK_MISSING", queue.coverageTask); } catch (error) { addIssue(issues, "LEGACY_COVERAGE_INDEX_ERROR", error instanceof Error ? error.message : String(error)); } const legacySummary = plan?.summary ?? {}; const capabilities = matrix?.entries ?? []; const mappedGapIds = [...new Set(capabilities.flatMap((entry) => entry.sourceGaps ?? []))]; const capabilityClusters = Object.fromEntries(capabilities.map((entry) => [entry.family, { capabilityId: entry.capabilityId, sourceGaps: (entry.sourceGaps ?? []).length, parityLevel: entry.parityLevel, targetParityLevel: entry.targetParityLevel, lifecycleStatus: entry.lifecycleStatus, }])); const runnableInputs = active ? { taskCard: fs.existsSync(path.join(root, "docs/tasks", `${active.id}.md`)), capabilityTask: active.id.startsWith("WBV2-"), fixture: active.id.startsWith("WBV2-") ? null : fs.existsSync(path.join(root, "tests/files/web/generated", `${active.id}-${active.gapId.replace(/[^A-Za-z0-9_.-]+/gu, "-")}.blend`)), generator: active.id.startsWith("WBV2-") ? null : fs.existsSync(path.join(root, "tools/web/generated", `${active.id}.py`)), } : null; if (!allowUnrunnable && runnableInputs && (!runnableInputs.taskCard || (!runnableInputs.capabilityTask && (!runnableInputs.fixture || !runnableInputs.generator)))) { addIssue(issues, "ACTIVE_TASK_NOT_RUNNABLE", JSON.stringify(runnableInputs)); } const report = { schemaVersion: 1, operation: "BLENDER_WEB_EXECUTION_CONTROL_PLANE", status: issues.length === 0 ? "PASS" : "BLOCKED", authority: { executionPointer: "docs/EXECUTION_QUEUE.md", productCapabilities: "docs/BLENDER_WASM_CAPABILITY_MATRIX_TEMPLATE.json", legacyCoverage: "tests/golden/M15-03A/next-task-plan.json", historicalEvidence: "tests/golden/corrective/", }, queue: queue ? { currentTask: queue.currentTask, parentManifest: queue.parentManifest, parentTask: parent?.task ?? null, parentNextTask: parent?.nextTask ?? null, activeCatalogTask: active?.id ?? null, legacyCoverageTask: queue.coverageTask, legacyCoverageParentManifest: queue.coverageParentManifest, legacyCoverageState: legacyCoverage?.state ?? null, runnableInputs, } : null, efficiency: { legacyGapTasks: legacySummary.taskCount ?? catalog.length, legacyCompleted: completed.length, legacyPending: legacySummary.pending ?? null, capabilityClusters: capabilities.length, mappedLegacyGaps: mappedGapIds.length, averageGapsPerCapability: capabilities.length === 0 ? 0 : Number((mappedGapIds.length / capabilities.length).toFixed(2)), productiveLane: capabilities.filter((entry) => entry.lifecycleStatus === "done" && ["L2", "L3", "L4", "L5"].includes(entry.parityLevel)).length, rule: "one capability task may close multiple legacy cases; legacy cases remain regression coverage", }, capabilityClusters, issues, remediation: issues.map(({ code }) => ({ PARENT_NEXT_TASK_MISMATCH: "repair generated queue state from the completion registry", INDEX_ACTIVE_TASK_MISMATCH: "run generate-task-index.mjs after regenerating the plan", CATALOG_ACTIVE_TASK_MISMATCH: "run generate-task-index.mjs; do not edit catalog offsets", ACTIVE_TASK_COUNT: "leave exactly one active task in the generated catalog", ACTIVE_TASK_NOT_RUNNABLE: "generate the task card and fixture/generator before Blender/WASM startup", }[code] ?? "inspect the control-plane issue and preserve its evidence")), allowUnrunnable, inputDigest: sha256(JSON.stringify({ queue, parent: parent?.nextTask, active: active?.id, completed: completed.length, capabilities: capabilities.length })), }; process.stdout.write(`${JSON.stringify(report)}\n`); if (issues.length > 0) process.exitCode = 2;