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"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const abs = (file) => path.join(root, file); const read = (file) => fs.readFileSync(abs(file), "utf8"); const json = (file) => JSON.parse(read(file)); const sha = (file) => crypto.createHash("sha256").update(fs.readFileSync(abs(file))).digest("hex"); const writeJson = (file, value) => { fs.mkdirSync(path.dirname(abs(file)), { recursive: true }); fs.writeFileSync(abs(file), `${JSON.stringify(value, null, 2)}\n`); }; const parentPath = "tests/golden/WBV2-P25-000/manifest.json"; const planPath = "tests/golden/WBV2-P25-000/implementation-task-plan.json"; const protocolPath = "web/protocol/web-engine.ts"; const registryPath = "web/protocol/web-engine-capabilities.ts"; const clientPath = "web/app/src/engine-client/WebEngineClient.ts"; const apiPath = "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"; const task = "WBV2-P25-001"; const out = `tests/golden/${task}`; const reportPath = `${out}/command-registry-report.json`; const statusPath = `docs/status/${task}.md`; const manifestPath = `${out}/manifest.json`; const parent = json(parentPath); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, task); const protocol = read(protocolPath); const registry = read(registryPath); const client = read(clientPath); const api = read(apiPath); const protocolTypes = [...protocol.matchAll(/^\s*\|\s*\{\s*type:\s*"([^"]+)"/gmu)].map((match) => match[1]); if (protocol.includes("| NlaMoveStripCommand")) protocolTypes.push("moveNLAStrip"); const commandTypes = [...new Set(protocolTypes)]; assert.ok(commandTypes.length >= 60, `unexpectedly small WebEngine command surface: ${commandTypes.length}`); assert.match(registry, /satisfies Record/u); assert.match(registry, /getWebEngineCommandCapability/u); assert.match(client, /getWebEngineCommandCapability/u); assert.match(client, /Unsupported WebEngine command/u); assert.match(api, /web_engine_apply_command/u); assert.match(api, /moveObjectToCollection/u); for (const type of commandTypes) assert.match(registry, new RegExp(`\\b${type}\\s*:`)); fs.mkdirSync(abs(out), { recursive: true }); const report = { schemaVersion: 1, task, operation: "IMPLEMENT_MAIN_COMMAND_REGISTRY_AND_DISPATCH", status: "PASS", source: { protocol: { path: protocolPath, sha256: sha(protocolPath) }, registry: { path: registryPath, sha256: sha(registryPath) }, client: { path: clientPath, sha256: sha(clientPath) }, mainApi: { path: apiPath, sha256: sha(apiPath) }, }, implementation: { commandTypeCount: commandTypes.length, registryIsTypeComplete: true, clientUnknownCommandBoundary: true, mainDispatcherRetained: true, sourceBacked: true, duplicateShimAdded: false, }, verification: { status: "DEFERRED", chromium: false, malformedProductionReceipt: false, undoRedo: false, saveReopen: false, deterministicSecondRun: false }, inventory: { total: 6900, legacyCompleted: 281, legacyPending: 6618, effectiveCompleted: 290, effectivePending: 6609, overlayChanged: false }, nextTask: "WBV2-P25-002", }; writeJson(reportPath, report); fs.mkdirSync(abs("docs/status"), { recursive: true }); fs.writeFileSync(abs(statusPath), `# ${task} status\n\n- task: \`${task}\`; parent: \`WBV2-P25-000\`; status: \`done\`\n- implementation: typed registry covers \`${commandTypes.length}\` WebEngine edit command types\n- client rejects unknown command types before worker dispatch; existing Main dispatch is retained\n- verification wave: \`DEFERRED\`; no Chromium receipt or completion overlay written\n- inventory unchanged: \`6900\` total, \`281\` legacy completed, \`6618\` legacy pending, \`290/6609\` effective carried forward\n- nextTask: \`WBV2-P25-002\`\n`); const plan = json(planPath); plan.status = "ACTIVE_SOURCE_BACKED_IMPLEMENTATION_QUEUE"; plan.tasks = plan.tasks.map((candidate) => candidate.id === task ? { ...candidate, status: "done", ready: false } : candidate.id === "WBV2-P25-002" ? { ...candidate, status: "in_progress", ready: true } : { ...candidate, ready: false }); writeJson(planPath, plan); fs.writeFileSync(abs("docs/tasks/WBV2-P25-002.md"), read("docs/tasks/WBV2-P25-002.md").replace("- status: planned", "- status: in_progress")); execFileSync(process.execPath, [abs("tools/web/generate-v2-task-index.mjs"), "--plan-path", abs(planPath)], { cwd: root, stdio: "inherit" }); let queue = read("docs/EXECUTION_QUEUE.md"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P25-002` |") .replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P25-001/manifest.json` |") .replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P25-002.md`](tasks/WBV2-P25-002.md) |"); fs.writeFileSync(abs("docs/EXECUTION_QUEUE.md"), queue); execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", task, "--write"], { cwd: root, stdio: "inherit" }); writeJson(manifestPath, { schemaVersion: 1, task, parentTask: "WBV2-P25-000", operation: "IMPLEMENT_MAIN_COMMAND_REGISTRY_AND_DISPATCH", status: "done", enablingTask: false, parityStateChange: false, runtime: "SOURCE_BACKED_PROTOCOL_CLIENT_DISPATCH", artifacts: { parentManifest: { path: parentPath, sha256: sha(parentPath) }, protocol: { path: protocolPath, sha256: sha(protocolPath) }, registry: { path: registryPath, sha256: sha(registryPath) }, client: { path: clientPath, sha256: sha(clientPath) }, mainApi: { path: apiPath, sha256: sha(apiPath) }, report: { path: reportPath, sha256: sha(reportPath) }, status: { path: statusPath, sha256: sha(statusPath) }, plan: { path: planPath, sha256: sha(planPath) }, taskContext: { path: `${out}/task-context.json`, sha256: sha(`${out}/task-context.json`) }, }, assertions: { commandTypeCount: commandTypes.length, registryTypeComplete: true, unknownCommandRejected: true, mainDispatchPreserved: true, sourceBacked: true, verificationDeferred: true, completionOverlayWritten: false, inventoryTotal: 6900, effectiveCompleted: 290, effectivePending: 6609, historicalReproduction: false }, nextTask: "WBV2-P25-002", }); execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", "WBV2-P25-002", "--write"], { cwd: root, stdio: "inherit" }); let next = read("nextTask.md"); next = next.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P25-002` |") .replace(/\| Current task state \| `[^`]+` \|/u, "| Current task state | `in_progress` |") .replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P25-001/manifest.json` |") .replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P25-002.md` |") .replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P25-003` |") .replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `node tools/web/generate-wbv2-implementation-slice.mjs --task WBV2-P25-002 --write` |"); fs.writeFileSync(abs("nextTask.md"), next); console.log(`wbv2-main-command-registry-written task=${task} commandTypes=${commandTypes.length} next=WBV2-P25-002 verification=DEFERRED`);