import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import os from "node:os"; 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 write = process.argv.includes("--write"); const absolute = (file) => path.join(root, file); const read = (file) => fs.readFileSync(absolute(file), "utf8"); const readJson = (file) => JSON.parse(read(file)); const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(absolute(file))).digest("hex"); const outputRoot = "tests/golden/WBV2-P10-001"; const historicalPath = "tests/golden/WBV2-P0-002/source-hashes.json"; const currentPath = `${outputRoot}/current-source-hashes.json`; const dependencyPath = `${outputRoot}/dependency-inventory.current.json`; const reportPath = `${outputRoot}/inventory-rebaseline-report.json`; const statusPath = "docs/status/WBV2-P10-001.md"; const planPath = `${outputRoot}/native-parity-task-plan.json`; const manifestPath = `${outputRoot}/manifest.json`; const parentManifestPath = "tests/golden/WBV2-P10-000/manifest.json"; const historicalDependencyPath = "tests/golden/M13-05C/dependency-inventory.json"; const previousReportPath = "tests/golden/WBV2-P8-001/recovery-baseline-report.json"; const parent = readJson(parentManifestPath); assert.equal(parent.task, "WBV2-P10-000"); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, "WBV2-P10-001"); for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path); const historical = readJson(historicalPath); const previous = readJson(previousReportPath); const drift = Object.entries(historical.sources).map(([file, expected]) => ({ file, expected, actual: sha256(file) })).filter((entry) => entry.expected !== entry.actual); assert.deepEqual(drift.map((entry) => entry.file), ["docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json"]); assert.equal(previous.wbv2.drift.length, 1); assert.equal(previous.wbv2.drift[0].file, drift[0].file); assert.equal(previous.wbv2.drift[0].actual, drift[0].actual); const historicalDependency = readJson(historicalDependencyPath); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "wbv2-p10-dependency-")); const generatedPath = path.join(temporary, "dependency-inventory.json"); try { execFileSync(process.execPath, [absolute("tools/web/generate-dependency-inventory.mjs"), generatedPath], { cwd: root, stdio: "pipe" }); } finally { // The generated bytes are read before cleanup; the temporary directory is // deliberately outside the repository and never becomes evidence. } const dependency = JSON.parse(fs.readFileSync(generatedPath, "utf8")); assert.deepEqual(dependency.roots, historicalDependency.roots); assert.deepEqual(dependency.categoryCounts, historicalDependency.categoryCounts); assert.deepEqual(dependency.packages, historicalDependency.packages); assert.equal(dependency.lockfile.sha256, historicalDependency.lockfile.sha256); assert.equal(dependency.packages.length, 144); const current = { schemaVersion: 1, task: "WBV2-P10-001", operation: "CURRENT_WBV2_SOURCE_HASH_BASELINE", historicalSourceHashes: historicalPath, historicalSourceHashesSha256: sha256(historicalPath), sources: Object.fromEntries(Object.keys(historical.sources).map((file) => [file, sha256(file)])), drift: drift.map(({ file, expected, actual }) => ({ file, expected, actual, classification: "AUTHORIZED_MUTABLE_PLAN" })) }; const report = { schemaVersion: 1, task: "WBV2-P10-001", operation: "RECONCILE_WBV2_INVENTORY_SOURCE_DRIFT", status: "PASS", historical: { sourceHashes: historicalPath, sourceHashesSha256: sha256(historicalPath), manifestsRewritten: false, baselinePreserved: true }, currentSourceBaseline: { path: currentPath, sha256: "PENDING", planPath: "docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json", planSha256: sha256("docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json") }, drift: { count: drift.length, entries: current.drift, onlyAuthorizedMutablePlan: true }, dependency: { historicalInventory: historicalDependencyPath, currentInventory: dependencyPath, packageCount: dependency.packages.length, packageHashChanged: dependency.package.sha256 !== historicalDependency.package.sha256, lockfileHashStable: dependency.lockfile.sha256 === historicalDependency.lockfile.sha256, rootsStable: JSON.stringify(dependency.roots) === JSON.stringify(historicalDependency.roots), closureStable: JSON.stringify(dependency.packages) === JSON.stringify(historicalDependency.packages), packagesAdded: 0 }, policy: { historicalP0Unchanged: true, historicalP8Unchanged: true, additiveCurrentBaseline: true, falseHistoricalRestoration: false }, nextTask: "WBV2-P10-002" }; if (!write) { process.stdout.write(`wbv2-inventory-rebaseline-dry-ok task=WBV2-P10-001 planDrift=${drift.length} dependencyPackages=${dependency.packages.length} closureStable=1\n`); process.exit(0); } fs.mkdirSync(absolute(outputRoot), { recursive: true }); fs.writeFileSync(absolute(currentPath), `${JSON.stringify(current, null, 2)}\n`); fs.copyFileSync(generatedPath, absolute(dependencyPath)); fs.rmSync(temporary, { recursive: true, force: true }); report.currentSourceBaseline.sha256 = sha256(currentPath); fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(absolute(statusPath), `# WBV2-P10-001 status\n\n- task: \`WBV2-P10-001\`; parent: \`WBV2-P10-000\`; status: \`done\`\n- plan drift: \`1\` authorized mutable plan hash\n- dependency packages: \`144\`; lockfile/roots/closure: \`STABLE\`\n- historical P0 hashes: preserved\n- nextTask: \`WBV2-P10-002\`\n\nThe current WBV2 plan hash is recorded as an additive epoch-1 baseline. The historical P0-002 source-hashes.json and all historical manifests remain unchanged. Dependency roots, lockfile, package closure, and package count remain stable.\n`); const previousPlan = readJson("tests/golden/WBV2-P10-000/native-parity-task-plan.json"); const nextPlan = structuredClone(previousPlan); nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P10-000/native-parity-task-plan.json", sha256: sha256("tests/golden/WBV2-P10-000/native-parity-task-plan.json") }; nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P10-001" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P10-002" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false }); fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`); const nextCardPath = "docs/tasks/WBV2-P10-002.md"; const nextCard = read(nextCardPath).replace("- status: planned", "- status: in_progress"); fs.writeFileSync(absolute(nextCardPath), nextCard); execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", absolute(planPath)], { cwd: root, stdio: "inherit" }); execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P10-001", "--write"], { cwd: root, stdio: "inherit" }); execFileSync(process.execPath, [absolute("tools/web/check-wbv2-inventory-rebaseline.mjs")], { cwd: root, stdio: "inherit" }); const artifactPaths = { parentManifest: parentManifestPath, historicalSourceHashes: historicalPath, historicalDependencyManifest: "tests/golden/M13-05C/manifest.json", historicalRecoveryReport: previousReportPath, currentSourceHashes: currentPath, currentDependencyInventory: dependencyPath, sourcePlan: "docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json", generator: "tools/web/generate-wbv2-inventory-rebaseline.mjs", checker: "tools/web/check-wbv2-inventory-rebaseline.mjs", report: reportPath, status: statusPath, parityPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }])); const manifest = { schemaVersion: 1, task: "WBV2-P10-001", parentTask: "WBV2-P10-000", operation: "RECONCILE_WBV2_INVENTORY_SOURCE_DRIFT", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { historicalP0Preserved: true, historicalP8Preserved: true, authorizedPlanDrift: 1, currentBaseline: true, dependencyPackages: 144, dependencyClosureStable: true, lockfileStable: true, nextEngineTask: "WBV2-P10-002" }, nextTask: "WBV2-P10-002" }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); let queue = read("docs/EXECUTION_QUEUE.md"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P10-002` |") .replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P10-001/manifest.json` |") .replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P10-002.md`](tasks/WBV2-P10-002.md) |"); fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue); process.stdout.write(`wbv2-inventory-rebaseline-written task=WBV2-P10-001 planDrift=1 dependencyPackages=144 closureStable=1 next=WBV2-P10-002 artifacts=${Object.keys(artifacts).length}\n`);