/** * Shared commit loop for the durable remote task ledger's live dispatch path * (WFT-22) — claim, heartbeat, completion, and requeue all attempt a single * pure transition against the current ledger record through * `storage.conditionalBatch`, retrying only when the compare-and-swap itself * is lost (a concurrent writer changed the record between read and write), * never when the transition's own precondition rejects the freshly re-read * record. A precondition rejection means the requested transition genuinely * does not apply — for example the task is already terminal — and retrying * the same rejected transition would just reject again. * * `commitTaskLedgerDelete` (WFT-24) is the delete counterpart, used by * terminal-task retention: a conditional delete gated on a precondition * function rather than a transition function, since deletion has no next * record to write. * * @module server/runtime/task-ledger-runtime */ import { type ConditionalBatchCondition, type Storage } from '../../storage/interface.ts'; import type { TaskLedgerPreconditionResult, TaskLedgerTransitionResult } from '../task-ledger-transitions.ts'; import { type RemoteTaskRecord } from '../task-ledger.ts'; export type TaskLedgerCommitResult = Readonly<{ ok: true; record: T; }> | Readonly<{ ok: false; reason: string; }>; export type TaskLedgerDeleteResult = Readonly<{ ok: true; }> | Readonly<{ ok: false; reason: string; }>; /** * Read the current ledger record, apply `transitionFn`, and commit the * result with `expectedValue` set to the exact bytes just read — CAS by raw * byte equality, not `encode(decode(bytes))` re-serialization (see * `task-ledger-transitions.ts`'s module doc comment). * * Retries only a lost CAS (up to `maxAttempts`), re-reading and * re-evaluating `transitionFn` against the fresh record each time — a * concurrent heartbeat renewal, for instance, is a benign generation bump * worth retrying past. A `transitionFn` precondition rejection returns * immediately without consuming a retry, since the fresh record already * reflects reality and re-attempting the identical transition cannot change * the outcome. */ export declare function commitTaskLedgerTransition(storage: Storage, operationId: string, transitionFn: (current: RemoteTaskRecord | null, now: number) => TaskLedgerTransitionResult, maxAttempts?: number, /** * Extra same-transaction preconditions to fence the commit on — for * example, "the target workflow's persisted state is still exactly the * bytes read at dispatch time" (WFT-20's revision-staleness gate). Checked * in the SAME `conditionalBatch` call as the ledger key's own CAS, so a * concurrent write to any of these keys between read and commit loses the * whole batch atomically, closing the gap a standalone pre-check * (read-then-later-write, with unrelated work in between) cannot close. * Re-supplied by the caller on each retry attempt since the caller, not * this loop, owns re-reading whatever these conditions guard. */ additionalConditions?: readonly ConditionalBatchCondition[]): Promise>; /** * Read the current ledger record, check a delete-only precondition (WFT-24 * retention: {@link canDeleteRetainedTerminalTask}), and conditionally delete * with `expectedValue` set to the exact bytes just read — the delete * counterpart to {@link commitTaskLedgerTransition}, needed because * `canDeleteRetainedTerminalTask` has no next record to write, only a * precondition to satisfy before issuing a delete. * * Retries only a lost CAS, same as `commitTaskLedgerTransition` — a * concurrent writer that changed the record between read and delete (for * example a later adoption call bumping `retentionGeneration`) means the * fresh record deserves a fresh precondition check, not a delete of state * the caller never actually observed. */ export declare function commitTaskLedgerDelete(storage: Storage, operationId: string, preconditionFn: (current: RemoteTaskRecord | null) => TaskLedgerPreconditionResult, maxAttempts?: number): Promise;