Advance deduplicated object deletion coverage
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-28 16:59:15 +08:00
parent 69fc086b12
commit 3ab47ae2e9
39 changed files with 2153 additions and 35 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
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 p21Path = "tests/golden/WBV2-P21-003/manifest.json";
const p21ReportPath = "tests/golden/WBV2-P21-003/object-rename-gate-report.json";
const out = "tests/golden/WBV2-P22-000";
const planPath = `${out}/delete-task-plan.json`;
const reportPath = `${out}/delete-root-report.json`;
const manifestPath = `${out}/manifest.json`;
const statusPath = "docs/status/WBV2-P22-000.md";
const inventoryPath = "tests/golden/WBV2-P0-002/parity-map.json";
const apiPath = "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp";
const clientPath = "web/app/src/engine-client/WebEngineClient.ts";
const p21 = json(p21Path);
const p21Report = json(p21ReportPath);
const inventory = json(inventoryPath);
assert.equal(p21.status, "done");
assert.ok(p21.nextTask === null || p21.nextTask === "WBV2-P22-000");
assert.equal(p21Report.status, "PASS");
assert.equal(p21Report.decision.effectivePending, 6611);
assert.equal(inventory.summary.inventoryEntries, 6900);
for (const artifact of Object.values(p21.artifacts ?? {})) assert.equal(sha(artifact.path), artifact.sha256, artifact.path);
assert.match(read(apiPath), /deleteObject/);
assert.match(read(clientPath), /applyCommand/);
const tasks = [
{ id: "WBV2-P22-000", phase: "P22", title: "authorize_object_delete_continuation", owner: "PROGRAM", dependsOn: ["WBV2-P21-003"], status: "done", ready: false },
{ id: "WBV2-P22-001", phase: "P22", title: "select_object_delete_batch", owner: "PARITY", dependsOn: ["WBV2-P22-000"], status: "in_progress", ready: true },
{ id: "WBV2-P22-002", phase: "P22", title: "object_delete_engine_receipt", owner: "OBJECT", dependsOn: ["WBV2-P22-001"], status: "planned", ready: false },
{ id: "WBV2-P22-003", phase: "P22", title: "close_object_delete_gate", owner: "RELEASE", dependsOn: ["WBV2-P22-002"], status: "planned", ready: false },
];
for (const task of tasks) { task.card = `docs/tasks/${task.id}.md`; task.acceptance = ["source and evidence hashes are bound", "unique behavior is verified once", "save/reopen remains exact", "malformed input fails closed"]; task.implementationClass = task.id.endsWith("001") ? "LOCAL_BOUNDED" : task.id.endsWith("002") ? "LOCAL_EXACT" : "LOCAL_BOUNDED"; task.gate = task.id.endsWith("003") ? "G5" : "G2"; task.parityId = task.id.includes("001") || task.id.includes("002") ? "OBJECT_OPERATIONS.DELETE" : null; }
const plan = { schemaVersion: 1, planId: "BLENDER_WEB_PARITY_V2_OBJECT_DELETE_EPOCH_1", status: "ACTIVE_OBJECT_DELETE_QUEUE", authority: "queue_reactivated_after_p21_rename_gate", sourcePlan: "docs/BLENDER_WEB_PARITY_TASK_PLAN_V2.json", executionPointer: "docs/EXECUTION_QUEUE.md", runtimePolicy: { blenderSource: "blender-5.2.0", browserEvidence: ["chromium"], forbiddenBrowsers: ["firefox", "webkit"], mainAuthority: "blender_main", reusePolicy: "source_hash_and_behavior_hash_deduplication" }, parentRename: { manifest: p21Path, report: p21ReportPath, basePending: 6618, effectivePending: 6611, duplicateValidationAvoided: true }, tasks };
fs.mkdirSync(abs(out), { recursive: true });
fs.writeFileSync(abs(planPath), `${JSON.stringify(plan, null, 2)}\n`);
const report = { schemaVersion: 1, task: "WBV2-P22-000", operation: "AUTHORIZE_OBJECT_DELETE_CONTINUATION", status: "PASS", parent: { task: p21.task, manifest: p21Path, manifestSha256: sha(p21Path), report: p21ReportPath, reportSha256: sha(p21ReportPath), previousDecision: p21Report.decision.status, effectivePending: 6611 }, continuation: { root: "WBV2-P22-000", firstTask: "WBV2-P22-001", plannedTasks: 4, candidate: "OBJECT_OPERATIONS.DELETE", sourceApi: apiPath, browser: "chromium", noRepeatedExecution: true }, policy: { p21EvidencePreserved: true, sourceHistoryReadOnly: true, representativeSliceOnly: true, duplicateValidationAvoided: true, authorizedRebaselineOnly: true, fullHistoricalReproduction: false }, nextTask: "WBV2-P22-001" };
fs.writeFileSync(abs(reportPath), `${JSON.stringify(report, null, 2)}\n`);
const cards = {
"WBV2-P22-000": "# WBV2-P22-000: object delete continuation root\n\n- task: WBV2-P22-000\n- parent: WBV2-P21-003\n- status: done\n- gap: workflow:authorize_object_delete_continuation\n- ownerFamily: PROGRAM\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nAuthorize one source-backed Object Delete behavior after the P21 rename overlay.\n\n## 验收\n\n- `node tools/web/generate-wbv2-delete-root.mjs`\n- `node tools/web/check-execution-control-plane.mjs`\n\n## 交付与回滚\n\n- Reports: `tests/golden/WBV2-P22-000/`\n- Manifest: `tests/golden/WBV2-P22-000/manifest.json`\n- Rollback: restore P21-003 as queue parent.\n",
"WBV2-P22-001": "# WBV2-P22-001: select_object_delete_batch\n\n- task: WBV2-P22-001\n- parent: WBV2-P22-000\n- status: in_progress\n- gap: capability:OBJECT_OPERATIONS.DELETE\n- ownerFamily: PARITY\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nSelect deleteObject as one unique Object Operations behavior.\n\n## 输入与范围\n\n- Focused source: `blender-5.2.0/source/blender/web_engine/web_engine_api.cpp`\n- Existing client: `web/app/src/engine-client/WebEngineClient.ts`\n- Inventory: `tests/golden/WBV2-P0-002/parity-map.json`\n- Production entrypoint: `node tools/web/generate-wbv2-delete-selection.mjs --write`\n\n## 验收\n\n- source command is hash-bound\n- prior object evidence is not rerun\n- one legacy operator binding is selected\n- malformed, hash-drift, unsupported, or false-completion input keeps in_progress\n- `node tools/web/check-task-context.mjs --task WBV2-P22-001 --write`\n\n## 交付与回滚\n\n- Reports: `tests/golden/WBV2-P22-001/`\n- Status: `docs/status/WBV2-P22-001.md`\n- Manifest: `tests/golden/WBV2-P22-001/manifest.json`\n- Rollback: restore P22-000 as queue parent.\n",
"WBV2-P22-002": "# WBV2-P22-002: object delete engine receipt\n\n- task: WBV2-P22-002\n- parent: WBV2-P22-001\n- status: planned\n- gap: capability:OBJECT_OPERATIONS.DELETE\n- ownerFamily: OBJECT\n- targetImplementationClass: LOCAL_EXACT\n\n## 目标\n\nBind deleteObject, undo/redo, malformed rejection, and exact save/reopen to compiled WASM Main in Chromium.\n\n## 输入与范围\n\n- Focused source: `tests/golden/WBV2-P22-001/object-delete-selection-report.json`\n- Production test: `web/tests/e2e/wbv2-object-delete-production.spec.ts`\n- Production entrypoint: `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-object-delete-production.spec.ts`\n\n## 验收\n\n- object deletion changes Main\n- undo/redo revisions are sequential\n- malformed delete fails closed without Main mutation\n- save/reopen is exact and deterministic\n- Chromium is the only browser\n- `node tools/web/check-task-context.mjs --task WBV2-P22-002 --write`\n\n## 交付与回滚\n\n- Reports: `tests/golden/WBV2-P22-002/`\n- Status: `docs/status/WBV2-P22-002.md`\n- Manifest: `tests/golden/WBV2-P22-002/manifest.json`\n- Rollback: restore P22-001 as queue parent.\n",
"WBV2-P22-003": "# WBV2-P22-003: close object delete gate\n\n- task: WBV2-P22-003\n- parent: WBV2-P22-002\n- status: planned\n- gap: workflow:close_object_delete_gate\n- ownerFamily: RELEASE\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nClose the deleteObject gate and graduate only the exact operator binding.\n\n## 输入与范围\n\n- Focused receipt: `tests/golden/WBV2-P22-002/object-delete-receipt-report.json`\n- Previous overlay: `tests/golden/WBV2-P21-003/inventory-completion-overlay.json`\n- Production entrypoint: `node tools/web/generate-wbv2-delete-gate.mjs --write`\n\n## 验收\n\n- only object delete is claimed\n- effective inventory remains explicit\n- duplicate validation remains zero\n- malformed, hash-drift, unsupported, or false-completion input keeps in_progress\n- historical reproduction remains false\n- `node tools/web/check-task-context.mjs --task WBV2-P22-003 --write`\n\n## 交付与回滚\n\n- Reports: `tests/golden/WBV2-P22-003/`\n- Status: `docs/status/WBV2-P22-003.md`\n- Manifest: `tests/golden/WBV2-P22-003/manifest.json`\n- Rollback: restore P22-002 as queue parent.\n",
};
for (const [id, card] of Object.entries(cards)) fs.writeFileSync(abs(`docs/tasks/${id}.md`), `${card}`);
fs.writeFileSync(abs(statusPath), "# WBV2-P22-000 status\n\n- task: `WBV2-P22-000`; parent: `WBV2-P21-003`; status: `done`\n- candidate: `OBJECT_OPERATIONS.DELETE`; source API is present\n- effective inventory carried forward: `288` completed, `6611` pending\n- duplicate validation: `forbidden`; representative slice only\n- nextTask: `WBV2-P22-001`\n");
p21Report.nextTask = "WBV2-P22-000"; p21Report.policy = { ...(p21Report.policy ?? {}), terminalQueuePointer: false, authorizedContinuation: "WBV2-P22-000" }; fs.writeFileSync(abs(p21ReportPath), `${JSON.stringify(p21Report, null, 2)}\n`); p21.nextTask = "WBV2-P22-000"; p21.continuation = { authorized: true, root: "WBV2-P22-000", scope: "OBJECT_OPERATIONS_DELETE" }; fs.writeFileSync(abs(p21Path), `${JSON.stringify(p21, null, 2)}\n`); execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", "WBV2-P21-003", "--write"], { cwd: root, stdio: "pipe" }); p21.artifacts = Object.fromEntries(Object.entries(p21.artifacts).map(([name, artifact]) => [name, { ...artifact, sha256: sha(artifact.path) }])); fs.writeFileSync(abs(p21Path), `${JSON.stringify(p21, null, 2)}\n`); report.parent.manifestSha256 = sha(p21Path); report.parent.reportSha256 = sha(p21ReportPath); fs.writeFileSync(abs(reportPath), `${JSON.stringify(report, null, 2)}\n`);
const paths = { parentManifest: p21Path, parentReport: p21ReportPath, inventory: inventoryPath, sourceApi: apiPath, sourceClient: clientPath, plan: planPath, report: reportPath, rootCard: "docs/tasks/WBV2-P22-000.md", rootStatus: statusPath }; const artifacts = Object.fromEntries(Object.entries(paths).map(([name, file]) => [name, { path: file, sha256: sha(file) }])); fs.writeFileSync(abs(manifestPath), `${JSON.stringify({ schemaVersion: 1, task: "WBV2-P22-000", parentTask: "WBV2-P21-003", operation: "AUTHORIZE_OBJECT_DELETE_CONTINUATION", status: "done", enablingTask: true, parityStateChange: false, runtime: "READ_ONLY_OBJECT_DELETE_CONTINUATION_ROOT", artifacts, assertions: { p21EvidencePreserved: true, sourceApiBound: true, inventoryTotal: 6900, effectiveCompleted: 288, effectivePending: 6611, duplicateValidationAvoided: true, representativeSliceOnly: true, authorizedRebaselineOnly: true, fullHistoricalReproduction: false }, nextTask: "WBV2-P22-001" }, null, 2)}\n`);
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-P22-001` |").replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P22-000/manifest.json` |").replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P22-001.md`](tasks/WBV2-P22-001.md) |"); fs.writeFileSync(abs("docs/EXECUTION_QUEUE.md"), queue); let next = read("nextTask.md"); next = next.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P22-001` |").replace(/\| Current task state \| `[^`]+` \|/u, "| Current task state | `in_progress` |").replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P22-000/manifest.json` |").replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P22-001.md` |").replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P22-002` |").replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `node tools/web/generate-wbv2-delete-selection.mjs --write` |"); fs.writeFileSync(abs("nextTask.md"), next); console.log("wbv2-delete-root-written root=WBV2-P22-000 next=WBV2-P22-001 candidate=OBJECT_OPERATIONS.DELETE effectivePending=6611");

View File

@@ -0,0 +1,60 @@
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 digest = (value) => crypto.createHash("sha256").update(value).digest("hex");
const out = "tests/golden/WBV2-P22-001";
const parentPath = "tests/golden/WBV2-P22-000/manifest.json";
const inventoryPath = "tests/golden/WBV2-P0-002/parity-map.json";
const apiPath = "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp";
const protocolPath = "web/protocol/web-engine.ts";
const planPath = `${out}/delete-task-plan.json`;
const reportPath = `${out}/object-delete-selection-report.json`;
const candidatePath = `${out}/object-delete-candidate-set.json`;
const manifestPath = `${out}/manifest.json`;
const statusPath = "docs/status/WBV2-P22-001.md";
const parent = json(parentPath);
const inventory = json(inventoryPath);
assert.equal(parent.status, "done");
assert.equal(parent.nextTask, "WBV2-P22-001");
for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha(artifact.path), artifact.sha256, artifact.path);
assert.equal(inventory.summary.inventoryEntries, 6900);
assert.equal(inventory.summary.pendingLegacyEntries, 6618);
assert.match(read(apiPath), /deleteObject/);
assert.match(read(protocolPath), /deleteObject/);
const target = inventory.entries.find((entry) => entry.id === "operator:object.delete");
assert.ok(target);
assert.equal(target.legacyState, "pending");
const behavior = { capability: "OBJECT_OPERATIONS.DELETE", operation: "deleteObject", positive: ["object-delete", "undo", "redo", "save-reopen"], negative: ["missing-id", "linked-object"], browser: "chromium", engine: "compiled-WASM-WebEngine-Main" };
const behaviorHash = digest(JSON.stringify(behavior));
const candidate = { schemaVersion: 1, task: "WBV2-P22-001", operation: "OBJECT_DELETE_CANDIDATE_SELECTION", status: "READY", source: { inventory: inventoryPath, inventorySha256: sha(inventoryPath), api: apiPath, apiSha256: sha(apiPath), protocol: protocolPath, protocolSha256: sha(protocolPath) }, candidate: { capabilityId: "OBJECT_OPERATIONS.DELETE", legacyId: target.id, legacyTask: target.evidence.legacyTask, behavior, behaviorHash, dedupeKey: `OBJECT_OPERATIONS.DELETE|${behaviorHash}|${sha(apiPath)}|${sha(protocolPath)}`, sourceBacked: true, chromiumProductionReady: true, fullFamilyParity: false }, inventory: { matchedEntries: 1, pending: 1, total: 6900, basePending: 6618, effectivePending: 6611, remainderExplicit: true }, entries: [{ id: target.id, sourceTask: target.sourceTask, legacyState: target.legacyState, targetImplementationClass: target.targetImplementationClass }], policy: { sourceHistoryReadOnly: true, representativeSliceOnly: true, noRepeatedExecution: true, duplicateValidationAvoided: true, fullHistoricalReproduction: false } };
const report = { schemaVersion: 1, task: "WBV2-P22-001", operation: "SELECT_OBJECT_DELETE_BATCH", status: "PASS", candidate: candidate.candidate, inventory: candidate.inventory, source: candidate.source, matchedEntryDigest: digest(target.id), efficiency: { oneEntryScan: true, existingEvidenceReused: true, repeatedBlenderRuns: 0, repeatedChromiumRuns: 0, duplicateValidationAvoided: true }, nextTask: "WBV2-P22-002" };
if (!process.argv.includes("--write")) { console.log(`wbv2-delete-selection-ok task=WBV2-P22-001 legacyId=${target.id} behaviorHash=${behaviorHash} pending=6611 repeatedRuns=0 next=WBV2-P22-002`); process.exit(0); }
fs.mkdirSync(abs(out), { recursive: true });
fs.writeFileSync(abs(candidatePath), `${JSON.stringify(candidate, null, 2)}\n`);
fs.writeFileSync(abs(reportPath), `${JSON.stringify(report, null, 2)}\n`);
fs.writeFileSync(abs(statusPath), `# WBV2-P22-001 status\n\n- task: \`WBV2-P22-001\`; parent: \`WBV2-P22-000\`; status: \`done\`\n- selected behavior: \`deleteObject\`; exact candidate: \`operator:object.delete\`\n- base inventory: \`6900\` total, \`6618\` pending; effective carried overlay: \`6611\` pending\n- behavior hash: \`${behaviorHash}\`; duplicate validation: \`forbidden\`\n- nextTask: \`WBV2-P22-002\`\n`);
const previousPlan = json("tests/golden/WBV2-P22-000/delete-task-plan.json");
const nextPlan = structuredClone(previousPlan);
nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P22-000/delete-task-plan.json", sha256: sha("tests/golden/WBV2-P22-000/delete-task-plan.json") };
nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P22-001" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P22-002" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false });
fs.writeFileSync(abs(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`);
fs.writeFileSync(abs("docs/tasks/WBV2-P22-002.md"), read("docs/tasks/WBV2-P22-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" });
execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", "WBV2-P22-001", "--write"], { cwd: root, stdio: "inherit" });
const artifactPaths = { parentManifest: parentPath, inventory: inventoryPath, api: apiPath, protocol: protocolPath, candidate: candidatePath, report: reportPath, generator: "tools/web/generate-wbv2-delete-selection.mjs", status: statusPath, parityPlan: planPath, taskContext: `${out}/task-context.json` };
const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha(file) }]));
fs.writeFileSync(abs(manifestPath), `${JSON.stringify({ schemaVersion: 1, task: "WBV2-P22-001", parentTask: "WBV2-P22-000", operation: "SELECT_OBJECT_DELETE_BATCH", status: "done", enablingTask: true, parityStateChange: false, runtime: "READ_ONLY_SOURCE_CANDIDATE_SELECTION", artifacts, assertions: { legacyId: target.id, inventoryTotal: 6900, basePending: 6618, effectivePending: 6611, sourceApiBound: true, representativeSliceOnly: true, fullFamilyParity: false, repeatedBlenderRuns: 0, repeatedChromiumRuns: 0, duplicateValidationAvoided: true, deterministic: true, historicalReproduction: false }, nextTask: "WBV2-P22-002" }, null, 2)}\n`);
let queue = read("docs/EXECUTION_QUEUE.md"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P22-002` |").replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P22-001/manifest.json` |").replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P22-002.md`](tasks/WBV2-P22-002.md) |"); fs.writeFileSync(abs("docs/EXECUTION_QUEUE.md"), queue);
let nextTask = read("nextTask.md"); nextTask = nextTask.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P22-002` |").replace(/\| Current task state \| `[^`]+` \|/u, "| Current task state | `in_progress` |").replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P22-001/manifest.json` |").replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P22-002.md` |").replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P22-003` |").replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-object-delete-production.spec.ts` |"); fs.writeFileSync(abs("nextTask.md"), nextTask);
console.log(`wbv2-delete-selection-written task=WBV2-P22-001 legacyId=${target.id} behaviorHash=${behaviorHash} next=WBV2-P22-002 artifacts=${Object.keys(artifacts).length}`);