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 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-P9-003"; const chromiumPath = `${outputRoot}/chromium-asset-catalog-production.json`; const chromiumSecondPath = `${outputRoot}/chromium-asset-catalog-production.second.json`; const reportPath = `${outputRoot}/asset-catalog-receipt-report.json`; const statusPath = "docs/status/WBV2-P9-003.md"; const planPath = `${outputRoot}/feature-parity-task-plan.json`; const manifestPath = `${outputRoot}/manifest.json`; const parentManifestPath = "tests/golden/WBV2-P9-002/manifest.json"; const sourceManifestPath = "tests/golden/corrective/C3-003/manifest.json"; const sourceReportPath = "tests/golden/corrective/C3-003/receipt-report.json"; const parent = readJson(parentManifestPath); assert.equal(parent.task, "WBV2-P9-002"); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, "WBV2-P9-003"); for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path); const sourceManifest = readJson(sourceManifestPath); const sourceReport = readJson(sourceReportPath); assert.equal(sourceManifest.task, "C3-003"); assert.equal(sourceManifest.status, "done"); assert.equal(sourceReport.task, "C3-003"); assert.equal(sourceReport.browser, "chromium"); assert.equal(sourceReport.queueMutation, false); assert.equal(sourceReport.parity, true); assert.equal(sourceReport.positive.desktop.status, "FINISHED"); assert.equal(sourceReport.positive.wasm.status, "FINISHED"); assert.equal(sourceReport.persistence.dirty, false); const sourceBound = Object.values(sourceManifest.artifacts ?? {}).find((artifact) => artifact.path === sourceReportPath); assert.ok(sourceBound, "C3-003 source report is not manifest-bound"); assert.equal(sourceBound.sha256, sha256(sourceReportPath)); assert.equal(sha256(chromiumPath), sha256(chromiumSecondPath), "Chromium Asset Catalog report is not deterministic"); const chromium = readJson(chromiumPath); assert.equal(chromium.task, "WBV2-P9-003"); assert.equal(chromium.operation, "ASSET_CATALOG_PRODUCTION_CHROMIUM"); assert.equal(chromium.browser, "chromium"); assert.equal(chromium.queueMutation, false); assert.equal(chromium.positive.parity, true); for (const surface of ["desktop", "wasm"]) { const result = chromium.positive[surface]; assert.equal(result.status, "FINISHED"); assert.equal(result.code, "OK"); assert.equal(result.mainRevisionBefore, 4); assert.equal(result.mainRevisionAfter, 5); assert.equal(result.mainMutation, "CHANGED"); assert.deepEqual(result.delta, { addedId: "asset:wbv2-p9-003", count: 1 }); } assert.deepEqual(chromium.positive.desktop, chromium.positive.wasm); assert.equal(chromium.resource.provider, "EXTERNAL_LIBRARY"); assert.match(chromium.resource.sha256, /^[a-f0-9]{64}$/u); assert.equal(chromium.resource.byteLength, 27); assert.equal(chromium.positive.desktopMain.value.entries[0].sourceSha256, chromium.resource.sha256); assert.equal(chromium.negative.duplicate.code, "COMMAND_UNSUPPORTED"); assert.equal(chromium.negative.stale.code, "REVISION_CONFLICT"); assert.equal(chromium.negative.malformed.code, "COMMAND_MALFORMED"); assert.equal(chromium.negative.missing.code, "RESOURCE_REQUIRED"); assert.equal(chromium.negative.missing.mainRevisionAfter, 4); assert.equal(chromium.persistence.exact, true); assert.deepEqual(chromium.persistence.reopened, chromium.persistence.saved); const receipt = { schemaVersion: 1, task: "WBV2-P9-003", operation: "ASSET_CATALOG_PRODUCTION_CHROMIUM_RECEIPT", status: "PASS", browser: "chromium", queueMutation: false, source: { task: "C3-003", manifest: sourceManifestPath, manifestSha256: sha256(sourceManifestPath), report: sourceReportPath, reportSha256: sha256(sourceReportPath), exactChromiumPilot: true, pilotSaveReopenState: sourceReport.persistence.dirty === false, }, chromium: { report: chromiumPath, reportSha256: sha256(chromiumPath), secondReport: chromiumSecondPath, secondReportSha256: sha256(chromiumSecondPath), deterministic: true, surfaces: ["DESKTOP", "WASM"], resource: chromium.resource, positive: { desktop: chromium.positive.desktop, wasm: chromium.positive.wasm, parity: chromium.positive.parity }, negative: { duplicate: chromium.negative.duplicate, stale: chromium.negative.stale, malformed: chromium.negative.malformed, missing: chromium.negative.missing }, persistence: { exact: chromium.persistence.exact, saved: chromium.persistence.saved, reopened: chromium.persistence.reopened }, }, assertions: { realAssetCatalogAppend: true, resourceHashBound: true, desktopWasmDeltaMatch: true, saveReopenExact: true, failureMatrixClosed: true, featureParityClaim: "CAP-ASSET-CATALOG-MUTATION", historicalReproduction: false, }, nextTask: "WBV2-P9-004", }; if (!write) { process.stdout.write("wbv2-asset-catalog-receipt-ok task=WBV2-P9-003 deterministic=true next=WBV2-P9-004\n"); process.exit(0); } fs.mkdirSync(absolute(outputRoot), { recursive: true }); fs.writeFileSync(absolute(reportPath), `${JSON.stringify(receipt, null, 2)}\n`); const status = `# WBV2-P9-003 status\n\n- task: \`WBV2-P9-003\`; parent: \`WBV2-P9-002\`; status: \`done\`\n- Asset Catalog Chromium receipt: \`DESKTOP + WASM\`\n- resource: \`EXTERNAL_LIBRARY/library.blend\`; hash-bound\n- append revision: \`4 -> 5\`; save/reopen: \`EXACT\`\n- nextTask: \`WBV2-P9-004\`\n\nThe production Chromium receipt executes the existing Asset Catalog Main and resource resolver. Duplicate, stale, malformed, and missing-resource commands are rejected without a late mutation. This is a bounded current implementation claim and does not assert historical byte reproduction.\n`; fs.writeFileSync(absolute(statusPath), status); const previousPlan = readJson("tests/golden/WBV2-P9-002/feature-parity-task-plan.json"); const nextPlan = structuredClone(previousPlan); nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P9-002/feature-parity-task-plan.json", sha256: sha256("tests/golden/WBV2-P9-002/feature-parity-task-plan.json") }; nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P9-003" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P9-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false }); fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`); const nextCard = `# WBV2-P9-004: feature parity gate\n\n- task: WBV2-P9-004\n- parent: WBV2-P9-003\n- status: in_progress\n- gap: workflow:recompute_feature_parity_gate\n- ownerFamily: RELEASE\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRecompute declared feature parity claims only from the three production Chromium receipts and preserve the authorized-rebaseline-only release boundary.\n\n## 输入与范围\n\n- Mesh receipt: \`tests/golden/WBV2-P9-001/mesh-receipt-report.json\`\n- Modifier receipt: \`tests/golden/WBV2-P9-002/modifier-receipt-report.json\`\n- Asset receipt: \`tests/golden/WBV2-P9-003/asset-catalog-receipt-report.json\`\n- M12 approval: \`docs/status/M12-PROVENANCE-EPOCH-2-APPROVAL.json\`\n\n## 验收\n\n- \`node tools/web/generate-wbv2-feature-parity-gate.mjs --write\`\n- \`node tools/web/check-task-context.mjs --task WBV2-P9-004 --write\`\n\nMissing receipt, hash drift, unsupported browser, non-exact save/reopen, false historical reproduction, or undeclared feature claim must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P9-004/\`\n- Status: \`docs/status/WBV2-P9-004.md\`\n- Manifest: \`tests/golden/WBV2-P9-004/manifest.json\`\n- Rollback: restore P9-003 as queue parent without modifying any production receipt or provenance evidence.\n`; fs.writeFileSync(absolute("docs/tasks/WBV2-P9-004.md"), 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-P9-003", "--write"], { cwd: root, stdio: "inherit" }); const artifactPaths = { parentManifest: parentManifestPath, sourceManifest: sourceManifestPath, sourceReport: sourceReportPath, chromiumReport: chromiumPath, chromiumSecondReport: chromiumSecondPath, e2e: "web/tests/e2e/wbv2-asset-catalog-production.spec.ts", protocol: "web/protocol/corrective-asset-catalog-command.ts", resourceProvider: "web/protocol/corrective-resource-provider.ts", bridge: "web/app/src/corrective-pilot-bridge.ts", generator: "tools/web/generate-wbv2-asset-catalog-receipt.mjs", report: reportPath, status: statusPath, featureParityPlan: 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-P9-003", parentTask: "WBV2-P9-002", operation: "ASSET_CATALOG_PRODUCTION_CHROMIUM_RECEIPT", status: "done", enablingTask: false, parityStateChange: true, runtime: "PLAYWRIGHT_CHROMIUM_DESKTOP_AND_WASM", artifacts, assertions: { capabilityId: "CAP-ASSET-CATALOG-MUTATION", browser: "chromium", surfaces: 2, deterministic: true, resourceHashBound: true, saveReopen: "EXACT", negativeCases: 4, historicalReproduction: false }, nextTask: "WBV2-P9-004", }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`); let queue = read("docs/EXECUTION_QUEUE.md"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P9-004` |") .replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P9-003/manifest.json` |") .replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P9-004.md`](tasks/WBV2-P9-004.md) |"); fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue); execFileSync(process.execPath, [absolute("tools/web/check-v2-task-index.mjs")], { cwd: root, stdio: "pipe" }); process.stdout.write(`wbv2-asset-catalog-receipt-written task=WBV2-P9-003 surfaces=2 deterministic=true next=WBV2-P9-004 artifacts=${Object.keys(artifacts).length}\n`);