Files
workinf_Blender_Wasm/tools/web/check-corrective-plan.mjs
mes123456 a081c68c87
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Reorganize Blender Web execution around capabilities
2026-08-24 18:37:46 -04:00

93 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const read = (relative) => fs.readFileSync(path.join(root, relative), "utf8");
const json = (relative) => JSON.parse(read(relative));
const exists = (relative) => fs.existsSync(path.join(root, relative));
const planPath = "docs/BLENDER_WASM_CORRECTIVE_TASK_PLAN.json";
const matrixPath = "docs/BLENDER_WASM_CAPABILITY_MATRIX_TEMPLATE.json";
const plan = json(planPath);
const matrix = json(matrixPath);
const queue = read("docs/EXECUTION_QUEUE.md");
const handoff = read("nextTask1.md");
const currentTask = queue.match(/\|\s*当前任务\s*\|\s*`([^`]+)`/u)?.[1];
const parentPath = queue.match(/\|\s*parent manifest\s*\|\s*`([^`]+)`/iu)?.[1];
const parent = json(parentPath);
const currentCorrectiveTask = handoff.match(/current corrective task\s*\|\s*`([^`]+)`/iu)?.[1];
function isLegacyCheckpointAncestor(checkpointTask) {
if (checkpointTask === currentTask) return true;
let cursor = parent;
const visited = new Set();
while (cursor?.task && !visited.has(cursor.task)) {
visited.add(cursor.task);
if (cursor.task === checkpointTask) return true;
if (!cursor.parentTask) return false;
const manifestPath = path.join(root, "tests/golden", cursor.parentTask, "manifest.json");
if (!fs.existsSync(manifestPath)) return false;
cursor = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
}
return false;
}
assert.equal(plan.schemaVersion, 2, "corrective task plan schema must be v2");
assert.equal(plan.queuePolicy.mode, "READ_ONLY_UNTIL_C6_003");
assert.ok(isLegacyCheckpointAncestor(plan.queuePolicy.currentTask), "corrective queue checkpoint must be current or an ancestor");
assert.ok(isLegacyCheckpointAncestor(plan.queuePolicy.parentNextTask), "corrective parent checkpoint must be current or an ancestor");
assert.equal(plan.queuePolicy.currentTask, plan.currentCheckpoint.queueCurrentTask);
assert.equal(plan.queuePolicy.parentNextTask, plan.currentCheckpoint.queueParentNextTask);
assert.notEqual(plan.currentCheckpoint.queueParentNextTask, plan.currentCheckpoint.reservedSuccessor);
assert.equal(parent.nextTask, currentTask);
assert.ok(currentCorrectiveTask, "nextTask1 must identify the current corrective task");
assert.equal(plan.tasks.filter((task) => task.ready === true).map((task) => task.id).join(","), currentCorrectiveTask);
assert.equal(plan.tasks.find((task) => task.id === "C0-001").status, "done");
assert.equal(plan.tasks.find((task) => task.id === "C0-002").status, "done");
assert.ok(["planned", "in_progress", "done"].includes(plan.tasks.find((task) => task.id === "C0-003").status));
assert.ok(["planned", "in_progress"].includes(plan.tasks.find((task) => task.id === currentCorrectiveTask).status));
const taskIds = new Set(plan.tasks.map((task) => task.id));
assert.equal(taskIds.size, plan.tasks.length, "corrective task IDs must be unique");
const lifecycleStatuses = new Set(plan.taskContract.lifecycleStatuses);
for (const task of plan.tasks) {
assert.ok(lifecycleStatuses.has(task.status ?? "planned"), `${task.id} lifecycle status is invalid`);
if (task.ready === true) assert.ok(["planned", "in_progress"].includes(task.status ?? "planned"), `${task.id} ready task has invalid lifecycle status`);
if (task.ready === true || task.phase === "P0") {
assert.ok(task.card && exists(task.card), `${task.id} task card is missing`);
const card = read(task.card);
const cardStatus = card.match(/(?:lifecycleStatus|状态)\s*[:]\s*`?([a-z_]+|done|blocked|planned|in_progress)/iu)?.[1]?.toLowerCase();
if (task.status) assert.equal(cardStatus, task.status, `${task.id} card status is stale`);
if (task.id === "C6-003") assert.match(card, /queueMutationAllowed\s*[:]\s*`?(?:false|true)/iu, `${task.id} card must declare queue mutation policy`);
else assert.match(card, /queueMutationAllowed\s*[:]\s*`?false/iu, `${task.id} card must forbid queue mutation`);
}
assert.ok(task.gate && plan.gates.some((gate) => gate.id === task.gate), `${task.id} gate is invalid`);
for (const dependency of task.dependsOn ?? []) assert.ok(taskIds.has(dependency), `${task.id} dependency is missing: ${dependency}`);
assert.equal(task.queueMutationAllowed ?? false, task.id === "C6-003", `${task.id} queue mutation policy is invalid`);
}
const allowedLevels = new Set(matrix.allowedParityLevels);
const allowedClasses = new Set(matrix.allowedCompletionClasses);
const allowedProvenanceStates = new Set(matrix.allowedProvenanceStates);
assert.equal(matrix.schemaVersion, 2, "capability matrix schema must be v2");
assert.equal(matrix.queuePolicy.mode, "SHADOW_ONLY");
for (const entry of matrix.entries) {
assert.ok(allowedLevels.has(entry.parityLevel), `${entry.capabilityId} parity level is invalid`);
assert.ok(allowedLevels.has(entry.targetParityLevel), `${entry.capabilityId} target parity level is invalid`);
assert.ok(allowedClasses.has(entry.completionClass), `${entry.capabilityId} completion class is invalid`);
assert.ok(["planned", "in_progress", "blocked", "done"].includes(entry.lifecycleStatus), `${entry.capabilityId} lifecycle status is invalid`);
if (entry.lifecycleStatus !== "done") assert.notEqual(entry.completionClass, "FEATURE_PARITY", `${entry.capabilityId} cannot claim feature parity before done`);
if (entry.lifecycleStatus === "planned") assert.equal(entry.parityLevel, "L0", `${entry.capabilityId} planned entry must not claim proven parity`);
assert.ok(["PASS", "FAIL", "BLOCKED", "NOT_RUN"].includes(entry.evidence?.status), `${entry.capabilityId} evidence status is invalid`);
assert.ok(allowedProvenanceStates.has(entry.evidence?.provenanceState), `${entry.capabilityId} provenance state is invalid`);
assert.ok(entry.evidence && Array.isArray(entry.evidence.required), `${entry.capabilityId} evidence contract is missing`);
assert.equal(entry.migration?.legacyCompletionPreserved, true, `${entry.capabilityId} must preserve legacy completion records`);
}
assert.match(read("docs/BLENDER_WASM_CORRECTIVE_EXECUTION_RUNBOOK.md"), /C0-001.*capture-corrective-baseline/isu);
assert.match(read("docs/BLENDER_WASM_CORRECTIVE_MIGRATION_PLAN.md"), /MIGRATION_SHADOW_ONLY/iu);
assert.equal(plan.tasks.find((task) => task.id === currentCorrectiveTask).ready, true);
process.stdout.write(`corrective-plan-ok tasks=${plan.tasks.length} capabilities=${matrix.entries.length} queue=${currentTask} parentNext=${parent.nextTask} corrective=${currentCorrectiveTask}\n`);