86 lines
19 KiB
JavaScript
86 lines
19 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 p11ManifestPath = "tests/golden/WBV2-P11-004/manifest.json";
|
|
const p11ReportPath = "tests/golden/WBV2-P11-004/engine-module-gate-report.json";
|
|
const p11StatusPath = "docs/status/WBV2-P11-004.md";
|
|
const outputRoot = "tests/golden/WBV2-P12-000";
|
|
const planPath = `${outputRoot}/domain-expansion-task-plan.json`;
|
|
const reportPath = `${outputRoot}/domain-expansion-root-report.json`;
|
|
const manifestPath = `${outputRoot}/manifest.json`;
|
|
const rootStatusPath = "docs/status/WBV2-P12-000.md";
|
|
|
|
const p11 = readJson(p11ManifestPath);
|
|
const p11Report = readJson(p11ReportPath);
|
|
assert.equal(p11.task, "WBV2-P11-004");
|
|
assert.equal(p11.status, "done");
|
|
assert.ok(p11.nextTask === null || p11.nextTask === "WBV2-P12-000");
|
|
assert.equal(p11Report.task, "WBV2-P11-004");
|
|
assert.equal(p11Report.status, "PASS");
|
|
assert.equal(p11Report.decision.fullHistoricalReproduction, false);
|
|
for (const artifact of Object.values(p11.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
|
|
|
|
const basePlan = readJson("tests/golden/WBV2-P11-004/module-expansion-task-plan.json");
|
|
const tasks = basePlan.tasks.map((task) => ({ ...task, ready: false, status: task.id === "WBV2-P11-004" ? "done" : task.status }));
|
|
const p12 = [
|
|
{ id: "WBV2-P12-000", phase: "P12", title: "authorize_domain_expansion_continuation", kind: "enabling", owner: "PROGRAM", dependsOn: ["WBV2-P11-004"], parityId: null, implementationClass: "LOCAL_BOUNDED", parallelGroup: "p12-governance", gate: "G8", card: "docs/tasks/WBV2-P12-000.md", behavior: "Authorize expansion from the three engine-bound modules to the remaining high-value Blender Web domains using existing source evidence.", outputs: ["continuation root", "domain expansion plan", "scope limits"], acceptance: ["P11 evidence remains hash-bound", "domain tasks use existing source receipts", "one ledger task is active"], status: "done", ready: false, queueMutationAllowed: true },
|
|
{ id: "WBV2-P12-001", phase: "P12", title: "reconcile_existing_capability_ledger", kind: "enabling", owner: "PARITY", dependsOn: ["WBV2-P12-000"], parityId: null, implementationClass: "LOCAL_BOUNDED", parallelGroup: "p12-ledger", gate: "G8", card: "docs/tasks/WBV2-P12-001.md", behavior: "Aggregate existing WBV2 P1-P6 and P10-P11 manifests into a conservative capability ledger without inventing claims.", outputs: ["capability ledger", "evidence index", "remaining gap map"], acceptance: ["all source manifests are hash-verified", "only done receipts enter proven evidence", "L0/metadata and blocked cases remain explicit", "inventory remainder is preserved"], status: "in_progress", ready: true, queueMutationAllowed: false },
|
|
{ id: "WBV2-P12-002", phase: "P12", title: "scene_object_workflow_engine_receipt", kind: "feature", owner: "EDITOR", dependsOn: ["WBV2-P12-001"], parityId: "EDITOR.SCENE_OBJECT_ENGINE", implementationClass: "LOCAL_EXACT", parallelGroup: "p12-scene", gate: "G8", card: "docs/tasks/WBV2-P12-002.md", behavior: "Bind object visibility and transform edits to the compiled WebEngine WASM Main with save/reopen and revision guards.", outputs: ["scene/object receipt", "negative matrix", "manifest"], acceptance: ["visibility and transform mutate Main", "desktop source oracle is bound", "save/reopen is exact", "stale and malformed commands fail closed"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
{ id: "WBV2-P12-003", phase: "P12", title: "material_animation_engine_receipt", kind: "feature", owner: "AUTHORING", dependsOn: ["WBV2-P12-002"], parityId: "AUTHORING.MATERIAL_ANIMATION_ENGINE", implementationClass: "LOCAL_EXACT", parallelGroup: "p12-authoring", gate: "G8", card: "docs/tasks/WBV2-P12-003.md", behavior: "Bind material Principled/texture and TRS keyframe edits to the compiled WebEngine WASM Main.", outputs: ["authoring receipt", "negative matrix", "manifest"], acceptance: ["material and keyframe Main mutations pass", "source hashes are bound", "save/reopen is exact", "unsupported graph and stale frame inputs fail closed"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
{ id: "WBV2-P12-004", phase: "P12", title: "viewport_nonmesh_engine_receipt", kind: "feature", owner: "VIEWPORT", dependsOn: ["WBV2-P12-003"], parityId: "VIEWPORT.NONMESH_ENGINE", implementationClass: "LOCAL_BOUNDED", parallelGroup: "p12-viewport", gate: "G8", card: "docs/tasks/WBV2-P12-004.md", behavior: "Bind viewport SceneIR and bounded non-mesh read/edit paths to the compiled WebEngine runtime with explicit unsupported boundaries.", outputs: ["viewport receipt", "nonmesh boundary map", "manifest"], acceptance: ["nonblank viewport evidence is bound", "non-mesh data is explicit", "unsupported and budget cases fail closed", "save/reopen and resource disposal are exact"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
{ id: "WBV2-P12-005", phase: "P12", title: "recompute_domain_parity_gate", kind: "enabling", owner: "RELEASE", dependsOn: ["WBV2-P12-004"], parityId: null, implementationClass: "LOCAL_BOUNDED", parallelGroup: "p12-release", gate: "G9", card: "docs/tasks/WBV2-P12-005.md", behavior: "Recompute domain parity from the complete evidence ledger while retaining explicit inventory and provenance limits.", outputs: ["domain parity gate", "limited release declaration", "manifest"], acceptance: ["only engine-bound domain claims are emitted", "unproven inventory remains explicit", "epoch 2 remains authorized rebaseline only", "full historical reproduction remains false"], status: "planned", ready: false, queueMutationAllowed: false },
|
|
];
|
|
const plan = { ...basePlan, planId: "BLENDER_WEB_PARITY_V2_DOMAIN_EXPANSION_EPOCH_1", status: "ACTIVE_DOMAIN_EXPANSION_QUEUE", authority: "queue_reactivated_after_p11_engine_gate", recoveryRoot: "WBV2-P12-000", parentRecoveryPlan: { path: "tests/golden/WBV2-P11-004/module-expansion-task-plan.json", sha256: sha256("tests/golden/WBV2-P11-004/module-expansion-task-plan.json") }, tasks: [...tasks, ...p12] };
|
|
|
|
fs.mkdirSync(absolute(outputRoot), { recursive: true });
|
|
fs.writeFileSync(absolute(planPath), `${JSON.stringify(plan, null, 2)}\n`);
|
|
const report = { schemaVersion: 1, task: "WBV2-P12-000", operation: "AUTHORIZE_DOMAIN_EXPANSION_CONTINUATION", status: "PASS", parent: { task: p11.task, manifest: p11ManifestPath, manifestSha256: sha256(p11ManifestPath), previousDecision: p11Report.decision.status, fullHistoricalReproduction: p11Report.decision.fullHistoricalReproduction }, continuation: { root: "WBV2-P12-000", firstTask: "WBV2-P12-001", plannedTasks: 6, scope: ["CAPABILITY_LEDGER", "SCENE_OBJECT", "MATERIAL_ANIMATION", "VIEWPORT_NONMESH", "DOMAIN_GATE"], browser: "chromium", nativeEngineAlreadyBound: true }, policy: { historicalEvidencePreserved: true, authorizedRebaselineOnly: true, ledgerClaimsConservative: true, fullHistoricalReproduction: false, inventoryRemainderExplicit: true }, nextTask: "WBV2-P12-001" };
|
|
fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-000.md"), `# WBV2-P12-000: domain expansion continuation root\n\n- task: WBV2-P12-000\n- parent: WBV2-P11-004\n- status: done\n- gap: workflow:authorize_domain_expansion_continuation\n- ownerFamily: PROGRAM\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nAuthorize the next high-value Blender Web domains after P11 established engine-bound Mesh, Modifier, and Asset evidence. P12 uses existing source receipts first, then adds only bounded native/WASM domain behavior.\n\n## 验收\n\n- \`node tools/web/generate-wbv2-domain-expansion-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-P12-000/\`\n- Manifest: \`tests/golden/WBV2-P12-000/manifest.json\`\n- Rollback: restore P11-004 as queue parent without rewriting prior evidence.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-001.md"), `# WBV2-P12-001: reconcile existing capability ledger\n\n- task: WBV2-P12-001\n- parent: WBV2-P12-000\n- status: in_progress\n- gap: workflow:reconcile_existing_capability_ledger\n- ownerFamily: PARITY\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nAggregate existing WBV2 P1-P6 and P10-P11 manifests into a conservative capability ledger without inventing claims or rewriting historical receipts.\n\n## 输入与范围\n\n- Capability matrix: \`docs/BLENDER_WASM_CAPABILITY_MATRIX_TEMPLATE.json\`\n- Existing WBV2 manifests: \`tests/golden/WBV2-P1-001/manifest.json\` through completed domain manifests\n- P11 gate: \`tests/golden/WBV2-P11-004/engine-module-gate-report.json\`\n- Inventory map: \`tests/golden/WBV2-P0-002/parity-map.json\`\n\n## 验收\n\n- \`node tools/web/generate-wbv2-capability-ledger.mjs --write\`\n- \`node tools/web/check-wbv2-capability-ledger.mjs\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P12-001 --write\`\n\nMalformed manifest, hash drift, unsupported evidence status, false parity claim, or inventory over-claim must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P12-001/\`\n- Status: \`docs/status/WBV2-P12-001.md\`\n- Manifest: \`tests/golden/WBV2-P12-001/manifest.json\`\n- Rollback: restore P12-000 as queue parent; preserve all source manifests.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-002.md"), `# WBV2-P12-002: scene and object engine receipt\n\n- task: WBV2-P12-002\n- parent: WBV2-P12-001\n- status: planned\n- gap: workflow:scene_object_workflow_engine_receipt\n- ownerFamily: EDITOR\n- targetImplementationClass: LOCAL_EXACT\n\n## 目标\n\nBind object visibility and transform edits to the compiled WebEngine WASM Main with save/reopen and revision guards.\n\n## 输入与范围\n\n- Scene fixture: \`tests/files/web/generated/WBV2-P2-002-OBJECT.TRANSFORM_HIERARCHY.blend\`\n- Source oracle: \`tests/golden/WBV2-P2-002/object-transform-report.json\`\n- Client: \`web/app/src/engine-client/WebEngineClient.ts\`\n\n## 验收\n\n- \`npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-scene-object-production.spec.ts\`\n- \`node tools/web/generate-wbv2-scene-object-receipt.mjs --write\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P12-002 --write\`\n\nMalformed, stale, locked, cycle, unsupported, non-Chromium, or non-exact save/reopen cases must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P12-002/\`\n- Status: \`docs/status/WBV2-P12-002.md\`\n- Manifest: \`tests/golden/WBV2-P12-002/manifest.json\`\n- Rollback: restore P12-001 as queue parent.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-003.md"), `# WBV2-P12-003: material and animation engine receipt\n\n- task: WBV2-P12-003\n- parent: WBV2-P12-002\n- status: planned\n- gap: workflow:material_animation_engine_receipt\n- ownerFamily: AUTHORING\n- targetImplementationClass: LOCAL_EXACT\n\n## 目标\n\nBind material Principled/texture and TRS keyframe edits to the compiled WebEngine WASM Main.\n\n## 输入与范围\n\n- Material fixture: \`tests/files/web/generated/WBV2-P3-001-MATERIAL.PBR_UV.blend\`\n- Animation fixture: \`tests/files/web/generated/WBV2-P3-002-ANIMATION.TRS_ACTION.blend\`\n- Existing reports: \`tests/golden/WBV2-P3-001/material-report.json\`, \`tests/golden/WBV2-P3-002/animation-report.json\`\n\n## 验收\n\n- \`npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-material-animation-production.spec.ts\`\n- \`node tools/web/generate-wbv2-material-animation-receipt.mjs --write\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P12-003 --write\`\n\nMalformed graph, unsupported node, stale frame, missing image, non-Chromium, or non-exact save/reopen cases must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P12-003/\`\n- Status: \`docs/status/WBV2-P12-003.md\`\n- Manifest: \`tests/golden/WBV2-P12-003/manifest.json\`\n- Rollback: restore P12-002 as queue parent.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-004.md"), `# WBV2-P12-004: viewport and non-mesh engine receipt\n\n- task: WBV2-P12-004\n- parent: WBV2-P12-003\n- status: planned\n- gap: workflow:viewport_nonmesh_engine_receipt\n- ownerFamily: VIEWPORT\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nBind viewport SceneIR and bounded non-mesh read/edit paths to the compiled WebEngine runtime with explicit unsupported boundaries.\n\n## 输入与范围\n\n- Viewport report: \`tests/golden/WBV2-P2-004/viewport-report.json\`\n- Non-mesh report: \`tests/golden/WBV2-P4-003/sculpt-report.json\`\n- WebEngine client: \`web/app/src/engine-client/WebEngineClient.ts\`\n\n## 验收\n\n- \`npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-viewport-nonmesh-production.spec.ts\`\n- \`node tools/web/generate-wbv2-viewport-nonmesh-receipt.mjs --write\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P12-004 --write\`\n\nUnsupported data, malformed geometry, budget overflow, device loss, non-Chromium, or non-exact save/reopen cases must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P12-004/\`\n- Status: \`docs/status/WBV2-P12-004.md\`\n- Manifest: \`tests/golden/WBV2-P12-004/manifest.json\`\n- Rollback: restore P12-003 as queue parent.\n`);
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P12-005.md"), `# WBV2-P12-005: domain parity gate\n\n- task: WBV2-P12-005\n- parent: WBV2-P12-004\n- status: planned\n- gap: workflow:recompute_domain_parity_gate\n- ownerFamily: RELEASE\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRecompute domain parity from P10-P12 receipts while retaining explicit inventory and provenance limits.\n\n## 输入与范围\n\n- Capability ledger: \`tests/golden/WBV2-P12-001/capability-ledger-report.json\`\n- P11 gate: \`tests/golden/WBV2-P11-004/engine-module-gate-report.json\`\n- P12 domain receipts\n- Provenance approval: \`docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json\`\n\n## 验收\n\n- \`node tools/web/generate-wbv2-domain-parity-gate.mjs --write\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P12-005 --write\`\n\nMissing receipt, hash drift, false inventory completion, unsupported browser, undeclared claim, or false historical reproduction must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P12-005/\`\n- Status: \`docs/status/WBV2-P12-005.md\`\n- Manifest: \`tests/golden/WBV2-P12-005/manifest.json\`\n- Rollback: restore P12-004 as queue parent.\n`);
|
|
fs.writeFileSync(absolute(rootStatusPath), `# WBV2-P12-000 status\n\n- task: \`WBV2-P12-000\`; parent: \`WBV2-P11-004\`; status: \`done\`\n- nextTask: \`WBV2-P12-001\`\n\nDomain expansion is authorized after P11 engine-bound evidence. P12 first builds a conservative capability ledger from existing receipts, then adds scene, authoring, viewport, and non-mesh coverage.\n`);
|
|
|
|
p11.nextTask = "WBV2-P12-000";
|
|
p11.continuation = { authorized: true, root: "WBV2-P12-000", scope: "DOMAIN_EXPANSION" };
|
|
p11Report.nextTask = "WBV2-P12-000";
|
|
p11Report.policy = { ...(p11Report.policy ?? {}), terminalQueuePointer: false, authorizedContinuation: "WBV2-P12-000" };
|
|
fs.writeFileSync(absolute(p11ReportPath), `${JSON.stringify(p11Report, null, 2)}\n`);
|
|
const currentP11Status = read(p11StatusPath).trim();
|
|
fs.writeFileSync(absolute(p11StatusPath), currentP11Status.includes("authorized continuation: `WBV2-P12-000`") ? `${currentP11Status}\n` : `${currentP11Status}\n- authorized continuation: \`WBV2-P12-000\`\n\nDomain expansion may continue under a new manifest root. This does not change P11 evidence or the authorized-rebaseline-only release scope.\n`);
|
|
fs.writeFileSync(absolute(p11ManifestPath), `${JSON.stringify(p11, null, 2)}\n`);
|
|
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P11-004", "--write"], { cwd: root, stdio: "pipe" });
|
|
p11.artifacts = Object.fromEntries(Object.entries(p11.artifacts).map(([name, artifact]) => [name, { ...artifact, sha256: sha256(artifact.path) }]));
|
|
fs.writeFileSync(absolute(p11ManifestPath), `${JSON.stringify(p11, null, 2)}\n`);
|
|
const artifactPaths = { parentManifest: p11ManifestPath, parentReport: p11ReportPath, approval: "docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json", plan: planPath, report: reportPath, rootCard: "docs/tasks/WBV2-P12-000.md", rootStatus: rootStatusPath, activeCard: "docs/tasks/WBV2-P12-001.md" };
|
|
const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }]));
|
|
const manifest = { schemaVersion: 1, task: "WBV2-P12-000", parentTask: "WBV2-P11-004", operation: "AUTHORIZE_DOMAIN_EXPANSION_CONTINUATION", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { p11EvidencePreserved: true, existingSourceEvidenceReusable: true, nativeEngineAlreadyBound: true, authorizedRebaselineOnly: true, fullHistoricalReproduction: false, domainScope: ["CAPABILITY_LEDGER", "SCENE_OBJECT", "MATERIAL_ANIMATION", "VIEWPORT_NONMESH"] }, nextTask: "WBV2-P12-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-P12-001` |")
|
|
.replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P12-000/manifest.json` |")
|
|
.replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P12-001.md`](tasks/WBV2-P12-001.md) |");
|
|
fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
|
|
let nextTaskFile = read("nextTask.md");
|
|
nextTaskFile = nextTaskFile.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P12-001` |")
|
|
.replace(/\| Current task state \| `[^`]+` \|/u, "| Current task state | `in_progress` |")
|
|
.replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P12-000/manifest.json` |")
|
|
.replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P12-001.md` |")
|
|
.replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P12-002` |")
|
|
.replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `node tools/web/generate-wbv2-capability-ledger.mjs --write` |");
|
|
fs.writeFileSync(absolute("nextTask.md"), nextTaskFile);
|
|
process.stdout.write("wbv2-domain-expansion-root-ok root=WBV2-P12-000 next=WBV2-P12-001 scope=CAPABILITY_LEDGER,SCENE_OBJECT,MATERIAL_ANIMATION,VIEWPORT_NONMESH\n");
|