@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.
- package/dist/__tests__/db-inventory.test.d.ts +25 -0
- package/dist/__tests__/db-inventory.test.d.ts.map +1 -0
- package/dist/__tests__/db-inventory.test.js +148 -0
- package/dist/__tests__/db-inventory.test.js.map +1 -0
- package/dist/__tests__/memory-wire-shapes.test.js.map +1 -1
- package/dist/__tests__/task-type-narrowing.test.d.ts +14 -0
- package/dist/__tests__/task-type-narrowing.test.d.ts.map +1 -0
- package/dist/__tests__/task-type-narrowing.test.js +51 -0
- package/dist/__tests__/task-type-narrowing.test.js.map +1 -0
- package/dist/backup-verify.d.ts +136 -0
- package/dist/backup-verify.d.ts.map +1 -0
- package/dist/backup-verify.js +29 -0
- package/dist/backup-verify.js.map +1 -0
- package/dist/brain.d.ts +103 -0
- package/dist/brain.d.ts.map +1 -1
- package/dist/db-inventory.d.ts +200 -0
- package/dist/db-inventory.d.ts.map +1 -0
- package/dist/db-inventory.js +76 -0
- package/dist/db-inventory.js.map +1 -0
- package/dist/db-inventory.json +165 -0
- package/dist/db-recovery.d.ts +141 -0
- package/dist/db-recovery.d.ts.map +1 -0
- package/dist/db-recovery.js +29 -0
- package/dist/db-recovery.js.map +1 -0
- package/dist/doctor.d.ts +605 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/observe.d.ts +8 -0
- package/dist/memory/observe.d.ts.map +1 -1
- package/dist/operations/tasks.d.ts +4 -4
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/task.d.ts +13 -2
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js.map +1 -1
- package/package.json +5 -4
- package/src/__tests__/db-inventory.test.ts +184 -0
- package/src/__tests__/memory-wire-shapes.test.ts +1 -0
- package/src/__tests__/task-type-narrowing.test.ts +56 -0
- package/src/backup-verify.ts +140 -0
- package/src/brain.ts +114 -0
- package/src/db-inventory.json +165 -0
- package/src/db-inventory.ts +234 -0
- package/src/db-recovery.ts +144 -0
- package/src/doctor.ts +634 -0
- package/src/index.ts +46 -0
- package/src/memory/observe.ts +8 -0
- package/src/operations/tasks.ts +4 -4
- 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":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-wire-shapes.test.js","sourceRoot":"","sources":["../../src/__tests__/memory-wire-shapes.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAa9C,OAAO,EACL,8BAA8B,GAI/B,MAAM,sBAAsB,CAAC;
|
|
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
|
package/dist/brain.d.ts.map
CHANGED
|
@@ -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"}
|