Reorganize Blender Web execution around capabilities
This commit is contained in:
@@ -24,19 +24,20 @@ const tokenEstimate = (value) => Math.ceil(Buffer.byteLength(value, "utf8") / 4)
|
||||
|
||||
const queuePath = path.join(root, "docs/EXECUTION_QUEUE.md");
|
||||
const planPath = path.join(root, "tests/golden/M15-03A/next-task-plan.json");
|
||||
const taskIndexPath = path.join(root, "tests/golden/M15-03A/task-index.json");
|
||||
const legacyTaskIndexPath = path.join(root, "tests/golden/M15-03A/task-index.json");
|
||||
const capabilityTaskIndexPath = path.join(root, "tests/golden/WBV2/task-index.json");
|
||||
const taskIndexPathFor = (task) => task?.startsWith("WBV2-") ? capabilityTaskIndexPath : legacyTaskIndexPath;
|
||||
const sha256File = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
||||
let cachedTaskIndex;
|
||||
let taskIndexLoaded = false;
|
||||
const cachedTaskIndexes = new Map();
|
||||
const safeName = (value) => value.replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 96) || "gap";
|
||||
const expandTask = (task) => ({
|
||||
...task,
|
||||
fixture: { path: `tests/files/web/generated/${task.id}-${safeName(task.gapId)}.blend`, state: "REQUIRED" },
|
||||
desktopCommand: `build_blender_5.2.0/bin/blender -b --factory-startup --python tools/web/generated/${task.id}.py -- tests/files/web/generated/${task.id}-${safeName(task.gapId)}.blend`,
|
||||
webCommand: `npm --prefix web run test:generated-gap -- --task ${task.id}`,
|
||||
comparator: `node tools/web/check-generated-gap.mjs --task ${task.id}`,
|
||||
exitCriteria: ["desktop fixture evidence exists", "WASM uses the same fixture", "comparator passes", "save/reopen preserves Main", "manifest hashes all artifacts"],
|
||||
});
|
||||
const expandTask = (task) => task.id.startsWith("WBV2-") ? task : ({
|
||||
...task,
|
||||
fixture: { path: `tests/files/web/generated/${task.id}-${safeName(task.gapId)}.blend`, state: "REQUIRED" },
|
||||
desktopCommand: `build_blender_5.2.0/bin/blender -b --factory-startup --python tools/web/generated/${task.id}.py -- tests/files/web/generated/${task.id}-${safeName(task.gapId)}.blend`,
|
||||
webCommand: `npm --prefix web run test:generated-gap -- --task ${task.id}`,
|
||||
comparator: `node tools/web/check-generated-gap.mjs --task ${task.id}`,
|
||||
exitCriteria: ["desktop fixture evidence exists", "WASM uses the same fixture", "comparator passes", "save/reopen preserves Main", "manifest hashes all artifacts"],
|
||||
});
|
||||
|
||||
export function parseQueue() {
|
||||
const source = read(queuePath);
|
||||
@@ -48,23 +49,25 @@ export function parseQueue() {
|
||||
return { path: queuePath, source, currentTask: current, parentManifest };
|
||||
}
|
||||
|
||||
function loadTaskIndex() {
|
||||
if (taskIndexLoaded) return cachedTaskIndex;
|
||||
taskIndexLoaded = true;
|
||||
function loadTaskIndex(task = parseQueue().currentTask) {
|
||||
const taskIndexPath = taskIndexPathFor(task);
|
||||
if (cachedTaskIndexes.has(taskIndexPath)) return cachedTaskIndexes.get(taskIndexPath);
|
||||
if (!fs.existsSync(taskIndexPath)) {
|
||||
if (fs.existsSync(planPath)) throw new Error(`missing task index; run node tools/web/generate-task-index.mjs (${relative(taskIndexPath)})`);
|
||||
const command = task?.startsWith("WBV2-") ? "node tools/web/generate-v2-task-index.mjs" : "node tools/web/generate-task-index.mjs";
|
||||
if (fs.existsSync(planPath)) throw new Error(`missing task index; run ${command} (${relative(taskIndexPath)})`);
|
||||
return null;
|
||||
}
|
||||
const index = json(taskIndexPath);
|
||||
if (index.schemaVersion !== 1 || !index.source?.path || !index.catalog?.path || !index.entries) {
|
||||
throw new Error(`invalid task index: ${relative(taskIndexPath)}`);
|
||||
}
|
||||
cachedTaskIndex = index;
|
||||
return cachedTaskIndex;
|
||||
cachedTaskIndexes.set(taskIndexPath, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
export function verifyTaskIndex() {
|
||||
const index = loadTaskIndex();
|
||||
export function verifyTaskIndex(task) {
|
||||
const taskIndexPath = taskIndexPathFor(task ?? parseQueue().currentTask);
|
||||
const index = loadTaskIndex(task);
|
||||
if (!index) return null;
|
||||
const sourcePath = path.join(root, index.source.path);
|
||||
const catalogPath = path.join(root, index.catalog.path);
|
||||
@@ -77,7 +80,7 @@ export function verifyTaskIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
function indexedEntry(task, index = loadTaskIndex()) {
|
||||
function indexedEntry(task, index = loadTaskIndex(task)) {
|
||||
const metadata = index?.entries?.[task];
|
||||
if (!metadata) return null;
|
||||
const catalogPath = path.join(root, index.catalog.path);
|
||||
@@ -105,7 +108,7 @@ function planEntry(task) {
|
||||
}
|
||||
|
||||
function planParent(task) {
|
||||
const index = loadTaskIndex();
|
||||
const index = loadTaskIndex(task);
|
||||
if (index?.entries?.[task]) return index.entries[task].previous;
|
||||
return null;
|
||||
}
|
||||
@@ -308,15 +311,16 @@ export function compactTaskContext(context) {
|
||||
}
|
||||
|
||||
function nextFromPlan(task) {
|
||||
const index = loadTaskIndex();
|
||||
const index = loadTaskIndex(task);
|
||||
if (index?.entries?.[task]) return index.entries[task].next;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function buildTaskContext(requestedTask) {
|
||||
verifyTaskIndex();
|
||||
const queue = parseQueue();
|
||||
const task = requestedTask ?? queue.currentTask;
|
||||
const taskIndexPath = taskIndexPathFor(task);
|
||||
verifyTaskIndex(task);
|
||||
const taskPath = path.join(root, "docs/tasks", `${task}.md`);
|
||||
const entry = planEntry(task);
|
||||
const taskExists = fs.existsSync(taskPath);
|
||||
|
||||
Reference in New Issue
Block a user