/** * Canonical DB-open chokepoint for all CLEO SQLite databases. * * ## Invariant (ADR-068 §3, T9047) * * Every CLEO SQLite database open MUST flow through `openCleoDb(role, cwd)`. * Raw `new DatabaseSync(path)` calls outside `packages/core/src/store/` are * rejected by the `db-open-guard` CI job (`scripts/lint-no-raw-db-opens.mjs`). * * ## Rationale (ADR-069 Coordination Layers) * * - **Pragma consistency**: every handle receives the SSoT pragma set from * `specs/sqlite-pragmas.json` (busy_timeout, WAL, cache_size, mmap_size). * - **Topology visibility**: the `CleoDbRole` union enumerates all databases; * `cleo health` can audit which are open. * - **Lifecycle centralisation**: singleton management and WAL state live in * one place, preventing lock contention between concurrent CLI processes. * * ## Usage * * ```typescript * import { openCleoDb } from '@cleocode/core/store/open-cleo-db'; * * // Dual-scope consolidated cleo.db (D1″ lifecycle — E3/E4 exodus): * const projHandle = await openCleoDb('project', cwd); * const globHandle = await openCleoDb('global'); * // use handle.db (native DatabaseSync) ... * await handle.close(); * ``` * * ## Dual-scope delegation (T11517 · E3 · T11526 · E6-L6) * * `openCleoDb` accepts ONLY `'project'` | `'global'` and delegates directly to * {@link openDualScopeDb} from `./dual-scope-db.ts`. The legacy 8-role API * (`tasks` / `brain` / `sessions` / `signaldock` / `conduit` / `nexus` / * `skills` / `llmtxt`) was removed in E6-L6 (T11526) — the per-domain leaf * modules (L1–L5) now open the consolidated `cleo.db` through `openDualScopeDb` * directly, so the chokepoint no longer needs the per-role dispatch table. * * The returned `CleoDbHandle.db` is the **native** `DatabaseSync` handle * (extracted from the Drizzle wrapper's `$client`), preserving the contract * the legacy roles exposed so callers that issue raw `prepare`/`exec` SQL keep * working with only a role-string swap. * * ## Snapshot opener (read-only, no migrations) * * For short-lived read-only opens (backup verification, schema integrity * checks, registry reads from non-CLEO processes like Studio), use * {@link openCleoDbSnapshot}. It applies the same pragma SSoT but skips * migrations and singleton management, so the caller owns the handle's * lifecycle directly. * * @task T9047, T9685, T11517, T11526 * @adr ADR-068, ADR-069 */ import type { DatabaseSync } from 'node:sqlite'; import type { DualScope } from './dual-scope-db.js'; /** * Canonical scopes for the consolidated CLEO `cleo.db` databases (ADR-068). * * - `'project'` — consolidated per-project `cleo.db` (delegates to {@link openDualScopeDb}) * - `'global'` — consolidated per-user `cleo.db` (delegates to {@link openDualScopeDb}) * * The legacy 8-role API (`tasks` / `brain` / `sessions` / `signaldock` / * `conduit` / `nexus` / `skills` / `llmtxt`) was removed in E6-L6 (T11526). * `tasks` / `brain` / `sessions` / `conduit` map to `'project'`; `nexus` / * `signaldock` / `skills` map to `'global'`. * * @task T11517 (E3-T1 · SG-DB-SUBSTRATE-V2), T11526 (E6-L6) */ export type CleoDbRole = DualScope; /** Handle returned by {@link openCleoDb}. */ export interface CleoDbHandle { db: unknown; role: CleoDbRole; close(): Promise; } /** @deprecated Use {@link CleoDbHandle}. */ export type DBHandle = CleoDbHandle; /** * Cross-check `.cleo/project-info.json::projectId` against the project_id * recorded for this project's path inside the DB being opened. * * No-ops (returns silently) for ALL non-drift cases: * - The role does not track `project_id` (see {@link PROJECT_ID_TRACKING_ROLES}). * - `.cleo/project-info.json` is missing or unparseable (pre-init / fresh clone). * - The `projectId` field is empty (pre-T5333 install). * - The DB's project-tracking table does not yet exist (pre-bootstrap). * - No row in the tracking table matches the caller's project path * (project not yet registered with nexus). * * Throws `CleoError(CONFIG_ERROR, "E_PROJECT_ID_DRIFT", ...)` IFF every * condition is satisfied: * 1. The role tracks project_id, AND * 2. `.cleo/project-info.json` reports a non-empty `projectId`, AND * 3. The DB has a project-registry row for the caller's `projectRoot`, AND * 4. That row's `project_id` differs from the project-info projectId. * * Read-only: never writes to the DB. * * @task T10322 * @saga T10281 (SG-BRAIN-DB-RESILIENCE) * @epic T10285 (E4-DB-CROSS-LINKS) * @adr ADR-068 */ export declare function validateProjectIdConsistency(role: CleoDbRole, db: unknown, cwd?: string): void; /** * Open (or create) a CLEO database by dual-scope selector. * * ## Dual-scope delegation (T11517 · E3 · T11526 · E6-L6) * * Accepts ONLY `'project'` | `'global'` and delegates to {@link openDualScopeDb} * from `./dual-scope-db.ts`, which applies the pragma SSoT, runs migrations, * and manages the singleton cache for the consolidated `cleo.db`. * * ```ts * const handle = await openCleoDb('project', cwd); * const handle = await openCleoDb('global'); * ``` * * `CleoDbHandle.db` is the **native** `DatabaseSync` handle (extracted from the * Drizzle wrapper's `$client`). This preserves the contract the legacy 8-role * API exposed, so callers issuing raw `prepare`/`exec` SQL keep working after * swapping their role string (`tasks`/`brain`/`sessions`/`conduit` → `project`; * `nexus`/`signaldock`/`skills` → `global`). * * Single chokepoint for all DB opens. Enforces the worktree-isolation guard * (T9806) for the project scope on top of T9803's path-layer THROWS-on-orphan * fix. * * @task T9047, T9685, T11517, T11526 * @adr ADR-068, ADR-069 */ export declare function openCleoDb(role: CleoDbRole, cwd?: string): Promise; /** Options accepted by {@link openCleoDbSnapshot}. */ export interface CleoDbSnapshotOptions { /** * Open the file with `readOnly: true`. Default `true` — the snapshot opener * is meant for read-only inspection (backup verification, schema queries, * registry reads from short-lived processes like a SvelteKit request). */ readOnly?: boolean; /** * Apply the canonical pragma set (cache_size, mmap_size, busy_timeout, * temp_store, wal_autocheckpoint). Default `true`. WAL/foreign_keys are * suppressed automatically when `readOnly === true`. */ applyPragmas?: boolean; } /** * Handle returned by {@link openCleoDbSnapshot}. Caller-owned lifecycle — * `close()` calls `DatabaseSync.close()` directly because snapshot opens are * NOT managed by a singleton. */ export interface CleoDbSnapshotHandle { /** The native node:sqlite handle. */ db: DatabaseSync; /** Absolute path the handle was opened against. */ path: string; /** Close the underlying handle. Safe to call multiple times. */ close(): void; } /** * Open a SQLite database file as a read-only snapshot, applying the * canonical pragma SSoT but skipping migrations and singleton management. * * ## When to use * * - Backup verification (e.g. `migration/checksum.ts`) * - Atomic / read-side database validation (e.g. `store/atomic.ts`) * - Short-lived registry reads from a non-CLEO process (e.g. Studio * SvelteKit endpoints that read nexus.db for project listings) * * Do NOT use for the long-lived role databases — those go through * {@link openCleoDb} which manages singletons + migrations. * * ## Pragma application * * When `applyPragmas !== false` (default), the canonical performance pragmas * are applied via `applyPerfPragmas`. For read-only handles, `enableWal` is * forced `false` because WAL mode is unsettable on a read-only connection. * * ## Lifecycle * * The handle is caller-owned — call `handle.close()` when done. Unlike * {@link openCleoDb}, the snapshot opener does NOT participate in any * singleton cache, so leaking a snapshot handle leaks a file descriptor. * * @task T9685 * @adr ADR-068, ADR-069 * * @example * ```typescript * import { openCleoDbSnapshot } from '@cleocode/core/store/open-cleo-db'; * * const snap = openCleoDbSnapshot('/path/to/cleo.db'); * try { * const rows = snap.db.prepare('SELECT * FROM nexus_project_registry').all(); * // ... * } finally { * snap.close(); * } * ``` */ export declare function openCleoDbSnapshot(path: string, options?: CleoDbSnapshotOptions): CleoDbSnapshotHandle; //# sourceMappingURL=open-cleo-db.d.ts.map