99 lines
16 KiB
JavaScript
99 lines
16 KiB
JavaScript
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 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 p9ManifestPath = "tests/golden/WBV2-P9-004/manifest.json";
|
|
const p9ReportPath = "tests/golden/WBV2-P9-004/feature-parity-gate-report.json";
|
|
const p9StatusPath = "docs/status/WBV2-P9-004.md";
|
|
const outputRoot = "tests/golden/WBV2-P10-000";
|
|
const planPath = `${outputRoot}/native-parity-task-plan.json`;
|
|
const reportPath = `${outputRoot}/native-parity-root-report.json`;
|
|
const manifestPath = `${outputRoot}/manifest.json`;
|
|
const rootStatusPath = "docs/status/WBV2-P10-000.md";
|
|
|
|
const p9 = readJson(p9ManifestPath);
|
|
const p9Report = readJson(p9ReportPath);
|
|
assert.equal(p9.task, "WBV2-P9-004");
|
|
assert.equal(p9.status, "done");
|
|
assert.ok(p9.nextTask === null || p9.nextTask === "WBV2-P10-000");
|
|
assert.equal(p9Report.task, "WBV2-P9-004");
|
|
assert.equal(p9Report.status, "PASS");
|
|
assert.equal(p9Report.decision.fullHistoricalReproduction, false);
|
|
assert.equal(p9Report.decision.releaseScope, "AUTHORIZED_REBASELINE_ONLY");
|
|
for (const artifact of Object.values(p9.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
|
|
|
|
const basePlan = readJson("tests/golden/WBV2-P9-004/feature-parity-task-plan.json");
|
|
assert.equal(basePlan.recoveryRoot, "WBV2-P9-000");
|
|
const tasks = basePlan.tasks.map((task) => ({ ...task, ready: false, status: task.id === "WBV2-P9-004" ? "done" : task.status }));
|
|
const p10 = [
|
|
{ id: "WBV2-P10-000", phase: "P10", title: "authorize_native_engine_parity_continuation", kind: "enabling", owner: "PROGRAM", dependsOn: ["WBV2-P9-004"], parityId: null, implementationClass: "LOCAL_BOUNDED", parallelGroup: "p10-governance", gate: "G6", card: "docs/tasks/WBV2-P10-000.md", behavior: "Authorize native Blender/WASM engine parity continuation after bounded Chromium capability evidence.", outputs: ["continuation receipt", "native parity plan", "scope limits"], acceptance: ["P9 evidence remains hash-bound", "native build/runtime is explicit", "one native parity task is active"], status: "done", ready: false, queueMutationAllowed: true },
|
|
{ id: "WBV2-P10-001", phase: "P10", title: "reconcile_wbv2_inventory_source_drift", kind: "enabling", owner: "PARITY", dependsOn: ["WBV2-P10-000"], parityId: null, implementationClass: "LOCAL_BOUNDED", parallelGroup: "p10-inventory", gate: "G6", card: "docs/tasks/WBV2-P10-001.md", behavior: "Record the authorized mutable WBV2 plan drift and prove dependency closure stability without rewriting historical P0 evidence.", outputs: ["current source hash baseline", "drift report", "checker compatibility"], acceptance: ["historical P0 hashes remain unchanged", "only authorized plan drift is recorded", "dependency roots/lockfile/closure remain stable", "inventory checker passes using current baseline"], status: "in_progress", ready: true, queueMutationAllowed: false },
|
|
{ id: "WBV2-P10-002", phase: "P10", title: "bind_native_and_wasm_engine_receipts", kind: "feature", owner: "ENGINE", dependsOn: ["WBV2-P10-001"], parityId: "ENGINE.NATIVE_WASM_BRIDGE", implementationClass: "LOCAL_EXACT", parallelGroup: "p10-engine", gate: "G6", card: "docs/tasks/WBV2-P10-002.md", behavior: "Bind Mesh, Modifier, and Asset Catalog receipts to the compiled Blender native binary and web_engine.wasm runtime.", outputs: ["native receipt", "WASM receipt", "Chromium engine report"], acceptance: ["native Blender binary is source-bound", "WASM binary and JS are hash-bound", "same mutation reaches real engine/Main", "save/reopen and failures remain exact"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
{ id: "WBV2-P10-003", phase: "P10", title: "expand_core_module_operator_matrix", kind: "feature", owner: "PARITY", dependsOn: ["WBV2-P10-002"], parityId: "CORE.MODULE_MATRIX", implementationClass: "LOCAL_BOUNDED", parallelGroup: "p10-matrix", gate: "G7", card: "docs/tasks/WBV2-P10-003.md", behavior: "Expand the three proven capability modules with data-driven operator coverage and aggregate regression receipts.", outputs: ["module matrix", "shard reports", "aggregate manifest"], acceptance: ["positive cases are L2+", "negative cases do not mutate Main", "shards use one catalog hash", "unmapped and retained-negative cases stay explicit"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
];
|
|
const plan = { ...basePlan, planId: "BLENDER_WEB_PARITY_V2_NATIVE_ENGINE_EPOCH_1", status: "ACTIVE_NATIVE_ENGINE_QUEUE", authority: "queue_reactivated_after_p9_bounded_parity", recoveryRoot: "WBV2-P10-000", parentRecoveryPlan: { path: "tests/golden/WBV2-P9-004/feature-parity-task-plan.json", sha256: sha256("tests/golden/WBV2-P9-004/feature-parity-task-plan.json") }, tasks: [...tasks, ...p10] };
|
|
|
|
fs.mkdirSync(absolute(outputRoot), { recursive: true });
|
|
fs.writeFileSync(absolute(planPath), `${JSON.stringify(plan, null, 2)}\n`);
|
|
const report = { schemaVersion: 1, task: "WBV2-P10-000", operation: "AUTHORIZE_NATIVE_ENGINE_PARITY_CONTINUATION", status: "PASS", parent: { task: p9.task, manifest: p9ManifestPath, manifestSha256: sha256(p9ManifestPath), previousDecision: p9Report.decision.status, fullHistoricalReproduction: p9Report.decision.fullHistoricalReproduction }, continuation: { root: "WBV2-P10-000", firstTask: "WBV2-P10-001", plannedTasks: 4, scope: ["INVENTORY_DRIFT", "NATIVE_ENGINE", "WASM_ENGINE", "MODULE_MATRIX"], browser: "chromium", nativeBinary: "build_blender_native/bin/blender", wasmBinary: "web/app/src/vendor/blender/web_engine.wasm" }, policy: { historicalEvidencePreserved: true, authorizedRebaselineAccepted: true, nativeEngineReceiptsRequired: true, fullHistoricalReproduction: false, releaseScope: "AUTHORIZED_REBASELINE_ONLY" }, nextTask: "WBV2-P10-001" };
|
|
fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P10-000.md"), `# WBV2-P10-000: native engine parity continuation root\n\n- task: WBV2-P10-000\n- parent: WBV2-P9-004\n- status: done\n- gap: workflow:authorize_native_engine_parity_continuation\n- ownerFamily: PROGRAM\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nAuthorize the next parity epoch after P9 bounded Chromium receipts. The next work must bind evidence to the compiled Blender native binary and web_engine.wasm; it must not claim full historical reproduction.\n\n## 验收\n\n- \`node tools/web/generate-wbv2-native-parity-root.mjs\`\n- \`node tools/web/check-execution-control-plane.mjs\`\n- \`node tools/web/check-v2-task-index.mjs\`\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P10-000/\`\n- Manifest: \`tests/golden/WBV2-P10-000/manifest.json\`\n- Rollback: restore P9-004 as queue parent without rewriting P7/P8/P9 evidence.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P10-001.md"), `# WBV2-P10-001: reconcile WBV2 inventory source drift\n\n- task: WBV2-P10-001\n- parent: WBV2-P10-000\n- status: in_progress\n- gap: workflow:reconcile_wbv2_inventory_source_drift\n- ownerFamily: PARITY\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRecord the already observed mutable plan drift and prove dependency closure stability. The historical P0-002 source hashes remain immutable; a current baseline is an additive P10 artifact.\n\n## 输入与范围\n\n- Historical source hashes: \`tests/golden/WBV2-P0-002/source-hashes.json\`\n- Current plan: \`docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json\`\n- Dependency baseline: \`tests/golden/M13-05C/dependency-inventory.json\`\n- Checker: \`tools/web/check-wbv2-inventory.mjs\`\n- Rebaseline generator: \`tools/web/generate-wbv2-inventory-rebaseline.mjs\`\n\n## 验收\n\n- \`node tools/web/generate-wbv2-inventory-rebaseline.mjs --write\`\n- \`node tools/web/check-wbv2-inventory.mjs\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P10-001 --write\`\n\nFailure cases (malformed input, unsupported dependency, unexpected drift, changed closure, historical hash rewrite, missing baseline, or failed checker) must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P10-001/\`\n- Status: \`docs/status/WBV2-P10-001.md\`\n- Manifest: \`tests/golden/WBV2-P10-001/manifest.json\`\n- Rollback: restore P10-000 as queue parent; keep P0-002 hashes untouched.\n`);
|
|
fs.writeFileSync(absolute(rootStatusPath), `# WBV2-P10-000 status\n\n- task: \`WBV2-P10-000\`; parent: \`WBV2-P9-004\`; status: \`done\`\n- nextTask: \`WBV2-P10-001\`\n\nNative engine parity continuation is authorized after P9 bounded Chromium evidence. P10 first reconciles the known WBV2 inventory drift, then requires receipts from the compiled Blender native binary and web_engine.wasm. Full historical reproduction remains false.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P10-002.md"), `# WBV2-P10-002: native and WASM engine receipts\n\n- task: WBV2-P10-002\n- parent: WBV2-P10-001\n- status: planned\n- gap: workflow:bind_native_and_wasm_engine_receipts\n- ownerFamily: ENGINE\n- targetImplementationClass: LOCAL_EXACT\n\n## 目标\n\nBind the proven Mesh, Modifier, and Asset Catalog mutations to the compiled Blender native binary and the checked-in web_engine.wasm runtime, with Chromium as the browser gate.\n\n## 验收\n\n- \`build_blender_native/bin/blender --version\`\n- \`node tools/web/check-wbv2-native-engine.mjs --write\`\n- \`npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-native-engine-production.spec.ts\`\n\nMissing binary, source hash drift, fake bridge execution, unsupported browser, non-exact save/reopen, or failure mutation must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P10-002/\`\n- Status: \`docs/status/WBV2-P10-002.md\`\n- Manifest: \`tests/golden/WBV2-P10-002/manifest.json\`\n- Rollback: restore P10-001 as queue parent without modifying P9 receipts.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P10-003.md"), `# WBV2-P10-003: core module operator matrix\n\n- task: WBV2-P10-003\n- parent: WBV2-P10-002\n- status: planned\n- gap: workflow:expand_core_module_operator_matrix\n- ownerFamily: PARITY\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRun one hash-bound data-driven matrix for Mesh, Modifier, and Asset Catalog module cases instead of creating one production task per legacy operator.\n\n## 验收\n\n- \`node tools/web/generate-wbv2-module-matrix.mjs --write\`\n- \`npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-module-matrix.spec.ts\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P10-003 --write\`\n\nNew failures, unexpected blocked cases, catalog hash drift, or a positive case without Main mutation must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P10-003/\`\n- Status: \`docs/status/WBV2-P10-003.md\`\n- Manifest: \`tests/golden/WBV2-P10-003/manifest.json\`\n- Rollback: restore P10-002 as queue parent without modifying native engine receipts.\n`);
|
|
|
|
const p10ActiveCardPath = "docs/tasks/WBV2-P10-001.md";
|
|
const p10ActiveCard = read(p10ActiveCardPath).replaceAll("node tools/web/check-wbv2-inventory.mjs", "node tools/web/check-wbv2-inventory-rebaseline.mjs");
|
|
fs.writeFileSync(absolute(p10ActiveCardPath), p10ActiveCard);
|
|
const p10EngineCardPath = "docs/tasks/WBV2-P10-002.md";
|
|
const p10EngineCard = read(p10EngineCardPath)
|
|
.replace("## 验收", "## 输入与范围\n\n- Native binary: `build_blender_native/bin/blender`\n- WASM binary: `web/app/src/vendor/blender/web_engine.wasm`\n- WASM loader: `web/app/src/vendor/blender/web_engine.js`\n- Native Web Engine API: `blender-5.2.0/source/blender/web_engine/web_engine_api.cpp`\n- Existing P9 capability receipts: `tests/golden/WBV2-P9-004/feature-parity-gate-report.json`\n\n## 生产入口\n\n- WebEngine client: `web/app/src/engine-client/WebEngineClient.ts`\n- WebEngine worker: `web/app/src/workers/web-engine.worker.ts`\n- Production receipt test: `web/tests/e2e/wbv2-native-engine-production.spec.ts`\n\n## 验收")
|
|
.replace("- `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-native-engine-production.spec.ts`", "- `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-native-engine-production.spec.ts`\n- `node tools/web/generate-wbv2-native-engine-receipt.mjs --write`\n- `node tools/web/check-task-context.mjs --task WBV2-P10-002 --write`")
|
|
.replace("- status: planned", "- status: in_progress");
|
|
fs.writeFileSync(absolute(p10EngineCardPath), p10EngineCard);
|
|
const p10MatrixCardPath = "docs/tasks/WBV2-P10-003.md";
|
|
const p10MatrixCard = read(p10MatrixCardPath)
|
|
.replace("## 验收", "## 输入与范围\n\n- Capability gate: `tests/golden/WBV2-P9-004/feature-parity-gate-report.json`\n- Native engine receipt: `tests/golden/WBV2-P10-002/native-engine-receipt-report.json`\n- Operator inventory: `tests/golden/WBV2-P0-002/parity-map.json`\n\n## 验收")
|
|
.replace("- `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-module-matrix.spec.ts`", "- `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-module-matrix.spec.ts`\n- `node tools/web/generate-wbv2-module-matrix-receipt.mjs --write`")
|
|
.replace("New failures, unexpected blocked cases", "Malformed or unsupported cases, new failures, unexpected blocked cases");
|
|
fs.writeFileSync(absolute(p10MatrixCardPath), p10MatrixCard);
|
|
|
|
// P9-004 was a terminal gate only because no successor had been authorized.
|
|
// Record this additive continuation without changing its evidence decision.
|
|
p9.nextTask = "WBV2-P10-000";
|
|
p9.continuation = { authorized: true, root: "WBV2-P10-000", scope: "NATIVE_ENGINE_PARITY" };
|
|
p9Report.nextTask = "WBV2-P10-000";
|
|
p9Report.policy.terminalQueuePointer = false;
|
|
p9Report.policy.authorizedContinuation = "WBV2-P10-000";
|
|
fs.writeFileSync(absolute(p9ReportPath), `${JSON.stringify(p9Report, null, 2)}\n`);
|
|
const currentP9Status = read(p9StatusPath).trim();
|
|
const p9Status = currentP9Status.includes("authorized continuation: `WBV2-P10-000`")
|
|
? `${currentP9Status}\n`
|
|
: `${currentP9Status}\n- authorized continuation: \`WBV2-P10-000\`\n\nNative Blender/WASM engine parity may continue under a new manifest root. This does not change the bounded P9 claims or the authorized-rebaseline-only release scope.\n`;
|
|
fs.writeFileSync(absolute(p9StatusPath), p9Status);
|
|
fs.writeFileSync(absolute(p9ManifestPath), `${JSON.stringify(p9, null, 2)}\n`);
|
|
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P9-004", "--write"], { cwd: root, stdio: "pipe" });
|
|
p9.artifacts = Object.fromEntries(Object.entries(p9.artifacts).map(([name, artifact]) => [name, { ...artifact, sha256: sha256(artifact.path) }]));
|
|
fs.writeFileSync(absolute(p9ManifestPath), `${JSON.stringify(p9, null, 2)}\n`);
|
|
|
|
const artifactPaths = { parentManifest: p9ManifestPath, parentReport: p9ReportPath, approval: "docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json", plan: planPath, report: reportPath, rootCard: "docs/tasks/WBV2-P10-000.md", rootStatus: rootStatusPath, activeCard: "docs/tasks/WBV2-P10-001.md" };
|
|
const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }]));
|
|
const manifest = { schemaVersion: 1, task: "WBV2-P10-000", parentTask: "WBV2-P9-004", operation: "AUTHORIZE_NATIVE_ENGINE_PARITY_CONTINUATION", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { boundedP9EvidencePreserved: true, nativeEngineRequired: true, authorizedRebaselineOnly: true, fullHistoricalReproduction: false, featureScope: ["INVENTORY_DRIFT", "NATIVE_ENGINE", "WASM_ENGINE", "MODULE_MATRIX"] }, nextTask: "WBV2-P10-001" };
|
|
fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", absolute(planPath)], { cwd: root, stdio: "inherit" });
|
|
let queue = read("docs/EXECUTION_QUEUE.md");
|
|
queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P10-001` |")
|
|
.replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P10-000/manifest.json` |")
|
|
.replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P10-001.md`](tasks/WBV2-P10-001.md) |");
|
|
fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
|
|
process.stdout.write("wbv2-native-parity-root-ok root=WBV2-P10-000 next=WBV2-P10-001 scope=INVENTORY_DRIFT,NATIVE_ENGINE,WASM_ENGINE,MODULE_MATRIX\n");
|