/** * Centralized SQLite performance pragma application for all CLEO databases. * * Single source of truth for the pragma set applied to every node:sqlite * `DatabaseSync` handle opened across CLEO. The canonical (name, value) * pairs are embedded directly as a TypeScript literal (option B of T9157) * rather than loaded from `specs/sqlite-pragmas.json` at runtime. * * **Why embedded rather than file-loaded (T9157 fix)**: * The original `readFileSync` approach used a 4-level `..` path from * `import.meta.url` that assumed monorepo layout. This broke in two * environments: * 1. CI: `NODE_PATH` resolution landed one level above the project root. * 2. npm-installed consumers: `node_modules/@cleocode/core/dist/store/` * has no `specs/` sibling anywhere up its ancestor chain. * Embedding the data as a TypeScript literal eliminates all filesystem * lookups — the module is self-contained regardless of where it is * installed or how CI lays out the workspace. * * The Rust crate `signaldock-storage` still consumes `specs/sqlite-pragmas.json` * from a `build.rs` codegen step (T9053 SSoT direction). Both sides must * be kept in sync manually; a future T9053 codegen step can validate * equivalence at build time. * * Keeping this in one place avoids the historical drift where some * open sites (sqlite-native, conduit-sqlite) carried tuned pragmas * while others (backup-pack, agent-registry-accessor, one-shot * installers) opened raw connections that fell back to SQLite * defaults — silently penalizing every operation those code paths drove. * * The pragma set is tuned for CLEO's workload profile: * - Multi-process CLI invocations against shared project DBs * (tasks.db, brain.db, conduit.db) — expect concurrent readers + writer. * - Read-heavy with bursty writes (find/show vs add/update/complete). * - Local SSD or network-mounted project dirs (e.g. /mnt/projects). * - Durable-on-commit but tolerant of last-transaction loss on power cut * (CLI tasks, not financial ledgers). * * @remarks * Choices and rationale for the values below (mirroring `specs/sqlite-pragmas.json`): * * - `busy_timeout = 30000` — Wait up to 30 s for a competing writer's lock * before failing with SQLITE_BUSY. Without this, concurrent CLI * invocations (verify + complete + tests) immediately error out; * with this, they queue politely. Raised from 5 s to 30 s (T11363, * SG-DB-SUBSTRATE-V2) so the consolidated single-file-per-scope substrate — * where many domains now contend for one writer lock — tolerates the longer * tail under heavy parallel-agent fan-out. * - `journal_mode = WAL` — Enables concurrent reader+writer access. * - `synchronous = NORMAL` — Safe with WAL: durable on commit, only the * in-flight transaction is at risk on power cut. ~2-3× faster than the * default `FULL` for write-heavy workloads. * - `foreign_keys = ON` — Enforce referential integrity by default. * - `cache_size = -64000` — 64 MB page cache (negative = KB; positive = * pages). The SQLite default of ~2 MB makes any non-trivial query * thrash. * - `mmap_size = 268435456` — 256 MB memory-mapped read window. * - `temp_store = MEMORY` — Temp tables live in RAM rather than on disk. * - `wal_autocheckpoint = 1000` — Auto-checkpoint after 1000 frames * (~4 MB at 4 KB pages). * * @remarks * For read-only opens (`{ readOnly: true }` constructor option), most * pragmas still apply. Pragmas that require write access (e.g. * `journal_mode = WAL` if not already set) silently no-op on read-only * handles, which is fine — the writer set WAL when it created the DB. * * @task T-PERF-PRAGMAS, T9053, T9157, T10313 * * @remarks SQLite version pin (T10313, Saga T10281 / Epic T10283 E2-DB-INTEGRITY) * * `specs/sqlite-pragmas.json` declares `sqliteVersionPin.version = "3.51.2"` and * `nodeMinimum = "24.13.0"`. This saga's brain.db malformation (T10281, * 2026-05-23) occurred under node:sqlite v3.51.2 — upstream concurrent-write * race suspected per T10301 RCA (D012). Pin maintained until upstream fix * confirmed via `https://sqlite.org/src/timeline`. * * The pin is enforced as a SOFT pin: `sqlite-version-pin.test.ts` warns when * `process.versions.sqlite` differs from the declared version but does NOT * fail the gate. Hard-pinning here would break every Node minor release. */ import type { DatabaseSync } from 'node:sqlite'; /** * Canonical (name, value) pairs in declared order — mirror of the * Rust `SQLITE_PRAGMAS` const generated by * `crates/signaldock-storage/build.rs` from the same JSON file. */ export declare const CANONICAL_PRAGMAS: ReadonlyArray; /** * Render a single (name, value) pair to its `PRAGMA name = value` SQL * form. The Rust side (`build.rs`) emits the identical formatting. * * @param entry - Tuple of `[pragma name, value]` from the SSoT. * @returns The pragma SQL statement (no trailing semicolon). * * @example * ```ts * renderPragmaSql(['journal_mode', 'WAL']); // "PRAGMA journal_mode = WAL" * ``` */ export declare function renderPragmaSql(entry: readonly [string, string]): string; /** * Canonical pragma SQL — every entry rendered and joined with `;\n`. * Byte-identical to `signaldock_storage::sqlite_pragmas::SQLITE_PRAGMA_SQL`. */ export declare const CANONICAL_PRAGMA_SQL: string; /** * Configuration for `applyPerfPragmas`. All fields are optional — * defaults come from the SSoT spec. Any override here only affects the * matching pragma when it is present in the SSoT; unknown overrides * are ignored. */ export interface PerfPragmaOptions { /** * Whether to set `PRAGMA journal_mode = WAL`. Set `false` for read-only * handles where WAL was already established by the writer. Default `true`. */ enableWal?: boolean; /** * Whether to set `PRAGMA foreign_keys = ON`. Set `false` for vitest * environments where fixtures intentionally insert orphan refs. * Default: `true` outside vitest, `false` inside (auto-detected via * `process.env.VITEST`). */ enableForeignKeys?: boolean; /** * Page cache size in KB (passed as the negative form to PRAGMA cache_size). * Default comes from the SSoT (`-64000` = 64 MB). */ cacheSizeKb?: number; /** * Memory-mapped I/O size in bytes. Default comes from the SSoT * (`268435456` = 256 MB). Set `0` to disable mmap entirely. */ mmapSizeBytes?: number; /** * Busy timeout in milliseconds — how long writers wait for a competing * lock before SQLITE_BUSY. Default comes from the SSoT (`30000`). */ busyTimeoutMs?: number; } /** * Apply the canonical CLEO performance pragma set to a freshly-opened * SQLite connection. * * Pragmas are applied via `db.exec(...)` (not prepare/run) because they * are configuration, not queries — most return no rows and prepared * statement caching offers no benefit. * * The order and the value for each pragma come from * `specs/sqlite-pragmas.json` (T9053 SSoT). `applyPerfPragmas` only * deviates from the spec for the small set of toggles documented on * `PerfPragmaOptions` (read-only handles disable WAL, vitest disables * foreign_keys, callers may override numeric values). * * Safe to call multiple times on the same handle: every pragma here is * idempotent. Safe to call on read-only handles: write-requiring pragmas * silently no-op. * * @param db - Open `node:sqlite` `DatabaseSync` handle. * @param options - Optional overrides for individual pragma values. */ export declare function applyPerfPragmas(db: DatabaseSync, options?: PerfPragmaOptions): void; /** * Run `PRAGMA optimize` before closing a long-lived handle. * * SQLite collects table statistics opportunistically while a connection * is open. `PRAGMA optimize` flushes those stats so the next process * can use them for query planning. Cheap (~ms) and recommended by the * SQLite docs as the standard close-time pragma. * * Safe to call on read-only handles (no-op). * * @param db - Open `node:sqlite` `DatabaseSync` handle. */ export declare function optimizeBeforeClose(db: DatabaseSync): void; //# sourceMappingURL=sqlite-pragmas.d.ts.map