/** * GovernanceStore: the append-only lifecycle ledger + spend ledger behind the * governance services. * * Deliberately a SEPARATE seam from SkillsProductStore: the product seam is a * published contract third parties implement, and bolting receipt/reservation * methods onto it would make every implementation carry controls only the * hosted path needs. This seam owns four things: * * - skills_lifecycle_receipts: append-only. There is no update or delete * surface here - a receipt is a fact that happened. * - skills_credit_reservations: reserve before dispatch, reconcile once at * terminal state. * - artifact row deletion / quarantine key rewrite, so the expiry sweep and * cancellation can retire rows without growing the product seam. * - ceiling reads: active run count and monthly spend per org. */ import { Database } from "bun:sqlite"; import type { ServerArtifact } from "../server/types.js"; export type ReceiptKind = "delete" | "quarantine" | "cancel"; export interface LifecycleReceipt { id: string; kind: ReceiptKind; orgId: string; runId: string; artifactId?: string; /** Stable, non-secret identity of the requester: a principal email, an api key id, a worker id. */ requestedBy: string; metadata: Record; createdAt: string; } export type ReservationStatus = "reserved" | "charged" | "released"; export interface CreditReservation { id: string; orgId: string; runId: string; /** Per-reservation integer cents (0..2147483647) across all store backends. */ estimatedCents: number; /** Settled integer cents in the same per-reservation range. */ actualCents?: number; status: ReservationStatus; createdAt: string; reconciledAt?: string; } export interface GovernanceStore { readonly backend: string; close?(): Promise; appendReceipt(receipt: Omit): Promise; listReceipts(orgId: string, runId: string): Promise; createReservation(input: { orgId: string; runId: string; estimatedCents: number; }): Promise; reservationsForRun(orgId: string, runId: string): Promise; /** First terminal reconciliation wins; retries return that persisted state unchanged. */ reconcileReservation(reservationId: string, actualCents: number, status: "charged" | "released"): Promise; /** Sum of cost_cents of runs created in the given calendar month (YYYY-MM), plus un-reconciled reservations. */ monthlySpendCents(orgId: string, monthPrefix: string): Promise; /** Runs currently admitted (queued/running/cancelling), the concurrency ceiling's subject. */ activeRunCount(orgId: string): Promise; /** Artifacts whose expires_at is at or before `nowIso`, the expiry sweep's subjects. */ listExpiredArtifacts(nowIso: string): Promise; /** Permanently remove an artifact row. The object deletion is the storage's job. */ deleteArtifactRow(artifactId: string, orgId: string): Promise; /** Rewrite an artifact's storage key (quarantine move); row and object move together. */ updateArtifactStorageKey(artifactId: string, orgId: string, storageKey: string): Promise; } export declare function receiptId(): string; export declare function reservationId(): string; /** In-memory governance store: parity for tests, non-durable like its product twin. */ export declare class MemoryGovernanceStore implements GovernanceStore { readonly backend = "memory"; private receipts; private reservations; private artifacts; private runs; constructor(seed?: { runs?: Array<{ orgId: string; costCents: number; status: string; createdAt: string; }>; }); appendReceipt(receipt: Omit): Promise; listReceipts(orgId: string, runId: string): Promise; createReservation(input: { orgId: string; runId: string; estimatedCents: number; }): Promise; reservationsForRun(orgId: string, runId: string): Promise; reconcileReservation(reservationId: string, actualCents: number, status: "charged" | "released"): Promise; monthlySpendCents(orgId: string, monthPrefix: string): Promise; activeRunCount(orgId: string): Promise; listExpiredArtifacts(at: string): Promise; deleteArtifactRow(artifactId: string, orgId: string): Promise; updateArtifactStorageKey(artifactId: string, orgId: string, storageKey: string): Promise; /** Test hook: seed artifacts and runs directly. */ seedArtifacts(artifacts: ServerArtifact[]): void; seedRuns(runs: Array<{ orgId: string; costCents: number; status: string; createdAt: string; }>): void; } /** SQLite governance store: a second connection to the same database file, WAL-safe. */ export declare class SqliteGovernanceStore implements GovernanceStore { readonly backend = "sqlite"; private db; private closed; constructor(path?: string, options?: { migrate?: boolean; }); close(): Promise; get database(): Database; appendReceipt(receipt: Omit): Promise; listReceipts(orgId: string, runId: string): Promise; createReservation(input: { orgId: string; runId: string; estimatedCents: number; }): Promise; reservationsForRun(orgId: string, runId: string): Promise; reconcileReservation(reservationId: string, actualCents: number, status: "charged" | "released"): Promise; monthlySpendCents(orgId: string, monthPrefix: string): Promise; activeRunCount(orgId: string): Promise; listExpiredArtifacts(at: string): Promise; deleteArtifactRow(artifactId: string, orgId: string): Promise; updateArtifactStorageKey(artifactId: string, orgId: string, storageKey: string): Promise; private reservationFrom; } /** Postgres governance store. Same semantics as the SQLite twin; tenant context via set_config. */ export declare class PostgresGovernanceStore implements GovernanceStore { readonly backend = "postgres"; private sql; constructor(databaseUrl: string); close(): Promise; private withContext; appendReceipt(receipt: Omit): Promise; listReceipts(orgId: string, runId: string): Promise; createReservation(input: { orgId: string; runId: string; estimatedCents: number; }): Promise; reservationsForRun(orgId: string, runId: string): Promise; reconcileReservation(reservationId: string, actualCents: number, status: "charged" | "released"): Promise; monthlySpendCents(orgId: string, monthPrefix: string): Promise; activeRunCount(orgId: string): Promise; listExpiredArtifacts(at: string): Promise; deleteArtifactRow(artifactId: string, orgId: string): Promise; updateArtifactStorageKey(artifactId: string, orgId: string, storageKey: string): Promise; private reservationFrom; } /** Open the governance store matching a database target: sqlite (default) or postgres. */ export declare function createGovernanceStore(databaseUrl?: string): Promise;