fix(web): preserve batch contract on scheduler failures
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-09-03 20:35:10 +08:00
parent b28b365f3a
commit d6c5fbe1fc
2 changed files with 11 additions and 0 deletions

View File

@@ -219,6 +219,15 @@ function sourceMappingReadiness(pointer) {
const card = read(cardPath);
const implementation = card.match(/- Implementation entrypoint: `([^`]+)`/u)?.[1];
if (!implementation) return { ok: false, reason: "implementation_entrypoint_missing", taskClass };
if (/targetImplementationClass:\s*LOCAL_EXACT/u.test(card)) {
const batchId = card.match(/^- batchId:\s*`?([^`\n]+)`?/mu)?.[1];
const selected = card.match(/^- selectedRecordIds:\s*(\[[^\n]+\])/mu)?.[1];
if (!batchId || !selected) return { ok: false, reason: "batch_contract_missing", taskClass };
try {
const selectedRecordIds = JSON.parse(selected);
if (!Array.isArray(selectedRecordIds) || selectedRecordIds.length === 0) return { ok: false, reason: "batch_selection_missing", taskClass };
} catch { return { ok: false, reason: "batch_selection_invalid", taskClass }; }
}
// Continuation source tasks must have an explicit, non-generic phase mapping.
// This prevents a long-running loop from replaying an older marker forever.

View File

@@ -81,10 +81,12 @@ const stopOnFatal = (error, source) => {
if (fatalHandled) return;
fatalHandled = true;
try {
failureLedger.record({ task: currentTask()?.task ?? "UNKNOWN", phase: "supervisor", reason: source, detail: error?.message ?? String(error) });
writeCheckpoint({ state: "blocked", iteration, reason: source, error: error?.message ?? String(error) });
appendLog(`blocked reason=${source} error=${error?.message ?? String(error)}`);
}
finally {
failureLedger.flush();
releaseLock();
process.exit(1);
}