/** * `cleo doctor` row `sync_triggers` (journal spec §2.3a rule 9, §3.5 Rule 4; * C2(b), T12819). * * Read-only. It opens the project `cleo.db` as a snapshot and reports: * - `cleo_trigger_suspend` missing (every write to tasks, sessions and * acceptance criteria then fails, and a 9.24 binary fails closed) or * holding a committed row; * - a trigger no class rule covers; * - an owned guard or side-effect trigger missing, or whose live text * differs from its owned DDL (for example without its suspension clause); * - a capture trigger (`_sync_cap_*`) while `_sync_capture` is missing; * - any trigger whose body references a table that does not exist, or * inserts into a column its table lacks (T12754). SQLite only notices this * when the trigger fires, so every write to its table fails until then. * * The repair is the open pass: any `cleo` command against the project * recreates the table (step 0) and re-runs the owned DDL of every differing * trigger. `cleo doctor sync-triggers --repair` ({@link repairSyncTriggers}) * runs the same steps on demand and reports what it changed. A store whose * journal has not reached the C2 migration yet is reported as pending, not * broken. * * @module * @task T12819 * @task T12754 */ import type { DatabaseSync } from 'node:sqlite'; import { type OwnedTriggerFinding } from '../store/sync/trigger-classes.js'; /** The `sync_triggers` row of `cleo doctor`. */ export interface SyncTriggersDoctorCheck { readonly check: 'sync_triggers'; readonly status: 'ok' | 'warning' | 'error'; readonly message: string; readonly details?: Record; readonly fix?: string; } /** What {@link inspectSyncTriggers} found. */ export interface SyncTriggersReport { readonly dbPath: string; readonly storeExists: boolean; /** The C2 migration is journaled. */ readonly clauseMigrationApplied: boolean; readonly suspendTable: 'present' | 'missing'; readonly suspendRows: number; readonly unclassified: string[]; readonly owned: OwnedTriggerFinding[]; readonly orphanedCaptureTriggers: string[]; /** With `sync.capture` on: capture triggers missing, differing from the generated text, or extra (rule 9). */ readonly captureDrift: { missing: string[]; differing: string[]; extra: string[]; }; /** Triggers that reference a missing table or insert into a missing column (T12754). */ readonly dangling: DanglingTrigger[]; } /** A trigger whose body references objects the store does not have (T12754). */ export interface DanglingTrigger { readonly name: string; /** `table ` or `column .`, for each missing object. */ readonly missing: string[]; } /** Inspect the project store's triggers. Read-only. */ export declare function inspectSyncTriggers(projectRoot: string): SyncTriggersReport; /** * Every trigger whose text references a table the store lacks, or inserts * into a column its table lacks (T12754). SQLite resolves trigger bodies only * when the trigger fires, so such a trigger fails every write to its table. * * The scan tokenises the trigger (string literals and comments dropped, * quoted identifiers kept). A table is the name after `FROM` or `JOIN` * (the WHEN clause included; not `IS [NOT] DISTINCT FROM`), or the target of * `INSERT … INTO`, `REPLACE INTO`, `UPDATE` or `DELETE FROM` in the body. * A name followed by `(` is a table-valued function, a CTE name is local to * its statement, and a schema other than `main` is not judged. * * Every table of a comma-separated FROM list is checked (T13206). * * Not checked (false negatives by design): `UPDATE … SET` columns and * `NEW.` / `OLD.` columns. */ export declare function danglingTriggers(db: DatabaseSync): DanglingTrigger[]; /** The `sync_triggers` row of the default `cleo doctor` report. */ export declare function syncTriggersDoctorCheck(projectRoot: string): SyncTriggersDoctorCheck; /** What {@link repairSyncTriggers} did. */ export interface SyncTriggersRepairResult { /** The `sync_triggers` row before the repair. */ readonly before: SyncTriggersDoctorCheck; /** The row after it. A trigger CLEO does not own is reported, never dropped. */ readonly after: SyncTriggersDoctorCheck; /** One line per change. */ readonly actions: string[]; } /** * `cleo doctor sync-triggers --repair` (T12754): run the open pass's trigger * steps on the project store now, and report the row before and after. * * - step 0: recreate `cleo_trigger_suspend`, or clear a committed row; * - owned guard and side-effect triggers: drop and re-run their owned DDL * when missing, differing or dangling; * - capture triggers: match `sync.capture`. With it on, the outbox tables are * healed and the triggers regenerated for the current schema; with it off, * they are dropped. * * A trigger CLEO does not own that references a missing object is reported in * `after`, never dropped: it is not CLEO's to remove. */ export declare function repairSyncTriggers(projectRoot: string): Promise; /** What changed between two trigger reports, one line per repair. */ export declare function repairActions(was: SyncTriggersReport, now: SyncTriggersReport): string[]; //# sourceMappingURL=sync-triggers.d.ts.map