@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,25 @@
1
+ /**
2
+ * Contract tests for the CLEO database inventory SSoT.
3
+ *
4
+ * Asserts the structural invariants the inventory MUST hold:
5
+ *
6
+ * 1. Every {@link DbInventoryEntry.role} is unique across the array.
7
+ * 2. Every {@link DbInventoryEntry.tier} is a valid member of the
8
+ * {@link DbTier} union.
9
+ * 3. Every non-null {@link DbInventoryEntry.drizzleSchemaPath} resolves
10
+ * to a real file on disk relative to the monorepo root.
11
+ * 4. Every non-null {@link DbInventoryEntry.migrationsDir} resolves to a
12
+ * real directory on disk relative to the monorepo root.
13
+ * 5. The list is non-empty and covers the AC1-mandated role set.
14
+ *
15
+ * These invariants are the gating contract for downstream saga tasks
16
+ * (T10307 fleet survey, T10310 pragma drift, T10311 migration coverage,
17
+ * T10312 doctor integrity, T10320 cross-DB invariants). If this suite
18
+ * fails, the inventory has drifted from disk reality and CI MUST block.
19
+ *
20
+ * @task T10305
21
+ * @epic T10282
22
+ * @saga T10281
23
+ */
24
+ export {};
25
+ //# sourceMappingURL=db-inventory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-inventory.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/db-inventory.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Contract tests for the CLEO database inventory SSoT.
3
+ *
4
+ * Asserts the structural invariants the inventory MUST hold:
5
+ *
6
+ * 1. Every {@link DbInventoryEntry.role} is unique across the array.
7
+ * 2. Every {@link DbInventoryEntry.tier} is a valid member of the
8
+ * {@link DbTier} union.
9
+ * 3. Every non-null {@link DbInventoryEntry.drizzleSchemaPath} resolves
10
+ * to a real file on disk relative to the monorepo root.
11
+ * 4. Every non-null {@link DbInventoryEntry.migrationsDir} resolves to a
12
+ * real directory on disk relative to the monorepo root.
13
+ * 5. The list is non-empty and covers the AC1-mandated role set.
14
+ *
15
+ * These invariants are the gating contract for downstream saga tasks
16
+ * (T10307 fleet survey, T10310 pragma drift, T10311 migration coverage,
17
+ * T10312 doctor integrity, T10320 cross-DB invariants). If this suite
18
+ * fails, the inventory has drifted from disk reality and CI MUST block.
19
+ *
20
+ * @task T10305
21
+ * @epic T10282
22
+ * @saga T10281
23
+ */
24
+ import { existsSync, statSync } from 'node:fs';
25
+ import { dirname, resolve } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { describe, expect, it } from 'vitest';
28
+ import { DB_INVENTORY } from '../db-inventory.js';
29
+ /**
30
+ * Resolve the monorepo root from this test file.
31
+ *
32
+ * `__tests__/db-inventory.test.ts` lives at
33
+ * `packages/contracts/src/__tests__/` — four levels above the repo root.
34
+ */
35
+ const THIS_FILE = fileURLToPath(import.meta.url);
36
+ const REPO_ROOT = resolve(dirname(THIS_FILE), '..', '..', '..', '..');
37
+ const VALID_TIERS = new Set(['project', 'global', 'derived']);
38
+ /**
39
+ * Roles AC1 of T10305 explicitly requires the inventory to enumerate.
40
+ * The set must be covered exactly; extras are acceptable when documented.
41
+ */
42
+ const REQUIRED_ROLES = [
43
+ 'tasks',
44
+ 'brain',
45
+ 'conduit',
46
+ 'manifest',
47
+ 'llmtxt',
48
+ 'nexus',
49
+ 'signaldock-project',
50
+ 'signaldock-global',
51
+ 'telemetry',
52
+ 'skills',
53
+ 'global-brain',
54
+ 'global-tasks',
55
+ ];
56
+ describe('CLEO database inventory (T10305 SSoT)', () => {
57
+ it('is non-empty', () => {
58
+ expect(DB_INVENTORY.length).toBeGreaterThan(0);
59
+ });
60
+ it('covers every role required by T10305 AC1', () => {
61
+ const seen = new Set(DB_INVENTORY.map((e) => e.role));
62
+ for (const role of REQUIRED_ROLES) {
63
+ expect(seen.has(role), `inventory missing role '${role}'`).toBe(true);
64
+ }
65
+ });
66
+ it('has unique role IDs across all entries', () => {
67
+ const roles = DB_INVENTORY.map((e) => e.role);
68
+ const unique = new Set(roles);
69
+ expect(unique.size).toBe(roles.length);
70
+ });
71
+ it('uses only valid tier literals', () => {
72
+ for (const entry of DB_INVENTORY) {
73
+ expect(VALID_TIERS.has(entry.tier), `role '${entry.role}' has invalid tier '${entry.tier}'`).toBe(true);
74
+ }
75
+ });
76
+ it('uses valid path tokens in filePathTemplate', () => {
77
+ // Project tier MUST use <projectRoot>/ prefix; global tier MUST use
78
+ // $XDG_DATA_HOME/cleo/ prefix. Derived tier may use either.
79
+ for (const entry of DB_INVENTORY) {
80
+ const t = entry.filePathTemplate;
81
+ if (entry.tier === 'project') {
82
+ expect(t.startsWith('<projectRoot>/'), `project-tier entry '${entry.role}' uses non-canonical path '${t}'`).toBe(true);
83
+ }
84
+ else if (entry.tier === 'global') {
85
+ expect(t.startsWith('$XDG_DATA_HOME/cleo/'), `global-tier entry '${entry.role}' uses non-canonical path '${t}'`).toBe(true);
86
+ }
87
+ }
88
+ });
89
+ it('resolves every non-null drizzleSchemaPath to a real file', () => {
90
+ for (const entry of DB_INVENTORY) {
91
+ if (entry.drizzleSchemaPath === null)
92
+ continue;
93
+ const absolutePath = resolve(REPO_ROOT, entry.drizzleSchemaPath);
94
+ expect(existsSync(absolutePath), `role '${entry.role}' schema path missing: ${entry.drizzleSchemaPath}`).toBe(true);
95
+ expect(statSync(absolutePath).isFile(), `role '${entry.role}' schema path is not a file: ${entry.drizzleSchemaPath}`).toBe(true);
96
+ }
97
+ });
98
+ it('resolves every non-null migrationsDir to a real directory', () => {
99
+ for (const entry of DB_INVENTORY) {
100
+ if (entry.migrationsDir === null)
101
+ continue;
102
+ const absolutePath = resolve(REPO_ROOT, entry.migrationsDir);
103
+ expect(existsSync(absolutePath), `role '${entry.role}' migrations dir missing: ${entry.migrationsDir}`).toBe(true);
104
+ expect(statSync(absolutePath).isDirectory(), `role '${entry.role}' migrations path is not a directory: ${entry.migrationsDir}`).toBe(true);
105
+ }
106
+ });
107
+ it('omits migrationsDir only for derived or reserved roles', () => {
108
+ // A null migrationsDir is only valid for:
109
+ // - derived tier (schema owned by upstream lib — e.g. manifest)
110
+ // - reserved roles whose opener throws not-yet-implemented (e.g. llmtxt)
111
+ for (const entry of DB_INVENTORY) {
112
+ if (entry.migrationsDir !== null)
113
+ continue;
114
+ const isDerived = entry.tier === 'derived';
115
+ const isReserved = /reserved|not yet implemented/i.test(entry.openedVia);
116
+ expect(isDerived || isReserved, `role '${entry.role}' has null migrationsDir but is neither derived nor reserved`).toBe(true);
117
+ }
118
+ });
119
+ it('exposes a readonly array type at compile time', () => {
120
+ // Compile-time check: assigning to an index must be rejected.
121
+ // Runtime check: Object.isFrozen is not guaranteed by `readonly` but the
122
+ // type-system reject is the primary defence. We only assert the runtime
123
+ // shape is array-like here.
124
+ expect(Array.isArray(DB_INVENTORY)).toBe(true);
125
+ });
126
+ it('keeps each entry self-consistent (sample-fields populated)', () => {
127
+ // Sanity: every entry must populate the structural fields. The contract
128
+ // forbids `null` placeholders on non-optional fields.
129
+ const sampleRequiredKeys = [
130
+ 'role',
131
+ 'tier',
132
+ 'filePathTemplate',
133
+ 'ownerPackage',
134
+ 'openedVia',
135
+ 'concurrency',
136
+ 'privacy',
137
+ 'backupPath',
138
+ 'documentedIn',
139
+ ];
140
+ for (const entry of DB_INVENTORY) {
141
+ for (const key of sampleRequiredKeys) {
142
+ const value = entry[key];
143
+ expect(typeof value === 'string' && value.length > 0, `role '${entry.role}' field '${String(key)}' is empty or non-string`).toBe(true);
144
+ }
145
+ }
146
+ });
147
+ });
148
+ //# sourceMappingURL=db-inventory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-inventory.test.js","sourceRoot":"","sources":["../../src/__tests__/db-inventory.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAsC,MAAM,oBAAoB,CAAC;AAEtF;;;;;GAKG;AACH,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEtE,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAE3F;;;GAGG;AACH,MAAM,cAAc,GAAsB;IACxC,OAAO;IACP,OAAO;IACP,SAAS;IACT,UAAU;IACV,QAAQ;IACR,OAAO;IACP,oBAAoB;IACpB,mBAAmB;IACnB,WAAW;IACX,QAAQ;IACR,cAAc;IACd,cAAc;CACf,CAAC;AAEF,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;QACtB,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9D,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,2BAA2B,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,CACJ,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAC3B,SAAS,KAAK,CAAC,IAAI,uBAAuB,KAAK,CAAC,IAAI,GAAG,CACxD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,oEAAoE;QACpE,4DAA4D;QAC5D,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC;YACjC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,CACJ,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAC9B,uBAAuB,KAAK,CAAC,IAAI,8BAA8B,CAAC,GAAG,CACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACnC,MAAM,CACJ,CAAC,CAAC,UAAU,CAAC,sBAAsB,CAAC,EACpC,sBAAsB,KAAK,CAAC,IAAI,8BAA8B,CAAC,GAAG,CACnE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,iBAAiB,KAAK,IAAI;gBAAE,SAAS;YAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACjE,MAAM,CACJ,UAAU,CAAC,YAAY,CAAC,EACxB,SAAS,KAAK,CAAC,IAAI,0BAA0B,KAAK,CAAC,iBAAiB,EAAE,CACvE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,MAAM,CACJ,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAC/B,SAAS,KAAK,CAAC,IAAI,gCAAgC,KAAK,CAAC,iBAAiB,EAAE,CAC7E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI;gBAAE,SAAS;YAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;YAC7D,MAAM,CACJ,UAAU,CAAC,YAAY,CAAC,EACxB,SAAS,KAAK,CAAC,IAAI,6BAA6B,KAAK,CAAC,aAAa,EAAE,CACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,MAAM,CACJ,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,EACpC,SAAS,KAAK,CAAC,IAAI,yCAAyC,KAAK,CAAC,aAAa,EAAE,CAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,0CAA0C;QAC1C,iEAAiE;QACjE,0EAA0E;QAC1E,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI;gBAAE,SAAS;YAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;YAC3C,MAAM,UAAU,GAAG,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACzE,MAAM,CACJ,SAAS,IAAI,UAAU,EACvB,SAAS,KAAK,CAAC,IAAI,8DAA8D,CAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,8DAA8D;QAC9D,yEAAyE;QACzE,wEAAwE;QACxE,4BAA4B;QAC5B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,wEAAwE;QACxE,sDAAsD;QACtD,MAAM,kBAAkB,GAA0C;YAChE,MAAM;YACN,MAAM;YACN,kBAAkB;YAClB,cAAc;YACd,WAAW;YACX,aAAa;YACb,SAAS;YACT,YAAY;YACZ,cAAc;SACf,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;gBACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;gBACzB,MAAM,CACJ,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAC7C,SAAS,KAAK,CAAC,IAAI,YAAY,MAAM,CAAC,GAAG,CAAC,0BAA0B,CACrE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"memory-wire-shapes.test.js","sourceRoot":"","sources":["../../src/__tests__/memory-wire-shapes.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-wire-shapes.test.js","sourceRoot":"","sources":["../../src/__tests__/memory-wire-shapes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
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
+ export {};
14
+ //# sourceMappingURL=task-type-narrowing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-type-narrowing.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/task-type-narrowing.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,51 @@
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
+ import { describe, expectTypeOf, it } from 'vitest';
14
+ describe('TaskType discriminator', () => {
15
+ it('narrows when type === "saga"', () => {
16
+ const t = {
17
+ id: 'T1',
18
+ title: 'demo',
19
+ description: 'demo task',
20
+ status: 'pending',
21
+ priority: 'medium',
22
+ type: 'saga',
23
+ createdAt: '2026-01-01T00:00:00.000Z',
24
+ };
25
+ if (t.type === 'saga') {
26
+ expectTypeOf(t.type).toEqualTypeOf();
27
+ }
28
+ });
29
+ it('narrows when type === "epic"', () => {
30
+ const t = {
31
+ id: 'T2',
32
+ title: 'demo',
33
+ description: 'demo task',
34
+ status: 'pending',
35
+ priority: 'medium',
36
+ type: 'epic',
37
+ createdAt: '2026-01-01T00:00:00.000Z',
38
+ };
39
+ if (t.type === 'epic') {
40
+ expectTypeOf(t.type).toEqualTypeOf();
41
+ }
42
+ });
43
+ it('accepts all 4 tier values', () => {
44
+ const types = ['saga', 'epic', 'task', 'subtask'];
45
+ expectTypeOf(types).toEqualTypeOf();
46
+ });
47
+ it('TaskType is the 4-element union per ADR-083 §2.5', () => {
48
+ expectTypeOf().toEqualTypeOf();
49
+ });
50
+ });
51
+ //# sourceMappingURL=task-type-narrowing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-type-narrowing.test.js","sourceRoot":"","sources":["../../src/__tests__/task-type-narrowing.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAGpD,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,GAAS;YACd,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,WAAW;YACxB,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,0BAA0B;SACtC,CAAC;QACF,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAU,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,GAAS;YACd,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,WAAW;YACxB,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,0BAA0B;SACtC,CAAC;QACF,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAU,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,KAAK,GAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9D,YAAY,CAAC,KAAK,CAAC,CAAC,aAAa,EAAc,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,YAAY,EAAY,CAAC,aAAa,EAAwC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,136 @@
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
+ * Verdict roll-up for a single role.
30
+ *
31
+ * @public
32
+ */
33
+ export type BackupVerifyVerdict = 'healthy' | 'stale' | 'corrupt' | 'missing';
34
+ /**
35
+ * Per-snapshot freshness + integrity record.
36
+ *
37
+ * @remarks
38
+ * Returned for both the canonical (`.cleo/backups/sqlite/`) and legacy
39
+ * (`.cleo/backups/snapshot/`) snapshot directories. When no snapshot exists
40
+ * in the corresponding directory the field on the parent
41
+ * {@link BackupVerifyDbReport} is `null` rather than this shape with empty
42
+ * fields — that lets consumers distinguish "no snapshot ever taken" from
43
+ * "snapshot exists but is unhealthy".
44
+ *
45
+ * @public
46
+ */
47
+ export interface BackupVerifySnapshot {
48
+ /** Absolute path to the snapshot file. */
49
+ readonly path: string;
50
+ /** Snapshot mtime in epoch ms (the freshness signal). */
51
+ readonly mtime: number;
52
+ /**
53
+ * `true` when the snapshot file opened cleanly AND `PRAGMA integrity_check`
54
+ * returned the single row `ok`. `false` otherwise — see {@link error} for
55
+ * details.
56
+ */
57
+ readonly integrityOK: boolean;
58
+ /**
59
+ * Snapshot file size in bytes when `stat()` succeeded; `null` when the
60
+ * file vanished between discovery and stat.
61
+ */
62
+ readonly sizeBytes: number | null;
63
+ /**
64
+ * Human-readable error message when the snapshot could not be opened or
65
+ * its integrity check threw. `null` on success.
66
+ */
67
+ readonly error: string | null;
68
+ }
69
+ /**
70
+ * Per-DB report — one entry per role in `DB_INVENTORY`.
71
+ *
72
+ * @public
73
+ */
74
+ export interface BackupVerifyDbReport {
75
+ /** Canonical role identifier (matches `DB_INVENTORY[i].role`). */
76
+ readonly role: string;
77
+ /** Lifecycle tier copied through from the inventory for downstream UI. */
78
+ readonly tier: 'project' | 'global' | 'derived';
79
+ /**
80
+ * Freshest snapshot found under `.cleo/backups/sqlite/` (canonical per
81
+ * T10315) — or `null` when the canonical directory has no snapshot for
82
+ * this role.
83
+ */
84
+ readonly freshSnapshot: BackupVerifySnapshot | null;
85
+ /**
86
+ * Freshest snapshot found under `.cleo/backups/snapshot/` (legacy, kept
87
+ * read-only during the deprecation window) — or `null` when the legacy
88
+ * directory has no snapshot for this role.
89
+ */
90
+ readonly legacySnapshot: BackupVerifySnapshot | null;
91
+ /**
92
+ * Hours between `Date.now()` and the freshest available snapshot's mtime
93
+ * (whichever of {@link freshSnapshot} / {@link legacySnapshot} is newer).
94
+ * `null` when {@link verdict} is `missing` — no snapshot to measure from.
95
+ */
96
+ readonly dataLossEstimateHours: number | null;
97
+ /**
98
+ * Roll-up verdict for this role — see {@link BackupVerifyVerdict}.
99
+ */
100
+ readonly verdict: BackupVerifyVerdict;
101
+ }
102
+ /**
103
+ * Aggregated counters across every role in the survey. Used by the CLI verb
104
+ * to drive its non-zero exit decision (`stale > 0 || corrupt > 0`).
105
+ *
106
+ * @public
107
+ */
108
+ export interface BackupVerifySummary {
109
+ /** Number of roles whose verdict is `healthy`. */
110
+ readonly healthy: number;
111
+ /** Number of roles whose verdict is `stale`. */
112
+ readonly stale: number;
113
+ /** Number of roles whose verdict is `corrupt`. */
114
+ readonly corrupt: number;
115
+ /** Number of roles whose verdict is `missing`. */
116
+ readonly missing: number;
117
+ }
118
+ /**
119
+ * Envelope payload returned by `cleo backup verify` (and the underlying
120
+ * {@link runBackupVerify} core helper).
121
+ *
122
+ * @remarks
123
+ * The shape is keyed by role rather than indexed by array position so
124
+ * consumers can `.dbs['brain']` without scanning. The summary is provided
125
+ * pre-computed because the CLI exit code is driven from it — recomputing it
126
+ * downstream would invite drift.
127
+ *
128
+ * @public
129
+ */
130
+ export interface BackupVerifyResult {
131
+ /** Per-role reports keyed by `DB_INVENTORY[i].role`. */
132
+ readonly dbs: Readonly<Record<string, BackupVerifyDbReport>>;
133
+ /** Aggregated counters used to drive the CLI exit code. */
134
+ readonly summary: BackupVerifySummary;
135
+ }
136
+ //# sourceMappingURL=backup-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-verify.d.ts","sourceRoot":"","sources":["../src/backup-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAC7D,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACvC"}
@@ -0,0 +1,29 @@
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
+ export {};
29
+ //# sourceMappingURL=backup-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-verify.js","sourceRoot":"","sources":["../src/backup-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}
package/dist/brain.d.ts CHANGED
@@ -85,4 +85,107 @@ export interface SupersededEntry {
85
85
  /** Topic or category grouping these entries together. */
86
86
  grouping: string;
87
87
  }
88
+ /**
89
+ * Result of a brain.db auto-recovery attempt.
90
+ *
91
+ * Returned by `recoverMalformedBrainDb()` (T10303) when the chokepoint
92
+ * detects malformation via `ERR_SQLITE_ERROR errcode=11` or a failing
93
+ * `PRAGMA integrity_check`/`PRAGMA quick_check`. The corrupt DB has been
94
+ * moved to a quarantine directory and the freshest validated snapshot has
95
+ * been copied to `.cleo/brain.db`.
96
+ *
97
+ * Consumers (the brain.db open chokepoint, telemetry, the upcoming T10302
98
+ * regression test) read this structure to decide whether to re-attempt the
99
+ * open and to emit user-facing diagnostics with the data-loss window.
100
+ */
101
+ export interface BrainRecoveryResult {
102
+ /** Absolute path to the snapshot that was restored, or null if recovery failed. */
103
+ restoredFrom: string | null;
104
+ /**
105
+ * Approximate hours between the snapshot's timestamp and the recovery
106
+ * event. `null` when the snapshot timestamp could not be parsed.
107
+ * Used in the recovery warning to make the data-loss window legible.
108
+ */
109
+ dataLossWindowHours: number | null;
110
+ /**
111
+ * Number of `brain_observations` rows in the restored DB. Best-effort —
112
+ * `null` when the count query failed (table missing, FK violation, …).
113
+ */
114
+ observationsRecovered: number | null;
115
+ /** `true` if the restored DB passes `PRAGMA quick_check`. */
116
+ integrityOK: boolean;
117
+ /**
118
+ * Absolute path to the quarantine directory where the corrupt DB plus
119
+ * its `-wal`/`-shm` sidecars were moved before restore.
120
+ */
121
+ quarantineDir: string | null;
122
+ }
123
+ /**
124
+ * Per-table row counts probed from the restored brain.db.
125
+ *
126
+ * Best-effort — any individual count may be `null` when the table is
127
+ * missing (very old snapshot predating that schema rev) or when the count
128
+ * query throws. The values feed both the `cleo backup recover brain`
129
+ * envelope and the recovery runbook's user-facing diagnostics.
130
+ *
131
+ * @task T10304
132
+ * @epic T10286
133
+ * @saga T10281
134
+ */
135
+ export interface BrainRecoveredRowCounts {
136
+ /** `brain_observations` row count, or `null` when the count failed. */
137
+ observations: number | null;
138
+ /** `brain_decisions` row count, or `null` when the count failed. */
139
+ decisions: number | null;
140
+ /** `brain_learnings` row count, or `null` when the count failed. */
141
+ learnings: number | null;
142
+ }
143
+ /**
144
+ * Envelope payload returned by `cleo backup recover brain`.
145
+ *
146
+ * The CLI verb (T10304) wraps {@link BrainRecoveryResult} from
147
+ * {@link recoverMalformedBrainDb} (T10303) and enriches it with per-table
148
+ * row counts plus the operator-friendly snapshot/quarantine paths required
149
+ * by the AC envelope shape.
150
+ *
151
+ * Surfaces:
152
+ * - `--dry-run` plan envelopes (with `restoredFrom` being the snapshot that
153
+ * *would* be restored and `quarantinedTo` being the directory the corrupt
154
+ * DB *would* be moved to).
155
+ * - Real-recovery envelopes after the pipeline has executed.
156
+ *
157
+ * @task T10304
158
+ * @epic T10286
159
+ * @saga T10281
160
+ */
161
+ export interface BackupRecoverBrainResult {
162
+ /**
163
+ * Absolute path to the snapshot that was (or would be) restored. Empty
164
+ * string when no snapshot was available — the envelope's caller surfaces
165
+ * that as a failure mode.
166
+ */
167
+ restoredFrom: string;
168
+ /** Per-table row counts probed from the restored DB. */
169
+ rowsRecovered: BrainRecoveredRowCounts;
170
+ /**
171
+ * Approximate hours between the snapshot's timestamp and the recovery
172
+ * event. `null` when the snapshot timestamp could not be parsed (e.g.
173
+ * legacy `brain.db.PRE-DUP-FIX-*` fallback artifact).
174
+ */
175
+ dataLossWindowHours: number | null;
176
+ /** `true` when the restored DB passes `PRAGMA quick_check`. */
177
+ integrityOK: boolean;
178
+ /**
179
+ * Absolute path to the quarantine directory where the corrupt DB plus
180
+ * its `-wal`/`-shm` sidecars were moved. Empty string in `--dry-run`
181
+ * mode where no files were touched.
182
+ */
183
+ quarantinedTo: string;
184
+ /**
185
+ * `true` when invoked with `--dry-run` — indicates the envelope is a
186
+ * plan, NOT a record of mutations performed. Consumers MUST check this
187
+ * before treating `restoredFrom` as a post-condition.
188
+ */
189
+ dryRun: boolean;
190
+ }
88
191
  //# sourceMappingURL=brain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;AAExE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,cAAc,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvF,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,MAAM,EAAE,aAAa,CAAC;IACtB,8CAA8C;IAC9C,MAAM,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,iDAAiD;IACjD,WAAW,EAAE,iBAAiB,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;CAClB"}
1
+ {"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;AAExE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,cAAc,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvF,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,MAAM,EAAE,aAAa,CAAC;IACtB,8CAA8C;IAC9C,MAAM,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,iDAAiD;IACjD,WAAW,EAAE,iBAAiB,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;OAGG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAMD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,uBAAuB;IACtC,uEAAuE;IACvE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB"}