@cleocode/core 2026.4.53 → 2026.4.55

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.
@@ -1 +1 @@
1
- {"version":3,"file":"brain-sqlite.d.ts","sourceRoot":"","sources":["../../src/store/brain-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAC;AAejD,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAW5C;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAMrD;AAoJD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AA0BD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,OAAO,WAAW,CAAC,CAAC,CAqE9F;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAcnC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAexC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,YAAY,GAAG,IAAI,CAEtD;AAED,YAAY,EAAE,kBAAkB,EAAE,CAAC;AACnC;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"brain-sqlite.d.ts","sourceRoot":"","sources":["../../src/store/brain-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAC;AAejD,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAW5C;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAMrD;AAuJD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AA0BD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,OAAO,WAAW,CAAC,CAAC,CAqE9F;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAcnC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAexC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,YAAY,GAAG,IAAI,CAEtD;AAED,YAAY,EAAE,kBAAkB,EAAE,CAAC;AACnC;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"migration-manager.d.ts","sourceRoot":"","sources":["../../src/store/migration-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;CACb;AAOD;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAK9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAIlD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CASvD;AAiCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,YAAY,EACtB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,GACnB,IAAI,CAkKN;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAE9B,EAAE,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAC3B,gBAAgB,EAAE,MAAM,EACxB,QAAQ,CAAC,EAAE,YAAY,EACvB,cAAc,CAAC,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAkCN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,cAAc,EAAE,EACjC,YAAY,EAAE,MAAM,GACnB,IAAI,CAmBN"}
1
+ {"version":3,"file":"migration-manager.d.ts","sourceRoot":"","sources":["../../src/store/migration-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;CACb;AAOD;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAK9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAIlD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CASvD;AA8HD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,YAAY,EACtB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,GACnB,IAAI,CA2KN;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAE9B,EAAE,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAC3B,gBAAgB,EAAE,MAAM,EACxB,QAAQ,CAAC,EAAE,YAAY,EACvB,cAAc,CAAC,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAkCN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,cAAc,EAAE,EACjC,YAAY,EAAE,MAAM,GACnB,IAAI,CAmBN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/core",
3
- "version": "2026.4.53",
3
+ "version": "2026.4.55",
4
4
  "description": "CLEO core business logic kernel — tasks, sessions, memory, orchestration, lifecycle, with bundled SQLite store",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -63,13 +63,13 @@
63
63
  "write-file-atomic": "^7.0.1",
64
64
  "yaml": "^2.8.3",
65
65
  "zod": "^4.3.6",
66
- "@cleocode/adapters": "2026.4.53",
67
- "@cleocode/agents": "2026.4.53",
68
- "@cleocode/contracts": "2026.4.53",
69
- "@cleocode/lafs": "2026.4.53",
70
- "@cleocode/caamp": "2026.4.53",
71
- "@cleocode/nexus": "2026.4.53",
72
- "@cleocode/skills": "2026.4.53"
66
+ "@cleocode/adapters": "2026.4.55",
67
+ "@cleocode/agents": "2026.4.55",
68
+ "@cleocode/contracts": "2026.4.55",
69
+ "@cleocode/caamp": "2026.4.55",
70
+ "@cleocode/lafs": "2026.4.55",
71
+ "@cleocode/nexus": "2026.4.55",
72
+ "@cleocode/skills": "2026.4.55"
73
73
  },
74
74
  "engines": {
75
75
  "node": ">=24.0.0"
@@ -25,9 +25,15 @@ vi.mock('../../audit.js', () => ({
25
25
  queryAudit: mocks.queryAudit,
26
26
  }));
27
27
 
28
- vi.mock('../../paths.js', () => ({
29
- getCleoDirAbsolute: (cwd?: string) => (cwd ? join(cwd, '.cleo') : mocks.tempCleoDir.value),
30
- }));
28
+ // Use importActual + spread so OTHER exports of paths.js remain available — see
29
+ // session-grade.test.ts for the T633 root-cause comment.
30
+ vi.mock('../../paths.js', async () => {
31
+ const actual = await vi.importActual<typeof import('../../paths.js')>('../../paths.js');
32
+ return {
33
+ ...actual,
34
+ getCleoDirAbsolute: (cwd?: string) => (cwd ? join(cwd, '.cleo') : mocks.tempCleoDir.value),
35
+ };
36
+ });
31
37
 
32
38
  import { gradeSession, readGrades } from '../session-grade.js';
33
39
 
@@ -21,9 +21,18 @@ vi.mock('../../audit.js', () => ({
21
21
  queryAudit: mocks.queryAudit,
22
22
  }));
23
23
 
24
- vi.mock('../../paths.js', () => ({
25
- getCleoDirAbsolute: (cwd?: string) => (cwd ? join(cwd, '.cleo') : mocks.tempCleoDir.value),
26
- }));
24
+ // Use importActual + spread so OTHER exports of paths.js remain available to any
25
+ // module that loads this test's mocked paths.js from the vitest module registry.
26
+ // Without this, tests that run AFTER this file in the same shard see paths.js
27
+ // missing every export except getCleoDirAbsolute (T633 root cause: nexus-e2e
28
+ // shard-2 audit failures, graph-memory-bridge mock errors).
29
+ vi.mock('../../paths.js', async () => {
30
+ const actual = await vi.importActual<typeof import('../../paths.js')>('../../paths.js');
31
+ return {
32
+ ...actual,
33
+ getCleoDirAbsolute: (cwd?: string) => (cwd ? join(cwd, '.cleo') : mocks.tempCleoDir.value),
34
+ };
35
+ });
27
36
 
28
37
  // Import after mocks are set up
29
38
  import { gradeSession, readGrades } from '../session-grade.js';
@@ -152,12 +152,15 @@ function runBrainMigrations(
152
152
  );
153
153
  }
154
154
 
155
- // T417: agent provenance field on brain_observations.
156
- // Added here as a safety net because the T417 migration file is an ALTER TABLE
157
- // that is skipped on fresh databases when the migration journal reconciler marks
158
- // all migrations as applied without actually running them (Scenario 2 race).
159
- // ensureColumns is idempotent — no-op if the column already exists.
160
- ensureColumns(nativeDb, 'brain_observations', [{ name: 'agent', ddl: 'text' }], 'brain');
155
+ // T632 root-cause fix: the migration journal reconciler (Sub-case B) now uses
156
+ // a per-migration DDL probe instead of wholesale-marking-all-applied. ALTER TABLE
157
+ // ADD COLUMN migrations (T417 agent, T528 provenance) now run correctly when
158
+ // their columns are missing.
159
+ //
160
+ // The previous `ensureColumns` band-aids (for `agent` and `provenance`) were
161
+ // removed in v2026.4.54 alongside the reconciler fix. If schema regressions
162
+ // recur, the right action is to debug `probeAndMarkApplied` in
163
+ // migration-manager.ts — NOT to add new band-aids here.
161
164
 
162
165
  // T626-M1: Normalize co_retrieved edge type — idempotent safety-net UPDATE.
163
166
  // The shipped Hebbian strengthener emitted edge_type = 'relates_to' instead of
@@ -98,6 +98,99 @@ function insertJournalEntry(
98
98
  );
99
99
  }
100
100
 
101
+ /**
102
+ * Probe a migration's DDL against the live schema and mark the journal entry
103
+ * applied IF AND ONLY IF all DDL targets already exist in the database.
104
+ *
105
+ * Supports three DDL forms commonly emitted by drizzle migrations:
106
+ * - `ALTER TABLE foo ADD COLUMN bar text` → mark applied if column foo.bar exists
107
+ * - `CREATE TABLE foo (...)` → mark applied if table foo exists
108
+ * - `CREATE INDEX [IF NOT EXISTS] idx_foo ON foo(...)` → mark applied if index exists
109
+ *
110
+ * If the migration contains DDL that doesn't fall into these patterns, or if any
111
+ * target is missing, the function returns false and DOES NOT mark applied —
112
+ * leaving the migration for Drizzle's normal `migrate()` to run.
113
+ *
114
+ * Used by:
115
+ * - Scenario 2 Sub-case B (after journal reset, decide what was already applied)
116
+ * - Scenario 3 (originally inline; now extracted for reuse)
117
+ *
118
+ * Replaces the broken "wholesale mark applied" pattern that was the root cause
119
+ * of the ensureColumns band-aid sprawl (T632).
120
+ *
121
+ * @param nativeDb - Native SQLite database handle
122
+ * @param migration - One entry from drizzle's readMigrationFiles
123
+ * @param logSubsystem - Logger subsystem name
124
+ * @returns true if the journal entry was inserted; false if migration must run
125
+ */
126
+ function probeAndMarkApplied(
127
+ nativeDb: DatabaseSync,
128
+ migration: { hash: string; folderMillis: number; name?: string; sql?: string | string[] },
129
+ logSubsystem: string,
130
+ ): boolean {
131
+ const sqlStatements = Array.isArray(migration.sql) ? migration.sql : [migration.sql ?? ''];
132
+ const fullSql = sqlStatements.join('\n');
133
+
134
+ // Extract DDL targets we can probe.
135
+ const alterColumnRegex = /ALTER\s+TABLE\s+[`"]?(\w+)[`"]?\s+ADD\s+COLUMN\s+[`"]?(\w+)[`"]?/gi;
136
+ const createTableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"]?(\w+)[`"]?/gi;
137
+ const createIndexRegex =
138
+ /CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"]?(\w+)[`"]?/gi;
139
+
140
+ const alterTargets: Array<{ table: string; column: string }> = [];
141
+ for (const m of fullSql.matchAll(alterColumnRegex)) {
142
+ alterTargets.push({ table: m[1] as string, column: m[2] as string });
143
+ }
144
+ const tableTargets: string[] = [];
145
+ for (const m of fullSql.matchAll(createTableRegex)) {
146
+ tableTargets.push(m[1] as string);
147
+ }
148
+ const indexTargets: string[] = [];
149
+ for (const m of fullSql.matchAll(createIndexRegex)) {
150
+ indexTargets.push(m[1] as string);
151
+ }
152
+
153
+ const totalTargets = alterTargets.length + tableTargets.length + indexTargets.length;
154
+ if (totalTargets === 0) {
155
+ // No probable DDL — could be UPDATE/INSERT/DELETE/etc. Leave for migrate().
156
+ return false;
157
+ }
158
+
159
+ // Probe each target.
160
+ const allAltersPresent = alterTargets.every(({ table, column }) => {
161
+ if (!tableExists(nativeDb, table)) return false;
162
+ const cols = nativeDb.prepare(`PRAGMA table_info(${table})`).all() as Array<{
163
+ name: string;
164
+ }>;
165
+ return cols.some((c) => c.name === column);
166
+ });
167
+ const allTablesPresent = tableTargets.every((t) => tableExists(nativeDb, t));
168
+ const allIndexesPresent = indexTargets.every((idx) => {
169
+ const rows = nativeDb
170
+ .prepare(`SELECT name FROM sqlite_master WHERE type='index' AND name=?`)
171
+ .all(idx) as Array<{ name: string }>;
172
+ return rows.length > 0;
173
+ });
174
+
175
+ if (allAltersPresent && allTablesPresent && allIndexesPresent) {
176
+ insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? '');
177
+ const log = getLogger(logSubsystem);
178
+ log.debug(
179
+ {
180
+ migration: migration.name,
181
+ alters: alterTargets.length,
182
+ tables: tableTargets.length,
183
+ indexes: indexTargets.length,
184
+ },
185
+ `Migration ${migration.name} DDL already present in schema — marked applied.`,
186
+ );
187
+ return true;
188
+ }
189
+
190
+ // At least one target missing — leave for drizzle migrate() to run.
191
+ return false;
192
+ }
193
+
101
194
  /**
102
195
  * Bootstrap and reconcile the Drizzle migration journal.
103
196
  *
@@ -184,14 +277,23 @@ export function reconcileJournal(
184
277
  );
185
278
  } else {
186
279
  // Sub-case B: Genuine stale hashes from an older CLEO version.
280
+ // ROOT-CAUSE FIX (T632): The previous implementation DELETEd the journal
281
+ // and INSERTed all local migrations as applied WITHOUT running their SQL.
282
+ // That meant ALTER TABLE migrations (T417 agent, T528 provenance, etc.)
283
+ // got marked applied but their columns were never added — forcing
284
+ // ensureColumns band-aids in brain-sqlite.ts to patch the missing schema.
285
+ //
286
+ // Real fix: clear orphaned entries, then PROBE each local migration's
287
+ // DDL. Mark applied ONLY if the DDL targets already exist in the schema.
288
+ // Drizzle's migrate() (called next) will run whatever remains unjournaled.
187
289
  const log = getLogger(logSubsystem);
188
290
  log.warn(
189
291
  { orphaned: orphanedEntries.length },
190
- `Detected stale migration journal entries from a previous CLEO version. Reconciling.`,
292
+ `Detected stale migration journal entries from a previous CLEO version. Reconciling via DDL probe.`,
191
293
  );
192
294
  nativeDb.exec('DELETE FROM "__drizzle_migrations"');
193
295
  for (const m of localMigrations) {
194
- insertJournalEntry(nativeDb, m.hash, m.folderMillis, m.name ?? '');
296
+ probeAndMarkApplied(nativeDb, m, logSubsystem);
195
297
  }
196
298
  }
197
299
  }