import { type WriteOrigin } from './origin.js'; import { type WorkItemMatch } from './search.js'; import type { VerifyMode, VerifyPolicy } from './verify-policy.js'; import type { WorkItemEventKind } from './event-log.js'; /** * Work-item store — the substrate of the Todos ledger (GRS-002, elevated by * GRS-021a design §1). * * A work item ("Todo" in surface language) is the durable unit of intended * work; a session is one execution attempt against it (linked via the nullable * `sessions.work_item_id` FK). This module and the guarded * `work-items/transitions.ts` are the ONLY write paths. * * GRS-021a additions: the 8-status vocabulary + 7-value provenance enum * (`migrate.ts` owns the DDL + rebuild), acceptance criteria, verify policy * (TRUST/VERIFY/THOROUGH + verifier + maxRounds), rounds, budget (spend is * NEVER stored — always derived live from linked sessions' total_cost), the * approval fields (ORTHOGONAL to lifecycle position; a fresh item's approval is * always none — the §1.3 anti-bottleneck principle: creates cannot attach one), * and the append-only `work_item_events` audit. * * Trust the DB, not just TS callers: status/priority/source are enforced by * CHECK constraints and machine-minted idempotency by a partial UNIQUE index * (DDL in `migrate.ts`). */ export type WorkItemStatus = 'backlog' | 'assigned' | 'executing' | 'in_review' | 'done' | 'blocked' | 'escalated' | 'cancelled'; export type WorkItemSource = 'human' | 'delegation' | 'cron' | 'workflow' | 'session' | 'connector' | 'goal'; export type ApprovalState = 'pending' | 'approved' | 'rejected'; export type ApprovalTargetKind = 'employee' | 'virtual' | 'none'; /** Sticky terminals (design §1.1): the reconciler never derives an item OUT of * these — `done`/`cancelled` are decisions, `escalated` is a deliberate routing * to the operator that session churn must not silently undo. */ export declare const STICKY_STATUSES: ReadonlySet; export type { VerifyMode, VerifyPolicy } from './verify-policy.js'; /** Provenance defaults when `verify_policy` is NULL (design §1.5, operator-ruled): * machine pulses auto-close (cron per fire; workflow runs carry their own gates), * everything a mind delegates or captures is reviewed. */ export declare const DEFAULT_VERIFY_MODE_BY_SOURCE: Readonly>; /** Bounce ceilings when the policy does not set `maxRounds` (design §1.5). */ export declare const DEFAULT_MAX_ROUNDS: Readonly>; /** Resolve the effective verify mode for an item (explicit policy, else the * provenance default). Exported for the reconciler's TRUST hook and, later, * the phase-2 dispatcher. */ export declare function effectiveVerifyMode(item: Pick): VerifyMode; /** Resolve the effective bounce ceiling for an item. */ export declare function effectiveMaxRounds(item: Pick): number; export interface WorkItem { id: string; title: string; body: string | null; status: WorkItemStatus; department: string | null; assignee: string | null; /** Who asked for this item: 'operator', an employee slug, or 'system'. */ createdBy: string; /** Sub-task tree (Todos v2): parent link, denormalized root, and depth 0..3. */ parentId: string | null; rootId: string; depth: number; dueAt: string | null; priority: number; /** Nullable manual order key. Lower ranked values render first. */ rank: number | null; /** Monotonic row revision used for whole-Todo optimistic concurrency. */ version: number; source: WorkItemSource; sourceRef: string | null; acceptance: string | null; /** Parsed `verify_policy` JSON; null = provenance default applies. A corrupt * stored value fails closed to VERIFY rather than falling back to a source * default such as cron/workflow TRUST. */ verifyPolicy: VerifyPolicy | null; rounds: number; budgetUsd: number | null; approvalState: ApprovalState | null; approvalRequest: string | null; approvalRef: string | null; /** Offered variants when the current approval asks for a PICK (else null). */ approvalOptions: string[] | null; approvalChoice: string | null; /** The current approval is reserved for the human operator: no employee may * decide it, not the COO and not through escalation. */ approvalOperatorOnly: boolean; approvalTarget: string | null; approvalTargetKind: ApprovalTargetKind | null; approvalEscalatedAt: string | null; approvalDecidedBy: string | null; approvalDecidedAt: string | null; createdAt: string; updatedAt: string; closedAt: string | null; } export interface CreateWorkItemInput { title: string; body?: string | null; status?: WorkItemStatus; department?: string | null; assignee?: string | null; /** Creator identity; defaults to 'operator' for source=human, 'system' otherwise. */ createdBy?: string; /** Create as a sub-task of an existing Todo (depth ≤ 3). Department is * inherited from the parent when not given explicitly. */ parentId?: string | null; /** Optional ISO 8601 deadline. */ dueAt?: string | null; priority?: number; source?: WorkItemSource; /** * Stable key for machine-minted items (e.g. `cron::`, * `workflow::`). When set, `createWorkItem` is idempotent on * `(source, sourceRef)` — a repeat insert returns the existing row instead of * creating a duplicate. NULL refs never collide. */ sourceRef?: string | null; acceptance?: string | null; verifyPolicy?: VerifyPolicy | null; budgetUsd?: number | null; origin?: WriteOrigin; } export interface ListWorkItemsFilter { status?: WorkItemStatus; department?: string; assignee?: string; source?: WorkItemSource; needsAttentionFor?: string; /** Exact creator identity (`created_by`). */ createdBy?: string; /** Direct children of this Todo. */ parentId?: string; /** Whole family sharing this root Todo. */ rootId?: string; /** Only tree roots (parentless items). */ rootsOnly?: boolean; /** Board scopes — `kept`: pinned (ICI-1357). `home`: pinned OR operator-created (PLA-230). */ kept?: boolean; home?: boolean; /** Items carrying this label, matched by exact label id (`lbl_…`) or stored * (normalized kebab-case) name — callers normalize display names first. */ label?: string; /** Free text, matched by the FTS5 indexes over title, body and comments. Relevance-ordered, exact Todo id first. */ text?: string; /** Inclusive ISO timestamp bounds over `updated_at`. */ since?: string; until?: string; /** Cap rows in SQL (LIMIT) instead of the caller slicing after a full-table load. */ limit?: number; /** Zero-based row offset, applied after the canonical ordering. */ offset?: number; } export interface SearchWorkItemsFilter extends ListWorkItemsFilter { } export type WorkItemTotals = Record; export interface WorkItemPage { workItems: WorkItem[]; /** Exact count matching the filters, before LIMIT/OFFSET. */ total: number; /** Exact matching counts by raw stored status, before LIMIT/OFFSET. */ totals: WorkItemTotals; limit: number; offset: number; nextOffset: number | null; /** Why each returned Todo matched, best reason first, keyed by Todo id. * Present only when the query carried `text`. */ matches?: Record; } export interface WorkItemEvent { id: string; workItemId: string; kind: WorkItemEventKind; fromStatus: WorkItemStatus | null; toStatus: WorkItemStatus | null; actor: string | null; /** Parsed JSON payload (critique text, session id, policy note, …). */ detail: Record | null; createdAt: string; } export interface AppendWorkItemEventInput { workItemId: string; kind: WorkItemEventKind; fromStatus?: WorkItemStatus | null; toStatus?: WorkItemStatus | null; actor?: string | null; detail?: Record | null; /** `state` advances the Todo revision for a standalone operator-visible note * or verification result. `companion` records the audit for a row mutation * that already advanced it. `audit` is telemetry/visibility only. */ versionEffect?: 'state' | 'companion' | 'audit'; } /** Append one audit event. Callers inside a transaction compose naturally * (better-sqlite3 nests via savepoints). Never throws on payload shape — the * detail is stringified verbatim. */ export declare function appendWorkItemEvent(input: AppendWorkItemEventInput): WorkItemEvent; /** Reading the trail back lives in event-log.ts, along with the actors whose * writes are derived rather than declared. Re-exported here so the audit * surface stays one import for every caller. */ export { RECONCILER_ACTOR, WORKFLOW_RUN_ACTOR, isBlockDeclared, isReviewBounceDeclared, listWorkItemEvents, listWorkItemEventsForItems, } from './event-log.js'; export type { WorkItemEventKind } from './event-log.js'; /** * Create a work item (status defaults to `backlog`, source to `human`). * Idempotent for machine-minted items: when `sourceRef` is set and a row already * exists for that `(source, sourceRef)` pair, the existing row is returned * unchanged — a repeat for the same key never duplicates AND never re-appends a * `created` event. The check+insert(+event) runs in one transaction; if a * concurrent writer wins the `(source, source_ref)` race between our SELECT and * INSERT, the UNIQUE violation is caught and we re-select the winner's row. * Invalid enum values are rejected by the table's CHECK constraints. */ export declare function createWorkItem(input: CreateWorkItemInput): WorkItem; /** The company Todo namespace: derived from configured company name, or the * explicit `portal.companyPrefix` override; configless homes keep `JIN`. */ export declare function resolveCompanyPrefix(): string; /** Register a department in the registry if it is not there yet (review F2): * EVERY write that lands a non-null department calls this inside its own * transaction, so /api/departments can never omit a department that holds * live Todos. Items keep their birth ID prefix — this only mints the row. */ export declare function ensureDepartmentRegistered(slug: string | null | undefined): void; export declare function getWorkItem(id: string): WorkItem | undefined; /** Read a bounded set of Todos in caller order with one row query and one * approval hydration pass. Unknown ids are omitted. */ export declare function getWorkItems(ids: readonly string[]): WorkItem[]; /** Look up a machine-minted item by its stable key — how the workflow bridge * resolves a run's Todo without threading ids through the driver. */ export declare function getWorkItemBySourceRef(source: WorkItemSource, sourceRef: string): WorkItem | undefined; export declare const WORK_ITEM_STATUS_VALUES: readonly WorkItemStatus[]; /** Paginated, deterministic AND-composed Todo query. Counts are computed from * the identical WHERE clause before pagination, so a capped page can never * masquerade as the full ledger. */ export declare function queryWorkItems(filter?: ListWorkItemsFilter): WorkItemPage; /** List work items, recently-updated first, optionally filtered. Compatibility * wrapper: an omitted limit still means the full matching set. */ export declare function listWorkItems(filter?: ListWorkItemsFilter): WorkItem[]; /** Deterministic AND-composed Todo search (GRS-021c). */ export declare function searchWorkItems(filter: SearchWorkItemsFilter, limit?: number): WorkItem[]; export type WorkItemTreeNode = WorkItem & { children: WorkItemTreeNode[]; }; export interface WorkItemTree { root: WorkItemTreeNode; /** Status counts over the returned subtree (root included). */ totals: WorkItemTotals; /** Live derived spend over every session linked anywhere in the subtree. */ spendUsd: number; } /** Read multiple work-item subtrees without query fan-out. One indexed query * fetches every requested family via root_id, then one grouped session query * fetches spend for those families; each requested subtree is assembled and * totalled in memory. Unknown IDs are omitted from the returned record. */ export declare function getWorkItemTrees(ids: readonly string[]): Record; /** Read one work item's subtree. The batch implementation is the source of * truth so the additive batch route stays byte-for-byte shape-compatible. */ export declare function getWorkItemTree(id: string): WorkItemTree | undefined; export interface UpdateWorkItemInput { title?: string; body?: string | null; assignee?: string | null; department?: string | null; priority?: number; rank?: number | null; /** Todos v2 slice 4 — the widened metadata pen also covers these. */ acceptance?: string | null; dueAt?: string | null; /** Todos v2 slice 6 — the rail's verify picker (operator-only at the route). * null clears to the provenance default. */ verifyPolicy?: VerifyPolicy | null; } export interface ConditionalWorkItemUpdateOptions { expectedVersion: number; idempotencyKey?: string; actor?: string | null; origin?: WriteOrigin; } export interface ConditionalWorkItemUpdateResult { item: WorkItem; replayed: boolean; } export declare class WorkItemVersionConflictError extends Error { readonly currentVersion: number; constructor(currentVersion: number); } export declare class WorkItemIdempotencyConflictError extends Error { readonly currentVersion: number; constructor(currentVersion: number); } /** Atomic row-level compare-and-update for the operator metadata surface. * Partial fields are patch semantics, but `expectedVersion` protects the whole * Todo row: any intervening editable or lifecycle mutation conflicts. An exact * idempotency replay is resolved before that guard and never writes again. */ export declare function updateWorkItemConditional(id: string, input: UpdateWorkItemInput, opts: ConditionalWorkItemUpdateOptions): ConditionalWorkItemUpdateResult | undefined; /** Internal compatibility write for migration and trusted non-HTTP callers. * Public operator edits use updateWorkItemConditional. Status is deliberately * absent: lifecycle changes belong to the guarded transitions module. */ export declare function updateWorkItem(id: string, input: UpdateWorkItemInput, actor?: string | null): WorkItem | undefined; /** Live spend over an item's execution attempts: `SUM(total_cost)` across linked * sessions. Never stored (design §1.6) — always derived, never stale. */ export declare function getWorkItemSpend(id: string): number; /** * Link an execution attempt (session) to a work item. Touches two rows * (`sessions.work_item_id` + `work_items.updated_at`) so it runs in one * transaction: if the work item does not exist, the session write is rolled back * and nothing is half-linked. Throws when either the session or the work item is * missing. Appends a `session_linked` audit event on an ACTUAL write. * * Idempotent-in-writes: if the session already carries this exact `work_item_id`, * the call verifies both rows exist and then returns WITHOUT writing — so a * redundant re-link (e.g. a cron re-fire re-linking the same item to the same session) * does not churn `work_items.updated_at` or the event log. */ export declare function linkSession(workItemId: string, sessionId: string, actor?: string | null): void; //# sourceMappingURL=store.d.ts.map