@cleocode/cleo-os 2026.4.108 → 2026.4.109

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/README.md CHANGED
@@ -305,6 +305,72 @@ cleo version
305
305
 
306
306
  ---
307
307
 
308
+ ## Startup Migration Verify
309
+
310
+ CleoOS runs a migration verify pre-check every time `cleoos` starts — before
311
+ Pi is imported and before any workers are dispatched. This prevents agents from
312
+ running against a drifted database and producing confusing mid-session errors.
313
+
314
+ ### How It Works
315
+
316
+ `cleoos` invokes `cleo upgrade --diagnose --json` via subprocess (CLI
317
+ boundary — no direct imports from `@cleocode/core`). The result is classified
318
+ into three severity levels:
319
+
320
+ | Severity | Condition | Action |
321
+ |----------|-----------|--------|
322
+ | **ok** | All 5 DB findings are `status:"ok"` | Silent pass-through |
323
+ | **warn** | One or more findings are `status:"warning"` | Logged to stdout; startup continues |
324
+ | **fatal** | One or more findings are `status:"error"`, OR the `cleo` binary could not be invoked | Structured error printed to stderr; startup halts (exit 2) |
325
+
326
+ `warn` corresponds to states the reconciler already auto-fixed (e.g. a missing
327
+ column that was backfilled, or `signaldock.db` not yet created). `fatal`
328
+ corresponds to states the reconciler could not resolve — a missing migration
329
+ folder, a schema missing a required table, or an unreachable `cleo` binary.
330
+
331
+ ### Operator Remediation
332
+
333
+ When CleoOS exits with code 2 and prints a fatal drift error:
334
+
335
+ ```
336
+ CleoOS startup blocked — DB migration drift detected
337
+ ═══════════════════════════════════════════════════════════
338
+
339
+ The following issues prevent safe worker spawn:
340
+
341
+ [FATAL] tasks.db.journal
342
+ Migration folder missing: .cleo/migrations/tasks does not exist
343
+ Fix: Run: cleo init or cleo upgrade
344
+ ```
345
+
346
+ Try these remediation steps in order:
347
+
348
+ 1. **`cleo upgrade`** — applies pending migrations and repairs known drift.
349
+ 2. **`cleo upgrade --diagnose`** — detailed DB inspection showing every check.
350
+ 3. **`cleo init --force`** — resets `tasks.db` to a fresh state. **Warning:
351
+ this destroys all task data**. Run `cleo backup add` first if possible.
352
+ 4. **`cleo restore backup --file tasks.db`** — restore from the most recent
353
+ snapshot if the DB is corrupted.
354
+
355
+ ### Bypassing the Check (CI / Test Harnesses)
356
+
357
+ Set `CLEO_SKIP_MIGRATION_CHECK=1` to skip the check entirely. This is
358
+ intended for CI environments where the DB is freshly initialised by the test
359
+ harness and no persistent CLEO state exists:
360
+
361
+ ```bash
362
+ CLEO_SKIP_MIGRATION_CHECK=1 cleoos --version
363
+ ```
364
+
365
+ ### Dedicated Verb (Future)
366
+
367
+ The current implementation falls back to `cleo upgrade --diagnose --json`
368
+ because `cleo admin migrations verify` does not yet exist. When that verb
369
+ lands, the `verifyMigrations()` function will be updated to prefer it.
370
+ `MigrationVerifyResult.usedDedicatedVerb` will flip to `true`.
371
+
372
+ ---
373
+
308
374
  ## Wave Roadmap
309
375
 
310
376
  CleoOS ships in named waves. Each wave targets a vertical slice of the platform.
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { dirname, join } from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { getPlatformPaths } from '@cleocode/core/system/platform-paths.js';
17
17
  import { renderDoctorReport, runDoctor } from './commands/doctor.js';
18
+ import { renderFatalDriftError, renderWarnDrift, verifyMigrations, } from './health/verify-migrations.js';
18
19
  import { AgentRegistry } from './registry/agent-registry.js';
19
20
  import { ProviderMatrix } from './registry/provider-matrix.js';
20
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -196,6 +197,20 @@ async function main() {
196
197
  if (await handleDiagnosticsFlags(userArgs)) {
197
198
  return;
198
199
  }
200
+ // Migration verify pre-check — fail-fast if DBs are in a drift state that
201
+ // the runtime reconciler cannot safely auto-fix before workers are spawned.
202
+ // Skip the check when CLEO_SKIP_MIGRATION_CHECK=1 is set (useful in CI
203
+ // environments where the DB is freshly initialised by the test harness).
204
+ if (process.env['CLEO_SKIP_MIGRATION_CHECK'] !== '1') {
205
+ const migrateResult = await verifyMigrations();
206
+ if (!migrateResult.ok) {
207
+ process.stderr.write(renderFatalDriftError(migrateResult));
208
+ process.exit(2);
209
+ }
210
+ if (migrateResult.severity === 'warn' && migrateResult.drift.length > 0) {
211
+ process.stdout.write(renderWarnDrift(migrateResult));
212
+ }
213
+ }
199
214
  // Dynamically import Pi — it's a peerDependency, may not be installed
200
215
  let piMain;
201
216
  try {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tests for the cleo-os startup migration verify check (T1185).
3
+ *
4
+ * All tests stub the {@link CliRunner} interface — no real `cleo` binary is
5
+ * invoked. Coverage:
6
+ *
7
+ * 1. Happy path — all findings ok → `ok: true`, `severity: "ok"`, empty drift.
8
+ * 2. Drift fixture (warning) — `status:"warning"` finding → `warn`, non-blocking.
9
+ * 3. Drift fixture (error) — `status:"error"` finding → `fatal`, blocking.
10
+ * 4. Classification — Scenario 1 (name-backfill warning) is `warn`, not `fatal`.
11
+ * 5. Classification — ENOENT binary failure is `fatal`.
12
+ * 6. Mixed warn+ok — aggregate is `warn`, `ok: true`.
13
+ * 7. Mixed fatal+warn — aggregate is `fatal`, `ok: false`.
14
+ * 8. Malformed JSON output → `fatal` with parse check.
15
+ * 9. Missing findings array → `fatal` with parse check.
16
+ * 10. Subprocess spawn error (non-ENOENT) → `fatal`.
17
+ * 11. `usedDedicatedVerb` is always `false` (dedicated verb not yet implemented).
18
+ * 12. `renderFatalDriftError` output includes remediation instructions.
19
+ * 13. `renderWarnDrift` output lists the warning check name.
20
+ *
21
+ * @packageDocumentation
22
+ */
23
+ export {};
24
+ //# sourceMappingURL=verify-migrations.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-migrations.test.d.ts","sourceRoot":"","sources":["../../../src/health/__tests__/verify-migrations.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Tests for the cleo-os startup migration verify check (T1185).
3
+ *
4
+ * All tests stub the {@link CliRunner} interface — no real `cleo` binary is
5
+ * invoked. Coverage:
6
+ *
7
+ * 1. Happy path — all findings ok → `ok: true`, `severity: "ok"`, empty drift.
8
+ * 2. Drift fixture (warning) — `status:"warning"` finding → `warn`, non-blocking.
9
+ * 3. Drift fixture (error) — `status:"error"` finding → `fatal`, blocking.
10
+ * 4. Classification — Scenario 1 (name-backfill warning) is `warn`, not `fatal`.
11
+ * 5. Classification — ENOENT binary failure is `fatal`.
12
+ * 6. Mixed warn+ok — aggregate is `warn`, `ok: true`.
13
+ * 7. Mixed fatal+warn — aggregate is `fatal`, `ok: false`.
14
+ * 8. Malformed JSON output → `fatal` with parse check.
15
+ * 9. Missing findings array → `fatal` with parse check.
16
+ * 10. Subprocess spawn error (non-ENOENT) → `fatal`.
17
+ * 11. `usedDedicatedVerb` is always `false` (dedicated verb not yet implemented).
18
+ * 12. `renderFatalDriftError` output includes remediation instructions.
19
+ * 13. `renderWarnDrift` output lists the warning check name.
20
+ *
21
+ * @packageDocumentation
22
+ */
23
+ import { describe, expect, it } from 'vitest';
24
+ import { renderFatalDriftError, renderWarnDrift, verifyMigrations } from '../verify-migrations.js';
25
+ // ---------------------------------------------------------------------------
26
+ // Stub runner factory
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Build a {@link CliRunner} stub that resolves to a given JSON string.
30
+ *
31
+ * @param output - Raw JSON string to return from `runDiagnose()`.
32
+ */
33
+ function stubRunner(output) {
34
+ return {
35
+ async runDiagnose() {
36
+ return output;
37
+ },
38
+ };
39
+ }
40
+ /**
41
+ * Build a {@link CliRunner} stub that rejects with the given error.
42
+ *
43
+ * @param message - Error message to throw from `runDiagnose()`.
44
+ */
45
+ function failRunner(message) {
46
+ return {
47
+ async runDiagnose() {
48
+ throw new Error(message);
49
+ },
50
+ };
51
+ }
52
+ /**
53
+ * Build a well-formed `cleo upgrade --diagnose --json` envelope where all
54
+ * findings have the given status list.
55
+ */
56
+ function makeEnvelope(findings) {
57
+ const ok = findings.filter((f) => f.status === 'ok').length;
58
+ const warnings = findings.filter((f) => f.status === 'warning').length;
59
+ const errors = findings.filter((f) => f.status === 'error').length;
60
+ return JSON.stringify({
61
+ success: true,
62
+ data: {
63
+ success: true,
64
+ findings,
65
+ summary: { ok, warnings, errors },
66
+ },
67
+ });
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Fixtures
71
+ // ---------------------------------------------------------------------------
72
+ const ALL_OK_ENVELOPE = makeEnvelope([
73
+ { check: 'tasks.db.columns', status: 'ok', details: 'All required columns present' },
74
+ { check: 'tasks.db.journal', status: 'ok', details: '17 migrations in journal, all valid' },
75
+ { check: 'tasks.db.integrity', status: 'ok', details: 'SQLite integrity check passed' },
76
+ { check: 'brain.db.tables', status: 'ok', details: 'All 4 expected tables present' },
77
+ { check: 'brain.db.journal', status: 'ok', details: '15 migrations in journal, all valid' },
78
+ { check: 'brain.db.schema_columns', status: 'ok', details: 'All brain schema columns present' },
79
+ { check: 'signaldock.db', status: 'ok', details: 'signaldock.db exists' },
80
+ { check: 'conduit.db', status: 'ok', details: 'conduit.db exists' },
81
+ ]);
82
+ const WARNING_ENVELOPE = makeEnvelope([
83
+ { check: 'tasks.db.columns', status: 'ok', details: 'All required columns present' },
84
+ {
85
+ check: 'signaldock.db',
86
+ status: 'warning',
87
+ details: 'signaldock.db not found',
88
+ fix: 'Run: cleo upgrade',
89
+ },
90
+ { check: 'conduit.db', status: 'ok', details: 'conduit.db exists' },
91
+ ]);
92
+ const ERROR_ENVELOPE = makeEnvelope([
93
+ { check: 'tasks.db.columns', status: 'ok', details: 'All required columns present' },
94
+ {
95
+ check: 'tasks.db.journal',
96
+ status: 'error',
97
+ details: 'Migration folder missing: .cleo/migrations/tasks does not exist',
98
+ fix: 'Run: cleo init or cleo upgrade',
99
+ },
100
+ ]);
101
+ const MIXED_WARN_OK_ENVELOPE = makeEnvelope([
102
+ { check: 'tasks.db.journal', status: 'ok', details: 'Journal valid' },
103
+ {
104
+ check: 'brain.db.schema_columns',
105
+ status: 'warning',
106
+ details: 'provenance column missing (auto-backfilled)',
107
+ fix: 'Run: cleo upgrade',
108
+ },
109
+ ]);
110
+ const MIXED_FATAL_WARN_ENVELOPE = makeEnvelope([
111
+ {
112
+ check: 'tasks.db.journal',
113
+ status: 'error',
114
+ details: 'Required tasks table missing from schema',
115
+ },
116
+ {
117
+ check: 'signaldock.db',
118
+ status: 'warning',
119
+ details: 'signaldock.db not found',
120
+ fix: 'Run: cleo upgrade',
121
+ },
122
+ ]);
123
+ // ---------------------------------------------------------------------------
124
+ // Test 1: Happy path
125
+ // ---------------------------------------------------------------------------
126
+ describe('verifyMigrations', () => {
127
+ it('1: happy path — all findings ok → ok: true, severity ok, empty drift', async () => {
128
+ const result = await verifyMigrations(stubRunner(ALL_OK_ENVELOPE));
129
+ expect(result.ok).toBe(true);
130
+ expect(result.severity).toBe('ok');
131
+ expect(result.drift).toHaveLength(0);
132
+ expect(result.usedDedicatedVerb).toBe(false);
133
+ });
134
+ // -------------------------------------------------------------------------
135
+ // Test 2: Warning drift fixture
136
+ // -------------------------------------------------------------------------
137
+ it('2: drift fixture (warning) → warn severity, ok: true (non-blocking)', async () => {
138
+ const result = await verifyMigrations(stubRunner(WARNING_ENVELOPE));
139
+ expect(result.ok).toBe(true);
140
+ expect(result.severity).toBe('warn');
141
+ expect(result.drift).toHaveLength(1);
142
+ expect(result.drift[0]?.severity).toBe('warn');
143
+ expect(result.drift[0]?.check).toBe('signaldock.db');
144
+ });
145
+ // -------------------------------------------------------------------------
146
+ // Test 3: Error drift fixture
147
+ // -------------------------------------------------------------------------
148
+ it('3: drift fixture (error) → fatal severity, ok: false (blocking)', async () => {
149
+ const result = await verifyMigrations(stubRunner(ERROR_ENVELOPE));
150
+ expect(result.ok).toBe(false);
151
+ expect(result.severity).toBe('fatal');
152
+ expect(result.drift).toHaveLength(1);
153
+ expect(result.drift[0]?.severity).toBe('fatal');
154
+ expect(result.drift[0]?.check).toBe('tasks.db.journal');
155
+ });
156
+ // -------------------------------------------------------------------------
157
+ // Test 4: Scenario 1 classification (name-backfill warning → warn)
158
+ // -------------------------------------------------------------------------
159
+ it('4: Scenario 1 — name-backfill warning is classified as warn, not fatal', async () => {
160
+ const scenario1Envelope = makeEnvelope([
161
+ {
162
+ check: 'brain.db.schema_columns',
163
+ status: 'warning',
164
+ details: 'provenance column missing (name-backfill auto-applied)',
165
+ fix: 'Run: cleo upgrade',
166
+ },
167
+ ]);
168
+ const result = await verifyMigrations(stubRunner(scenario1Envelope));
169
+ expect(result.ok).toBe(true);
170
+ expect(result.severity).toBe('warn');
171
+ expect(result.drift[0]?.severity).toBe('warn');
172
+ });
173
+ // -------------------------------------------------------------------------
174
+ // Test 5: ENOENT binary failure → fatal
175
+ // -------------------------------------------------------------------------
176
+ it('5: ENOENT — cleo binary not on PATH → fatal, ok: false', async () => {
177
+ const result = await verifyMigrations(failRunner('spawn cleo ENOENT'));
178
+ expect(result.ok).toBe(false);
179
+ expect(result.severity).toBe('fatal');
180
+ expect(result.drift).toHaveLength(1);
181
+ expect(result.drift[0]?.check).toBe('cleo-binary');
182
+ expect(result.drift[0]?.severity).toBe('fatal');
183
+ expect(result.drift[0]?.details).toMatch(/not found/i);
184
+ });
185
+ // -------------------------------------------------------------------------
186
+ // Test 6: Mixed warn + ok → aggregate warn, ok: true
187
+ // -------------------------------------------------------------------------
188
+ it('6: mixed warn+ok — aggregate severity is warn, ok: true', async () => {
189
+ const result = await verifyMigrations(stubRunner(MIXED_WARN_OK_ENVELOPE));
190
+ expect(result.ok).toBe(true);
191
+ expect(result.severity).toBe('warn');
192
+ });
193
+ // -------------------------------------------------------------------------
194
+ // Test 7: Mixed fatal + warn → aggregate fatal, ok: false
195
+ // -------------------------------------------------------------------------
196
+ it('7: mixed fatal+warn — aggregate severity is fatal, ok: false', async () => {
197
+ const result = await verifyMigrations(stubRunner(MIXED_FATAL_WARN_ENVELOPE));
198
+ expect(result.ok).toBe(false);
199
+ expect(result.severity).toBe('fatal');
200
+ // Both findings should be in drift (warn is included because severity !== ok)
201
+ expect(result.drift.some((d) => d.severity === 'fatal')).toBe(true);
202
+ expect(result.drift.some((d) => d.severity === 'warn')).toBe(true);
203
+ });
204
+ // -------------------------------------------------------------------------
205
+ // Test 8: Malformed JSON output → fatal
206
+ // -------------------------------------------------------------------------
207
+ it('8: malformed JSON output → fatal with parse check', async () => {
208
+ const result = await verifyMigrations(stubRunner('not-valid-json'));
209
+ expect(result.ok).toBe(false);
210
+ expect(result.severity).toBe('fatal');
211
+ expect(result.drift[0]?.check).toBe('parse');
212
+ expect(result.drift[0]?.severity).toBe('fatal');
213
+ });
214
+ // -------------------------------------------------------------------------
215
+ // Test 9: Missing findings array → fatal
216
+ // -------------------------------------------------------------------------
217
+ it('9: missing findings array in response → fatal with parse check', async () => {
218
+ const badEnvelope = JSON.stringify({ success: true, data: { success: true } });
219
+ const result = await verifyMigrations(stubRunner(badEnvelope));
220
+ expect(result.ok).toBe(false);
221
+ expect(result.severity).toBe('fatal');
222
+ expect(result.drift[0]?.check).toBe('parse');
223
+ });
224
+ // -------------------------------------------------------------------------
225
+ // Test 10: Subprocess non-ENOENT error → fatal
226
+ // -------------------------------------------------------------------------
227
+ it('10: subprocess spawn error (non-ENOENT) → fatal with details', async () => {
228
+ const result = await verifyMigrations(failRunner('Process exited with code 1: permission denied'));
229
+ expect(result.ok).toBe(false);
230
+ expect(result.severity).toBe('fatal');
231
+ expect(result.drift[0]?.check).toBe('cleo-binary');
232
+ // Non-ENOENT: details should describe the actual error, not "not found"
233
+ expect(result.drift[0]?.details).toMatch(/failed|permission/i);
234
+ });
235
+ // -------------------------------------------------------------------------
236
+ // Test 11: usedDedicatedVerb is always false
237
+ // -------------------------------------------------------------------------
238
+ it('11: usedDedicatedVerb is always false (dedicated verb not yet implemented)', async () => {
239
+ const okResult = await verifyMigrations(stubRunner(ALL_OK_ENVELOPE));
240
+ expect(okResult.usedDedicatedVerb).toBe(false);
241
+ const failResult = await verifyMigrations(failRunner('ENOENT'));
242
+ expect(failResult.usedDedicatedVerb).toBe(false);
243
+ });
244
+ });
245
+ // ---------------------------------------------------------------------------
246
+ // Rendering helpers
247
+ // ---------------------------------------------------------------------------
248
+ describe('renderFatalDriftError', () => {
249
+ it('12: includes remediation instructions and fatal check name', () => {
250
+ const result = {
251
+ ok: false,
252
+ severity: 'fatal',
253
+ drift: [
254
+ {
255
+ check: 'tasks.db.journal',
256
+ severity: 'fatal',
257
+ details: 'Migration folder missing',
258
+ fix: 'Run: cleo init',
259
+ },
260
+ ],
261
+ usedDedicatedVerb: false,
262
+ };
263
+ const output = renderFatalDriftError(result);
264
+ expect(output).toContain('CleoOS startup blocked');
265
+ expect(output).toContain('tasks.db.journal');
266
+ expect(output).toContain('Migration folder missing');
267
+ expect(output).toContain('cleo upgrade');
268
+ expect(output).toContain('cleo init --force');
269
+ });
270
+ });
271
+ describe('renderWarnDrift', () => {
272
+ it('13: lists warning check name and includes upgrade hint', () => {
273
+ const result = {
274
+ ok: true,
275
+ severity: 'warn',
276
+ drift: [
277
+ {
278
+ check: 'signaldock.db',
279
+ severity: 'warn',
280
+ details: 'signaldock.db not found',
281
+ fix: 'Run: cleo upgrade',
282
+ },
283
+ ],
284
+ usedDedicatedVerb: false,
285
+ };
286
+ const output = renderWarnDrift(result);
287
+ expect(output).toContain('signaldock.db');
288
+ expect(output).toContain('signaldock.db not found');
289
+ expect(output).toContain('cleo upgrade');
290
+ });
291
+ });
292
+ //# sourceMappingURL=verify-migrations.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-migrations.test.js","sourceRoot":"","sources":["../../../src/health/__tests__/verify-migrations.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * CleoOS Startup Migration Verify — fail-fast DB drift check.
3
+ *
4
+ * Runs `cleo upgrade --diagnose --json` via subprocess (CLI boundary — no
5
+ * direct imports from `@cleocode/core` or `@cleocode/cleo`). Classifies each
6
+ * finding into one of three severity levels and returns an aggregate result
7
+ * that the harness startup path uses to decide whether to block worker spawn.
8
+ *
9
+ * ## Classification Rules
10
+ *
11
+ * - **ok**: All DB findings are `status:"ok"`. Silent pass-through.
12
+ * - **warn**: One or more findings are `status:"warning"`. These correspond to
13
+ * states the reconciler already auto-fixed (Scenarios 1-4 in the migration
14
+ * reconciler). Logged but not blocking.
15
+ * - **fatal**: One or more findings are `status:"error"`, OR the `cleo`
16
+ * binary could not be invoked, OR the JSON response was unparseable. These
17
+ * represent states the reconciler could not resolve — typically a missing
18
+ * migration folder (T1166 ENOENT class) or a schema missing a required
19
+ * table. Worker spawn is blocked.
20
+ *
21
+ * ## Subprocess Boundary
22
+ *
23
+ * This module calls `cleo upgrade --diagnose --json` — the same check
24
+ * exposed by `cleo upgrade --diagnose` — because it is the richest single
25
+ * command that validates all five DBs and migration journals without starting
26
+ * an interactive session. If `cleo admin migrations verify` is added in a
27
+ * future sprint, the runner interface makes it trivial to swap.
28
+ *
29
+ * @see ADR-036 — CleoOS Database Topology
30
+ * @see ADR-049 — CleoOS Sovereignty Invariants
31
+ * @task T1185
32
+ * @epic T1150
33
+ * @packageDocumentation
34
+ */
35
+ /**
36
+ * Severity of a single DB finding returned by the diagnose command.
37
+ *
38
+ * @public
39
+ */
40
+ export type DriftSeverity = 'ok' | 'warn' | 'fatal';
41
+ /**
42
+ * A single DB drift report entry derived from a `cleo upgrade --diagnose`
43
+ * finding.
44
+ *
45
+ * @public
46
+ */
47
+ export interface DriftReport {
48
+ /** The check name (e.g. `"tasks.db.journal"`, `"brain.db.tables"`). */
49
+ check: string;
50
+ /** Severity classification applied by the verifier. */
51
+ severity: DriftSeverity;
52
+ /** Human-readable detail from the diagnose output. */
53
+ details: string;
54
+ /** Remediation hint when severity is `"warn"` or `"fatal"`. */
55
+ fix?: string;
56
+ }
57
+ /**
58
+ * Aggregated result returned by {@link verifyMigrations}.
59
+ *
60
+ * @public
61
+ */
62
+ export interface MigrationVerifyResult {
63
+ /**
64
+ * `true` when the harness may proceed to spawn workers.
65
+ * `false` when at least one fatal drift was detected.
66
+ */
67
+ ok: boolean;
68
+ /** Aggregate severity across all findings. */
69
+ severity: DriftSeverity;
70
+ /** Per-finding drift reports. Empty on a clean run. */
71
+ drift: DriftReport[];
72
+ /**
73
+ * Whether `cleo admin migrations verify` was available.
74
+ *
75
+ * Currently always `false` — this verb does not exist yet. The verifier
76
+ * falls back to `cleo upgrade --diagnose --json`. Set to `true` in a future
77
+ * sprint once the dedicated verb lands.
78
+ */
79
+ usedDedicatedVerb: boolean;
80
+ }
81
+ /**
82
+ * Abstraction over the subprocess invocation so tests can inject a stub.
83
+ *
84
+ * @public
85
+ */
86
+ export interface CliRunner {
87
+ /**
88
+ * Run `cleo upgrade --diagnose --json` and return the raw stdout.
89
+ *
90
+ * @throws When the subprocess exits non-zero or cannot be spawned.
91
+ */
92
+ runDiagnose(): Promise<string>;
93
+ }
94
+ /**
95
+ * Default {@link CliRunner} that shells out to the real `cleo` binary.
96
+ *
97
+ * @public
98
+ */
99
+ export declare class DefaultCliRunner implements CliRunner {
100
+ /** @inheritdoc */
101
+ runDiagnose(): Promise<string>;
102
+ }
103
+ /**
104
+ * Run the migration verify pre-check and return a structured result.
105
+ *
106
+ * Invokes `cleo upgrade --diagnose --json` via subprocess (CLI boundary).
107
+ * Classifies every non-ok finding into `warn` or `fatal`. A single `fatal`
108
+ * finding causes `ok: false` — the harness startup path must block spawn and
109
+ * print remediation instructions before exiting.
110
+ *
111
+ * @param runner - Injectable {@link CliRunner}; defaults to
112
+ * {@link DefaultCliRunner} which shells out to the real `cleo` binary.
113
+ * @returns Structured {@link MigrationVerifyResult}.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const result = await verifyMigrations();
118
+ * if (!result.ok) {
119
+ * // print structured error + remediation, then exit
120
+ * }
121
+ * ```
122
+ *
123
+ * @public
124
+ */
125
+ export declare function verifyMigrations(runner?: CliRunner): Promise<MigrationVerifyResult>;
126
+ /**
127
+ * Format a {@link MigrationVerifyResult} as a human-readable fatal error
128
+ * block for printing to stderr.
129
+ *
130
+ * Only called when `result.ok === false` (fatal drift detected).
131
+ *
132
+ * @param result - The failed verify result.
133
+ * @returns Multi-line string ready for `process.stderr.write`.
134
+ *
135
+ * @public
136
+ */
137
+ export declare function renderFatalDriftError(result: MigrationVerifyResult): string;
138
+ /**
139
+ * Format a {@link MigrationVerifyResult} as a human-readable warning block
140
+ * for printing to stdout.
141
+ *
142
+ * Only called when `result.severity === "warn"` (reconciler already handled
143
+ * the drift — non-blocking).
144
+ *
145
+ * @param result - The warn-severity verify result.
146
+ * @returns Multi-line string ready for `process.stdout.write`.
147
+ *
148
+ * @public
149
+ */
150
+ export declare function renderWarnDrift(result: MigrationVerifyResult): string;
151
+ //# sourceMappingURL=verify-migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-migrations.d.ts","sourceRoot":"","sources":["../../src/health/verify-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAWH;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,QAAQ,EAAE,aAAa,CAAC;IACxB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,EAAE,EAAE,OAAO,CAAC;IACZ,8CAA8C;IAC9C,QAAQ,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;;;;;;OAMG;IACH,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAqCD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAChD,kBAAkB;IACZ,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;CAMrC;AA6GD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,GAAE,SAAkC,GACzC,OAAO,CAAC,qBAAqB,CAAC,CAsChC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CA+B3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAgBrE"}
@@ -0,0 +1,279 @@
1
+ /**
2
+ * CleoOS Startup Migration Verify — fail-fast DB drift check.
3
+ *
4
+ * Runs `cleo upgrade --diagnose --json` via subprocess (CLI boundary — no
5
+ * direct imports from `@cleocode/core` or `@cleocode/cleo`). Classifies each
6
+ * finding into one of three severity levels and returns an aggregate result
7
+ * that the harness startup path uses to decide whether to block worker spawn.
8
+ *
9
+ * ## Classification Rules
10
+ *
11
+ * - **ok**: All DB findings are `status:"ok"`. Silent pass-through.
12
+ * - **warn**: One or more findings are `status:"warning"`. These correspond to
13
+ * states the reconciler already auto-fixed (Scenarios 1-4 in the migration
14
+ * reconciler). Logged but not blocking.
15
+ * - **fatal**: One or more findings are `status:"error"`, OR the `cleo`
16
+ * binary could not be invoked, OR the JSON response was unparseable. These
17
+ * represent states the reconciler could not resolve — typically a missing
18
+ * migration folder (T1166 ENOENT class) or a schema missing a required
19
+ * table. Worker spawn is blocked.
20
+ *
21
+ * ## Subprocess Boundary
22
+ *
23
+ * This module calls `cleo upgrade --diagnose --json` — the same check
24
+ * exposed by `cleo upgrade --diagnose` — because it is the richest single
25
+ * command that validates all five DBs and migration journals without starting
26
+ * an interactive session. If `cleo admin migrations verify` is added in a
27
+ * future sprint, the runner interface makes it trivial to swap.
28
+ *
29
+ * @see ADR-036 — CleoOS Database Topology
30
+ * @see ADR-049 — CleoOS Sovereignty Invariants
31
+ * @task T1185
32
+ * @epic T1150
33
+ * @packageDocumentation
34
+ */
35
+ import { execFile } from 'node:child_process';
36
+ import { promisify } from 'node:util';
37
+ const execFileAsync = promisify(execFile);
38
+ /**
39
+ * Default {@link CliRunner} that shells out to the real `cleo` binary.
40
+ *
41
+ * @public
42
+ */
43
+ export class DefaultCliRunner {
44
+ /** @inheritdoc */
45
+ async runDiagnose() {
46
+ const { stdout } = await execFileAsync('cleo', ['upgrade', '--diagnose', '--json'], {
47
+ timeout: 15_000,
48
+ });
49
+ return stdout;
50
+ }
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Severity classification
54
+ // ---------------------------------------------------------------------------
55
+ /**
56
+ * Map a raw `status` string from the diagnose output to a {@link DriftSeverity}.
57
+ *
58
+ * Uses conservative thresholds per the T1185 classification contract:
59
+ * - `"ok"` → `ok`
60
+ * - `"warning"` → `warn` (reconciler already handled it; non-blocking)
61
+ * - `"error"` → `fatal` (reconciler could not resolve; block spawn)
62
+ *
63
+ * @param status - Raw status string from the diagnose finding.
64
+ * @returns Classified severity.
65
+ *
66
+ * @internal
67
+ */
68
+ function classifyStatus(status) {
69
+ switch (status) {
70
+ case 'ok':
71
+ return 'ok';
72
+ case 'warning':
73
+ return 'warn';
74
+ case 'error':
75
+ return 'fatal';
76
+ default:
77
+ // Treat unknown status values conservatively.
78
+ return 'fatal';
79
+ }
80
+ }
81
+ /**
82
+ * Derive the aggregate severity across all drift reports.
83
+ *
84
+ * `fatal` dominates `warn` dominates `ok`.
85
+ *
86
+ * @param reports - Array of per-finding reports.
87
+ * @returns Highest severity found, or `"ok"` when the array is empty.
88
+ *
89
+ * @internal
90
+ */
91
+ function aggregateSeverity(reports) {
92
+ let highest = 'ok';
93
+ for (const r of reports) {
94
+ if (r.severity === 'fatal')
95
+ return 'fatal';
96
+ if (r.severity === 'warn')
97
+ highest = 'warn';
98
+ }
99
+ return highest;
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // Parse helpers
103
+ // ---------------------------------------------------------------------------
104
+ /**
105
+ * Parse the raw stdout from `cleo upgrade --diagnose --json` into an array
106
+ * of {@link DriftReport} entries.
107
+ *
108
+ * Returns a single `fatal` entry when parsing fails so callers always have a
109
+ * consistent result shape to act on.
110
+ *
111
+ * @param raw - Raw stdout string from the subprocess.
112
+ * @returns Parsed drift reports.
113
+ *
114
+ * @internal
115
+ */
116
+ function parseDiagnoseOutput(raw) {
117
+ let envelope;
118
+ try {
119
+ envelope = JSON.parse(raw);
120
+ }
121
+ catch {
122
+ return [
123
+ {
124
+ check: 'parse',
125
+ severity: 'fatal',
126
+ details: 'Failed to parse cleo upgrade --diagnose --json output',
127
+ fix: 'Run `cleo upgrade --diagnose` manually to inspect DB state',
128
+ },
129
+ ];
130
+ }
131
+ const findings = envelope.data?.findings;
132
+ if (!Array.isArray(findings)) {
133
+ return [
134
+ {
135
+ check: 'parse',
136
+ severity: 'fatal',
137
+ details: 'cleo upgrade --diagnose response missing findings array',
138
+ fix: 'Run `cleo upgrade --diagnose` manually to inspect DB state',
139
+ },
140
+ ];
141
+ }
142
+ return findings
143
+ .filter((f) => f.status !== 'ok')
144
+ .map((f) => ({
145
+ check: f.check,
146
+ severity: classifyStatus(f.status),
147
+ details: f.details,
148
+ fix: f.fix,
149
+ }));
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Public API
153
+ // ---------------------------------------------------------------------------
154
+ /**
155
+ * Run the migration verify pre-check and return a structured result.
156
+ *
157
+ * Invokes `cleo upgrade --diagnose --json` via subprocess (CLI boundary).
158
+ * Classifies every non-ok finding into `warn` or `fatal`. A single `fatal`
159
+ * finding causes `ok: false` — the harness startup path must block spawn and
160
+ * print remediation instructions before exiting.
161
+ *
162
+ * @param runner - Injectable {@link CliRunner}; defaults to
163
+ * {@link DefaultCliRunner} which shells out to the real `cleo` binary.
164
+ * @returns Structured {@link MigrationVerifyResult}.
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const result = await verifyMigrations();
169
+ * if (!result.ok) {
170
+ * // print structured error + remediation, then exit
171
+ * }
172
+ * ```
173
+ *
174
+ * @public
175
+ */
176
+ export async function verifyMigrations(runner = new DefaultCliRunner()) {
177
+ let rawOutput;
178
+ try {
179
+ rawOutput = await runner.runDiagnose();
180
+ }
181
+ catch (err) {
182
+ // Subprocess spawn failure (ENOENT: cleo not on PATH, timeout, etc.).
183
+ const detail = err instanceof Error ? err.message : String(err);
184
+ const isEnoent = detail.includes('ENOENT') ||
185
+ detail.includes('not found') ||
186
+ detail.includes('command not found');
187
+ const drift = [
188
+ {
189
+ check: 'cleo-binary',
190
+ severity: 'fatal',
191
+ details: isEnoent
192
+ ? 'cleo binary not found on PATH — cannot verify DB migration state'
193
+ : `cleo upgrade --diagnose failed: ${detail}`,
194
+ fix: isEnoent
195
+ ? 'Install cleo: npm install -g @cleocode/cleo-os'
196
+ : 'Run `cleo upgrade --diagnose` manually; check exit code',
197
+ },
198
+ ];
199
+ return { ok: false, severity: 'fatal', drift, usedDedicatedVerb: false };
200
+ }
201
+ const drift = parseDiagnoseOutput(rawOutput);
202
+ const severity = aggregateSeverity(drift);
203
+ return {
204
+ ok: severity !== 'fatal',
205
+ severity,
206
+ drift,
207
+ usedDedicatedVerb: false,
208
+ };
209
+ }
210
+ // ---------------------------------------------------------------------------
211
+ // Rendering helpers (used by cli.ts startup path)
212
+ // ---------------------------------------------------------------------------
213
+ /**
214
+ * Format a {@link MigrationVerifyResult} as a human-readable fatal error
215
+ * block for printing to stderr.
216
+ *
217
+ * Only called when `result.ok === false` (fatal drift detected).
218
+ *
219
+ * @param result - The failed verify result.
220
+ * @returns Multi-line string ready for `process.stderr.write`.
221
+ *
222
+ * @public
223
+ */
224
+ export function renderFatalDriftError(result) {
225
+ const lines = [
226
+ '',
227
+ 'CleoOS startup blocked — DB migration drift detected',
228
+ '═══════════════════════════════════════════════════════════',
229
+ '',
230
+ 'The following issues prevent safe worker spawn:',
231
+ '',
232
+ ];
233
+ for (const d of result.drift) {
234
+ if (d.severity === 'fatal') {
235
+ lines.push(` [FATAL] ${d.check}`);
236
+ lines.push(` ${d.details}`);
237
+ if (d.fix !== undefined) {
238
+ lines.push(` Fix: ${d.fix}`);
239
+ }
240
+ lines.push('');
241
+ }
242
+ }
243
+ lines.push('Remediation options:');
244
+ lines.push(' 1. Run `cleo upgrade` to apply pending migrations');
245
+ lines.push(' 2. Run `cleo upgrade --diagnose` for detailed DB inspection');
246
+ lines.push(' 3. If the project DB is corrupted: `cleo init --force` (WARNING: resets tasks.db)');
247
+ lines.push(' 4. For cloud-sync issues: `cleo init --provider signaldock`');
248
+ lines.push('');
249
+ lines.push('If this is a fresh clone, run `cleo init` to initialise the project database.');
250
+ lines.push('');
251
+ return lines.join('\n');
252
+ }
253
+ /**
254
+ * Format a {@link MigrationVerifyResult} as a human-readable warning block
255
+ * for printing to stdout.
256
+ *
257
+ * Only called when `result.severity === "warn"` (reconciler already handled
258
+ * the drift — non-blocking).
259
+ *
260
+ * @param result - The warn-severity verify result.
261
+ * @returns Multi-line string ready for `process.stdout.write`.
262
+ *
263
+ * @public
264
+ */
265
+ export function renderWarnDrift(result) {
266
+ const lines = ['[cleo-os] DB migration notice (non-blocking):'];
267
+ for (const d of result.drift) {
268
+ if (d.severity === 'warn') {
269
+ lines.push(` - ${d.check}: ${d.details}`);
270
+ if (d.fix !== undefined) {
271
+ lines.push(` Fix: ${d.fix}`);
272
+ }
273
+ }
274
+ }
275
+ lines.push(' Run `cleo upgrade` to resolve these warnings.');
276
+ lines.push('');
277
+ return lines.join('\n');
278
+ }
279
+ //# sourceMappingURL=verify-migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-migrations.js","sourceRoot":"","sources":["../../src/health/verify-migrations.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/cleo-os",
3
- "version": "2026.4.108",
3
+ "version": "2026.4.109",
4
4
  "description": "CleoOS — the batteries-included agentic development environment wrapping Pi",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -13,9 +13,9 @@
13
13
  "@mariozechner/pi-coding-agent": ">=0.60.0",
14
14
  "@sinclair/typebox": "^0.34.49",
15
15
  "env-paths": "^4.0.0",
16
- "@cleocode/cant": "2026.4.108",
17
- "@cleocode/core": "2026.4.108",
18
- "@cleocode/cleo": "2026.4.108"
16
+ "@cleocode/cant": "2026.4.109",
17
+ "@cleocode/core": "2026.4.109",
18
+ "@cleocode/cleo": "2026.4.109"
19
19
  },
20
20
  "devDependencies": {
21
21
  "typescript": "^6.0.2",