import type { SessionAttemptOutcome } from '../shared/types.js'; import { type TodoRunOutcome } from './runs-schema.js'; /** * The Todo run ledger — one row per work attempt (ICI-728). * * A Todo says what has to happen; a RUN says what one attempt at it actually * did. Which files it touched, which checks it ran, what it would tell the next * attempt, and what risk it left behind are facts about the attempt, so they * live here and not on the Todo, where a retry would overwrite them. * * Two rules the rest of the platform leans on: * * - A run is OPEN (no end, no outcome) or SETTLED (both). The DDL enforces * the pair; `closeWorkItemRun` enforces that settling happens once. * - A close takes exactly ONE run id. There is deliberately no bulk close: * a handoff describes one attempt, and one summary spread over several * attempts is a lie about all but one of them. */ export type { TodoRunOutcome } from './runs-schema.js'; /** What an attempt hands the next one. Everything is optional — the platform * stores and surfaces what the attempt reported, it never invents it. */ export interface TodoRunHandoff { changedFiles?: string[]; verification?: string; retryNotes?: string; residualRisk?: string; } export interface TodoRun { id: string; workItemId: string; sessionId: string; startedAt: string; /** NULL while the attempt is still running. */ endedAt: string | null; outcome: TodoRunOutcome | null; summary: string | null; handoff: TodoRunHandoff; error: string | null; } export interface OpenWorkItemRunInput { workItemId: string; sessionId: string; startedAt?: string; } export interface CloseWorkItemRunInput { outcome: TodoRunOutcome; summary?: string | null; /** Raw as the attempt reported it; normalized before it is stored. */ handoff?: unknown; error?: string | null; endedAt?: string; } export declare function normalizeTodoRunHandoff(value: unknown): TodoRunHandoff; /** * Start the ledger row for an attempt. Idempotent per session: a dispatch path * that runs twice for the same attempt (crash recovery re-entering the same * session) gets the run it already opened, never a second row. */ export declare function openWorkItemRun(input: OpenWorkItemRunInput): TodoRun; /** * Settle exactly one run. Refuses an unknown id, an outcome outside the frozen * six, and a run that is already settled — a second close would overwrite a * handoff somebody is meant to read. */ export declare function closeWorkItemRun(runId: string, input: CloseWorkItemRunInput): TodoRun; /** A Todo's attempts, oldest first — the order a reviewer reads them in. */ export declare function listWorkItemRuns(workItemId: string): TodoRun[]; /** The still-running attempt on this session, if the ledger has one. */ export declare function findOpenWorkItemRunBySession(sessionId: string): TodoRun | undefined; /** * Startup sweep: an attempt whose session no longer exists never reported an * outcome and never will. `crashed` is the honest reading — the work stopped * somewhere nobody recorded. Returns how many runs were settled. */ export declare function closeOrphanedWorkItemRuns(endedAt?: string): number; /** How a session's terminal receipt reads in this ledger's vocabulary. * `crashed` and `timed_out` are absent because no session receipt claims them: * a vanished session is settled by the orphan sweep instead. */ export declare const RUN_OUTCOME_BY_RECEIPT: Record; /** * How a receipt reads once its error text is taken into account. A session the * provider turned away did not fail at the work — it never got to do any — so a * quota window settles as `rate_limited` rather than as `blocked`, whatever the * receipt calls it. Only a receipt that did NOT succeed is reinterpreted: a * finished attempt is finished no matter what its logs mention. */ export declare function runOutcomeForReceipt(receipt: SessionAttemptOutcome, error: string | null): TodoRunOutcome; /** * Sweep: close every open run whose session has already reported a terminal * receipt. * * The per-Todo reconciler normally does this, but it is only reached for * NON-sticky statuses. Cancel a Todo while its delegated child is still running * and nothing ever looks at that child's receipt again — the orphan sweep above * does not save it either, because the session is present, not gone. The row * would read as still running forever, which is worse than no row at all. * * Driven off the open rows rather than the Todo list: it is O(open runs) and * covers `done` and `escalated` on the same terms, instead of re-deriving every * closed Todo in history on each tick. * * Workflow PHASE sessions are excluded — the workflow run settles those rows * itself, and closing one here would settle an attempt it is still retrying. * Returns how many runs were settled. */ export declare function closeRunsForSettledSessions(endedAt?: string): number; //# sourceMappingURL=runs.d.ts.map