Filbert approved round 1 (f167b85e). Manifest 782bcb62, 21 files, plus the QUEUE.md markers and the TOOLS.md section. Lead decision 35. Co-Authored-By: Claude Opus 5.5 <[email protected]>
81 lines
3.7 KiB
JavaScript
81 lines
3.7 KiB
JavaScript
#!/usr/bin/env node
|
|
// Usage: node agents/darkwing/work/queue-a2/map-check.mjs [QUEUE.md] [MAP]
|
|
//
|
|
// Run before genesis. The map names the QUEUE.md blob it was built from.
|
|
// This lists every table line that differs from that blob, then every row
|
|
// whose piece, owner or issues no longer match the map. Reads only; the
|
|
// one git call is `cat-file`. Exit 0 no drift, 1 drift, 4 usage.
|
|
import { execFileSync } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const top = join(dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
const { parseMigrationMap } = await import(join(top, "packages/queue/src/queue.mjs"));
|
|
|
|
// Table lines by id: `| N | ...` rows, then the parked table's items
|
|
// numbered on from the highest row, as the map numbers them.
|
|
export function tableLines(text) {
|
|
const out = new Map();
|
|
const items = [];
|
|
let parked = false;
|
|
for (const line of text.split("\n")) {
|
|
if (line.startsWith("## ")) parked = line.startsWith("## Parked");
|
|
const m = /^\| (\d+) \|/.exec(line);
|
|
if (m && !parked) out.set(Number(m[1]), line);
|
|
else if (parked && line.startsWith("| ") && !line.startsWith("| Item ") && !line.startsWith("|---")) items.push(line);
|
|
}
|
|
let next = Math.max(0, ...out.keys()) + 1;
|
|
for (const line of items) out.set(next++, line);
|
|
return out;
|
|
}
|
|
|
|
const cells = (line) => line.slice(2, -2).split(" | ");
|
|
|
|
export function check(queueText, mapText, oldText) {
|
|
const drift = [];
|
|
const now = tableLines(queueText);
|
|
const then = tableLines(oldText);
|
|
for (const id of new Set([...now.keys(), ...then.keys()])) {
|
|
if (now.get(id) !== then.get(id)) drift.push(`row ${id}: ${!then.has(id) ? "added" : !now.has(id) ? "removed" : "changed"} since the map's QUEUE.md blob`);
|
|
}
|
|
const map = parseMigrationMap(mapText);
|
|
const byId = new Map(map.rows.map((r) => [r.id, r]));
|
|
for (const [id, line] of now) {
|
|
const r = byId.get(id);
|
|
if (!r) { drift.push(`row ${id}: in QUEUE.md, not in the map`); continue; }
|
|
const c = cells(line);
|
|
if (!/^\d+$/.test(c[0])) {
|
|
if (c[0] !== r.piece) drift.push(`row ${id}: parked item ${JSON.stringify(c[0])} is not the map's piece`);
|
|
continue;
|
|
}
|
|
if (c[1] !== r.piece) drift.push(`row ${id}: piece differs from the map`);
|
|
const owner = /^[a-z][a-z0-9-]*/.exec(c[2])?.[0];
|
|
if (owner !== r.owner) drift.push(`row ${id}: owner ${owner} in QUEUE.md, ${r.owner} in the map`);
|
|
const issues = [...c[3].matchAll(/#(\d+)/g)].map((x) => Number(x[1]));
|
|
if (issues.join() !== r.issues.join()) drift.push(`row ${id}: issues ${issues.join(",") || "none"} in QUEUE.md, ${r.issues.join(",") || "none"} in the map`);
|
|
}
|
|
for (const id of byId.keys()) if (!now.has(id)) drift.push(`row ${id}: in the map, not in QUEUE.md`);
|
|
return drift;
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
const args = process.argv.slice(2);
|
|
if (args.length > 2) {
|
|
console.error("usage: map-check.mjs [QUEUE.md] [MAP]");
|
|
process.exit(4);
|
|
}
|
|
const queueText = readFileSync(args[0] ?? join(top, "docs/plans/QUEUE.md"), "utf8");
|
|
const mapText = readFileSync(args[1] ?? join(top, "agents/darkwing/work/queue-migration-map.md"), "utf8");
|
|
const blob = /QUEUE\.md` blob `([0-9a-f]{40})`/.exec(mapText)?.[1];
|
|
if (!blob) {
|
|
console.error("the map names no QUEUE.md blob");
|
|
process.exit(1);
|
|
}
|
|
const oldText = execFileSync("git", ["-C", top, "cat-file", "blob", blob]).toString("utf8");
|
|
const drift = check(queueText, mapText, oldText);
|
|
for (const d of drift) console.log(d);
|
|
console.log(drift.length ? `${drift.length} differences; update the map before genesis` : `ok: QUEUE.md matches the map (blob ${blob.slice(0, 8)})`);
|
|
process.exit(drift.length ? 1 : 0);
|
|
}
|