import type { SqliteDatabase } from '../sqlite/sqlite-driver.js'; import type { LoopRelease, LoopReleaseLifecycleStatus } from './loop-release-types.js'; /** * Low-level SQLite access for the `loop_release` table. The * migration is registered with the existing `openStateDb()` pipeline * via `src/services/skillhub/migrations/002-loop-release.sql` and * `004-loop-bee-extension.sql` (it applies every `*.sql` file in * lexicographic order). The function `ensureLoopReleaseTable` below * is a belt-and-suspenders re-applier for callers that pass a * database they built themselves (e.g. tests). * * Defense in depth: * - All JSON columns are stored as TEXT and parsed at the boundary. * No big JSON BLOB at the SQLite layer — the row stays small and * queryable. * - The migration is idempotent (CREATE TABLE IF NOT EXISTS, CREATE * INDEX IF NOT EXISTS) so re-running it is safe. * - The M3 migration (`004-loop-bee-extension.sql`) is non-breaking: * it only adds new columns with DEFAULTs and does NOT touch any */ /** * Re-apply the loop_release table migration against an already-open * database. Used by tests that build their own DB without the openStateDb * pipeline. Idempotent. * * M3: the CREATE here now includes the four share / desktop extension * columns from migration 004 (`shareable`, `share_excluded_paths`, * `desktop_visible`, `export_bundle_format`). For an existing M2-era * database, the migration runner will already have applied 004 and * the columns will exist; for a freshly-built DB via this helper the * columns are baked in. Either way the schema converges. */ export declare function ensureLoopReleaseTable(db: SqliteDatabase): void; /** * Insert a LoopRelease row. The row's `schema_version` is taken from * the input (must equal `peaks.loop/1`); `archived_at` is stamped * from the server clock here, so callers cannot backdate a row. * * Idempotent in the table sense (UNIQUE on `id`) — re-inserting the * same id throws a UNIQUE-constraint error; callers wanting upsert * semantics should use a separate path (M5 / crystallization event). */ export declare function insertLoopRelease(db: SqliteDatabase, row: LoopRelease): void; /** Read a single LoopRelease row by id; returns undefined if absent. */ export declare function getLoopRelease(db: SqliteDatabase, id: string): LoopRelease | undefined; /** List LoopRelease rows filtered by lifecycle_status. */ export declare function listLoopReleasesByStatus(db: SqliteDatabase, status: LoopReleaseLifecycleStatus): LoopRelease[]; /** * Search LoopRelease rows whose `scenario` contains the query string. * M1 uses a case-insensitive LIKE; FTS5 promotion is deferred. The * query is treated as a literal substring (no regex, no SQL * injection surface) — it is escaped via parameter binding. */ export declare function searchLoopReleasesByScenario(db: SqliteDatabase, query: string): LoopRelease[];