perf(web): add idempotent batch verification fast paths
This commit is contained in:
@@ -4,6 +4,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { batchContract, commandBudget, completedBatch } from "./wbv2-batch-contract.mjs";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const abs = (file) => path.join(root, file);
|
||||
@@ -11,6 +12,7 @@ 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 dynamicInventoryPath = "tests/golden/WBV2/dynamic-inventory.json";
|
||||
const out = `tests/golden/${process.argv[process.argv.indexOf("--task") + 1]}`;
|
||||
const writeJson = (file, value) => {
|
||||
fs.mkdirSync(path.dirname(abs(file)), { recursive: true });
|
||||
fs.writeFileSync(abs(file), `${JSON.stringify(value, null, 2)}\n`);
|
||||
@@ -35,8 +37,28 @@ const parentId = record.dependsOn?.at(-1);
|
||||
const parentPath = `tests/golden/${parentId}/manifest.json`;
|
||||
const parent = json(parentPath);
|
||||
if (parent.status !== "done" || parent.nextTask !== task) throw new Error(`module parent is not a completed handoff: ${parentId}`);
|
||||
if (record.status !== "in_progress" || record.ready !== true) throw new Error(`module task ${task} is not active`);
|
||||
if (!record.module.sourceBacked) throw new Error(`module ${record.module.moduleId} has no source-backed implementation`);
|
||||
const contract = batchContract(task, record.module);
|
||||
if (!record.batchId) record.batchId = contract.batchId;
|
||||
if (!record.selectedRecordIds) record.selectedRecordIds = contract.selectedRecordIds;
|
||||
if (!record.module.batchId) record.module.batchId = contract.batchId;
|
||||
if (!record.module.selectedRecordIds) record.module.selectedRecordIds = contract.selectedRecordIds;
|
||||
const commandContract = commandBudget(record.module.commands ?? []);
|
||||
if (!commandContract.withinBudget) throw new Error(`task ${task} exceeds command budget: ${JSON.stringify(commandContract)}`);
|
||||
const existingManifestPath = `${out}/manifest.json`;
|
||||
if (fs.existsSync(abs(existingManifestPath))) {
|
||||
try {
|
||||
const existingManifest = json(existingManifestPath);
|
||||
const legacyCompleted = existingManifest.status === "done"
|
||||
&& existingManifest.nextTask === null
|
||||
&& JSON.stringify(existingManifest.selectedRecordIds ?? record.module.selectedRecordIds ?? []) === JSON.stringify(contract.selectedRecordIds);
|
||||
if (completedBatch(existingManifest, task, record.module) || legacyCompleted) {
|
||||
process.stdout.write(`module-batch-idempotent task=${task} batchId=${contract.batchId} plannerRequired=true\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
} catch { /* A partial manifest is not evidence and follows the normal path. */ }
|
||||
}
|
||||
if (record.status !== "in_progress" || record.ready !== true) throw new Error(`module task ${task} is not active`);
|
||||
for (const source of record.module.sourcePaths ?? []) if (!fs.existsSync(abs(source))) throw new Error(`module source is missing: ${source}`);
|
||||
const sourceAuditPaths = (record.module.sourcePaths ?? []).filter((source) =>
|
||||
/^(blender-5\.2\.0\/source\/blender\/|web\/protocol\/|web\/app\/src\/(?!vendor\/))/u.test(source)
|
||||
@@ -48,7 +70,6 @@ const reusedReceipts = (record.module.reuseReceipts ?? []).map((receipt) => ({
|
||||
reportSha256: sha(receipt.report),
|
||||
}));
|
||||
|
||||
const out = `tests/golden/${task}`;
|
||||
const reportPath = `${out}/module-receipt.json`;
|
||||
const statusPath = `docs/status/${task}.md`;
|
||||
const logPath = `${out}/module-run.log`;
|
||||
@@ -130,7 +151,11 @@ const report = {
|
||||
schemaVersion: 1,
|
||||
task,
|
||||
operation: "MODULE_FUNCTION_EXECUTION",
|
||||
phase: failed || !sourceUnchanged ? "BLOCKED" : "CONTINUE",
|
||||
moduleId: record.module.moduleId,
|
||||
batchId: contract.batchId,
|
||||
selectedRecordIds: contract.selectedRecordIds,
|
||||
commandBudget: commandContract,
|
||||
status: failed || !sourceUnchanged ? "BLOCKED" : "PASS",
|
||||
source: { paths: record.module.sourcePaths, auditedPaths: sourceAuditPaths, before: sourceBefore, after: sourceAfter, unchanged: sourceUnchanged },
|
||||
reusedReceipts,
|
||||
@@ -159,7 +184,7 @@ if (failed || !sourceUnchanged) {
|
||||
const manifestPath = `${out}/manifest.json`;
|
||||
const evidenceArtifacts = Object.fromEntries((record.module.evidencePaths ?? []).filter((file) => fs.existsSync(abs(file))).map((file, item) => [`evidence${item + 1}`, { path: file, sha256: sha(file) }]));
|
||||
const artifacts = { parentManifest: { path: parentPath, sha256: sha(parentPath) }, ...Object.fromEntries(record.module.sourcePaths.map((source, item) => [`source${item + 1}`, { path: source, sha256: sha(source) }])), ...evidenceArtifacts, moduleReceipt: { path: reportPath, sha256: sha(reportPath) }, status: { path: statusPath, sha256: sha(statusPath) }, log: { path: logPath, sha256: sha(logPath) }, plan: { path: index.source.path, sha256: sha(index.source.path) }, taskContext: { path: `${out}/task-context.json`, sha256: sha(`${out}/task-context.json`) } };
|
||||
writeJson(manifestPath, { schemaVersion: 1, task, parentTask: parentId, operation: "MODULE_FUNCTION_EXECUTION", taskClass: "MODULE", status: "done", enablingTask: true, parityStateChange: false, runtime: "BLENDER_WEB_FUNCTION_MODULE", artifacts, assertions: { moduleId: record.module.moduleId, sourceBacked: true, positiveCases: record.module.positive.length, negativeCases: record.module.negative.length, recoveryCases: record.module.recovery.length, commandCount: results.length, reusedReceiptCount: reusedReceipts.length, sourceUnchanged, fullFamilyParity: false, dynamicInventoryPath, dynamicSourceTreeSha256: dynamicInventory.authority.sourceTreeSha256, dynamicUnitCount: dynamicInventory.summary.unitCount, dynamicVerifiedUnits: dynamicInventory.coverage.verifiedUnits, dynamicUnverifiedUnits: dynamicInventory.coverage.unverifiedUnits, planningEstimate: dynamicInventory.policy.planningEstimate, historicalInventoryTotal: 6900, historicalEffectiveCompleted: 290, historicalEffectivePending: 6609, historicalReproduction: false }, nextTask });
|
||||
writeJson(manifestPath, { schemaVersion: 1, task, parentTask: parentId, operation: "MODULE_FUNCTION_EXECUTION", phase: "CONTINUE", taskClass: "MODULE", status: "done", enablingTask: true, parityStateChange: false, runtime: "BLENDER_WEB_FUNCTION_MODULE", batchId: contract.batchId, selectedRecordIds: contract.selectedRecordIds, artifacts, assertions: { moduleId: record.module.moduleId, batchId: contract.batchId, selectedRecordIds: contract.selectedRecordIds, sourceBacked: true, positiveCases: record.module.positive.length, negativeCases: record.module.negative.length, recoveryCases: record.module.recovery.length, commandCount: results.length, commandBudget, reusedReceiptCount: reusedReceipts.length, sourceUnchanged, fullFamilyParity: false, dynamicInventoryPath, dynamicSourceTreeSha256: dynamicInventory.authority.sourceTreeSha256, dynamicUnitCount: dynamicInventory.summary.unitCount, dynamicVerifiedUnits: dynamicInventory.coverage.verifiedUnits, dynamicUnverifiedUnits: dynamicInventory.coverage.unverifiedUnits, planningEstimate: dynamicInventory.policy.planningEstimate, historicalInventoryTotal: 6900, historicalEffectiveCompleted: 290, historicalEffectivePending: 6609, historicalReproduction: false }, nextTask });
|
||||
if (record.module.coverageBindings?.length) {
|
||||
execFileSync(process.execPath, [abs("tools/web/bind-wbv2-module-coverage.mjs"), "--task", task], { cwd: root, stdio: "inherit" });
|
||||
execFileSync(process.execPath, [abs("tools/web/finalize-wbv2-module-batch.mjs"), "--task", task], { cwd: root, stdio: "inherit" });
|
||||
|
||||
Reference in New Issue
Block a user