/** * Table classification registry — the replication class of every physical * table in the project and global `cleo.db` (Gate A). * * Every table a store can hold is either classified here (by an explicit * entry or a pattern rule) or listed as pending an owner ruling. A table in * neither place is UNCLASSIFIED, and the Gate A test * (`__tests__/table-classification-gate.test.ts`) fails on it. That test * enumerates `sqlite_master` of freshly migrated stores and of the committed * live-store shape, so a table born in a migration, in runtime DDL, or in a * legacy lineage cannot slip through. * * ## Keyed on physical names, not the Drizzle schema * * The runtime often writes the BARE legacy twin ( * `architecture_decisions`, `session_narrative`, …) rather than the prefixed * table, while other bare twins are frozen copies nobody reads. A registry * keyed on the Drizzle schema would classify the frozen copy and miss the * live one, so both twins are listed here and the frozen ones carry * `status: 'frozen-legacy'`, `liveTwin` and `dropTask` (T12535 collapses them). * * ## Two tiers: every table is backed up, portable tables also sync * * - **Tier 1, backup (the invariant).** Every table in both stores, with every * row, whatever its class (including `local-only`, `derived`, pending and * even unclassified tables), is backed up: encrypted client-side and * versioned per device, so any machine can be restored bit-for-bit. A class * NEVER exempts a table from backup. {@link TABLE_CLASS_POLICY} encodes this * as `backup: true` for every class, and the Gate A test asserts it. * - **Tier 2, sync/merge across the owner's devices.** Only the portable * classes. `portable-personal` merges across the owner's own devices; * `portable-project` also merges into the project's shared space for * collaborators; `portable-secret` travels sealed end-to-end. `peer_scope` * governs sharing with OTHER people, never the owner's own machines. * * The test for a table: if this device died and the owner opened a new one, * would an agent have to redo work or lose knowledge? If yes, it is portable. * * `derived` is deliberately NARROW: only what is rebuilt deterministically, * cheaply and without an LLM. That is the FTS5/sqlite-vec shadow tables and * the nexus code graph (rebuilt from source by `cleo nexus analyze`). * Embeddings, sleep-cycle output and anything an LLM produced are NOT * derived: regenerating them costs money or time. `local-only` is for state * that is meaningless off-device: leases, queues, pids, paths, locations and * fs-keyed caches. * * ## STDP event history syncs (cleo-dev ruling 2026-09-29) * * `brain_plasticity_events` and `brain_weight_history` are `portable-personal` * (journal spec review, Q11, under the owner's direction that the cloud backs * up everything). The 2026-09-28 ruling had them local-only, with the current * learned weights to follow as a projection or snapshot blob. They are * append-only with 14M+ lifetime inserts, so they set the change journal's * op-rate ceiling; how much event history replicates is the journal spec's * open retention question (Q15). * * ## Sources * * The classes come from the classification draft * (`docs/research/table-classification-draft.md` in the cleo-nexus repo), the * core owner's rulings in its §F, and the core owner's two-tier ruling of * 2026-09-28, its follow-up rulings on the STDP tables, and its journal spec * review rulings of 2026-09-29 (STDP and `token_usage`). Table * classification was delegated to agents by the owner, so these are cleo-dev * rulings; the ruling supersedes earlier ones where they conflict (each such * entry carries the old reasoning in its `note`). Rows marked * `needs-owner-call` are provisional. Tables on the `pending` list have no * class and never sync (they are still backed up), and the Gate A test fails * while the list is non-empty: pending is a working state, never a merged * one. * * @task T12332 * @epic T12322 * @module store/table-classification */ import type { TableClass, TableClassification, TableScope, TableScopeRegistry } from '@cleocode/contracts'; /** * The classification registry for the project `cleo.db`. * * @task T12332 */ export declare const PROJECT_TABLE_REGISTRY: TableScopeRegistry; /** * The classification registry for the global `cleo.db`. * * @task T12332 */ export declare const GLOBAL_TABLE_REGISTRY: TableScopeRegistry; /** * Every class a table can carry, in a stable order for reports. * * @task T12332 */ export declare const TABLE_CLASSES: readonly TableClass[]; /** * What each class means for the two tiers. * * `backup` is `true` for EVERY class: that is the tier-1 invariant, and it is * typed as the literal `true` so a class can never opt out. `sync` is tier 2. * * @task T12332 */ export declare const TABLE_CLASS_POLICY: Readonly>; /** * Return the registry for a scope. * * @param scope - Which `cleo.db` the table lives in. * @returns That scope's registry. * @task T12332 */ export declare function getTableRegistry(scope: TableScope): TableScopeRegistry; /** * Classify one physical table name. * * Lookup order: explicit entry, then pattern rule, then the pending list. * Pure: no database is opened. * * @param scope - Which `cleo.db` the table lives in. * @param name - The physical name as `sqlite_master` reports it. * @returns The classification; `kind: 'unclassified'` fails Gate A. * * @example * ```ts * classifyTable('project', 'tasks_tasks'); // { kind: 'entry', class: 'portable-project', … } * classifyTable('project', 'brain_observations_fts_idx'); // { kind: 'pattern', class: 'derived', … } * ``` * @task T12332 */ export declare function classifyTable(scope: TableScope, name: string): TableClassification; /** * Whether a class syncs across devices (tier 2; sealed for secrets). * * A sync journal includes a table only when this is true for its class. * Pending and unclassified tables have no class and never sync. Backup * (tier 1) is not gated by this: every table is backed up. * * @param tableClass - The class to test. * @returns `true` for the three portable classes. * @task T12332 */ export declare function isPortableTableClass(tableClass: TableClass): boolean; /** * The `local-only` tables whose rows record the schema state of the database * file itself: migration journals and schema-version sentinels. * * They never sync (each device migrates its own file), but they are not * machine state either: they describe the file they sit in. A vault restore * therefore keeps the snapshot's rows for them, because the snapshot's file is * the one placed. Carrying another file's journal (or none, on a new machine) * makes the migrator stamp migrations applied without running them and re-run * old ones (T13104). * * Every name here is classified `local-only` in its scope (asserted by the * classification tests). * * @task T13104 */ export declare const SCHEMA_STATE_TABLES: Readonly>; /** * Whether a table records the schema state of its database file * ({@link SCHEMA_STATE_TABLES}). * * @param scope - Which `cleo.db` the table lives in. * @param name - The physical name as `sqlite_master` reports it. * @returns `true` for a migration journal or schema-version sentinel. * @task T13104 */ export declare function isSchemaStateTable(scope: TableScope, name: string): boolean; /** * The `local-only` tables whose rows record facts about OTHER rows of the same * file, so they are consistent only with the snapshot that holds those rows: * `_exodus_recovery_bare_accounts`, the key digests of the bare legacy tables * a reconcile carried (T13320; `BARE_ACCOUNTS_TABLE` in `store/sync/flags.ts`). * * They never sync, but a snapshot or bundle carries them with the rows they * describe, and a vault restore does not carry this machine's rows whole: it * keeps, per bare table, the record (the snapshot's, else this machine's) that * still matches the bare table as restored. Carrying only this machine's * record would refuse as a strand a restored store whose twins the snapshot's * reconcile settled (T13324). * * Every name here is classified `local-only` in its scope (asserted by the * classification tests). * * @task T13324 */ export declare const SNAPSHOT_STATE_TABLES: Readonly>; /** * Whether a table records facts about the rows of its own file * ({@link SNAPSHOT_STATE_TABLES}). * * @param scope - Which `cleo.db` the table lives in. * @param name - The physical name as `sqlite_master` reports it. * @returns `true` for a table a restore keeps as the snapshot has it. * @task T13324 */ export declare function isSnapshotStateTable(scope: TableScope, name: string): boolean; /** * The keys of a `local-only` key/value table (column `key`) that describe the * database file's own data or schema, among keys that describe this machine. */ export interface FileStateKeyRule { /** Keys that describe the file. */ readonly exact: readonly string[]; /** Key prefixes that describe the file. */ readonly prefixes: readonly string[]; /** * When the placed file lacks the table, create it (with this machine's DDL) * so this machine's keys are not lost: its schema is applied lazily from the * keys themselves (`_sync_meta`: the `sync.*` flags apply the sync schema). */ readonly createIfAbsent: boolean; } /** * `local-only` key/value tables that mix keys describing the database file * with keys describing this machine. A vault restore takes the file's keys * from the snapshot and this machine's keys from the live store (T13107, * T13108): * * - `tasks_row_identity_meta`: the uid recipe the rows were derived with, the * "uids have left the device" marker and the canonical-timestamp rewrite * marker describe the rows, and so does the audit-secret scrub marker * (`audit_secret_scrub_v1`, T13864: the rows it vouches for are the file's); * the local rename records (`renamed_from:*`) are this store's history. * - `_sync_meta`: the sync-schema ledger (`schema:`) describes which * sync tables the file has, and the suspect marks (`suspect:`) name * tables whose rows the file holds writes never captured, so its repair * signal travels with those rows (T13677); the `sync.*` flags, * `min_writer_version` (it follows the capture flag), replica seqs and * sealer counters are this machine's. * * Every table here is classified `local-only` (asserted by the classification * tests), so none of it syncs or is hashed by the manifest. * * @task T13107 * @task T13108 */ export declare const FILE_STATE_KEYS: Readonly>>>; /** * The file-state key rule of a table ({@link FILE_STATE_KEYS}), or `null`. * * @param scope - Which `cleo.db` the table lives in. * @param name - The physical name as `sqlite_master` reports it. * @returns The rule, or `null` when the table has none. * @task T13107 */ export declare function fileStateKeyRule(scope: TableScope, name: string): FileStateKeyRule | null; /** * Whether `key` describes the file under `rule`. * * @param rule - A table's file-state key rule. * @param key - A value of the table's `key` column. * @returns `true` for a key the snapshot supplies on restore. * @task T13107 */ export declare function isFileStateKey(rule: FileStateKeyRule, key: string): boolean; //# sourceMappingURL=table-classification.d.ts.map