97 lines
8.7 KiB
JavaScript
97 lines
8.7 KiB
JavaScript
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 readJson = (file) => JSON.parse(fs.readFileSync(absolute(file), "utf8"));
|
|
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(absolute(file))).digest("hex");
|
|
const same = (left, right, label) => assert.deepEqual(left, right, label);
|
|
const outputRoot = "tests/golden/WBV2-P8-001";
|
|
const currentDependencyPath = `${outputRoot}/dependency-inventory.current.json`;
|
|
const reportPath = `${outputRoot}/recovery-baseline-report.json`;
|
|
const statusPath = "docs/status/WBV2-P8-001.md";
|
|
const planPath = `${outputRoot}/recovery-task-plan.json`;
|
|
const manifestPath = `${outputRoot}/manifest.json`;
|
|
|
|
const parentManifest = readJson("tests/golden/WBV2-P8-000/manifest.json");
|
|
assert.equal(parentManifest.task, "WBV2-P8-000");
|
|
assert.equal(parentManifest.nextTask, "WBV2-P8-001");
|
|
for (const artifact of Object.values(parentManifest.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
|
|
|
|
const sourceHashes = readJson("tests/golden/WBV2-P0-002/source-hashes.json");
|
|
const wbv2Drift = Object.entries(sourceHashes.sources).map(([file, expected]) => ({ file, expected, actual: sha256(file) })).filter((entry) => entry.expected !== entry.actual);
|
|
assert.deepEqual(wbv2Drift.map((entry) => entry.file), ["docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json"]);
|
|
const recoveryPlan = readJson("tests/golden/WBV2-P8-000/recovery-task-plan.json");
|
|
assert.equal(recoveryPlan.parentPlan.sha256, sha256("docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json"));
|
|
for (const task of recoveryPlan.tasks.filter((task) => task.id.startsWith("WBV2-P") && !task.id.startsWith("WBV2-P8-"))) {
|
|
const manifest = readJson(`tests/golden/${task.id}/manifest.json`);
|
|
assert.equal(manifest.task, task.id);
|
|
assert.equal(task.status, manifest.status === "blocked" ? "blocked" : "done", `${task.id} recovery status drift`);
|
|
}
|
|
|
|
fs.mkdirSync(absolute(outputRoot), { recursive: true });
|
|
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "wbv2-dependency-current-"));
|
|
const generatedDependency = path.join(temporary, "dependency-inventory.json");
|
|
try {
|
|
execFileSync(process.execPath, [absolute("tools/web/generate-dependency-inventory.mjs"), generatedDependency], { cwd: root, stdio: "pipe" });
|
|
const historical = readJson("tests/golden/M13-05C/dependency-inventory.json");
|
|
const current = JSON.parse(fs.readFileSync(generatedDependency, "utf8"));
|
|
same(current.roots, historical.roots, "dependency roots drifted");
|
|
same(current.categoryCounts, historical.categoryCounts, "dependency category counts drifted");
|
|
same(current.sharedCount, historical.sharedCount, "dependency sharing drifted");
|
|
same(current.packages, historical.packages, "dependency closure drifted");
|
|
assert.equal(current.lockfile.sha256, historical.lockfile.sha256, "package-lock drifted");
|
|
fs.copyFileSync(generatedDependency, absolute(currentDependencyPath));
|
|
} finally {
|
|
fs.rmSync(temporary, { recursive: true, force: true });
|
|
}
|
|
|
|
const currentDependency = readJson(currentDependencyPath);
|
|
const historicalDependency = readJson("tests/golden/M13-05C/dependency-inventory.json");
|
|
const report = {
|
|
schemaVersion: 1,
|
|
task: "WBV2-P8-001",
|
|
operation: "REBASELINE_MUTABLE_INVENTORIES",
|
|
status: "PASS",
|
|
wbv2: { historicalInventory: "tests/golden/WBV2-P0-002/manifest.json", expectedMutableDriftOnly: true, drift: wbv2Drift },
|
|
dependency: { historicalInventory: "tests/golden/M13-05C/manifest.json", packageHashChanged: currentDependency.package.sha256 !== historicalDependency.package.sha256, lockfileHashStable: currentDependency.lockfile.sha256 === historicalDependency.lockfile.sha256, rootsStable: true, closureStable: true, packageCount: currentDependency.packages.length },
|
|
invariants: { historicalManifestsRewritten: false, capabilityClaimsAdded: 0, dependencyPackagesAdded: 0 },
|
|
nextTask: "WBV2-P8-002",
|
|
};
|
|
if (!write) {
|
|
process.stdout.write(`wbv2-recovery-baseline-ok wbv2Drift=${wbv2Drift.length} dependencyPackages=${currentDependency.packages.length} closureStable=1 next=WBV2-P8-002\n`);
|
|
process.exit(0);
|
|
}
|
|
|
|
fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`);
|
|
const status = `# WBV2-P8-001 status\n\n- task: \`WBV2-P8-001\`; parent: \`WBV2-P8-000\`; status: \`done\`\n- nextTask: \`WBV2-P8-002\`\n\nThe WBV2 drift is limited to the mutable execution plan. The npm dependency graph and lockfile are unchanged; only package scripts changed. Historical P0 and M13 manifests remain untouched.\n`;
|
|
fs.mkdirSync(absolute("docs/status"), { recursive: true });
|
|
fs.writeFileSync(absolute(statusPath), status);
|
|
const nextPlan = structuredClone(recoveryPlan);
|
|
nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P8-000/recovery-task-plan.json", sha256: sha256("tests/golden/WBV2-P8-000/recovery-task-plan.json") };
|
|
nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P8-001" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P8-002" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false });
|
|
fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`);
|
|
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P8-001", "--write"], { cwd: root, stdio: "pipe" });
|
|
|
|
const checkerPath = "tools/web/check-wbv2-recovery-baseline.mjs";
|
|
const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-000/manifest.json", historicalWbv2Manifest: "tests/golden/WBV2-P0-002/manifest.json", historicalDependencyManifest: "tests/golden/M13-05C/manifest.json", sourceHashes: "tests/golden/WBV2-P0-002/source-hashes.json", sourcePlan: "docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json", parentRecoveryPlan: "tests/golden/WBV2-P8-000/recovery-task-plan.json", recoveryPlan: planPath, dependencyInventory: currentDependencyPath, checker: checkerPath, package: "web/package.json", report: reportPath, status: statusPath, 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-P8-001", parentTask: "WBV2-P8-000", operation: "REBASELINE_MUTABLE_INVENTORIES", status: "done", enablingTask: true, parityStateChange: false, artifacts, nextTask: "WBV2-P8-002" };
|
|
fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
|
|
const nextCard = `# WBV2-P8-002: close atomic Chromium receipts\n\n- task: WBV2-P8-002\n- parent: WBV2-P8-001\n- status: in_progress\n- gap: workflow:close_atomic_chromium_receipts\n- ownerFamily: PARITY\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nProduce manifest-bound Chromium receipts for all 45 family behaviors by reusing verified WBV2 desktop/WASM/save-reopen evidence and running the shared production protocol in Chromium.\n\n## 输入与范围\n\n- Queue: \`tests/golden/WBV2-P7-002/atomic-queue-desktop-report.json\`\n- Existing receipts: \`tests/golden/WBV2-P7-003/atomic-chromium-receipt-index.json\`\n- Chromium runner: \`web/tests/e2e/wbv2-atomic-recovery.spec.ts\`\n\n## 验收\n\nnpm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-atomic-recovery.spec.ts\nnode tools/web/generate-wbv2-recovery-receipts.mjs --write\nnode tools/web/check-task-context.mjs --task WBV2-P8-002 --write\n\nMissing Chromium output, unsupported family mapping, source hash drift, or non-exact save/reopen must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P8-002/\`\n- Status: \`docs/status/WBV2-P8-002.md\`\n- Manifest: \`tests/golden/WBV2-P8-002/manifest.json\`\n- Rollback: restore P8-001 as queue parent without modifying earlier evidence.\n`;
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P8-002.md"), nextCard);
|
|
execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", planPath], { cwd: root, stdio: "inherit" });
|
|
let queue = fs.readFileSync(absolute("docs/EXECUTION_QUEUE.md"), "utf8");
|
|
queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P8-002` |")
|
|
.replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P8-001/manifest.json` |")
|
|
.replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P8-002.md`](tasks/WBV2-P8-002.md) |");
|
|
fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
|
|
process.stdout.write(`wbv2-recovery-baseline-written task=WBV2-P8-001 artifacts=${Object.keys(artifacts).length} next=WBV2-P8-002\n`);
|