import { type WorkItem } from './store.js'; export { currentApproval, listApprovals, type WorkItemApproval } from './approval-rows.js'; export { ApprovalChoiceError, ApprovalNotPendingError } from './approval-decision-row.js'; export { setTodoApprovalDecisionListener, type TodoApprovalDecisionEvent, type TodoApprovalDecisionListener, } from './approval-decision-listener.js'; /** * Todo approvals — the native write paths + the approval decision orchestrator * (GRS-021b, design §1.3). * * Two orthogonal facts about a Todo: its lifecycle POSITION (status) and whether * a routed decision is pending on it (the approval fields). This module owns the * approval fields; `transitions.ts` still owns status. The anti-bottleneck * principle is LAW: a fresh Todo NEVER carries an approval (the store's create * path structurally cannot attach one) — approval is attached only here, where a * routed decision is genuinely required for a deliberately-gated Todo. * * REQUESTING is agent-legal (`requestApproval`); DECIDING is authority-gated by * the gateway helper (manager/COO by default; operator/aCEO only after explicit * escalation). `decideWorkItemApproval` is the consequence engine after that * authority check succeeds. * * Consequence rules are FIXED and deterministic (not per-request config): * - approve + status `in_review` → `transition(done)` * - reject + status `in_review` → bounce `transition(executing)` (rounds++; * the bounce that reaches maxRounds `escalated`s instead — the transitions * module enforces that) * - any OTHER status → the decision is recorded, status UNTOUCHED * - a gate MIRRORED from a Workflow run → the decision is recorded, status * UNTOUCHED whatever it is. The run owns its own lifecycle: its gates are * mid-pipeline decisions ("pick a variant", "merge this"), not a review of * the Todo, and the phases after the gate have not run yet. */ /** A CHOICE approval offers variants to pick between instead of a bare yes/no. * Deliberately a short list of labels, not a schema: the label IS the value a * downstream consumer reads back. */ export declare const MAX_APPROVAL_OPTIONS = 8; export declare const MAX_APPROVAL_OPTION_LENGTH = 80; /** Normalize + validate an offered option set. Returns null for "no options" * so a plain approval and an empty list are the same thing. */ export declare function normalizeApprovalOptions(options: readonly string[] | null | undefined): string[] | null; export interface RequestApprovalInput { /** What is being asked of the routed approver (the gate/description text). */ request: string; /** Optional opaque audit/correlation reference. */ ref?: string | null; /** Offered variants — turns this into a CHOICE approval (see above). */ options?: readonly string[] | null; /** Employee slug expected to decide this approval (manager/COO by default). */ target?: string | null; /** Reserve the gate for the human operator: no employee may decide it, not the * COO and not through escalation. */ operatorOnly?: boolean; /** Who requested it (audit only). */ actor?: string | null; } /** * Attach a PENDING approval to an item (the native "any actor may REQUEST" path, * design §1.3). Writes a PENDING `work_item_approvals` row carrying the request * text + optional ref, and appends ONE `approval_requested` event — * status is orthogonal and left untouched. Idempotent when the item is already * pending on the identical (request, ref): no write, no duplicate event (so a * workflow-park re-mirror on every sweep stays event-silent). Throws on an * unknown item. */ export declare function requestApproval(id: string, input: RequestApprovalInput): WorkItem; export type ApprovalDecision = 'approve' | 'reject'; export interface ArchiveWorkItemOptions { human?: boolean; callerSessionId?: string; note?: string; /** Cancel every open descendant first (depth-first), each with its own audited * transition. Honored only together with `human: true` (operator authority). */ cascade?: boolean; } /** * Cancel a Todo while closing any outstanding approval record in the same SQLite * transaction. Callers are authority-checked by the gateway before reaching this * persistence primitive. With `cascade` (human authority only), open descendants * are cancelled first, deepest first, so the roll-up gate stays satisfied. */ export declare function archiveWorkItem(id: string, actor: string, opts?: ArchiveWorkItemOptions): WorkItem; /** Persist an explicit escalation to the operator/aCEO path. The routed manager * or COO still performs this write through the API/MCP authority helper; this * low-level function only records the state once authority is established. */ export declare function escalateApproval(id: string, actor: string, reason?: string): WorkItem; export interface DecideWorkItemApprovalInput { id: string; decision: ApprovalDecision; note?: string; /** The picked option. Required when the gate offers options and the decision * is `approve`; refused otherwise. Must be one of the offered labels. */ choice?: string; /** Audit actor for the decision + any consequent transition. Default `operator`. */ decidedBy?: string; } export type DecideWorkItemApprovalResult = { ok: false; code: 'not-found' | 'no-pending' | 'invalid-choice'; message: string; } | { ok: true; item: WorkItem; escalated: boolean; }; /** * Decide a Todo's pending approval and apply the fixed consequences (design §1.3). * The route's consequence engine — approval authority is enforced UPSTREAM by * the gateway helper; this function assumes the caller was already authorized. */ export declare function decideWorkItemApprovalSync(input: DecideWorkItemApprovalInput): DecideWorkItemApprovalResult; /** * Public async compatibility wrapper. The synchronous seam above exists for * callers that must compose the approval decision into a larger SQLite * transaction; routes and existing consumers keep the established Promise * contract (including rejected write failures). */ export declare function decideWorkItemApproval(input: DecideWorkItemApprovalInput): Promise; //# sourceMappingURL=approvals.d.ts.map