/** * The engine-side finalizer drive (`runWorkflowFinalizer`) for issue #446 Phase 2. * Dispatched by the scheduler when a `wf-teardown:` timer fires, it drives a * workflow's definition-level `finalizer` to durable completion after a * `cancelled`/`timed-out` terminal — claiming the durable teardown marker, running * the finalizer activity (via {@link runFinalizerActivity}), and then clearing, backing * off, or dead-lettering based on the outcome. The byte-level claim mechanics (CAS, * settle, re-arm, dead-letter, stale horizon, backoff) live in `./finalizer-claim.ts`; * this module is the orchestration that decides which to call. * * Concurrency model — a single durable, TIME-based claim: the durable `teardownOwed` * marker carries a `{ status, attempts, token, claimedAt }` claim ({@link TeardownClaim}). * A holder fenced-CAS's `owed → running` (stamping `claimedAt`) before running, and * settle-CAS's the exact `running` bytes it wrote when clearing or rescheduling — so a * concurrent reclaimer can never clobber a fresher claim. Liveness is decided purely by * the clock: a `running` claim is reclaimable once `claimedAt` is older than * {@link teardownStaleThresholdMs}. There is NO in-memory liveness set and NO epoch in * the record; crash recovery is an ordinary stale-claim retry driven by the timer that * survived the terminal batch — the tradeoff is a finalizer running past the stale * threshold may be re-driven concurrently, which is why finalizers must be idempotent. * * Self-heal invariant: every exit that does NOT settle the claim (a lost claim CAS, a * presumed-live `running` claim, a shutdown-aborted attempt, or a missing registration) * re-arms a future `wf-teardown:` timer before returning — the scheduler deletes the fired * timer on return, so a non-settling exit that forgot to re-arm strands the marker. * * @module core/engine/termination/finalizer */ import type { WorkflowState } from '../../types.ts'; import type { EngineInternals } from '../internals.ts'; export { teardownStaleThresholdMs, type TeardownDeadLetterRecord } from './finalizer-claim.ts'; /** The subset of termination callbacks the finalizer drive needs. */ export interface FinalizerDriveCallbacks { loadWorkflowState: (workflowId: string) => Promise; dispatchEvent: (event: Event) => void; handleCleanupError: (source: string, error: unknown, workflowId?: string) => void; } /** * Drive one teardown attempt for a workflow whose `wf-teardown:` timer just fired. * Never throws: the scheduler treats a thrown timer callback as "retry on the next * tick", which would defeat the backoff schedule, so every failure path is handled * internally and the function returns normally (letting the scheduler delete the * fired timer; a backoff/self-heal reschedule writes a fresh timer entry). */ export declare function runWorkflowFinalizer(internals: EngineInternals, workflowId: string, timerId: string, callbacks: FinalizerDriveCallbacks): Promise;