@cleocode/cleo-os 2026.4.108 → 2026.4.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/README.md +66 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/health/__tests__/verify-migrations.test.d.ts +24 -0
- package/dist/health/__tests__/verify-migrations.test.d.ts.map +1 -0
- package/dist/health/__tests__/verify-migrations.test.js +292 -0
- package/dist/health/__tests__/verify-migrations.test.js.map +1 -0
- package/dist/health/verify-migrations.d.ts +151 -0
- package/dist/health/verify-migrations.d.ts.map +1 -0
- package/dist/health/verify-migrations.js +279 -0
- package/dist/health/verify-migrations.js.map +1 -0
- package/package.json +4 -4
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":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,eAAuB;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;QACpD,OAAO,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CAAC,IAAc;IACxC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpC,kEAAkE;QAClE,IAAI,WAAW,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,IAAI,CACtB,SAAS,EACT,IAAI,EACJ,cAAc,EACd,WAAW,EACX,MAAM,EACN,cAAc,CACf,CAAC;YACF,WAAW,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,aAAa,WAAW,EAAE,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,KAAK,UAAU,sBAAsB,CAAC,IAAc;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC;IACrE,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC;IAErE,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CACT,KAAK,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CACzG,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,MAAM,KAAK,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YAClD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAC/H,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;YAC7E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CACT,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAClF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,qBAAqB;IAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,2EAA2E;IAC3E,sEAAsE;IACtE,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAC3D,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC;IAC9D,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAED,0EAA0E;IAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACpE,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,2CAA2C;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;IAC7D,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,uBAAuB,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,SAAS,CAAC,QAAkB,EAAE,cAAwB;IAC7D,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,cAAc,EAAE,GAAG,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,IAAI;IACjB,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,OAAO;IACT,CAAC;IAED,uEAAuE;IACvE,wEAAwE;IACxE,IAAI,MAAM,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,sEAAsE;IACtE,IAAI,MAAyC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,+BAA+B,CAAC,CAAC;QACzD,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CACX,oDAAoD;YAClD,qDAAqD;YACrD,wBAAwB,CAC3B,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,cAAc,GAAG,qBAAqB,EAAE,CAAC;IAC/C,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAEjD,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
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.
|
|
3
|
+
"version": "2026.4.110",
|
|
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.
|
|
17
|
-
"@cleocode/core": "2026.4.
|
|
18
|
-
"@cleocode/cleo": "2026.4.
|
|
16
|
+
"@cleocode/cant": "2026.4.110",
|
|
17
|
+
"@cleocode/core": "2026.4.110",
|
|
18
|
+
"@cleocode/cleo": "2026.4.110"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"typescript": "^6.0.2",
|