fix(kanban): recover malformed commands

req: sync/015
This commit is contained in:
slhx agent
2026-07-13 15:15:40 +02:00
parent 47715e3ba3
commit 54b706b706
3 changed files with 162 additions and 5 deletions
+19 -2
View File
@@ -110,10 +110,26 @@ async function storedCommands(database) {
return commands.sort((left, right) => left.causal - right.causal);
}
function invalidCommand(command, field) {
const id = command && typeof command.id === "string" && command.id ? command.id : "record";
throw new Error(`invalid durable command ${id}: ${field}`);
}
function validate(command) {
if (command.schemaVersion !== COMMAND_SCHEMA || command.kind !== "reorder_card" || !command.id || !command.actor || !command.session || !command.cardId || !Number.isSafeInteger(command.causal)) {
throw new Error(`unsupported durable command ${command && command.id ? command.id : "record"}`);
if (!command || typeof command !== "object") invalidCommand(command, "record");
if (!Number.isSafeInteger(command.schemaVersion)) invalidCommand(command, "schemaVersion");
if (command.schemaVersion !== COMMAND_SCHEMA) {
throw new Error(`unsupported durable command ${command.id || "record"}`);
}
if (typeof command.id !== "string" || !command.id) invalidCommand(command, "id");
if (typeof command.actor !== "string" || !command.actor) invalidCommand(command, "actor");
if (typeof command.session !== "string" || !command.session) invalidCommand(command, "session");
if (!Number.isSafeInteger(command.causal) || command.causal < 1) invalidCommand(command, "causal");
if (command.id !== `${command.actor}:${command.causal}`) invalidCommand(command, "id");
if (command.kind !== "reorder_card") invalidCommand(command, "kind");
if (typeof command.cardId !== "string" || !command.cardId) invalidCommand(command, "cardId");
if (typeof command.eventKind !== "string" || !command.eventKind) invalidCommand(command, "eventKind");
if (command.key !== null && typeof command.key !== "string") invalidCommand(command, "key");
return command;
}
@@ -138,6 +154,7 @@ function report(root, stage, error) {
root.setAttribute("data-kanban-command-error", `${stage}: ${message}`);
root.setAttribute("data-kanban-command-error-stage", stage);
root.setAttribute("data-kanban-command-error-code", code);
announce(root, `Local command ${stage} failed (${code}). Recovery controls remain available.`);
root.dispatchEvent(new CustomEvent("kanban:command-error", { detail: { stage, code, message } }));
}