@cleocode/contracts 2026.5.108 → 2026.5.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/__tests__/db-inventory.test.d.ts +25 -0
  2. package/dist/__tests__/db-inventory.test.d.ts.map +1 -0
  3. package/dist/__tests__/db-inventory.test.js +148 -0
  4. package/dist/__tests__/db-inventory.test.js.map +1 -0
  5. package/dist/__tests__/memory-wire-shapes.test.js.map +1 -1
  6. package/dist/__tests__/task-type-narrowing.test.d.ts +14 -0
  7. package/dist/__tests__/task-type-narrowing.test.d.ts.map +1 -0
  8. package/dist/__tests__/task-type-narrowing.test.js +51 -0
  9. package/dist/__tests__/task-type-narrowing.test.js.map +1 -0
  10. package/dist/backup-verify.d.ts +136 -0
  11. package/dist/backup-verify.d.ts.map +1 -0
  12. package/dist/backup-verify.js +29 -0
  13. package/dist/backup-verify.js.map +1 -0
  14. package/dist/brain.d.ts +103 -0
  15. package/dist/brain.d.ts.map +1 -1
  16. package/dist/db-inventory.d.ts +200 -0
  17. package/dist/db-inventory.d.ts.map +1 -0
  18. package/dist/db-inventory.js +76 -0
  19. package/dist/db-inventory.js.map +1 -0
  20. package/dist/db-inventory.json +165 -0
  21. package/dist/db-recovery.d.ts +141 -0
  22. package/dist/db-recovery.d.ts.map +1 -0
  23. package/dist/db-recovery.js +29 -0
  24. package/dist/db-recovery.js.map +1 -0
  25. package/dist/doctor.d.ts +605 -0
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/index.d.ts +6 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/memory/observe.d.ts +8 -0
  32. package/dist/memory/observe.d.ts.map +1 -1
  33. package/dist/operations/tasks.d.ts +4 -4
  34. package/dist/operations/tasks.d.ts.map +1 -1
  35. package/dist/task.d.ts +13 -2
  36. package/dist/task.d.ts.map +1 -1
  37. package/dist/task.js.map +1 -1
  38. package/package.json +5 -4
  39. package/src/__tests__/db-inventory.test.ts +184 -0
  40. package/src/__tests__/memory-wire-shapes.test.ts +1 -0
  41. package/src/__tests__/task-type-narrowing.test.ts +56 -0
  42. package/src/backup-verify.ts +140 -0
  43. package/src/brain.ts +114 -0
  44. package/src/db-inventory.json +165 -0
  45. package/src/db-inventory.ts +234 -0
  46. package/src/db-recovery.ts +144 -0
  47. package/src/doctor.ts +634 -0
  48. package/src/index.ts +46 -0
  49. package/src/memory/observe.ts +8 -0
  50. package/src/operations/tasks.ts +4 -4
  51. package/src/task.ts +13 -2
@@ -0,0 +1,56 @@
1
+ /**
2
+ * TaskType discriminator narrowing tests.
3
+ *
4
+ * Pins the post-ADR-083 §2.5 union shape (`'saga' | 'epic' | 'task' |
5
+ * 'subtask'`) so that:
6
+ * - Adding/removing a tier in the canonical {@link TaskType} union breaks
7
+ * this test loudly (caught at compile time AND runtime).
8
+ * - Discriminated-union narrowing on `task.type === 'saga'` keeps working
9
+ * for downstream consumers.
10
+ *
11
+ * @since SAGA T10326 · Epic T10277 · Task T10328 (W1.A)
12
+ */
13
+
14
+ import { describe, expectTypeOf, it } from 'vitest';
15
+ import type { Task, TaskType } from '../task.js';
16
+
17
+ describe('TaskType discriminator', () => {
18
+ it('narrows when type === "saga"', () => {
19
+ const t: Task = {
20
+ id: 'T1',
21
+ title: 'demo',
22
+ description: 'demo task',
23
+ status: 'pending',
24
+ priority: 'medium',
25
+ type: 'saga',
26
+ createdAt: '2026-01-01T00:00:00.000Z',
27
+ };
28
+ if (t.type === 'saga') {
29
+ expectTypeOf(t.type).toEqualTypeOf<'saga'>();
30
+ }
31
+ });
32
+
33
+ it('narrows when type === "epic"', () => {
34
+ const t: Task = {
35
+ id: 'T2',
36
+ title: 'demo',
37
+ description: 'demo task',
38
+ status: 'pending',
39
+ priority: 'medium',
40
+ type: 'epic',
41
+ createdAt: '2026-01-01T00:00:00.000Z',
42
+ };
43
+ if (t.type === 'epic') {
44
+ expectTypeOf(t.type).toEqualTypeOf<'epic'>();
45
+ }
46
+ });
47
+
48
+ it('accepts all 4 tier values', () => {
49
+ const types: TaskType[] = ['saga', 'epic', 'task', 'subtask'];
50
+ expectTypeOf(types).toEqualTypeOf<TaskType[]>();
51
+ });
52
+
53
+ it('TaskType is the 4-element union per ADR-083 §2.5', () => {
54
+ expectTypeOf<TaskType>().toEqualTypeOf<'saga' | 'epic' | 'task' | 'subtask'>();
55
+ });
56
+ });
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Type contract for `cleo backup verify` — per-DB freshness + integrity walker.
3
+ *
4
+ * `cleo backup verify` walks every entry in `DB_INVENTORY` and reports, for
5
+ * each role, the freshest snapshot in BOTH the canonical backup directory
6
+ * (`.cleo/backups/sqlite/` per T10315) and the legacy backup directory
7
+ * (`.cleo/backups/snapshot/`, kept read-only for one deprecation window).
8
+ * Each snapshot is opened via the canonical chokepoint, verified with
9
+ * `PRAGMA integrity_check`, and surfaced as a structured envelope. Operators
10
+ * use the envelope to detect stale and corrupt backups before they're needed
11
+ * for recovery.
12
+ *
13
+ * Verdict semantics (see {@link BackupVerifyVerdict}):
14
+ *
15
+ * - `healthy` — a fresh snapshot exists (≤ 24h old) AND `integrity_check`
16
+ * returned `ok`.
17
+ * - `stale` — a snapshot exists but its mtime is > 24h old. Integrity may
18
+ * still be OK; the operator should run `cleo backup add` to refresh.
19
+ * - `corrupt` — at least one snapshot exists but the FRESH snapshot failed
20
+ * `integrity_check`. The legacy snapshot may still be usable as a fallback.
21
+ * - `missing` — no snapshot was found in EITHER the canonical or legacy
22
+ * directory. Recovery from snapshot is impossible.
23
+ *
24
+ * @task T10319
25
+ * @epic T10284
26
+ * @saga T10281
27
+ */
28
+
29
+ /**
30
+ * Verdict roll-up for a single role.
31
+ *
32
+ * @public
33
+ */
34
+ export type BackupVerifyVerdict = 'healthy' | 'stale' | 'corrupt' | 'missing';
35
+
36
+ /**
37
+ * Per-snapshot freshness + integrity record.
38
+ *
39
+ * @remarks
40
+ * Returned for both the canonical (`.cleo/backups/sqlite/`) and legacy
41
+ * (`.cleo/backups/snapshot/`) snapshot directories. When no snapshot exists
42
+ * in the corresponding directory the field on the parent
43
+ * {@link BackupVerifyDbReport} is `null` rather than this shape with empty
44
+ * fields — that lets consumers distinguish "no snapshot ever taken" from
45
+ * "snapshot exists but is unhealthy".
46
+ *
47
+ * @public
48
+ */
49
+ export interface BackupVerifySnapshot {
50
+ /** Absolute path to the snapshot file. */
51
+ readonly path: string;
52
+ /** Snapshot mtime in epoch ms (the freshness signal). */
53
+ readonly mtime: number;
54
+ /**
55
+ * `true` when the snapshot file opened cleanly AND `PRAGMA integrity_check`
56
+ * returned the single row `ok`. `false` otherwise — see {@link error} for
57
+ * details.
58
+ */
59
+ readonly integrityOK: boolean;
60
+ /**
61
+ * Snapshot file size in bytes when `stat()` succeeded; `null` when the
62
+ * file vanished between discovery and stat.
63
+ */
64
+ readonly sizeBytes: number | null;
65
+ /**
66
+ * Human-readable error message when the snapshot could not be opened or
67
+ * its integrity check threw. `null` on success.
68
+ */
69
+ readonly error: string | null;
70
+ }
71
+
72
+ /**
73
+ * Per-DB report — one entry per role in `DB_INVENTORY`.
74
+ *
75
+ * @public
76
+ */
77
+ export interface BackupVerifyDbReport {
78
+ /** Canonical role identifier (matches `DB_INVENTORY[i].role`). */
79
+ readonly role: string;
80
+ /** Lifecycle tier copied through from the inventory for downstream UI. */
81
+ readonly tier: 'project' | 'global' | 'derived';
82
+ /**
83
+ * Freshest snapshot found under `.cleo/backups/sqlite/` (canonical per
84
+ * T10315) — or `null` when the canonical directory has no snapshot for
85
+ * this role.
86
+ */
87
+ readonly freshSnapshot: BackupVerifySnapshot | null;
88
+ /**
89
+ * Freshest snapshot found under `.cleo/backups/snapshot/` (legacy, kept
90
+ * read-only during the deprecation window) — or `null` when the legacy
91
+ * directory has no snapshot for this role.
92
+ */
93
+ readonly legacySnapshot: BackupVerifySnapshot | null;
94
+ /**
95
+ * Hours between `Date.now()` and the freshest available snapshot's mtime
96
+ * (whichever of {@link freshSnapshot} / {@link legacySnapshot} is newer).
97
+ * `null` when {@link verdict} is `missing` — no snapshot to measure from.
98
+ */
99
+ readonly dataLossEstimateHours: number | null;
100
+ /**
101
+ * Roll-up verdict for this role — see {@link BackupVerifyVerdict}.
102
+ */
103
+ readonly verdict: BackupVerifyVerdict;
104
+ }
105
+
106
+ /**
107
+ * Aggregated counters across every role in the survey. Used by the CLI verb
108
+ * to drive its non-zero exit decision (`stale > 0 || corrupt > 0`).
109
+ *
110
+ * @public
111
+ */
112
+ export interface BackupVerifySummary {
113
+ /** Number of roles whose verdict is `healthy`. */
114
+ readonly healthy: number;
115
+ /** Number of roles whose verdict is `stale`. */
116
+ readonly stale: number;
117
+ /** Number of roles whose verdict is `corrupt`. */
118
+ readonly corrupt: number;
119
+ /** Number of roles whose verdict is `missing`. */
120
+ readonly missing: number;
121
+ }
122
+
123
+ /**
124
+ * Envelope payload returned by `cleo backup verify` (and the underlying
125
+ * {@link runBackupVerify} core helper).
126
+ *
127
+ * @remarks
128
+ * The shape is keyed by role rather than indexed by array position so
129
+ * consumers can `.dbs['brain']` without scanning. The summary is provided
130
+ * pre-computed because the CLI exit code is driven from it — recomputing it
131
+ * downstream would invite drift.
132
+ *
133
+ * @public
134
+ */
135
+ export interface BackupVerifyResult {
136
+ /** Per-role reports keyed by `DB_INVENTORY[i].role`. */
137
+ readonly dbs: Readonly<Record<string, BackupVerifyDbReport>>;
138
+ /** Aggregated counters used to drive the CLI exit code. */
139
+ readonly summary: BackupVerifySummary;
140
+ }
package/src/brain.ts CHANGED
@@ -96,3 +96,117 @@ export interface SupersededEntry {
96
96
  /** Topic or category grouping these entries together. */
97
97
  grouping: string;
98
98
  }
99
+
100
+ // ============================================================
101
+ // T10303 — Brain.db auto-recovery (Saga T10281 / Epic T10286)
102
+ // ============================================================
103
+
104
+ /**
105
+ * Result of a brain.db auto-recovery attempt.
106
+ *
107
+ * Returned by `recoverMalformedBrainDb()` (T10303) when the chokepoint
108
+ * detects malformation via `ERR_SQLITE_ERROR errcode=11` or a failing
109
+ * `PRAGMA integrity_check`/`PRAGMA quick_check`. The corrupt DB has been
110
+ * moved to a quarantine directory and the freshest validated snapshot has
111
+ * been copied to `.cleo/brain.db`.
112
+ *
113
+ * Consumers (the brain.db open chokepoint, telemetry, the upcoming T10302
114
+ * regression test) read this structure to decide whether to re-attempt the
115
+ * open and to emit user-facing diagnostics with the data-loss window.
116
+ */
117
+ export interface BrainRecoveryResult {
118
+ /** Absolute path to the snapshot that was restored, or null if recovery failed. */
119
+ restoredFrom: string | null;
120
+ /**
121
+ * Approximate hours between the snapshot's timestamp and the recovery
122
+ * event. `null` when the snapshot timestamp could not be parsed.
123
+ * Used in the recovery warning to make the data-loss window legible.
124
+ */
125
+ dataLossWindowHours: number | null;
126
+ /**
127
+ * Number of `brain_observations` rows in the restored DB. Best-effort —
128
+ * `null` when the count query failed (table missing, FK violation, …).
129
+ */
130
+ observationsRecovered: number | null;
131
+ /** `true` if the restored DB passes `PRAGMA quick_check`. */
132
+ integrityOK: boolean;
133
+ /**
134
+ * Absolute path to the quarantine directory where the corrupt DB plus
135
+ * its `-wal`/`-shm` sidecars were moved before restore.
136
+ */
137
+ quarantineDir: string | null;
138
+ }
139
+
140
+ // ============================================================
141
+ // T10304 — `cleo backup recover brain` CLI verb (Saga T10281 / Epic T10286)
142
+ // ============================================================
143
+
144
+ /**
145
+ * Per-table row counts probed from the restored brain.db.
146
+ *
147
+ * Best-effort — any individual count may be `null` when the table is
148
+ * missing (very old snapshot predating that schema rev) or when the count
149
+ * query throws. The values feed both the `cleo backup recover brain`
150
+ * envelope and the recovery runbook's user-facing diagnostics.
151
+ *
152
+ * @task T10304
153
+ * @epic T10286
154
+ * @saga T10281
155
+ */
156
+ export interface BrainRecoveredRowCounts {
157
+ /** `brain_observations` row count, or `null` when the count failed. */
158
+ observations: number | null;
159
+ /** `brain_decisions` row count, or `null` when the count failed. */
160
+ decisions: number | null;
161
+ /** `brain_learnings` row count, or `null` when the count failed. */
162
+ learnings: number | null;
163
+ }
164
+
165
+ /**
166
+ * Envelope payload returned by `cleo backup recover brain`.
167
+ *
168
+ * The CLI verb (T10304) wraps {@link BrainRecoveryResult} from
169
+ * {@link recoverMalformedBrainDb} (T10303) and enriches it with per-table
170
+ * row counts plus the operator-friendly snapshot/quarantine paths required
171
+ * by the AC envelope shape.
172
+ *
173
+ * Surfaces:
174
+ * - `--dry-run` plan envelopes (with `restoredFrom` being the snapshot that
175
+ * *would* be restored and `quarantinedTo` being the directory the corrupt
176
+ * DB *would* be moved to).
177
+ * - Real-recovery envelopes after the pipeline has executed.
178
+ *
179
+ * @task T10304
180
+ * @epic T10286
181
+ * @saga T10281
182
+ */
183
+ export interface BackupRecoverBrainResult {
184
+ /**
185
+ * Absolute path to the snapshot that was (or would be) restored. Empty
186
+ * string when no snapshot was available — the envelope's caller surfaces
187
+ * that as a failure mode.
188
+ */
189
+ restoredFrom: string;
190
+ /** Per-table row counts probed from the restored DB. */
191
+ rowsRecovered: BrainRecoveredRowCounts;
192
+ /**
193
+ * Approximate hours between the snapshot's timestamp and the recovery
194
+ * event. `null` when the snapshot timestamp could not be parsed (e.g.
195
+ * legacy `brain.db.PRE-DUP-FIX-*` fallback artifact).
196
+ */
197
+ dataLossWindowHours: number | null;
198
+ /** `true` when the restored DB passes `PRAGMA quick_check`. */
199
+ integrityOK: boolean;
200
+ /**
201
+ * Absolute path to the quarantine directory where the corrupt DB plus
202
+ * its `-wal`/`-shm` sidecars were moved. Empty string in `--dry-run`
203
+ * mode where no files were touched.
204
+ */
205
+ quarantinedTo: string;
206
+ /**
207
+ * `true` when invoked with `--dry-run` — indicates the envelope is a
208
+ * plan, NOT a record of mutations performed. Consumers MUST check this
209
+ * before treating `restoredFrom` as a post-condition.
210
+ */
211
+ dryRun: boolean;
212
+ }
@@ -0,0 +1,165 @@
1
+ {
2
+ "$schemaNote": "SSoT machine-readable inventory of every CLEO SQLite database. Consumed by T10307 fleet survey, T10310 pragma drift, T10311 migration coverage, T10312 doctor integrity, T10320 cross-DB invariants. Owned by Saga T10281 SG-BRAIN-DB-RESILIENCE / Epic T10282 E1-DB-INVENTORY (task T10305). Amend together with ADR-068 charter; the CI drift gate (forthcoming under E1) compares entries to disk reality.",
3
+ "$pathTokens": {
4
+ "<projectRoot>": "Resolved at runtime from CLEO_ROOT env var or process.cwd(). Substituted by SSoT helpers `getCleoProjectDir()` in packages/brain/src/cleo-home.ts.",
5
+ "$XDG_DATA_HOME": "Resolved via env-paths through `getCleoHome()` in @cleocode/paths. Linux: ~/.local/share/cleo/, macOS: ~/Library/Application Support/cleo/."
6
+ },
7
+ "entries": [
8
+ {
9
+ "role": "tasks",
10
+ "tier": "project",
11
+ "filePathTemplate": "<projectRoot>/.cleo/tasks.db",
12
+ "drizzleSchemaPath": "packages/core/src/store/tasks-schema.ts",
13
+ "migrationsDir": "packages/core/migrations/drizzle-tasks/",
14
+ "ownerPackage": "@cleocode/core",
15
+ "openedVia": "openCleoDb('tasks', cwd)",
16
+ "concurrency": "single-writer",
17
+ "privacy": "local-only",
18
+ "backupPath": ".cleo/backups/sqlite/tasks-YYYYMMDD-HHmmss.db",
19
+ "documentedIn": "ADR-068 row 1; ADR-013 §9"
20
+ },
21
+ {
22
+ "role": "brain",
23
+ "tier": "project",
24
+ "filePathTemplate": "<projectRoot>/.cleo/brain.db",
25
+ "drizzleSchemaPath": "packages/core/src/store/memory-schema.ts",
26
+ "migrationsDir": "packages/core/migrations/drizzle-brain/",
27
+ "ownerPackage": "@cleocode/brain",
28
+ "openedVia": "openCleoDb('brain', cwd)",
29
+ "concurrency": "single-writer",
30
+ "privacy": "local-only-pii",
31
+ "backupPath": ".cleo/backups/sqlite/brain-YYYYMMDD-HHmmss.db",
32
+ "documentedIn": "ADR-068 row 2; ADR-013 §9; audit §2 (P0 malformed 2026-05-23)"
33
+ },
34
+ {
35
+ "role": "conduit",
36
+ "tier": "project",
37
+ "filePathTemplate": "<projectRoot>/.cleo/conduit.db",
38
+ "drizzleSchemaPath": "packages/core/src/store/conduit-schema.ts",
39
+ "migrationsDir": "packages/core/migrations/drizzle-conduit/",
40
+ "ownerPackage": "@cleocode/core",
41
+ "openedVia": "openCleoDb('conduit', cwd)",
42
+ "concurrency": "single-writer",
43
+ "privacy": "local-only",
44
+ "backupPath": ".cleo/backups/sqlite/conduit-YYYYMMDD-HHmmss.db",
45
+ "documentedIn": "ADR-068 row 3; ADR-037 (signaldock→conduit split)"
46
+ },
47
+ {
48
+ "role": "manifest",
49
+ "tier": "derived",
50
+ "filePathTemplate": "<projectRoot>/.cleo/blobs/manifest.db",
51
+ "drizzleSchemaPath": "packages/core/src/store/llmtxt-blob-adapter.ts",
52
+ "migrationsDir": null,
53
+ "ownerPackage": "@cleocode/core",
54
+ "openedVia": "CleoBlobStore via llmtxt/blob BlobFsAdapter (no openCleoDb role registered)",
55
+ "concurrency": "single-writer",
56
+ "privacy": "local-only",
57
+ "backupPath": "rebuildable-from-blob-store",
58
+ "documentedIn": "ADR-068 row 7 (derived); schema owned by llmtxt/blob BlobFsAdapter contract"
59
+ },
60
+ {
61
+ "role": "llmtxt",
62
+ "tier": "project",
63
+ "filePathTemplate": "<projectRoot>/.cleo/llmtxt/llmtxt.db",
64
+ "drizzleSchemaPath": null,
65
+ "migrationsDir": null,
66
+ "ownerPackage": "@cleocode/llmtxt-core",
67
+ "openedVia": "openCleoDb('llmtxt', cwd) — RESERVED; opener throws 'not yet implemented' (see open-cleo-db.ts L138)",
68
+ "concurrency": "single-writer",
69
+ "privacy": "local-only-pii",
70
+ "backupPath": ".cleo/backups/sqlite/llmtxt-YYYYMMDD-HHmmss.db",
71
+ "documentedIn": "ADR-068 row 8 (reserved); audit §1.1 row 4 (live at .cleo/llmtxt/llmtxt.db)"
72
+ },
73
+ {
74
+ "role": "nexus",
75
+ "tier": "global",
76
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/nexus.db",
77
+ "drizzleSchemaPath": "packages/core/src/store/nexus-schema.ts",
78
+ "migrationsDir": "packages/core/migrations/drizzle-nexus/",
79
+ "ownerPackage": "@cleocode/core",
80
+ "openedVia": "openCleoDb('nexus')",
81
+ "concurrency": "single-writer",
82
+ "privacy": "local-only-pii",
83
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/nexus-YYYYMMDD-HHmmss.db",
84
+ "documentedIn": "ADR-068 row 5"
85
+ },
86
+ {
87
+ "role": "signaldock-project",
88
+ "tier": "project",
89
+ "filePathTemplate": "<projectRoot>/.cleo/signaldock.db",
90
+ "drizzleSchemaPath": "packages/core/src/store/signaldock-schema.ts",
91
+ "migrationsDir": "packages/core/migrations/drizzle-signaldock/",
92
+ "ownerPackage": "@cleocode/core",
93
+ "openedVia": "HISTORICAL — project-tier signaldock.db was migrated into conduit.db post-T310/ADR-037. No live opener; charter row retained for migration provenance and legacy backup detection.",
94
+ "concurrency": "single-writer",
95
+ "privacy": "local-only",
96
+ "backupPath": ".cleo/backups/sqlite/signaldock-project-YYYYMMDD-HHmmss.db",
97
+ "documentedIn": "ADR-068 row 4 (stale — superseded by ADR-037); audit §1.1 (not in live project DB list)"
98
+ },
99
+ {
100
+ "role": "signaldock-global",
101
+ "tier": "global",
102
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/signaldock.db",
103
+ "drizzleSchemaPath": "packages/core/src/store/signaldock-schema.ts",
104
+ "migrationsDir": "packages/core/migrations/drizzle-signaldock/",
105
+ "ownerPackage": "@cleocode/core",
106
+ "openedVia": "openCleoDb('signaldock')",
107
+ "concurrency": "single-writer",
108
+ "privacy": "local-only",
109
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/signaldock-global-YYYYMMDD-HHmmss.db",
110
+ "documentedIn": "ADR-068 row 9; ADR-037 (post-split global identity registry)"
111
+ },
112
+ {
113
+ "role": "telemetry",
114
+ "tier": "global",
115
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/telemetry.db",
116
+ "drizzleSchemaPath": "packages/core/src/telemetry/schema.ts",
117
+ "migrationsDir": "packages/core/migrations/drizzle-telemetry/",
118
+ "ownerPackage": "@cleocode/core",
119
+ "openedVia": "@cleocode/core telemetry/sqlite.ts — lazy open on first event when opted-in; not yet registered as openCleoDb role",
120
+ "concurrency": "single-writer",
121
+ "privacy": "cloud-exportable-opt-in",
122
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/telemetry-YYYYMMDD-HHmmss.db",
123
+ "documentedIn": "ADR-068 row 6; T624"
124
+ },
125
+ {
126
+ "role": "skills",
127
+ "tier": "global",
128
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/skills.db",
129
+ "drizzleSchemaPath": "packages/core/src/store/skills-schema.ts",
130
+ "migrationsDir": "packages/core/migrations/drizzle-skills/",
131
+ "ownerPackage": "@cleocode/core",
132
+ "openedVia": "openCleoDb('skills')",
133
+ "concurrency": "single-writer",
134
+ "privacy": "local-only",
135
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/skills-YYYYMMDD-HHmmss.db",
136
+ "documentedIn": "T9651 (initial skills.db migration); audit §1.2 row 4 — NOT yet in ADR-068 table"
137
+ },
138
+ {
139
+ "role": "global-brain",
140
+ "tier": "global",
141
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/brain.db",
142
+ "drizzleSchemaPath": "packages/core/src/store/memory-schema.ts",
143
+ "migrationsDir": "packages/core/migrations/drizzle-brain/",
144
+ "ownerPackage": "@cleocode/brain",
145
+ "openedVia": "UNREGISTERED — file exists on disk (audit §1.2 row 3) but no live opener. Provenance unclear; likely orphan from a getCleoHome()-vs-project-resolution path bug. Tracked for cleanup decision under T10282/T10307.",
146
+ "concurrency": "single-writer",
147
+ "privacy": "local-only-pii",
148
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/global-brain-YYYYMMDD-HHmmss.db",
149
+ "documentedIn": "audit §1.2 row 3 — NEW per saga T10281; not in ADR-068"
150
+ },
151
+ {
152
+ "role": "global-tasks",
153
+ "tier": "global",
154
+ "filePathTemplate": "$XDG_DATA_HOME/cleo/tasks.db",
155
+ "drizzleSchemaPath": "packages/core/src/store/tasks-schema.ts",
156
+ "migrationsDir": "packages/core/migrations/drizzle-tasks/",
157
+ "ownerPackage": "@cleocode/core",
158
+ "openedVia": "UNREGISTERED — file exists on disk (audit §1.2 row 5, 4 KB) but no live opener. Provenance unclear; likely orphan from a cwd-cascade bug (T9550 class). Tracked for cleanup decision under T10282/T10307.",
159
+ "concurrency": "single-writer",
160
+ "privacy": "local-only",
161
+ "backupPath": "$XDG_DATA_HOME/cleo/backups/sqlite/global-tasks-YYYYMMDD-HHmmss.db",
162
+ "documentedIn": "audit §1.2 row 5 — NEW per saga T10281; not in ADR-068"
163
+ }
164
+ ]
165
+ }