@cleocode/core 2026.6.2 → 2026.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/docs/export-document.js +626 -310
  2. package/dist/docs/export-document.js.map +3 -3
  3. package/dist/llm/catalog-cache.d.ts +3 -0
  4. package/dist/llm/catalog-cache.d.ts.map +1 -1
  5. package/dist/llm/catalog-cache.js.map +1 -1
  6. package/dist/llm/catalog-model-resolver.d.ts +89 -0
  7. package/dist/llm/catalog-model-resolver.d.ts.map +1 -0
  8. package/dist/llm/catalog-model-resolver.js +158 -0
  9. package/dist/llm/catalog-model-resolver.js.map +1 -0
  10. package/dist/llm/cli-ops.d.ts +14 -0
  11. package/dist/llm/cli-ops.d.ts.map +1 -1
  12. package/dist/llm/cli-ops.js +35 -0
  13. package/dist/llm/cli-ops.js.map +1 -1
  14. package/dist/llm/index.d.ts +3 -0
  15. package/dist/llm/index.d.ts.map +1 -1
  16. package/dist/llm/index.js +3 -0
  17. package/dist/llm/index.js.map +1 -1
  18. package/dist/llm/oauth/pkce.d.ts +6 -0
  19. package/dist/llm/oauth/pkce.d.ts.map +1 -1
  20. package/dist/llm/oauth/pkce.js +36 -5
  21. package/dist/llm/oauth/pkce.js.map +1 -1
  22. package/dist/llm/plugin-facade.js +648 -324
  23. package/dist/llm/plugin-facade.js.map +3 -3
  24. package/dist/llm/provider-registry/builtin/openai.d.ts +44 -0
  25. package/dist/llm/provider-registry/builtin/openai.d.ts.map +1 -0
  26. package/dist/llm/provider-registry/builtin/openai.js +79 -0
  27. package/dist/llm/provider-registry/builtin/openai.js.map +1 -0
  28. package/dist/llm/provider-registry/index.d.ts.map +1 -1
  29. package/dist/llm/provider-registry/index.js +2 -0
  30. package/dist/llm/provider-registry/index.js.map +1 -1
  31. package/dist/llm/system-resolver.d.ts +94 -0
  32. package/dist/llm/system-resolver.d.ts.map +1 -0
  33. package/dist/llm/system-resolver.js +165 -0
  34. package/dist/llm/system-resolver.js.map +1 -0
  35. package/dist/store/dual-scope-db.d.ts +20 -2
  36. package/dist/store/dual-scope-db.d.ts.map +1 -1
  37. package/dist/store/dual-scope-db.js +74 -7
  38. package/dist/store/dual-scope-db.js.map +1 -1
  39. package/dist/store/exodus/archive.d.ts +216 -0
  40. package/dist/store/exodus/archive.d.ts.map +1 -0
  41. package/dist/store/exodus/archive.js +314 -0
  42. package/dist/store/exodus/archive.js.map +1 -0
  43. package/dist/store/exodus/index.d.ts +1 -0
  44. package/dist/store/exodus/index.d.ts.map +1 -1
  45. package/dist/store/exodus/index.js +1 -0
  46. package/dist/store/exodus/index.js.map +1 -1
  47. package/dist/store/exodus/migrate.d.ts.map +1 -1
  48. package/dist/store/exodus/migrate.js +118 -24
  49. package/dist/store/exodus/migrate.js.map +1 -1
  50. package/dist/store/exodus/on-open.d.ts.map +1 -1
  51. package/dist/store/exodus/on-open.js +95 -34
  52. package/dist/store/exodus/on-open.js.map +1 -1
  53. package/dist/store/exodus/types.d.ts +10 -1
  54. package/dist/store/exodus/types.d.ts.map +1 -1
  55. package/dist/store/exodus/types.js.map +1 -1
  56. package/dist/store/exodus/verify-migration.d.ts.map +1 -1
  57. package/dist/store/exodus/verify-migration.js +12 -1
  58. package/dist/store/exodus/verify-migration.js.map +1 -1
  59. package/dist/store/sqlite.d.ts +16 -0
  60. package/dist/store/sqlite.d.ts.map +1 -1
  61. package/dist/store/sqlite.js +160 -39
  62. package/dist/store/sqlite.js.map +1 -1
  63. package/dist/validation/doctor/checks.d.ts +22 -0
  64. package/dist/validation/doctor/checks.d.ts.map +1 -1
  65. package/dist/validation/doctor/checks.js +67 -0
  66. package/dist/validation/doctor/checks.js.map +1 -1
  67. package/dist/validation/doctor/index.d.ts +1 -1
  68. package/dist/validation/doctor/index.d.ts.map +1 -1
  69. package/dist/validation/doctor/index.js +1 -1
  70. package/dist/validation/doctor/index.js.map +1 -1
  71. package/package.json +12 -12
@@ -10555,6 +10555,13 @@ var init_plugin_llm = __esm({
10555
10555
  }
10556
10556
  });
10557
10557
 
10558
+ // packages/contracts/src/llm/system-resolver.ts
10559
+ var init_system_resolver = __esm({
10560
+ "packages/contracts/src/llm/system-resolver.ts"() {
10561
+ "use strict";
10562
+ }
10563
+ });
10564
+
10558
10565
  // packages/contracts/src/memory/observe.ts
10559
10566
  var BRAIN_OBSERVATION_SOURCE_TYPES;
10560
10567
  var init_observe = __esm({
@@ -12497,6 +12504,7 @@ var init_src = __esm({
12497
12504
  init_invariants();
12498
12505
  init_lafs();
12499
12506
  init_plugin_llm();
12507
+ init_system_resolver();
12500
12508
  init_observe();
12501
12509
  init_migration_parity();
12502
12510
  init_mvi();
@@ -14422,7 +14430,7 @@ var init_sql = __esm({
14422
14430
  return new SQL([new StringChunk(str)]);
14423
14431
  }
14424
14432
  _sql.raw = raw;
14425
- function join21(chunks, separator) {
14433
+ function join22(chunks, separator) {
14426
14434
  const result = [];
14427
14435
  for (const [i, chunk] of chunks.entries()) {
14428
14436
  if (i > 0 && separator !== void 0) result.push(separator);
@@ -14430,7 +14438,7 @@ var init_sql = __esm({
14430
14438
  }
14431
14439
  return new SQL(result);
14432
14440
  }
14433
- _sql.join = join21;
14441
+ _sql.join = join22;
14434
14442
  function identifier(value) {
14435
14443
  return new Name(value);
14436
14444
  }
@@ -16817,7 +16825,7 @@ var init_select2 = __esm({
16817
16825
  const baseTableName = this.tableName;
16818
16826
  const tableName = getTableLikeName(table);
16819
16827
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
16820
- if (typeof tableName === "string" && this.config.joins?.some((join21) => join21.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
16828
+ if (typeof tableName === "string" && this.config.joins?.some((join22) => join22.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
16821
16829
  if (!this.isPartialSelect) {
16822
16830
  if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") this.config.fields = { [baseTableName]: this.config.fields };
16823
16831
  if (typeof tableName === "string" && !is(table, SQL)) {
@@ -20627,7 +20635,7 @@ var init_dialect = __esm({
20627
20635
  if (!joins2) return;
20628
20636
  const withEntries = Object.entries(joins2).filter(([_, v]) => v);
20629
20637
  if (!withEntries.length) return;
20630
- return sql.join(withEntries.map(([k, join21]) => {
20638
+ return sql.join(withEntries.map(([k, join22]) => {
20631
20639
  const relation = tableConfig.relations[k];
20632
20640
  const isSingle2 = is(relation, One);
20633
20641
  const targetTable = aliasedTable(relation.targetTable, `d${currentDepth + 1}`);
@@ -20638,7 +20646,7 @@ var init_dialect = __esm({
20638
20646
  table: targetTable,
20639
20647
  mode: isSingle2 ? "first" : "many",
20640
20648
  schema,
20641
- queryConfig: join21,
20649
+ queryConfig: join22,
20642
20650
  tableConfig: schema[relation.targetTableName],
20643
20651
  relationWhere: filter,
20644
20652
  isNested: true,
@@ -20652,7 +20660,7 @@ var init_dialect = __esm({
20652
20660
  key: k,
20653
20661
  selection: innerQuery.selection,
20654
20662
  isArray: !isSingle2,
20655
- isOptional: (relation.optional ?? false) || join21 !== true && !!join21.where
20663
+ isOptional: (relation.optional ?? false) || join22 !== true && !!join22.where
20656
20664
  });
20657
20665
  const jsonColumns = sql.join(innerQuery.selection.map((s) => {
20658
20666
  return sql`${sql.raw(this.escapeString(s.key))}, ${s.selection ? sql`${jsonb3}(${sql.identifier(s.key)})` : sql.identifier(s.key)}`;
@@ -21473,7 +21481,7 @@ var init_update = __esm({
21473
21481
  createJoin(joinType) {
21474
21482
  return ((table, on) => {
21475
21483
  const tableName = getTableLikeName(table);
21476
- if (typeof tableName === "string" && this.config.joins.some((join21) => join21.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
21484
+ if (typeof tableName === "string" && this.config.joins.some((join22) => join22.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
21477
21485
  if (typeof on === "function") {
21478
21486
  const from = this.config.from ? is(table, SQLiteTable) ? table[Table.Symbol.Columns] : is(table, Subquery) ? table._.selectedFields : is(table, SQLiteViewBase) ? table[ViewBaseConfig].selectedFields : void 0 : void 0;
21479
21487
  on = on(new Proxy(this.config.table[Table.Symbol.Columns], new SelectionProxyHandler({
@@ -22506,8 +22514,8 @@ function probeAndMarkApplied(nativeDb, migration, logSubsystem) {
22506
22514
  });
22507
22515
  if (allAltersPresent && allTablesPresent && allIndexesPresent && allTriggersPresent) {
22508
22516
  insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
22509
- const log7 = getLogger(logSubsystem);
22510
- log7.debug(
22517
+ const log8 = getLogger(logSubsystem);
22518
+ log8.debug(
22511
22519
  {
22512
22520
  migration: migration.name,
22513
22521
  alters: alterTargets.length,
@@ -22547,14 +22555,14 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22547
22555
  if (hasOrphanedEntries) {
22548
22556
  const dbHashes = new Set(dbEntries.map((e) => e.hash));
22549
22557
  const allLocalHashesPresentInDb = localMigrations.every((m) => dbHashes.has(m.hash));
22550
- const log7 = getLogger(logSubsystem);
22558
+ const log8 = getLogger(logSubsystem);
22551
22559
  if (allLocalHashesPresentInDb) {
22552
- log7.debug(
22560
+ log8.debug(
22553
22561
  { extra: orphanedEntries.length },
22554
22562
  `Migration journal has ${orphanedEntries.length} entries for migrations not known to this install (DB is ahead). Skipping reconciliation.`
22555
22563
  );
22556
22564
  } else {
22557
- log7.warn(
22565
+ log8.warn(
22558
22566
  { orphaned: orphanedEntries.length },
22559
22567
  `Detected ${orphanedEntries.length} true-orphan journal entries from a previous CLEO lineage. Reconciling via DDL probe.`
22560
22568
  );
@@ -22590,8 +22598,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22590
22598
  if (alterMatches.length === 0) {
22591
22599
  const stripped = fullSql.replace(/--[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
22592
22600
  if (stripped === "") {
22593
- const log7 = getLogger(logSubsystem);
22594
- log7.debug(
22601
+ const log8 = getLogger(logSubsystem);
22602
+ log8.debug(
22595
22603
  { migration: migration.name },
22596
22604
  `Migration ${migration.name} is a comment-only baseline marker \u2014 marking applied on existing DB.`
22597
22605
  );
@@ -22628,8 +22636,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22628
22636
  }
22629
22637
  }
22630
22638
  if (missingColumns.length === 0) {
22631
- const log7 = getLogger(logSubsystem);
22632
- log7.warn(
22639
+ const log8 = getLogger(logSubsystem);
22640
+ log8.warn(
22633
22641
  { migration: migration.name, columns: alterMatches },
22634
22642
  `Detected partially-applied migration ${migration.name} \u2014 columns exist but journal entry missing. Auto-reconciling.`
22635
22643
  );
@@ -22637,8 +22645,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22637
22645
  continue;
22638
22646
  }
22639
22647
  if (existingColumns.length > 0 && missingColumns.length > 0) {
22640
- const log7 = getLogger(logSubsystem);
22641
- log7.warn(
22648
+ const log8 = getLogger(logSubsystem);
22649
+ log8.warn(
22642
22650
  {
22643
22651
  migration: migration.name,
22644
22652
  existingColumns: existingColumns.map((c) => `${c.table}.${c.column}`),
@@ -22650,12 +22658,12 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22650
22658
  if (!tableExists(nativeDb, table)) continue;
22651
22659
  try {
22652
22660
  nativeDb.exec(`ALTER TABLE ${table} ADD COLUMN ${column}${ddl ? ` ${ddl}` : ""}`);
22653
- log7.warn(
22661
+ log8.warn(
22654
22662
  { migration: migration.name, table, column },
22655
22663
  `T920: Added missing column ${table}.${column} to complete partial migration.`
22656
22664
  );
22657
22665
  } catch {
22658
- log7.warn(
22666
+ log8.warn(
22659
22667
  { migration: migration.name, table, column },
22660
22668
  `T920: Could not add missing column ${table}.${column} \u2014 will let Drizzle migrate() handle it.`
22661
22669
  );
@@ -22675,8 +22683,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
22675
22683
  for (const entry of unnamedEntries) {
22676
22684
  const migrationName = hashToName.get(entry.hash);
22677
22685
  if (!migrationName) continue;
22678
- const log7 = getLogger(logSubsystem);
22679
- log7.debug(
22686
+ const log8 = getLogger(logSubsystem);
22687
+ log8.debug(
22680
22688
  { id: entry.id, hash: entry.hash, name: migrationName },
22681
22689
  `Backfilling missing name on journal entry id=${entry.id} (Drizzle v1 beta legacy compat).`
22682
22690
  );
@@ -22817,13 +22825,13 @@ function ensureColumns(nativeDb, tableName, requiredColumns, logSubsystem, conte
22817
22825
  const existingCols = new Set(columns.map((c) => c.name));
22818
22826
  for (const req of requiredColumns) {
22819
22827
  if (!existingCols.has(req.name)) {
22820
- const log7 = getLogger(logSubsystem);
22828
+ const log8 = getLogger(logSubsystem);
22821
22829
  const message = `Adding missing column ${tableName}.${req.name} via ALTER TABLE`;
22822
22830
  const fields = { column: req.name, context };
22823
22831
  if (context === "fresh") {
22824
- log7.error(fields, `${message} \u2014 MIGRATION DEFECT (fresh DB should not need repair)`);
22832
+ log8.error(fields, `${message} \u2014 MIGRATION DEFECT (fresh DB should not need repair)`);
22825
22833
  } else {
22826
- log7.warn(fields, message);
22834
+ log8.warn(fields, message);
22827
22835
  }
22828
22836
  nativeDb.exec(`ALTER TABLE ${tableName} ADD COLUMN ${req.name} ${req.ddl}`);
22829
22837
  }
@@ -31359,14 +31367,159 @@ var init_ensure_config = __esm({
31359
31367
  }
31360
31368
  });
31361
31369
 
31370
+ // packages/core/src/store/exodus/archive.ts
31371
+ import {
31372
+ copyFileSync as copyFileSync2,
31373
+ existsSync as existsSync4,
31374
+ mkdirSync,
31375
+ renameSync,
31376
+ unlinkSync,
31377
+ writeFileSync
31378
+ } from "node:fs";
31379
+ import { basename as basename3, join as join6 } from "node:path";
31380
+ function scopeBaseDir(scope, cwd) {
31381
+ return scope === "project" ? resolveCleoDir(cwd) : getCleoHome();
31382
+ }
31383
+ function exodusArchiveDir(scope, cwd) {
31384
+ return join6(scopeBaseDir(scope, cwd), ARCHIVE_DIR_NAME);
31385
+ }
31386
+ function exodusMarkerPath(scope, cwd) {
31387
+ return join6(scopeBaseDir(scope, cwd), MARKER_FILENAME_BY_SCOPE[scope]);
31388
+ }
31389
+ function hasExodusCompleteMarker(scope, cwd) {
31390
+ try {
31391
+ return existsSync4(exodusMarkerPath(scope, cwd));
31392
+ } catch {
31393
+ return false;
31394
+ }
31395
+ }
31396
+ function writeExodusCompleteMarker(scope, archivedSources, cwd) {
31397
+ const markerPath = exodusMarkerPath(scope, cwd);
31398
+ const baseDir = scopeBaseDir(scope, cwd);
31399
+ mkdirSync(baseDir, { recursive: true });
31400
+ const marker = {
31401
+ version: 1,
31402
+ scope,
31403
+ cleoVersion: getCleoVersion(),
31404
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
31405
+ archivedSources: [...archivedSources]
31406
+ };
31407
+ const tmpPath = `${markerPath}.tmp`;
31408
+ writeFileSync(tmpPath, JSON.stringify(marker, null, 2) + "\n", "utf8");
31409
+ renameSync(tmpPath, markerPath);
31410
+ log.info({ scope, markerPath, archivedSources }, "exodus: wrote completion marker");
31411
+ return markerPath;
31412
+ }
31413
+ function moveFileInto(srcPath, destDir) {
31414
+ mkdirSync(destDir, { recursive: true });
31415
+ let dest = join6(destDir, basename3(srcPath));
31416
+ if (existsSync4(dest)) {
31417
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
31418
+ dest = join6(destDir, `${basename3(srcPath)}.${stamp}`);
31419
+ }
31420
+ try {
31421
+ renameSync(srcPath, dest);
31422
+ } catch {
31423
+ copyFileSync2(srcPath, dest);
31424
+ unlinkSync(srcPath);
31425
+ }
31426
+ return dest;
31427
+ }
31428
+ function archiveSourceDb(source, cwd) {
31429
+ if (!existsSync4(source.path)) {
31430
+ return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
31431
+ }
31432
+ const destDir = exodusArchiveDir(source.targetScope, cwd);
31433
+ const archivedTo = moveFileInto(source.path, destDir);
31434
+ for (const suffix of SIDECAR_SUFFIXES) {
31435
+ const sidecar = `${source.path}${suffix}`;
31436
+ if (existsSync4(sidecar)) {
31437
+ try {
31438
+ moveFileInto(sidecar, destDir);
31439
+ } catch (err) {
31440
+ log.warn(
31441
+ { err, sidecar, sourceName: source.name },
31442
+ "exodus-archive: failed to archive sidecar (non-fatal)"
31443
+ );
31444
+ }
31445
+ }
31446
+ }
31447
+ log.info(
31448
+ { sourceName: source.name, sourcePath: source.path, archivedTo, scope: source.targetScope },
31449
+ "exodus-archive: archived legacy source DB"
31450
+ );
31451
+ return {
31452
+ name: source.name,
31453
+ sourcePath: source.path,
31454
+ archivedTo,
31455
+ action: "archived"
31456
+ };
31457
+ }
31458
+ function archiveMigratedSources(consumed, cwd) {
31459
+ const results = [];
31460
+ const scopes = /* @__PURE__ */ new Set();
31461
+ for (const source of consumed) {
31462
+ scopes.add(source.targetScope);
31463
+ results.push(archiveSourceDb(source, cwd));
31464
+ }
31465
+ const markersWritten = [];
31466
+ for (const scope of scopes) {
31467
+ const archivedForScope = consumed.filter((s) => s.targetScope === scope).map((s) => s.name);
31468
+ writeExodusCompleteMarker(scope, archivedForScope, cwd);
31469
+ markersWritten.push(scope);
31470
+ }
31471
+ return { sources: results, markersWritten };
31472
+ }
31473
+ function detectStrandedResidue(sources, cwd) {
31474
+ const markedScopes = /* @__PURE__ */ new Set();
31475
+ for (const scope of ["project", "global"]) {
31476
+ if (hasExodusCompleteMarker(scope, cwd)) markedScopes.add(scope);
31477
+ }
31478
+ if (markedScopes.size === 0) return [];
31479
+ const stranded = [];
31480
+ for (const source of sources) {
31481
+ if (!markedScopes.has(source.targetScope)) continue;
31482
+ if (existsSync4(source.path)) {
31483
+ stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
31484
+ }
31485
+ }
31486
+ return stranded;
31487
+ }
31488
+ function archiveStrandedResidue(stranded, sources, cwd) {
31489
+ const byName = new Map(sources.map((s) => [s.name, s]));
31490
+ const results = [];
31491
+ for (const entry of stranded) {
31492
+ const descriptor = byName.get(entry.name);
31493
+ if (descriptor === void 0) continue;
31494
+ results.push(archiveSourceDb(descriptor, cwd));
31495
+ }
31496
+ return results;
31497
+ }
31498
+ var log, ARCHIVE_DIR_NAME, MARKER_FILENAME_BY_SCOPE, SIDECAR_SUFFIXES;
31499
+ var init_archive2 = __esm({
31500
+ "packages/core/src/store/exodus/archive.ts"() {
31501
+ "use strict";
31502
+ init_logger2();
31503
+ init_paths();
31504
+ init_ensure_config();
31505
+ log = getLogger("exodus-archive");
31506
+ ARCHIVE_DIR_NAME = "_archive";
31507
+ MARKER_FILENAME_BY_SCOPE = {
31508
+ project: "exodus-complete",
31509
+ global: "exodus-complete"
31510
+ };
31511
+ SIDECAR_SUFFIXES = ["-wal", "-shm"];
31512
+ }
31513
+ });
31514
+
31362
31515
  // packages/core/src/project-info.ts
31363
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync } from "node:fs";
31364
- import { join as join6 } from "node:path";
31516
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
31517
+ import { join as join7 } from "node:path";
31365
31518
  function getProjectInfoSync(cwd) {
31366
31519
  const projectRoot = resolveOrCwd(cwd);
31367
31520
  const cleoDir = getCleoDirAbsolute(projectRoot);
31368
- const infoPath = join6(cleoDir, "project-info.json");
31369
- if (!existsSync4(infoPath)) return null;
31521
+ const infoPath = join7(cleoDir, "project-info.json");
31522
+ if (!existsSync5(infoPath)) return null;
31370
31523
  try {
31371
31524
  const raw = readFileSync2(infoPath, "utf-8");
31372
31525
  const data = JSON.parse(raw);
@@ -31393,8 +31546,8 @@ var init_project_info = __esm({
31393
31546
  });
31394
31547
 
31395
31548
  // packages/core/src/store/worktree-isolation-guard.ts
31396
- import { existsSync as existsSync5, statSync } from "node:fs";
31397
- import { dirname as dirname4, join as join7 } from "node:path";
31549
+ import { existsSync as existsSync6, statSync } from "node:fs";
31550
+ import { dirname as dirname4, join as join8 } from "node:path";
31398
31551
  function assertDbPathIsNotWorktreeResident(role, cwd) {
31399
31552
  let cleoDir;
31400
31553
  try {
@@ -31403,10 +31556,10 @@ function assertDbPathIsNotWorktreeResident(role, cwd) {
31403
31556
  return;
31404
31557
  }
31405
31558
  const projectRoot = dirname4(cleoDir);
31406
- const projectGit = join7(projectRoot, ".git");
31559
+ const projectGit = join8(projectRoot, ".git");
31407
31560
  let isWorktreeGitlink = false;
31408
31561
  try {
31409
- isWorktreeGitlink = existsSync5(projectGit) && statSync(projectGit).isFile();
31562
+ isWorktreeGitlink = existsSync6(projectGit) && statSync(projectGit).isFile();
31410
31563
  } catch {
31411
31564
  }
31412
31565
  if (!isWorktreeGitlink) {
@@ -31881,15 +32034,15 @@ var init_types = __esm({
31881
32034
 
31882
32035
  // packages/core/src/store/exodus/migrate.ts
31883
32036
  import {
31884
- copyFileSync as copyFileSync2,
31885
- existsSync as existsSync6,
31886
- mkdirSync,
32037
+ copyFileSync as copyFileSync3,
32038
+ existsSync as existsSync7,
32039
+ mkdirSync as mkdirSync2,
31887
32040
  readFileSync as readFileSync3,
31888
- renameSync,
31889
- unlinkSync,
31890
- writeFileSync as writeFileSync2
32041
+ renameSync as renameSync2,
32042
+ unlinkSync as unlinkSync2,
32043
+ writeFileSync as writeFileSync3
31891
32044
  } from "node:fs";
31892
- import { join as join8 } from "node:path";
32045
+ import { join as join9 } from "node:path";
31893
32046
  function getSqliteVersion(db) {
31894
32047
  try {
31895
32048
  const row = db.prepare("SELECT sqlite_version() AS v").get();
@@ -31905,14 +32058,14 @@ function listTables(db) {
31905
32058
  return rows.map((r) => r.name);
31906
32059
  }
31907
32060
  function writeJournal(stagingDir, journal) {
31908
- const journalPath = join8(stagingDir, JOURNAL_FILENAME);
32061
+ const journalPath = join9(stagingDir, JOURNAL_FILENAME);
31909
32062
  const tmpPath = `${journalPath}.tmp`;
31910
- writeFileSync2(tmpPath, JSON.stringify(journal, null, 2) + "\n", "utf8");
31911
- renameSync(tmpPath, journalPath);
32063
+ writeFileSync3(tmpPath, JSON.stringify(journal, null, 2) + "\n", "utf8");
32064
+ renameSync2(tmpPath, journalPath);
31912
32065
  }
31913
32066
  function readJournal(stagingDir) {
31914
- const journalPath = join8(stagingDir, JOURNAL_FILENAME);
31915
- if (!existsSync6(journalPath)) return null;
32067
+ const journalPath = join9(stagingDir, JOURNAL_FILENAME);
32068
+ if (!existsSync7(journalPath)) return null;
31916
32069
  try {
31917
32070
  return JSON.parse(readFileSync3(journalPath, "utf8"));
31918
32071
  } catch {
@@ -31920,17 +32073,17 @@ function readJournal(stagingDir) {
31920
32073
  }
31921
32074
  }
31922
32075
  function clearExodusJournal(stagingDir) {
31923
- const journalPath = join8(stagingDir, JOURNAL_FILENAME);
32076
+ const journalPath = join9(stagingDir, JOURNAL_FILENAME);
31924
32077
  try {
31925
- if (!existsSync6(journalPath)) return false;
31926
- unlinkSync(journalPath);
31927
- log.info(
32078
+ if (!existsSync7(journalPath)) return false;
32079
+ unlinkSync2(journalPath);
32080
+ log2.info(
31928
32081
  { stagingDir },
31929
32082
  "exodus: cleared migrate journal after abort/rollback \u2014 a retry will RE-COPY all tables"
31930
32083
  );
31931
32084
  return true;
31932
32085
  } catch (err) {
31933
- log.warn(
32086
+ log2.warn(
31934
32087
  { err, stagingDir },
31935
32088
  'exodus: failed to clear migrate journal after rollback (a retry may skip already-"done" tables)'
31936
32089
  );
@@ -31955,11 +32108,11 @@ function lockPath(dbPath) {
31955
32108
  }
31956
32109
  function acquireAdvisoryLock(dbPath) {
31957
32110
  const lp = lockPath(dbPath);
31958
- writeFileSync2(lp, JSON.stringify({ pid: process.pid, ts: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
32111
+ writeFileSync3(lp, JSON.stringify({ pid: process.pid, ts: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
31959
32112
  }
31960
32113
  function releaseAdvisoryLock(dbPath) {
31961
32114
  try {
31962
- unlinkSync(lockPath(dbPath));
32115
+ unlinkSync2(lockPath(dbPath));
31963
32116
  } catch {
31964
32117
  }
31965
32118
  }
@@ -31979,6 +32132,11 @@ function enumNormExpr(targetTableName, col, srcRef) {
31979
32132
  const fn = ENUM_NORMALIZATIONS.get(key);
31980
32133
  return fn ? fn(srcRef) : null;
31981
32134
  }
32135
+ function numericClampExpr(targetTableName, col, srcRef) {
32136
+ const key = `${targetTableName}.${col}`;
32137
+ const fn = NUMERIC_CLAMPS.get(key);
32138
+ return fn ? fn(srcRef) : null;
32139
+ }
31982
32140
  function epochUnitForSource(sourceName) {
31983
32141
  const key = sourceName.toLowerCase();
31984
32142
  for (const [pattern, unit] of SOURCE_EPOCH_UNITS) {
@@ -32014,6 +32172,14 @@ function buildSelectExpr(attachAlias, legacyTable, targetTableName, col, srcType
32014
32172
  }
32015
32173
  return `${isoExpr} AS "${col}"`;
32016
32174
  }
32175
+ const clampExpr = numericClampExpr(targetTableName, col, srcRef);
32176
+ if (clampExpr !== null) {
32177
+ if (isNotNullWithoutDefault) {
32178
+ const defLiteral = typeDefaultLiteral(tgtInfo.type);
32179
+ return `COALESCE(${clampExpr}, ${defLiteral}) AS "${col}"`;
32180
+ }
32181
+ return `${clampExpr} AS "${col}"`;
32182
+ }
32017
32183
  const normExpr = enumNormExpr(targetTableName, col, srcRef);
32018
32184
  if (normExpr !== null) {
32019
32185
  if (isNotNullWithoutDefault) {
@@ -32031,7 +32197,7 @@ function buildSelectExpr(attachAlias, legacyTable, targetTableName, col, srcType
32031
32197
  function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyTableName, sourceName, targetSchema = "main") {
32032
32198
  const resolution = resolveConsolidatedTableName(sourceName, legacyTableName);
32033
32199
  if (resolution.kind === "skip") {
32034
- log.warn(
32200
+ log2.warn(
32035
32201
  { legacyTableName, sourceName, reason: resolution.reason },
32036
32202
  `Exodus: explicitly skipping table \u2014 ${resolution.reason}`
32037
32203
  );
@@ -32050,7 +32216,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
32050
32216
  ).get();
32051
32217
  if (!existsRow) {
32052
32218
  const reason = `consolidated target '${targetTableName}' not found (mapped from legacy '${legacyTableName}')`;
32053
- log.warn(
32219
+ log2.warn(
32054
32220
  { legacyTableName, targetTableName, sourceName, attachAlias, targetSchema },
32055
32221
  `Exodus: ${reason}`
32056
32222
  );
@@ -32061,13 +32227,13 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
32061
32227
  const sharedColumns = srcPragma.map((r) => r.name).filter((col) => tgtColMap.has(col));
32062
32228
  if (sharedColumns.length === 0) {
32063
32229
  const reason = `no overlapping columns between source '${legacyTableName}' and target '${targetTableName}'`;
32064
- log.warn({ legacyTableName, targetTableName, sourceName }, `Exodus: ${reason}`);
32230
+ log2.warn({ legacyTableName, targetTableName, sourceName }, `Exodus: ${reason}`);
32065
32231
  return { rowsCopied: 0, skipped: true, reason };
32066
32232
  }
32067
32233
  const srcOnlyColumns = srcPragma.map((r) => r.name).filter((c) => !tgtColMap.has(c));
32068
32234
  const tgtOnlyColumns = tgtPragma.map((r) => r.name).filter((c) => !srcColumns.has(c));
32069
32235
  if (srcOnlyColumns.length > 0 || tgtOnlyColumns.length > 0) {
32070
- log.info(
32236
+ log2.info(
32071
32237
  { legacyTableName, targetTableName, sourceName, srcOnlyColumns, tgtOnlyColumns },
32072
32238
  "Exodus: column drift detected \u2014 copying intersection, dropping src-only cols, using defaults for tgt-only cols"
32073
32239
  );
@@ -32082,7 +32248,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
32082
32248
  return isoGlobCols.has(col) && (upper.includes("INT") || upper === "" || upper === "NUMERIC");
32083
32249
  });
32084
32250
  if (coercedCols.length > 0) {
32085
- log.info(
32251
+ log2.info(
32086
32252
  {
32087
32253
  legacyTableName,
32088
32254
  targetTableName,
@@ -32098,11 +32264,20 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
32098
32264
  (col) => ENUM_NORMALIZATIONS.has(`${targetTableName}.${col}`)
32099
32265
  );
32100
32266
  if (normalizedCols.length > 0) {
32101
- log.info(
32267
+ log2.info(
32102
32268
  { legacyTableName, targetTableName, sourceName, normalizedCols },
32103
32269
  `Exodus: applying enum-value normalization for ${normalizedCols.length} column(s) (T11547)`
32104
32270
  );
32105
32271
  }
32272
+ const clampedCols = sharedColumns.filter(
32273
+ (col) => NUMERIC_CLAMPS.has(`${targetTableName}.${col}`)
32274
+ );
32275
+ if (clampedCols.length > 0) {
32276
+ log2.info(
32277
+ { legacyTableName, targetTableName, sourceName, clampedCols },
32278
+ `Exodus: applying non-finite numeric clamp for ${clampedCols.length} column(s) (T11782)`
32279
+ );
32280
+ }
32106
32281
  const selectExprs = sharedColumns.map((col) => {
32107
32282
  const srcType = srcTypeMap.get(col) ?? "";
32108
32283
  const tgtInfo = tgtColMap.get(col);
@@ -32139,13 +32314,13 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
32139
32314
  const dropped = sourceCount - rowsCopied;
32140
32315
  if (rowsCopied === 0) {
32141
32316
  const reason = `INSERT OR IGNORE dropped ALL ${sourceCount} rows from '${legacyTableName}'\u2192'${targetTableName}' (rowsCopied=0, sourceCount=${sourceCount}). Likely a CHECK/type constraint violation \u2014 check epoch coercion or enum values.`;
32142
- log.error(
32317
+ log2.error(
32143
32318
  { legacyTableName, targetTableName, sourceName, sourceCount, rowsCopied },
32144
32319
  `Exodus: ${reason}`
32145
32320
  );
32146
32321
  return { rowsCopied: 0, skipped: false, reason };
32147
32322
  }
32148
- log.warn(
32323
+ log2.warn(
32149
32324
  { legacyTableName, targetTableName, sourceName, sourceCount, rowsCopied, dropped },
32150
32325
  `Exodus: INSERT OR IGNORE dropped ${dropped}/${sourceCount} rows from '${legacyTableName}'\u2192'${targetTableName}' \u2014 may be idempotent-resume dedup or a constraint violation; verify will confirm`
32151
32326
  );
@@ -32156,16 +32331,16 @@ function checkSchemaVersion(journal, forceCrossVersion) {
32156
32331
  if (journal.targetSchemaVersion !== EXODUS_TARGET_SCHEMA_VERSION) {
32157
32332
  const msg = `Schema version mismatch: journal=${journal.targetSchemaVersion}, expected=${EXODUS_TARGET_SCHEMA_VERSION}`;
32158
32333
  if (forceCrossVersion) {
32159
- log.warn(msg + " (--force-cross-version: continuing anyway)");
32334
+ log2.warn(msg + " (--force-cross-version: continuing anyway)");
32160
32335
  return true;
32161
32336
  }
32162
- log.error(msg + " \u2014 pass --force-cross-version to override");
32337
+ log2.error(msg + " \u2014 pass --force-cross-version to override");
32163
32338
  return false;
32164
32339
  }
32165
32340
  return true;
32166
32341
  }
32167
32342
  async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
32168
- const { sources, stagingDir, diskPreflight, projectDbPath } = plan;
32343
+ const { sources, stagingDir, diskPreflight, projectDbPath, globalDbPath } = plan;
32169
32344
  if (!diskPreflight) {
32170
32345
  return {
32171
32346
  ok: false,
@@ -32175,10 +32350,10 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
32175
32350
  error: `Insufficient disk space: need \u22653\xD7 source size (${plan.totalSourceBytes} bytes source, ${plan.availableBytes} bytes available). Free up space or use a different storage location.`
32176
32351
  };
32177
32352
  }
32178
- mkdirSync(stagingDir, { recursive: true });
32353
+ mkdirSync2(stagingDir, { recursive: true });
32179
32354
  let sqliteVersion = "unknown";
32180
32355
  for (const src of sources) {
32181
- if (existsSync6(src.path)) {
32356
+ if (existsSync7(src.path)) {
32182
32357
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
32183
32358
  sqliteVersion = getSqliteVersion(snap.db);
32184
32359
  snap.close();
@@ -32203,6 +32378,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
32203
32378
  const backupPaths = [];
32204
32379
  const allTableResults = [];
32205
32380
  const lockedPaths = [];
32381
+ let projectHandle = null;
32382
+ let globalHandle = null;
32206
32383
  try {
32207
32384
  let extractNativeDb2 = function(handle) {
32208
32385
  const drizzleHandle = handle.db;
@@ -32217,24 +32394,28 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
32217
32394
  };
32218
32395
  var extractNativeDb = extractNativeDb2;
32219
32396
  for (const src of sources) {
32220
- if (!existsSync6(src.path)) continue;
32221
- const backupDest = join8(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
32222
- if (!existsSync6(backupDest)) {
32397
+ if (!existsSync7(src.path)) continue;
32398
+ const backupDest = join9(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
32399
+ if (!existsSync7(backupDest)) {
32223
32400
  onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
32224
- copyFileSync2(src.path, backupDest);
32401
+ copyFileSync3(src.path, backupDest);
32225
32402
  backupPaths.push(backupDest);
32226
32403
  }
32227
32404
  acquireAdvisoryLock(src.path);
32228
32405
  lockedPaths.push(src.path);
32229
32406
  }
32230
- onProgress?.("Opening consolidated project-scope cleo.db (running migrations)\u2026");
32231
- const projectHandle = await openDualScopeDb("project");
32232
- onProgress?.("Opening consolidated global-scope cleo.db (running migrations)\u2026");
32233
- const globalHandle = await openDualScopeDb("global");
32407
+ onProgress?.("Opening DEDICATED project-scope cleo.db connection (running migrations)\u2026");
32408
+ projectHandle = await openDualScopeDbAtPath("project", projectDbPath, void 0, {
32409
+ dedicated: true
32410
+ });
32411
+ onProgress?.("Opening DEDICATED global-scope cleo.db connection (running migrations)\u2026");
32412
+ globalHandle = await openDualScopeDbAtPath("global", globalDbPath, void 0, {
32413
+ dedicated: true
32414
+ });
32234
32415
  const projectNative = extractNativeDb2(projectHandle);
32235
32416
  const globalNative = extractNativeDb2(globalHandle);
32236
- const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync6(s.path));
32237
- const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync6(s.path));
32417
+ const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync7(s.path));
32418
+ const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync7(s.path));
32238
32419
  await migrateScope(
32239
32420
  "project",
32240
32421
  projectSources,
@@ -32256,14 +32437,20 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
32256
32437
  );
32257
32438
  journal.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
32258
32439
  writeJournal(stagingDir, journal);
32259
- projectHandle.close();
32260
- globalHandle.close();
32261
32440
  return { ok: true, tables: allTableResults, stagingDir, backupPaths };
32262
32441
  } catch (err) {
32263
32442
  const error = err instanceof Error ? err.message : String(err);
32264
- log.error({ err }, "Exodus migration failed");
32443
+ log2.error({ err }, "Exodus migration failed");
32265
32444
  return { ok: false, tables: allTableResults, stagingDir, backupPaths, error };
32266
32445
  } finally {
32446
+ try {
32447
+ projectHandle?.close();
32448
+ } catch {
32449
+ }
32450
+ try {
32451
+ globalHandle?.close();
32452
+ } catch {
32453
+ }
32267
32454
  for (const p of lockedPaths) {
32268
32455
  releaseAdvisoryLock(p);
32269
32456
  }
@@ -32273,7 +32460,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
32273
32460
  if (sources.length === 0) return;
32274
32461
  onProgress?.(`Migrating ${scope}-scope sources\u2026`);
32275
32462
  targetNativeDb.exec("PRAGMA foreign_keys = OFF");
32276
- log.info({ scope }, "Exodus: foreign_keys=OFF for bulk copy (T11533 FK-defer)");
32463
+ log2.info({ scope }, "Exodus: foreign_keys=OFF for bulk copy (T11533 FK-defer)");
32277
32464
  try {
32278
32465
  for (let i = 0; i < sources.length; i++) {
32279
32466
  const src = sources[i];
@@ -32335,12 +32522,12 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
32335
32522
  errorMsg = copyResult.reason;
32336
32523
  skipped = true;
32337
32524
  } else if (copyResult.reason) {
32338
- status = "skipped";
32525
+ status = "partial";
32339
32526
  errorMsg = copyResult.reason;
32340
32527
  }
32341
32528
  } catch (err) {
32342
32529
  const msg = err instanceof Error ? err.message : String(err);
32343
- log.warn({ tableName, sourceDb: src.name, err }, "Table copy failed \u2014 skipping");
32530
+ log2.warn({ tableName, sourceDb: src.name, err }, "Table copy failed \u2014 skipping");
32344
32531
  status = "skipped";
32345
32532
  errorMsg = msg;
32346
32533
  skipped = true;
@@ -32388,7 +32575,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
32388
32575
  targetNativeDb.exec(`DETACH DATABASE "${attachAlias}"`);
32389
32576
  onProgress?.(` [${src.name}] Detached "${attachAlias}"`);
32390
32577
  } catch (detachErr) {
32391
- log.warn(
32578
+ log2.warn(
32392
32579
  { attachAlias, sourceDb: src.name, err: detachErr },
32393
32580
  "DETACH failed \u2014 alias will be released on DB close"
32394
32581
  );
@@ -32398,7 +32585,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
32398
32585
  targetNativeDb.exec(`DETACH DATABASE "${crossAlias}"`);
32399
32586
  onProgress?.(` [${src.name}] Cross-scope target detached "${crossAlias}"`);
32400
32587
  } catch (detachErr) {
32401
- log.warn(
32588
+ log2.warn(
32402
32589
  { crossAlias, sourceDb: src.name, err: detachErr },
32403
32590
  "Cross-scope DETACH failed \u2014 alias will be released on DB close"
32404
32591
  );
@@ -32410,28 +32597,28 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
32410
32597
  try {
32411
32598
  const orphans = targetNativeDb.prepare("PRAGMA foreign_key_check").all();
32412
32599
  if (orphans.length > 0) {
32413
- log.warn(
32600
+ log2.warn(
32414
32601
  { scope, orphanCount: orphans.length, sample: orphans.slice(0, 5) },
32415
32602
  `Exodus: PRAGMA foreign_key_check found ${orphans.length} orphan row(s) after bulk copy \u2014 these are genuine data orphans (not ordering artifacts)`
32416
32603
  );
32417
32604
  } else {
32418
- log.info({ scope }, "Exodus: PRAGMA foreign_key_check PASSED \u2014 no orphan rows");
32605
+ log2.info({ scope }, "Exodus: PRAGMA foreign_key_check PASSED \u2014 no orphan rows");
32419
32606
  }
32420
32607
  } catch (checkErr) {
32421
- log.warn(
32608
+ log2.warn(
32422
32609
  { scope, err: checkErr },
32423
32610
  "Exodus: PRAGMA foreign_key_check failed (non-fatal) \u2014 target schema may not have FK constraints enabled"
32424
32611
  );
32425
32612
  }
32426
32613
  try {
32427
32614
  targetNativeDb.exec("PRAGMA foreign_keys = ON");
32428
- log.info({ scope }, "Exodus: foreign_keys=ON restored after bulk copy");
32615
+ log2.info({ scope }, "Exodus: foreign_keys=ON restored after bulk copy");
32429
32616
  } catch (fkErr) {
32430
- log.warn({ scope, err: fkErr }, "Exodus: could not restore foreign_keys=ON (non-fatal)");
32617
+ log2.warn({ scope, err: fkErr }, "Exodus: could not restore foreign_keys=ON (non-fatal)");
32431
32618
  }
32432
32619
  }
32433
32620
  }
32434
- var log, LOCK_SENTINEL_SUFFIX, JOURNAL_FILENAME, ENUM_NORMALIZATIONS, ISO_CHECK_REGEX, SOURCE_EPOCH_UNITS, EPOCH_SECONDS_THRESHOLD;
32621
+ var log2, LOCK_SENTINEL_SUFFIX, JOURNAL_FILENAME, ENUM_NORMALIZATIONS, NUMERIC_CLAMPS, ISO_CHECK_REGEX, SOURCE_EPOCH_UNITS, EPOCH_SECONDS_THRESHOLD;
32435
32622
  var init_migrate = __esm({
32436
32623
  "packages/core/src/store/exodus/migrate.ts"() {
32437
32624
  "use strict";
@@ -32441,7 +32628,7 @@ var init_migrate = __esm({
32441
32628
  init_open_cleo_db();
32442
32629
  init_table_name_map();
32443
32630
  init_types();
32444
- log = getLogger("exodus-migrate");
32631
+ log2 = getLogger("exodus-migrate");
32445
32632
  LOCK_SENTINEL_SUFFIX = ".exodus-lock";
32446
32633
  JOURNAL_FILENAME = "exodus-journal.json";
32447
32634
  ENUM_NORMALIZATIONS = /* @__PURE__ */ new Map([
@@ -32533,6 +32720,15 @@ var init_migrate = __esm({
32533
32720
  // brain target = runtime shape with no CHECK; legacy values like 'accepted',
32534
32721
  // 'rejected', 'prime' now survive VERBATIM instead of being coerced.)
32535
32722
  ]);
32723
+ NUMERIC_CLAMPS = /* @__PURE__ */ new Map([
32724
+ // --- brain_weight_history.delta_weight (T11782) -------------------------
32725
+ // +Inf → 1.0 (max canonical reinforcement), -Inf → -1.0 (max canonical
32726
+ // depression), NaN → 0.0 (no-op delta). Finite values pass through.
32727
+ [
32728
+ "brain_weight_history.delta_weight",
32729
+ (src) => `CASE WHEN ${src} = 9e999 THEN 1.0 WHEN ${src} = -9e999 THEN -1.0 WHEN ${src} != ${src} THEN 0.0 ELSE ${src} END`
32730
+ ]
32731
+ ]);
32536
32732
  ISO_CHECK_REGEX = /CHECK\s*\(\s*"([^"]+)"\s+IS\s+NULL\s+OR\s+"[^"]+"\s+GLOB\s+'\[0-9/gi;
32537
32733
  SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
32538
32734
  ["conduit", "seconds"],
@@ -32549,8 +32745,8 @@ var init_migrate = __esm({
32549
32745
  });
32550
32746
 
32551
32747
  // packages/core/src/store/exodus/plan.ts
32552
- import { existsSync as existsSync7, readdirSync as readdirSync2, statfsSync, statSync as statSync2 } from "node:fs";
32553
- import { join as join9 } from "node:path";
32748
+ import { existsSync as existsSync8, readdirSync as readdirSync2, statfsSync, statSync as statSync2 } from "node:fs";
32749
+ import { join as join10 } from "node:path";
32554
32750
  function buildSourceDescriptors(cwd) {
32555
32751
  const cleoDir = resolveCleoDir(cwd);
32556
32752
  const cleoHome = getCleoHome();
@@ -32558,33 +32754,33 @@ function buildSourceDescriptors(cwd) {
32558
32754
  // Project-tier — go into consolidated project-scope cleo.db
32559
32755
  {
32560
32756
  name: "tasks",
32561
- path: join9(cleoDir, "tasks.db"),
32757
+ path: join10(cleoDir, "tasks.db"),
32562
32758
  targetScope: "project"
32563
32759
  },
32564
32760
  {
32565
32761
  name: "brain (project)",
32566
- path: join9(cleoDir, "brain.db"),
32762
+ path: join10(cleoDir, "brain.db"),
32567
32763
  targetScope: "project"
32568
32764
  },
32569
32765
  {
32570
32766
  name: "conduit",
32571
- path: join9(cleoDir, "conduit.db"),
32767
+ path: join10(cleoDir, "conduit.db"),
32572
32768
  targetScope: "project"
32573
32769
  },
32574
32770
  // Global-tier — go into consolidated global-scope cleo.db
32575
32771
  {
32576
32772
  name: "nexus",
32577
- path: join9(cleoHome, "nexus.db"),
32773
+ path: join10(cleoHome, "nexus.db"),
32578
32774
  targetScope: "global"
32579
32775
  },
32580
32776
  {
32581
32777
  name: "signaldock",
32582
- path: join9(cleoHome, "signaldock.db"),
32778
+ path: join10(cleoHome, "signaldock.db"),
32583
32779
  targetScope: "global"
32584
32780
  },
32585
32781
  {
32586
32782
  name: "skills",
32587
- path: join9(cleoHome, "skills.db"),
32783
+ path: join10(cleoHome, "skills.db"),
32588
32784
  targetScope: "global"
32589
32785
  }
32590
32786
  ];
@@ -32613,7 +32809,7 @@ function findExistingStaging(cleoDir) {
32613
32809
  const entries = readdirSync2(cleoDir, { withFileTypes: true });
32614
32810
  const stagingDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => e.name).sort().reverse();
32615
32811
  if (stagingDirs.length > 0) {
32616
- return join9(cleoDir, stagingDirs[0]);
32812
+ return join10(cleoDir, stagingDirs[0]);
32617
32813
  }
32618
32814
  } catch {
32619
32815
  }
@@ -32626,7 +32822,7 @@ function buildExodusPlan(cwd) {
32626
32822
  const availableBytes = getAvailableBytes(cleoDir);
32627
32823
  const diskPreflight = totalSourceBytes === 0 || availableBytes >= 3 * totalSourceBytes;
32628
32824
  const existingStaging = findExistingStaging(cleoDir);
32629
- const stagingDir = existingStaging ?? join9(cleoDir, deriveStagingDirName());
32825
+ const stagingDir = existingStaging ?? join10(cleoDir, deriveStagingDirName());
32630
32826
  const resumeFromStaging = existingStaging !== null;
32631
32827
  const projectDbPath = resolveDualScopeDbPath("project", cwd);
32632
32828
  const globalDbPath = resolveDualScopeDbPath("global");
@@ -32642,7 +32838,7 @@ function buildExodusPlan(cwd) {
32642
32838
  };
32643
32839
  }
32644
32840
  function sourcesPresent(sources) {
32645
- return sources.some((s) => existsSync7(s.path));
32841
+ return sources.some((s) => existsSync8(s.path));
32646
32842
  }
32647
32843
  var init_plan2 = __esm({
32648
32844
  "packages/core/src/store/exodus/plan.ts"() {
@@ -32653,11 +32849,11 @@ var init_plan2 = __esm({
32653
32849
  });
32654
32850
 
32655
32851
  // packages/core/src/store/exodus/status.ts
32656
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
32657
- import { join as join10 } from "node:path";
32852
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
32853
+ import { join as join11 } from "node:path";
32658
32854
  function readJournal2(stagingDir) {
32659
- const p = join10(stagingDir, JOURNAL_FILENAME2);
32660
- if (!existsSync8(p)) return null;
32855
+ const p = join11(stagingDir, JOURNAL_FILENAME2);
32856
+ if (!existsSync9(p)) return null;
32661
32857
  try {
32662
32858
  return JSON.parse(readFileSync4(p, "utf8"));
32663
32859
  } catch {
@@ -32666,7 +32862,7 @@ function readJournal2(stagingDir) {
32666
32862
  }
32667
32863
  function findStagingDirs(cleoDir) {
32668
32864
  try {
32669
- return readdirSync3(cleoDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => join10(cleoDir, e.name)).sort().reverse();
32865
+ return readdirSync3(cleoDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => join11(cleoDir, e.name)).sort().reverse();
32670
32866
  } catch {
32671
32867
  return [];
32672
32868
  }
@@ -32689,15 +32885,15 @@ function runExodusStatus(cwd) {
32689
32885
  const sourcesInfo = plan.sources.map((s) => ({
32690
32886
  name: s.name,
32691
32887
  path: s.path,
32692
- exists: existsSync8(s.path),
32888
+ exists: existsSync9(s.path),
32693
32889
  bytes: safeBytes(s.path)
32694
32890
  }));
32695
32891
  return {
32696
32892
  hasStaging: latestStaging !== null,
32697
32893
  stagingDir: latestStaging,
32698
32894
  journal,
32699
- projectDbExists: existsSync8(projectDbPath),
32700
- globalDbExists: existsSync8(globalDbPath),
32895
+ projectDbExists: existsSync9(projectDbPath),
32896
+ globalDbExists: existsSync9(globalDbPath),
32701
32897
  sourcesPresent: sourcesInfo.some((s) => s.exists),
32702
32898
  sources: sourcesInfo
32703
32899
  };
@@ -32714,7 +32910,7 @@ var init_status = __esm({
32714
32910
  });
32715
32911
 
32716
32912
  // packages/core/src/store/exodus/verify-migration.ts
32717
- import { existsSync as existsSync9 } from "node:fs";
32913
+ import { existsSync as existsSync10 } from "node:fs";
32718
32914
  import { createRequire as createRequire3 } from "node:module";
32719
32915
  function orderByClause(db, tableName) {
32720
32916
  try {
@@ -32737,14 +32933,15 @@ function computeTableDigest(db, tableName, columns) {
32737
32933
  rows = db.prepare(`SELECT ${selectClause} FROM "${tableName}" ORDER BY ${orderBy}`).all();
32738
32934
  } catch (err) {
32739
32935
  const msg = err instanceof Error ? err.message : String(err);
32740
- log2.warn(
32936
+ log3.warn(
32741
32937
  { tableName, err: msg },
32742
32938
  "computeTableDigest: SELECT failed (possibly a virtual/FTS table) \u2014 treating as 0 rows"
32743
32939
  );
32744
32940
  return { count: 0, hash: "" };
32745
32941
  }
32746
32942
  for (const row of rows) {
32747
- hasher.update(JSON.stringify(row));
32943
+ const rowObj = row;
32944
+ hasher.update(JSON.stringify(rowObj, Object.keys(rowObj).sort()));
32748
32945
  }
32749
32946
  return {
32750
32947
  count: rows.length,
@@ -32849,7 +33046,7 @@ function foreignKeyCheck(db, scope) {
32849
33046
  try {
32850
33047
  const rows = db.prepare("PRAGMA foreign_key_check").all();
32851
33048
  if (rows.length > 0) {
32852
- log2.warn(
33049
+ log3.warn(
32853
33050
  { scope, count: rows.length, sample: rows.slice(0, 5) },
32854
33051
  `verifyMigration: PRAGMA foreign_key_check found ${rows.length} orphan row(s)`
32855
33052
  );
@@ -32861,7 +33058,7 @@ function foreignKeyCheck(db, scope) {
32861
33058
  fkid: r.fkid
32862
33059
  }));
32863
33060
  } catch (err) {
32864
- log2.warn({ scope, err }, "verifyMigration: PRAGMA foreign_key_check failed (non-fatal)");
33061
+ log3.warn({ scope, err }, "verifyMigration: PRAGMA foreign_key_check failed (non-fatal)");
32865
33062
  return [];
32866
33063
  }
32867
33064
  }
@@ -32898,13 +33095,13 @@ function sourceOrphanSignatures(db, sourceName, scope) {
32898
33095
  const rows = db.prepare("PRAGMA foreign_key_check").all();
32899
33096
  for (const r of rows) sigs.add(orphanSignature(db, r, sourceName));
32900
33097
  if (rows.length > 0) {
32901
- log2.warn(
33098
+ log3.warn(
32902
33099
  { scope, count: rows.length, sample: rows.slice(0, 5) },
32903
33100
  `verifyMigration: source already has ${rows.length} pre-existing FK orphan(s) \u2014 these are tolerated (carried forward losslessly, flagged for data-hygiene)`
32904
33101
  );
32905
33102
  }
32906
33103
  } catch (err) {
32907
- log2.warn({ scope, err }, "verifyMigration: source PRAGMA foreign_key_check failed (non-fatal)");
33104
+ log3.warn({ scope, err }, "verifyMigration: source PRAGMA foreign_key_check failed (non-fatal)");
32908
33105
  }
32909
33106
  return sigs;
32910
33107
  }
@@ -32916,7 +33113,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
32916
33113
  const preExistingForeignKeyViolations = [];
32917
33114
  const failureLines = [];
32918
33115
  const sourceOrphanSigs = /* @__PURE__ */ new Set();
32919
- if (!existsSync9(projectDbPath)) {
33116
+ if (!existsSync10(projectDbPath)) {
32920
33117
  return {
32921
33118
  ok: false,
32922
33119
  tables: [],
@@ -32927,7 +33124,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
32927
33124
  error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
32928
33125
  };
32929
33126
  }
32930
- if (!existsSync9(globalDbPath)) {
33127
+ if (!existsSync10(globalDbPath)) {
32931
33128
  return {
32932
33129
  ok: false,
32933
33130
  tables: [],
@@ -32942,7 +33139,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
32942
33139
  const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
32943
33140
  try {
32944
33141
  for (const src of sources) {
32945
- if (!existsSync9(src.path)) {
33142
+ if (!existsSync10(src.path)) {
32946
33143
  onProgress?.(`Skipping ${src.name} (not present)`);
32947
33144
  continue;
32948
33145
  }
@@ -33021,7 +33218,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
33021
33218
  `[${scope}] ${src.name}.${legacyTableName} \u2192 ${targetTableName}: DEFICIT \u2014 source=${srcDigest.count} rows, target=${tgtDigest.count} rows (${srcDigest.count - tgtDigest.count} missing), hashMatch=${hashMatch}`
33022
33219
  );
33023
33220
  } else if (tgtDigest.count > srcDigest.count) {
33024
- log2.warn(
33221
+ log3.warn(
33025
33222
  {
33026
33223
  scope,
33027
33224
  source: src.name,
@@ -33071,7 +33268,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
33071
33268
  }
33072
33269
  }
33073
33270
  if (preExistingForeignKeyViolations.length > 0) {
33074
- log2.warn(
33271
+ log3.warn(
33075
33272
  {
33076
33273
  count: preExistingForeignKeyViolations.length,
33077
33274
  sample: preExistingForeignKeyViolations.slice(0, 5)
@@ -33081,7 +33278,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
33081
33278
  }
33082
33279
  } catch (err) {
33083
33280
  const error = err instanceof Error ? err.message : String(err);
33084
- log2.error({ err }, "verifyMigration failed");
33281
+ log3.error({ err }, "verifyMigration failed");
33085
33282
  return {
33086
33283
  ok: false,
33087
33284
  tables,
@@ -33098,7 +33295,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
33098
33295
  if (failureLines.length > 0) {
33099
33296
  const error = `verifyMigration FAILED: ${failureLines.length} issue(s):
33100
33297
  ${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
33101
- log2.error({ failureCount: failureLines.length }, error);
33298
+ log3.error({ failureCount: failureLines.length }, error);
33102
33299
  return {
33103
33300
  ok: false,
33104
33301
  tables,
@@ -33118,7 +33315,7 @@ ${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
33118
33315
  enumDrift
33119
33316
  };
33120
33317
  }
33121
- var log2, _require, CHECK_ENUM_REGEX;
33318
+ var log3, _require, CHECK_ENUM_REGEX;
33122
33319
  var init_verify_migration = __esm({
33123
33320
  "packages/core/src/store/exodus/verify-migration.ts"() {
33124
33321
  "use strict";
@@ -33126,7 +33323,7 @@ var init_verify_migration = __esm({
33126
33323
  init_logger2();
33127
33324
  init_open_cleo_db();
33128
33325
  init_table_name_map();
33129
- log2 = getLogger("verify-migration");
33326
+ log3 = getLogger("verify-migration");
33130
33327
  _require = createRequire3(import.meta.url);
33131
33328
  CHECK_ENUM_REGEX = /CHECK\s*\(\s*"([^"]+)"\s+(?:IS\s+NULL\s+OR\s+"[^"]+"\s+)?IN\s*\(([^)]*)\)\s*\)/gi;
33132
33329
  }
@@ -33166,9 +33363,16 @@ var init_verify = __esm({
33166
33363
  var exodus_exports = {};
33167
33364
  __export(exodus_exports, {
33168
33365
  EXODUS_TARGET_SCHEMA_VERSION: () => EXODUS_TARGET_SCHEMA_VERSION,
33366
+ archiveMigratedSources: () => archiveMigratedSources,
33367
+ archiveSourceDb: () => archiveSourceDb,
33368
+ archiveStrandedResidue: () => archiveStrandedResidue,
33169
33369
  buildExodusPlan: () => buildExodusPlan,
33170
33370
  clearExodusJournal: () => clearExodusJournal,
33171
33371
  deriveStagingDirName: () => deriveStagingDirName,
33372
+ detectStrandedResidue: () => detectStrandedResidue,
33373
+ exodusArchiveDir: () => exodusArchiveDir,
33374
+ exodusMarkerPath: () => exodusMarkerPath,
33375
+ hasExodusCompleteMarker: () => hasExodusCompleteMarker,
33172
33376
  isDerivedOrInternalTable: () => isDerivedOrInternalTable,
33173
33377
  resolveConsolidatedTableName: () => resolveConsolidatedTableName,
33174
33378
  reverseLookup: () => reverseLookup,
@@ -33176,11 +33380,13 @@ __export(exodus_exports, {
33176
33380
  runExodusStatus: () => runExodusStatus,
33177
33381
  runExodusVerify: () => runExodusVerify,
33178
33382
  sourcesPresent: () => sourcesPresent,
33179
- verifyMigration: () => verifyMigration
33383
+ verifyMigration: () => verifyMigration,
33384
+ writeExodusCompleteMarker: () => writeExodusCompleteMarker
33180
33385
  });
33181
33386
  var init_exodus = __esm({
33182
33387
  "packages/core/src/store/exodus/index.ts"() {
33183
33388
  "use strict";
33389
+ init_archive2();
33184
33390
  init_migrate();
33185
33391
  init_plan2();
33186
33392
  init_status();
@@ -33198,7 +33404,7 @@ __export(on_open_exports, {
33198
33404
  isDataContinuityOk: () => isDataContinuityOk,
33199
33405
  maybeRunExodusOnOpen: () => maybeRunExodusOnOpen
33200
33406
  });
33201
- import { existsSync as existsSync10 } from "node:fs";
33407
+ import { existsSync as existsSync11 } from "node:fs";
33202
33408
  function isDisabledByEnv() {
33203
33409
  const v = process.env.CLEO_DISABLE_EXODUS_ON_OPEN;
33204
33410
  return v === "1" || v === "true";
@@ -33224,7 +33430,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
33224
33430
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%'"
33225
33431
  ).all().map((r) => r.name);
33226
33432
  } catch (err) {
33227
- log3.error({ err, scope }, "exodus-on-open: failed to enumerate tables for rollback");
33433
+ log4.error({ err, scope }, "exodus-on-open: failed to enumerate tables for rollback");
33228
33434
  return;
33229
33435
  }
33230
33436
  try {
@@ -33234,7 +33440,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
33234
33440
  try {
33235
33441
  nativeDb.exec(`DELETE FROM "${table}"`);
33236
33442
  } catch (err) {
33237
- log3.warn({ err, table, scope }, "exodus-on-open: failed to clear table during rollback");
33443
+ log4.warn({ err, table, scope }, "exodus-on-open: failed to clear table during rollback");
33238
33444
  }
33239
33445
  }
33240
33446
  nativeDb.exec("COMMIT");
@@ -33243,7 +33449,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
33243
33449
  nativeDb.exec("ROLLBACK");
33244
33450
  } catch {
33245
33451
  }
33246
- log3.error({ err, scope }, "exodus-on-open: rollback transaction failed");
33452
+ log4.error({ err, scope }, "exodus-on-open: rollback transaction failed");
33247
33453
  } finally {
33248
33454
  try {
33249
33455
  nativeDb.exec("PRAGMA foreign_keys = ON");
@@ -33251,20 +33457,27 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
33251
33457
  }
33252
33458
  }
33253
33459
  }
33254
- async function rollbackBothScopes(scope) {
33255
- const { openDualScopeDb: openDualScopeDb2 } = await Promise.resolve().then(() => (init_dual_scope_db(), dual_scope_db_exports));
33460
+ async function rollbackBothScopes(scope, projectDbPath, globalDbPath) {
33461
+ const { openDualScopeDbAtPath: openDualScopeDbAtPath2 } = await Promise.resolve().then(() => (init_dual_scope_db(), dual_scope_db_exports));
33256
33462
  for (const s of ["project", "global"]) {
33463
+ const path2 = s === "project" ? projectDbPath : globalDbPath;
33464
+ let handle = null;
33257
33465
  try {
33258
- const handle = s === "project" ? await openDualScopeDb2("project") : await openDualScopeDb2("global");
33466
+ handle = s === "project" ? await openDualScopeDbAtPath2("project", path2, void 0, { dedicated: true }) : await openDualScopeDbAtPath2("global", path2, void 0, { dedicated: true });
33259
33467
  const native = handle.db.$client;
33260
33468
  if (native) {
33261
33469
  rollbackConsolidatedToEmpty(native, s);
33262
33470
  }
33263
33471
  } catch (err) {
33264
- log3.warn(
33472
+ log4.warn(
33265
33473
  { err, scope: s, openingScope: scope },
33266
33474
  "exodus-on-open: could not roll back scope (best-effort)"
33267
33475
  );
33476
+ } finally {
33477
+ try {
33478
+ handle?.close();
33479
+ } catch {
33480
+ }
33268
33481
  }
33269
33482
  }
33270
33483
  }
@@ -33272,7 +33485,7 @@ function isDataContinuityOk(result) {
33272
33485
  const deficits = result.tables.filter((t) => t.targetCount < t.sourceCount);
33273
33486
  const surpluses = result.tables.filter((t) => t.targetCount > t.sourceCount);
33274
33487
  if (surpluses.length > 0) {
33275
- log3.warn(
33488
+ log4.warn(
33276
33489
  {
33277
33490
  surpluses: surpluses.map((t) => ({
33278
33491
  table: t.targetTable,
@@ -33294,13 +33507,19 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33294
33507
  if (isDisabledByEnv()) {
33295
33508
  return { outcome: "skipped", reason: "CLEO_DISABLE_EXODUS_ON_OPEN set" };
33296
33509
  }
33510
+ if (hasExodusCompleteMarker(scope, cwd)) {
33511
+ return {
33512
+ outcome: "skipped",
33513
+ reason: "exodus completion marker present \u2014 scope already migrated (cutover sealed)"
33514
+ };
33515
+ }
33297
33516
  if (!consolidatedIsEmpty(nativeDb, scope)) {
33298
33517
  return { outcome: "skipped", reason: "consolidated cleo.db already populated" };
33299
33518
  }
33300
33519
  const { buildExodusPlan: buildExodusPlan2, runExodusMigrate: runExodusMigrate2, verifyMigration: verifyMigration2, clearExodusJournal: clearExodusJournal2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
33301
33520
  const plan = buildExodusPlan2(cwd);
33302
33521
  const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
33303
- if (!scopeSources.some((s) => existsSync10(s.path))) {
33522
+ if (!scopeSources.some((s) => existsSync11(s.path))) {
33304
33523
  return {
33305
33524
  outcome: "skipped",
33306
33525
  reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
@@ -33313,11 +33532,11 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33313
33532
  if (!consolidatedIsEmpty(nativeDb, scope)) {
33314
33533
  return { outcome: "skipped", reason: "migrated by a concurrent process (lock winner)" };
33315
33534
  }
33316
- log3.info(
33535
+ log4.info(
33317
33536
  {
33318
33537
  scope,
33319
33538
  dbPath,
33320
- sources: plan.sources.filter((s) => existsSync10(s.path)).map((s) => s.name)
33539
+ sources: plan.sources.filter((s) => existsSync11(s.path)).map((s) => s.name)
33321
33540
  },
33322
33541
  "exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
33323
33542
  );
@@ -33326,23 +33545,23 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33326
33545
  const migrateResult = await runExodusMigrate2(
33327
33546
  plan,
33328
33547
  false,
33329
- (msg) => log3.debug({ scope }, `exodus-on-open: ${msg}`)
33548
+ (msg) => log4.debug({ scope }, `exodus-on-open: ${msg}`)
33330
33549
  );
33331
33550
  if (!migrateResult.ok) {
33332
- await rollbackBothScopes(scope);
33551
+ await rollbackBothScopes(scope, plan.projectDbPath, plan.globalDbPath);
33333
33552
  clearExodusJournal2(migrateResult.stagingDir);
33334
33553
  const reason = `migration failed: ${migrateResult.error ?? "unknown error"} \u2014 legacy DBs kept as source`;
33335
- log3.error({ scope, error: migrateResult.error }, `exodus-on-open: ${reason}`);
33554
+ log4.error({ scope, error: migrateResult.error }, `exodus-on-open: ${reason}`);
33336
33555
  return { outcome: "aborted", reason };
33337
33556
  }
33338
33557
  const verifyResult = verifyMigration2(
33339
33558
  plan.sources,
33340
33559
  plan.projectDbPath,
33341
33560
  plan.globalDbPath,
33342
- (msg) => log3.debug({ scope }, `exodus-on-open verify: ${msg}`)
33561
+ (msg) => log4.debug({ scope }, `exodus-on-open verify: ${msg}`)
33343
33562
  );
33344
33563
  if (!verifyResult.ok) {
33345
- log3.warn(
33564
+ log4.warn(
33346
33565
  {
33347
33566
  scope,
33348
33567
  enumDrift: verifyResult.enumDrift.length,
@@ -33352,11 +33571,11 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33352
33571
  );
33353
33572
  }
33354
33573
  if (!isDataContinuityOk(verifyResult)) {
33355
- await rollbackBothScopes(scope);
33574
+ await rollbackBothScopes(scope, plan.projectDbPath, plan.globalDbPath);
33356
33575
  clearExodusJournal2(plan.stagingDir);
33357
33576
  const deficits = verifyResult.tables.filter((t) => t.targetCount < t.sourceCount).map((t) => `${t.targetTable}(${t.sourceCount}\u2192${t.targetCount})`);
33358
33577
  const reason = `parity verification failed \u2014 cutover aborted, legacy DBs kept as source. count deficits: [${deficits.join(", ")}]; INTRODUCED fk orphans: ${verifyResult.introducedForeignKeyViolations.length} (pre-existing source orphans tolerated: ${verifyResult.preExistingForeignKeyViolations.length}). ` + `${verifyResult.error ?? ""}`.trim();
33359
- log3.error(
33578
+ log4.error(
33360
33579
  {
33361
33580
  scope,
33362
33581
  countDeficits: deficits,
@@ -33368,10 +33587,27 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33368
33587
  return { outcome: "aborted", reason };
33369
33588
  }
33370
33589
  const rowsCopied = migrateResult.tables.filter((t) => !t.skipped).reduce((n, t) => n + t.rowsCopied, 0);
33371
- log3.info(
33590
+ log4.info(
33372
33591
  { scope, rowsCopied, tables: migrateResult.tables.length },
33373
33592
  "exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
33374
33593
  );
33594
+ try {
33595
+ const consumed = plan.sources.filter((s) => existsSync11(s.path));
33596
+ const archiveResult = archiveMigratedSources(consumed, cwd);
33597
+ log4.info(
33598
+ {
33599
+ scope,
33600
+ archived: archiveResult.sources.filter((s) => s.action === "archived").length,
33601
+ markersWritten: archiveResult.markersWritten
33602
+ },
33603
+ "exodus-on-open: archived legacy sources + sealed completion marker(s)"
33604
+ );
33605
+ } catch (err) {
33606
+ log4.error(
33607
+ { err, scope },
33608
+ "exodus-on-open: post-migration archive/marker step failed (migration itself succeeded \u2014 legacy DBs left in place, will be re-checked by `cleo doctor exodus-residue`)"
33609
+ );
33610
+ }
33375
33611
  return {
33376
33612
  outcome: "migrated",
33377
33613
  reason: `migrated ${rowsCopied} rows across ${migrateResult.tables.length} tables; parity verified`,
@@ -33389,13 +33625,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
33389
33625
  function _isExodusInProgress() {
33390
33626
  return _exodusInProgress;
33391
33627
  }
33392
- var log3, _exodusInProgress;
33628
+ var log4, _exodusInProgress;
33393
33629
  var init_on_open = __esm({
33394
33630
  "packages/core/src/store/exodus/on-open.ts"() {
33395
33631
  "use strict";
33396
33632
  init_logger2();
33397
33633
  init_lock();
33398
- log3 = getLogger("exodus-on-open");
33634
+ init_archive2();
33635
+ log4 = getLogger("exodus-on-open");
33399
33636
  _exodusInProgress = false;
33400
33637
  }
33401
33638
  });
@@ -33410,17 +33647,17 @@ __export(dual_scope_db_exports, {
33410
33647
  resolveDualScopeDbPath: () => resolveDualScopeDbPath,
33411
33648
  upsertIdempotent: () => upsertIdempotent
33412
33649
  });
33413
- import { existsSync as existsSync11, mkdirSync as mkdirSync2 } from "node:fs";
33650
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3 } from "node:fs";
33414
33651
  import { createRequire as createRequire4 } from "node:module";
33415
- import { dirname as dirname5, join as join11 } from "node:path";
33652
+ import { dirname as dirname5, join as join12 } from "node:path";
33416
33653
  function cacheKey(scope, dbPath) {
33417
33654
  return `${scope}::${dbPath}`;
33418
33655
  }
33419
33656
  function resolveDualScopeDbPath(scope, cwd) {
33420
33657
  if (scope === "project") {
33421
- return join11(resolveCleoDir(cwd), "cleo.db");
33658
+ return join12(resolveCleoDir(cwd), "cleo.db");
33422
33659
  }
33423
- return join11(getCleoHome(), "cleo.db");
33660
+ return join12(getCleoHome(), "cleo.db");
33424
33661
  }
33425
33662
  function migrationsSetName(scope) {
33426
33663
  return scope === "project" ? "drizzle-cleo-project" : "drizzle-cleo-global";
@@ -33458,21 +33695,61 @@ async function openDualScopeDb(scope, cwd) {
33458
33695
  const dbPath = resolveDualScopeDbPath(scope, cwd);
33459
33696
  return scope === "project" ? openDualScopeDbAtPath("project", dbPath, cwd) : openDualScopeDbAtPath("global", dbPath, cwd);
33460
33697
  }
33461
- async function openDualScopeDbAtPath(scope, dbPath, exodusCwd) {
33698
+ async function openDedicatedDualScopeDb(scope, dbPath, log8) {
33699
+ log8.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
33700
+ const dir = dirname5(dbPath);
33701
+ if (!existsSync12(dir)) {
33702
+ mkdirSync3(dir, { recursive: true });
33703
+ }
33704
+ const DatabaseSyncCtor = getDatabaseSyncCtor();
33705
+ const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
33706
+ applyPerfPragmas(nativeDb);
33707
+ const schema = scope === "project" ? await loadProjectSchema() : await loadGlobalSchema();
33708
+ const drizzle2 = getDrizzle();
33709
+ const db = drizzle2({ client: nativeDb, schema });
33710
+ const migrationsFolder = resolveCorePackageMigrationsFolder(migrationsSetName(scope));
33711
+ reconcileJournal(nativeDb, migrationsFolder, existenceTable(scope), `dual-scope-db[${scope}]`);
33712
+ migrateWithRetry(
33713
+ db,
33714
+ migrationsFolder,
33715
+ nativeDb,
33716
+ existenceTable(scope),
33717
+ `dual-scope-db[${scope}]`
33718
+ );
33719
+ log8.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
33720
+ return {
33721
+ db,
33722
+ scope,
33723
+ dbPath,
33724
+ close() {
33725
+ try {
33726
+ nativeDb.close();
33727
+ } catch {
33728
+ }
33729
+ }
33730
+ };
33731
+ }
33732
+ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
33733
+ const dedicated = options?.dedicated === true;
33462
33734
  const key = cacheKey(scope, dbPath);
33463
- const existing = _cache.get(key);
33464
- if (existing) {
33465
- if (existing.initPromise) {
33466
- return existing.initPromise;
33735
+ if (!dedicated) {
33736
+ const existing = _cache.get(key);
33737
+ if (existing) {
33738
+ if (existing.initPromise) {
33739
+ return existing.initPromise;
33740
+ }
33741
+ return existing.handle;
33467
33742
  }
33468
- return existing.handle;
33469
33743
  }
33470
- const log7 = getLogger("dual-scope-db");
33744
+ const log8 = getLogger("dual-scope-db");
33745
+ if (dedicated) {
33746
+ return openDedicatedDualScopeDb(scope, dbPath, log8);
33747
+ }
33471
33748
  const initPromise = (async () => {
33472
- log7.debug({ scope, dbPath }, "opening dual-scope cleo.db");
33749
+ log8.debug({ scope, dbPath }, "opening dual-scope cleo.db");
33473
33750
  const dir = dirname5(dbPath);
33474
- if (!existsSync11(dir)) {
33475
- mkdirSync2(dir, { recursive: true });
33751
+ if (!existsSync12(dir)) {
33752
+ mkdirSync3(dir, { recursive: true });
33476
33753
  }
33477
33754
  const DatabaseSyncCtor = getDatabaseSyncCtor();
33478
33755
  const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
@@ -33489,7 +33766,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd) {
33489
33766
  existenceTable(scope),
33490
33767
  `dual-scope-db[${scope}]`
33491
33768
  );
33492
- log7.debug({ scope, dbPath }, "dual-scope cleo.db ready");
33769
+ log8.debug({ scope, dbPath }, "dual-scope cleo.db ready");
33493
33770
  const handle = {
33494
33771
  db,
33495
33772
  scope,
@@ -33513,7 +33790,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd) {
33513
33790
  const result = await maybeRunExodusOnOpen2(scope, dbPath, nativeDb, exodusCwd);
33514
33791
  if (result.outcome === "migrated" || result.outcome === "aborted") {
33515
33792
  if (result.outcome === "aborted") {
33516
- log7.warn(
33793
+ log8.warn(
33517
33794
  { scope, reason: result.reason },
33518
33795
  "exodus-on-open aborted; consolidated cleo.db left empty, legacy kept as source"
33519
33796
  );
@@ -33521,7 +33798,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd) {
33521
33798
  return scope === "project" ? openDualScopeDbAtPath("project", dbPath) : openDualScopeDbAtPath("global", dbPath);
33522
33799
  }
33523
33800
  } catch (err) {
33524
- log7.warn(
33801
+ log8.warn(
33525
33802
  { err, scope },
33526
33803
  "exodus-on-open hook failed (non-fatal); re-opening consolidated handle"
33527
33804
  );
@@ -34105,8 +34382,8 @@ __export(nexus_sqlite_exports, {
34105
34382
  resetNexusDbState: () => resetNexusDbState,
34106
34383
  resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
34107
34384
  });
34108
- import { copyFileSync as copyFileSync3, existsSync as existsSync12 } from "node:fs";
34109
- import { join as join12 } from "node:path";
34385
+ import { copyFileSync as copyFileSync4, existsSync as existsSync13 } from "node:fs";
34386
+ import { join as join13 } from "node:path";
34110
34387
  function getNexusDbPath(cwd) {
34111
34388
  return resolveDualScopeDbPath("project", cwd);
34112
34389
  }
@@ -34138,7 +34415,7 @@ function resolveNexusMigrationsFolder() {
34138
34415
  return resolveCorePackageMigrationsFolder("drizzle-nexus");
34139
34416
  }
34140
34417
  function getNestedNexusSentinelPath() {
34141
- return join12(getCleoHome(), "nexus", NESTED_NEXUS_SENTINEL);
34418
+ return join13(getCleoHome(), "nexus", NESTED_NEXUS_SENTINEL);
34142
34419
  }
34143
34420
  function detectAndWarnOnNestedNexus() {
34144
34421
  let nestedPath;
@@ -34147,12 +34424,12 @@ function detectAndWarnOnNestedNexus() {
34147
34424
  } catch {
34148
34425
  return false;
34149
34426
  }
34150
- if (!existsSync12(nestedPath)) return false;
34427
+ if (!existsSync13(nestedPath)) return false;
34151
34428
  if (_warnedNestedPaths.has(nestedPath)) return false;
34152
34429
  _warnedNestedPaths.add(nestedPath);
34153
34430
  const canonicalPath = getNexusDbPath();
34154
- const log7 = getLogger("nexus-sqlite");
34155
- log7.warn(
34431
+ const log8 = getLogger("nexus-sqlite");
34432
+ log8.warn(
34156
34433
  {
34157
34434
  nestedPath,
34158
34435
  canonicalPath,
@@ -34306,9 +34583,9 @@ function runNexusMigrations(nativeDb, db) {
34306
34583
  const migrationsFolder = resolveNexusMigrationsFolder();
34307
34584
  if (tableExists3(nativeDb, "nexus_nodes") && _nexusDbPath) {
34308
34585
  const backupPath = _nexusDbPath.replace(/\.db$/, "-pre-cleo.db.bak");
34309
- if (!existsSync12(backupPath)) {
34586
+ if (!existsSync13(backupPath)) {
34310
34587
  try {
34311
- copyFileSync3(_nexusDbPath, backupPath);
34588
+ copyFileSync4(_nexusDbPath, backupPath);
34312
34589
  } catch {
34313
34590
  }
34314
34591
  }
@@ -34438,10 +34715,10 @@ var init_nexus_sqlite = __esm({
34438
34715
 
34439
34716
  // packages/core/src/paths.ts
34440
34717
  import { AsyncLocalStorage } from "node:async_hooks";
34441
- import { existsSync as existsSync13, readFileSync as readFileSync5, statSync as statSync4 } from "node:fs";
34718
+ import { existsSync as existsSync14, readFileSync as readFileSync5, statSync as statSync4 } from "node:fs";
34442
34719
  import { createRequire as createRequire5 } from "node:module";
34443
34720
  import { homedir } from "node:os";
34444
- import { basename as basename3, dirname as dirname6, join as join13, resolve as resolve3 } from "node:path";
34721
+ import { basename as basename4, dirname as dirname6, join as join14, resolve as resolve3 } from "node:path";
34445
34722
  import {
34446
34723
  computeCanonicalProjectId as _computeCanonicalProjectId,
34447
34724
  getCanonicalTemplatesTildePath as _getCanonicalTemplatesTildePath,
@@ -34531,8 +34808,8 @@ function getCleoDirAbsolute(cwd, opts) {
34531
34808
  function _resolveProjectByCwdFromNexus(cwd) {
34532
34809
  try {
34533
34810
  const cleoHome = getCleoHome();
34534
- const globalDbPath = join13(cleoHome, "cleo.db");
34535
- if (!existsSync13(globalDbPath)) return null;
34811
+ const globalDbPath = join14(cleoHome, "cleo.db");
34812
+ if (!existsSync14(globalDbPath)) return null;
34536
34813
  const start = resolve3(cwd ?? process.cwd());
34537
34814
  let current = start;
34538
34815
  const DatabaseSync3 = _getDatabaseSyncCtor();
@@ -34571,7 +34848,7 @@ function _findCleoDirRoot(cwd) {
34571
34848
  const isDangerousRoot = current === homeRoot || current === "/" || current === "";
34572
34849
  if (!isDangerousRoot) {
34573
34850
  try {
34574
- if (statSync4(join13(current, ".cleo")).isDirectory()) {
34851
+ if (statSync4(join14(current, ".cleo")).isDirectory()) {
34575
34852
  return current;
34576
34853
  }
34577
34854
  } catch {
@@ -34586,7 +34863,7 @@ function _findCleoDirRoot(cwd) {
34586
34863
  function resolveCleoDir(cwd) {
34587
34864
  const scope = worktreeScope.getStore();
34588
34865
  if (scope !== void 0) {
34589
- return join13(scope.worktreeRoot, ".cleo");
34866
+ return join14(scope.worktreeRoot, ".cleo");
34590
34867
  }
34591
34868
  const override = _cleoDirEnvOverride();
34592
34869
  if (override !== null) {
@@ -34594,14 +34871,14 @@ function resolveCleoDir(cwd) {
34594
34871
  }
34595
34872
  const root = _findCleoDirRoot(cwd);
34596
34873
  if (root !== null) {
34597
- return join13(root, ".cleo");
34874
+ return join14(root, ".cleo");
34598
34875
  }
34599
34876
  const start = resolve3(cwd ?? process.cwd());
34600
34877
  let current = start;
34601
34878
  while (true) {
34602
34879
  const mainRepo = _resolveMainRepoFromGitlink(current);
34603
34880
  if (mainRepo !== null) {
34604
- return join13(mainRepo, ".cleo");
34881
+ return join14(mainRepo, ".cleo");
34605
34882
  }
34606
34883
  const parent = dirname6(current);
34607
34884
  if (parent === current) {
@@ -34643,9 +34920,9 @@ function _cwdHasGitAncestor(cwd) {
34643
34920
  const start = resolve3(cwd ?? process.cwd());
34644
34921
  let current = start;
34645
34922
  while (true) {
34646
- const gitMarker = join13(current, ".git");
34923
+ const gitMarker = join14(current, ".git");
34647
34924
  try {
34648
- if (existsSync13(gitMarker)) {
34925
+ if (existsSync14(gitMarker)) {
34649
34926
  return true;
34650
34927
  }
34651
34928
  } catch {
@@ -34657,8 +34934,8 @@ function _cwdHasGitAncestor(cwd) {
34657
34934
  }
34658
34935
  function _resolveMainRepoFromGitlink(gitlinkDir) {
34659
34936
  try {
34660
- const gitLinkPath = join13(gitlinkDir, ".git");
34661
- if (!existsSync13(gitLinkPath)) return null;
34937
+ const gitLinkPath = join14(gitlinkDir, ".git");
34938
+ if (!existsSync14(gitLinkPath)) return null;
34662
34939
  const stat2 = statSync4(gitLinkPath);
34663
34940
  if (!stat2.isFile()) return null;
34664
34941
  const gitLinkContent = readFileSync5(gitLinkPath, "utf-8").trim();
@@ -34666,7 +34943,7 @@ function _resolveMainRepoFromGitlink(gitlinkDir) {
34666
34943
  if (!match) return null;
34667
34944
  const gitdir = match[1].trim();
34668
34945
  const mainRepo = dirname6(dirname6(dirname6(gitdir)));
34669
- if (existsSync13(join13(mainRepo, ".cleo")) && validateProjectRoot(mainRepo)) {
34946
+ if (existsSync14(join14(mainRepo, ".cleo")) && validateProjectRoot(mainRepo)) {
34670
34947
  return mainRepo;
34671
34948
  }
34672
34949
  } catch {
@@ -34674,18 +34951,18 @@ function _resolveMainRepoFromGitlink(gitlinkDir) {
34674
34951
  return null;
34675
34952
  }
34676
34953
  function validateProjectRoot(candidate) {
34677
- const cleoDir = join13(candidate, ".cleo");
34678
- if (!existsSync13(cleoDir)) {
34954
+ const cleoDir = join14(candidate, ".cleo");
34955
+ if (!existsSync14(cleoDir)) {
34679
34956
  return false;
34680
34957
  }
34681
- const projectInfoPath = join13(cleoDir, "project-info.json");
34682
- if (existsSync13(projectInfoPath)) {
34958
+ const projectInfoPath = join14(cleoDir, "project-info.json");
34959
+ if (existsSync14(projectInfoPath)) {
34683
34960
  try {
34684
34961
  const raw = readFileSync5(projectInfoPath, "utf-8");
34685
34962
  const parsed = JSON.parse(raw);
34686
34963
  if (typeof parsed === "object" && parsed !== null && "projectId" in parsed && typeof parsed["projectId"] === "string" && parsed["projectId"] !== "") {
34687
- const gitMarker = join13(candidate, ".git");
34688
- if (existsSync13(gitMarker)) {
34964
+ const gitMarker = join14(candidate, ".git");
34965
+ if (existsSync14(gitMarker)) {
34689
34966
  try {
34690
34967
  if (!statSync4(gitMarker).isDirectory()) {
34691
34968
  return false;
@@ -34699,8 +34976,8 @@ function validateProjectRoot(candidate) {
34699
34976
  } catch {
34700
34977
  }
34701
34978
  }
34702
- const gitDir = join13(candidate, ".git");
34703
- if (existsSync13(gitDir)) {
34979
+ const gitDir = join14(candidate, ".git");
34980
+ if (existsSync14(gitDir)) {
34704
34981
  let isRealGitDir = false;
34705
34982
  try {
34706
34983
  const stat2 = statSync4(gitDir);
@@ -34747,16 +35024,16 @@ function getProjectRoot(cwd) {
34747
35024
  const homeRoot = homedir();
34748
35025
  const skippedCleoDirs = [];
34749
35026
  while (true) {
34750
- const cleoDir = join13(current, ".cleo");
34751
- const gitDir = join13(current, ".git");
35027
+ const cleoDir = join14(current, ".cleo");
35028
+ const gitDir = join14(current, ".git");
34752
35029
  const isDangerousRoot = current === homeRoot || current === "/" || current === "";
34753
- if (existsSync13(cleoDir) && !isDangerousRoot) {
35030
+ if (existsSync14(cleoDir) && !isDangerousRoot) {
34754
35031
  if (validateProjectRoot(current)) {
34755
35032
  return current;
34756
35033
  }
34757
35034
  skippedCleoDirs.push(current);
34758
35035
  }
34759
- if (existsSync13(gitDir) && !isDangerousRoot) {
35036
+ if (existsSync14(gitDir) && !isDangerousRoot) {
34760
35037
  let isRealGitDir = false;
34761
35038
  try {
34762
35039
  isRealGitDir = statSync4(gitDir).isDirectory();
@@ -34801,18 +35078,18 @@ function resolveOrCwd(maybeRoot) {
34801
35078
  return getProjectRoot();
34802
35079
  }
34803
35080
  function getConfigPath(cwd) {
34804
- return join13(_resolveCleoDir(cwd), "config.json");
35081
+ return join14(_resolveCleoDir(cwd), "config.json");
34805
35082
  }
34806
35083
  function getGlobalConfigPath() {
34807
- return join13(getCleoHome(), "config.json");
35084
+ return join14(getCleoHome(), "config.json");
34808
35085
  }
34809
35086
  function isAbsolutePath(path2) {
34810
35087
  return _isAbsolutePath(path2);
34811
35088
  }
34812
35089
  function _readProjectNameFromInfo(projectRoot) {
34813
35090
  try {
34814
- const infoPath = join13(projectRoot, ".cleo", "project-info.json");
34815
- if (!existsSync13(infoPath)) return void 0;
35091
+ const infoPath = join14(projectRoot, ".cleo", "project-info.json");
35092
+ if (!existsSync14(infoPath)) return void 0;
34816
35093
  const raw = readFileSync5(infoPath, "utf-8");
34817
35094
  const data = JSON.parse(raw);
34818
35095
  return typeof data.name === "string" && data.name.length > 0 ? data.name : void 0;
@@ -34834,12 +35111,12 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
34834
35111
  const existingRows = await db.select().from(projectRegistry2).where(eq10(projectRegistry2.projectId, immutableId)).limit(1);
34835
35112
  const now = (/* @__PURE__ */ new Date()).toISOString();
34836
35113
  const resolvedPath = resolve3(projectRoot);
34837
- const projectName = _readProjectNameFromInfo(resolvedPath) || basename3(projectRoot) || "unnamed";
35114
+ const projectName = _readProjectNameFromInfo(resolvedPath) || basename4(projectRoot) || "unnamed";
34838
35115
  if (existingRows.length > 0) {
34839
35116
  const existingPath = existingRows[0].projectPath;
34840
35117
  if (existingPath !== resolvedPath) {
34841
- const newBrainDbPath = join13(resolvedPath, ".cleo", "brain.db");
34842
- const newTasksDbPath = join13(resolvedPath, ".cleo", "tasks.db");
35118
+ const newBrainDbPath = join14(resolvedPath, ".cleo", "brain.db");
35119
+ const newTasksDbPath = join14(resolvedPath, ".cleo", "tasks.db");
34843
35120
  await db.update(projectRegistry2).set({
34844
35121
  projectPath: resolvedPath,
34845
35122
  projectHash: generateProjectHash2(resolvedPath),
@@ -34870,8 +35147,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
34870
35147
  lastSync: now,
34871
35148
  taskCount: 0,
34872
35149
  labelsJson: "[]",
34873
- brainDbPath: join13(resolvedPath, ".cleo", "brain.db"),
34874
- tasksDbPath: join13(resolvedPath, ".cleo", "tasks.db"),
35150
+ brainDbPath: join14(resolvedPath, ".cleo", "brain.db"),
35151
+ tasksDbPath: join14(resolvedPath, ".cleo", "tasks.db"),
34875
35152
  statsJson: "{}"
34876
35153
  }).onConflictDoNothing();
34877
35154
  const aliases = canonicalResult.legacyAliases ?? [];
@@ -35086,7 +35363,7 @@ __export(config_exports, {
35086
35363
  parseConfigValue: () => parseConfigValue,
35087
35364
  setConfigValue: () => setConfigValue
35088
35365
  });
35089
- import { existsSync as existsSync14 } from "node:fs";
35366
+ import { existsSync as existsSync15 } from "node:fs";
35090
35367
  import { mkdir as mkdir3, writeFile } from "node:fs/promises";
35091
35368
  import { dirname as dirname7 } from "node:path";
35092
35369
  function getNestedValue(obj, path2) {
@@ -35210,7 +35487,7 @@ function parseConfigValue(value) {
35210
35487
  }
35211
35488
  async function setConfigValue(key, value, cwd, opts) {
35212
35489
  const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
35213
- if (!existsSync14(configPath)) {
35490
+ if (!existsSync15(configPath)) {
35214
35491
  const dir = dirname7(configPath);
35215
35492
  await mkdir3(dir, { recursive: true });
35216
35493
  await writeFile(configPath, "{}", "utf-8");
@@ -35224,7 +35501,7 @@ async function setConfigValue(key, value, cwd, opts) {
35224
35501
  async function applyStrictnessPreset(preset, cwd, opts) {
35225
35502
  const definition = STRICTNESS_PRESETS[preset];
35226
35503
  const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
35227
- if (!existsSync14(configPath)) {
35504
+ if (!existsSync15(configPath)) {
35228
35505
  const dir = dirname7(configPath);
35229
35506
  await mkdir3(dir, { recursive: true });
35230
35507
  await writeFile(configPath, "{}", "utf-8");
@@ -35570,7 +35847,7 @@ function brainTablesAreConsolidatedShape(nativeDb) {
35570
35847
  return createdAt !== void 0 && createdAt.type.toUpperCase() !== "INTEGER";
35571
35848
  }
35572
35849
  function establishLegacyBrainSchema(nativeDb, db) {
35573
- const log7 = getLogger("brain-schema");
35850
+ const log8 = getLogger("brain-schema");
35574
35851
  if (brainTablesAreConsolidatedShape(nativeDb)) {
35575
35852
  const fkRow = nativeDb.prepare("PRAGMA foreign_keys").get();
35576
35853
  const fkWasOn = fkRow?.foreign_keys === 1;
@@ -35580,7 +35857,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
35580
35857
  try {
35581
35858
  nativeDb.exec(`DROP TABLE IF EXISTS \`${table}\``);
35582
35859
  } catch (err) {
35583
- log7.warn(
35860
+ log8.warn(
35584
35861
  { table, err },
35585
35862
  "Failed to drop consolidated brain table during legacy rebuild."
35586
35863
  );
@@ -35589,7 +35866,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
35589
35866
  } finally {
35590
35867
  nativeDb.exec(`PRAGMA foreign_keys=${fkWasOn ? "ON" : "OFF"}`);
35591
35868
  }
35592
- log7.debug(
35869
+ log8.debug(
35593
35870
  { count: CONSOLIDATED_BRAIN_TABLES.length },
35594
35871
  "Dropped consolidated (exodus-target) brain tables \u2014 rebuilding in legacy runtime shape."
35595
35872
  );
@@ -35600,7 +35877,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
35600
35877
  nativeDb,
35601
35878
  migrationsFolder
35602
35879
  );
35603
- log7.debug(
35880
+ log8.debug(
35604
35881
  { marked, applied },
35605
35882
  "brain consolidated cleo.db reconcile (T11647) \u2014 marked already-present migrations applied + executed the missing unprefixed-table migrations directly."
35606
35883
  );
@@ -36150,7 +36427,7 @@ async function upsertTask(db, row, archiveFields, allowOrphanParent = false) {
36150
36427
  if (allowOrphanParent) {
36151
36428
  row = { ...row, parentId: null };
36152
36429
  } else {
36153
- log4.warn(
36430
+ log5.warn(
36154
36431
  { taskId: row.id, parentId: row.parentId },
36155
36432
  "upsertTask: parentId references a non-existent task \u2014 parent relationship may be lost"
36156
36433
  );
@@ -36333,13 +36610,13 @@ async function loadRelationsForTasks(db, tasks2) {
36333
36610
  task.relates = relations && relations.length > 0 ? relations : [];
36334
36611
  }
36335
36612
  }
36336
- var log4;
36613
+ var log5;
36337
36614
  var init_db_helpers = __esm({
36338
36615
  "packages/core/src/store/db-helpers.ts"() {
36339
36616
  "use strict";
36340
36617
  init_logger2();
36341
36618
  init_tasks_schema();
36342
- log4 = getLogger("db-helpers");
36619
+ log5 = getLogger("db-helpers");
36343
36620
  }
36344
36621
  });
36345
36622
 
@@ -36397,10 +36674,10 @@ var init_telemetry_schema = __esm({
36397
36674
  });
36398
36675
 
36399
36676
  // packages/core/src/telemetry/sqlite.ts
36400
- import { mkdirSync as mkdirSync3 } from "node:fs";
36401
- import { dirname as dirname8, join as join14 } from "node:path";
36677
+ import { mkdirSync as mkdirSync4 } from "node:fs";
36678
+ import { dirname as dirname8, join as join15 } from "node:path";
36402
36679
  function getTelemetryDbPath() {
36403
- return join14(getCleoHome(), DB_FILENAME);
36680
+ return join15(getCleoHome(), DB_FILENAME);
36404
36681
  }
36405
36682
  function resolveTelemetryMigrationsFolder() {
36406
36683
  return resolveCorePackageMigrationsFolder("drizzle-telemetry");
@@ -36448,7 +36725,7 @@ async function getTelemetryDb() {
36448
36725
  _initPromise2 = (async () => {
36449
36726
  const dbPath = requestedPath;
36450
36727
  _dbPath2 = dbPath;
36451
- mkdirSync3(dirname8(dbPath), { recursive: true });
36728
+ mkdirSync4(dirname8(dbPath), { recursive: true });
36452
36729
  const nativeDb = openNativeDatabase(dbPath);
36453
36730
  _nativeDb2 = nativeDb;
36454
36731
  const db = drizzle({ client: nativeDb, schema: telemetry_schema_exports });
@@ -36481,11 +36758,11 @@ var init_sqlite2 = __esm({
36481
36758
  });
36482
36759
 
36483
36760
  // packages/core/src/store/agent-registry-store.ts
36484
- import { existsSync as existsSync15 } from "node:fs";
36485
- import { join as join15 } from "node:path";
36761
+ import { existsSync as existsSync16 } from "node:fs";
36762
+ import { join as join16 } from "node:path";
36486
36763
  function getGlobalAgentRegistryDbPath() {
36487
36764
  const cleoHome = getCleoHome();
36488
- const dbPath = join15(cleoHome, "cleo.db");
36765
+ const dbPath = join16(cleoHome, "cleo.db");
36489
36766
  if (!dbPath.startsWith(cleoHome)) {
36490
36767
  throw new Error(
36491
36768
  `BUG: getGlobalAgentRegistryDbPath() resolved to "${dbPath}" which is NOT under getCleoHome() ("${cleoHome}"). The Agent Registry is global-only per ADR-037. This indicates a code path that bypasses path resolution \u2014 fix the caller, do not suppress this error.`
@@ -36526,7 +36803,7 @@ function writeAgentRegistrySchemaVersionSentinel(db) {
36526
36803
  }
36527
36804
  async function ensureGlobalAgentRegistryDb() {
36528
36805
  const dbPath = getGlobalAgentRegistryDbPath();
36529
- const alreadyExists = existsSync15(dbPath);
36806
+ const alreadyExists = existsSync16(dbPath);
36530
36807
  const dualHandle = await openDualScopeDb("global");
36531
36808
  const nativeDb = dualHandle.db.$client ?? null;
36532
36809
  if (!nativeDb) {
@@ -36575,7 +36852,7 @@ var init_agent_registry_store = __esm({
36575
36852
  });
36576
36853
 
36577
36854
  // packages/core/src/store/conduit-sqlite.ts
36578
- import { existsSync as existsSync16 } from "node:fs";
36855
+ import { existsSync as existsSync17 } from "node:fs";
36579
36856
  import { createRequire as createRequire7 } from "node:module";
36580
36857
  function _getDrizzle2() {
36581
36858
  if (_drizzle3 === null) {
@@ -36606,7 +36883,7 @@ async function ensureConduitDb(projectRoot) {
36606
36883
  }
36607
36884
  if (_initPromise3) return _initPromise3;
36608
36885
  _initPromise3 = (async () => {
36609
- const alreadyExists = existsSync16(dbPath);
36886
+ const alreadyExists = existsSync17(dbPath);
36610
36887
  const dualHandle = await openDualScopeDb("project", projectRoot);
36611
36888
  const nativeDb = dualHandle.db.$client ?? null;
36612
36889
  if (!nativeDb) {
@@ -36826,11 +37103,11 @@ var init_skills_schema = __esm({
36826
37103
  });
36827
37104
 
36828
37105
  // packages/core/src/store/skills-db.ts
36829
- import { join as join16 } from "node:path";
37106
+ import { join as join17 } from "node:path";
36830
37107
  import { and as and3, eq as eq4 } from "drizzle-orm";
36831
37108
  function getDefaultSkillsDbPath() {
36832
37109
  const cleoHome = getCleoHome();
36833
- const dbPath = join16(cleoHome, "cleo.db");
37110
+ const dbPath = join17(cleoHome, "cleo.db");
36834
37111
  if (!dbPath.startsWith(cleoHome)) {
36835
37112
  throw new Error(
36836
37113
  `BUG: getDefaultSkillsDbPath() resolved to "${dbPath}" which is NOT under getCleoHome() ("${cleoHome}"). The skills registry is global-only per SG-CLEO-SKILLS-architecture-v3.md \xA74. Fix the caller, do not suppress.`
@@ -36899,14 +37176,14 @@ var init_skills_db = __esm({
36899
37176
  // packages/core/src/store/sqlite-backup.ts
36900
37177
  import {
36901
37178
  chmodSync,
36902
- copyFileSync as copyFileSync4,
36903
- existsSync as existsSync17,
36904
- mkdirSync as mkdirSync4,
37179
+ copyFileSync as copyFileSync5,
37180
+ existsSync as existsSync18,
37181
+ mkdirSync as mkdirSync5,
36905
37182
  readdirSync as readdirSync4,
36906
37183
  statSync as statSync5,
36907
- unlinkSync as unlinkSync2
37184
+ unlinkSync as unlinkSync3
36908
37185
  } from "node:fs";
36909
- import { join as join17 } from "node:path";
37186
+ import { join as join18 } from "node:path";
36910
37187
  function resolveInventoryPath(entry, cwd) {
36911
37188
  try {
36912
37189
  if (entry.tier === "global") {
@@ -36941,8 +37218,8 @@ async function openAgentRegistryDbForSnapshot() {
36941
37218
  }
36942
37219
  async function openTelemetryDbForSnapshot() {
36943
37220
  try {
36944
- const path2 = join17(getCleoHome(), "telemetry.db");
36945
- if (!existsSync17(path2)) return null;
37221
+ const path2 = join18(getCleoHome(), "telemetry.db");
37222
+ if (!existsSync18(path2)) return null;
36946
37223
  } catch {
36947
37224
  return null;
36948
37225
  }
@@ -36957,7 +37234,7 @@ function buildRawFileVacuumOpener(entry) {
36957
37234
  return async (cwd) => {
36958
37235
  const path2 = resolveInventoryPath(entry, cwd);
36959
37236
  if (!path2) return null;
36960
- if (!existsSync17(path2)) return null;
37237
+ if (!existsSync18(path2)) return null;
36961
37238
  try {
36962
37239
  const { DatabaseSync: DatabaseSync3 } = await import("node:sqlite");
36963
37240
  return new DatabaseSync3(path2, { readOnly: true });
@@ -37047,13 +37324,13 @@ function rotateSnapshots(backupDir, prefix) {
37047
37324
  const pattern = snapshotPattern(prefix);
37048
37325
  const files = readdirSync4(backupDir).filter((f) => pattern.test(f)).map((f) => ({
37049
37326
  name: f,
37050
- path: join17(backupDir, f),
37051
- mtimeMs: statSync5(join17(backupDir, f)).mtimeMs
37327
+ path: join18(backupDir, f),
37328
+ mtimeMs: statSync5(join18(backupDir, f)).mtimeMs
37052
37329
  })).sort((a, b) => a.mtimeMs - b.mtimeMs);
37053
37330
  while (files.length >= MAX_SNAPSHOTS) {
37054
37331
  const oldest = files.shift();
37055
37332
  if (!oldest) break;
37056
- unlinkSync2(oldest.path);
37333
+ unlinkSync3(oldest.path);
37057
37334
  }
37058
37335
  } catch {
37059
37336
  }
@@ -37073,7 +37350,7 @@ async function snapshotOne(target, backupDir, now, cwd) {
37073
37350
  if (!db) return;
37074
37351
  opened = db;
37075
37352
  }
37076
- const dest = join17(backupDir, `${target.prefix}-${formatTimestamp(now)}.db`);
37353
+ const dest = join18(backupDir, `${target.prefix}-${formatTimestamp(now)}.db`);
37077
37354
  try {
37078
37355
  db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
37079
37356
  rotateSnapshots(backupDir, target.prefix);
@@ -37094,8 +37371,8 @@ async function vacuumIntoBackup(opts = {}) {
37094
37371
  }
37095
37372
  try {
37096
37373
  const cleoDir = getCleoDir(opts.cwd);
37097
- const backupDir = join17(cleoDir, "backups", "sqlite");
37098
- mkdirSync4(backupDir, { recursive: true });
37374
+ const backupDir = join18(cleoDir, "backups", "sqlite");
37375
+ mkdirSync5(backupDir, { recursive: true });
37099
37376
  const target = SNAPSHOT_TARGETS.find((t) => t.prefix === prefix);
37100
37377
  if (!target) return;
37101
37378
  await snapshotOne(target, backupDir, /* @__PURE__ */ new Date(), opts.cwd);
@@ -37106,13 +37383,13 @@ async function vacuumIntoBackup(opts = {}) {
37106
37383
  function listSqliteBackupsForPrefix(prefix, cwd) {
37107
37384
  try {
37108
37385
  const cleoDir = getCleoDir(cwd);
37109
- const backupDir = join17(cleoDir, "backups", "sqlite");
37110
- if (!existsSync17(backupDir)) return [];
37386
+ const backupDir = join18(cleoDir, "backups", "sqlite");
37387
+ if (!existsSync18(backupDir)) return [];
37111
37388
  const pattern = snapshotPattern(prefix);
37112
37389
  return readdirSync4(backupDir).filter((f) => pattern.test(f)).map((f) => ({
37113
37390
  name: f,
37114
- path: join17(backupDir, f),
37115
- mtimeMs: statSync5(join17(backupDir, f)).mtimeMs
37391
+ path: join18(backupDir, f),
37392
+ mtimeMs: statSync5(join18(backupDir, f)).mtimeMs
37116
37393
  })).sort((a, b) => b.mtimeMs - a.mtimeMs);
37117
37394
  } catch {
37118
37395
  return [];
@@ -37168,7 +37445,7 @@ __export(sqlite_native_exports, {
37168
37445
  import { realpathSync } from "node:fs";
37169
37446
  import { createRequire as createRequire8 } from "node:module";
37170
37447
  import { tmpdir } from "node:os";
37171
- import { delimiter, dirname as dirname9, isAbsolute, join as join18, resolve as resolve4, sep } from "node:path";
37448
+ import { delimiter, dirname as dirname9, isAbsolute, join as join19, resolve as resolve4, sep } from "node:path";
37172
37449
  function getDbSyncConstructor() {
37173
37450
  if (_ctor === null) {
37174
37451
  const mod = _require5("node:sqlite");
@@ -37189,7 +37466,7 @@ function resolveAbsoluteSafe(p) {
37189
37466
  missingParts.unshift(cursor.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)));
37190
37467
  cursor = parent;
37191
37468
  try {
37192
- return join18(realpathSync(cursor), ...missingParts);
37469
+ return join19(realpathSync(cursor), ...missingParts);
37193
37470
  } catch {
37194
37471
  }
37195
37472
  }
@@ -37280,6 +37557,7 @@ var init_sqlite_native = __esm({
37280
37557
  var sqlite_exports = {};
37281
37558
  __export(sqlite_exports, {
37282
37559
  SQLITE_SCHEMA_VERSION: () => SQLITE_SCHEMA_VERSION,
37560
+ autoRecoverFromBackup: () => autoRecoverFromBackup,
37283
37561
  closeAllDatabases: () => closeAllDatabases,
37284
37562
  closeDb: () => closeDb,
37285
37563
  dbExists: () => dbExists,
@@ -37294,7 +37572,7 @@ __export(sqlite_exports, {
37294
37572
  resolveMigrationsFolder: () => resolveMigrationsFolder,
37295
37573
  schema: () => tasks_schema_exports
37296
37574
  });
37297
- import { copyFileSync as copyFileSync5, existsSync as existsSync18, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "node:fs";
37575
+ import { copyFileSync as copyFileSync6, existsSync as existsSync19, renameSync as renameSync3, unlinkSync as unlinkSync4 } from "node:fs";
37298
37576
  import { createRequire as createRequire9 } from "node:module";
37299
37577
  import { eq as eq5 } from "drizzle-orm";
37300
37578
  function _getDrizzle3() {
@@ -37308,6 +37586,22 @@ function _getDrizzle3() {
37308
37586
  function getDbPath(cwd) {
37309
37587
  return resolveDualScopeDbPath("project", cwd);
37310
37588
  }
37589
+ async function recountTasksFromDisk(dbPath) {
37590
+ const { openNativeDatabase: openNativeDatabase2 } = await Promise.resolve().then(() => (init_sqlite_native(), sqlite_native_exports));
37591
+ let probe = null;
37592
+ try {
37593
+ probe = openNativeDatabase2(dbPath, { readonly: true, enableWal: false });
37594
+ const row = probe.prepare("SELECT COUNT(*) as cnt FROM tasks_tasks").get();
37595
+ return row?.cnt ?? 0;
37596
+ } catch {
37597
+ return 0;
37598
+ } finally {
37599
+ try {
37600
+ probe?.close();
37601
+ } catch {
37602
+ }
37603
+ }
37604
+ }
37311
37605
  function countBackupTasks(backupDb) {
37312
37606
  for (const table of ["tasks_tasks", "tasks"]) {
37313
37607
  try {
@@ -37320,7 +37614,7 @@ function countBackupTasks(backupDb) {
37320
37614
  }
37321
37615
  async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
37322
37616
  const { openNativeDatabase: openNativeDatabase2 } = await Promise.resolve().then(() => (init_sqlite_native(), sqlite_native_exports));
37323
- const log7 = getLogger("sqlite");
37617
+ const log8 = getLogger("sqlite");
37324
37618
  try {
37325
37619
  const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks_tasks").get();
37326
37620
  const taskCount = countResult?.cnt ?? 0;
@@ -37340,36 +37634,56 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
37340
37634
  if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
37341
37635
  return;
37342
37636
  }
37343
- log7.warn(
37344
- { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
37345
- `Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
37346
- );
37347
- nativeDb.close();
37348
- const walPath = dbPath + "-wal";
37349
- const shmPath = dbPath + "-shm";
37350
- try {
37351
- unlinkSync3(walPath);
37352
- } catch {
37353
- }
37354
- try {
37355
- unlinkSync3(shmPath);
37356
- } catch {
37357
- }
37358
- const tempPath = dbPath + ".recovery-tmp";
37359
- copyFileSync5(newestBackup.path, tempPath);
37360
- renameSync2(tempPath, dbPath);
37361
- log7.info(
37362
- { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
37363
- "Database auto-recovered from backup successfully."
37637
+ const lockPath2 = dbPath + FIRST_OPEN_LOCK_SUFFIX;
37638
+ await withLock(
37639
+ lockPath2,
37640
+ async () => {
37641
+ const currentTaskCount = await recountTasksFromDisk(dbPath);
37642
+ if (currentTaskCount > 0) {
37643
+ log8.info(
37644
+ { dbPath, currentTaskCount },
37645
+ "Auto-recovery skipped: database was populated by a concurrent process while acquiring the first-open lock (T11662 double-checked re-query)."
37646
+ );
37647
+ return;
37648
+ }
37649
+ log8.warn(
37650
+ { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
37651
+ `Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
37652
+ );
37653
+ if (nativeDb.isOpen) {
37654
+ nativeDb.close();
37655
+ }
37656
+ const walPath = dbPath + "-wal";
37657
+ const shmPath = dbPath + "-shm";
37658
+ try {
37659
+ unlinkSync4(walPath);
37660
+ } catch {
37661
+ }
37662
+ try {
37663
+ unlinkSync4(shmPath);
37664
+ } catch {
37665
+ }
37666
+ const tempPath = dbPath + ".recovery-tmp";
37667
+ copyFileSync6(newestBackup.path, tempPath);
37668
+ renameSync3(tempPath, dbPath);
37669
+ log8.info(
37670
+ { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
37671
+ "Database auto-recovered from backup successfully."
37672
+ );
37673
+ _resetDualScopeDbCache();
37674
+ const restoredNativeDb = openNativeDatabase2(dbPath);
37675
+ _nativeDb3 = restoredNativeDb;
37676
+ const restoredDb = _getDrizzle3()({ client: restoredNativeDb, schema: tasks_schema_exports });
37677
+ runMigrations(restoredNativeDb, restoredDb);
37678
+ _db3 = restoredDb;
37679
+ },
37680
+ // Allow a generous stale window + retries: an exodus migration holding this
37681
+ // lock can take a while on a large fleet (matches on-open.ts), so a blocked
37682
+ // auto-recovery must wait rather than time out and race.
37683
+ { stale: 6e5, retries: 30 }
37364
37684
  );
37365
- _resetDualScopeDbCache();
37366
- const restoredNativeDb = openNativeDatabase2(dbPath);
37367
- _nativeDb3 = restoredNativeDb;
37368
- const restoredDb = _getDrizzle3()({ client: restoredNativeDb, schema: tasks_schema_exports });
37369
- runMigrations(restoredNativeDb, restoredDb);
37370
- _db3 = restoredDb;
37371
37685
  } catch (err) {
37372
- log7.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
37686
+ log8.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
37373
37687
  }
37374
37688
  }
37375
37689
  async function getDb(cwd) {
@@ -37469,7 +37783,7 @@ async function getSchemaVersion(cwd) {
37469
37783
  return result[0]?.value ?? null;
37470
37784
  }
37471
37785
  function dbExists(cwd) {
37472
- return existsSync18(resolveDualScopeDbPath("project", cwd));
37786
+ return existsSync19(resolveDualScopeDbPath("project", cwd));
37473
37787
  }
37474
37788
  function getNativeDb() {
37475
37789
  return _nativeDb3;
@@ -37490,12 +37804,13 @@ async function closeAllDatabases() {
37490
37804
  } catch {
37491
37805
  }
37492
37806
  }
37493
- var _drizzle4, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db3, _nativeDb3, _dbPath3, _initPromise4, MIN_BACKUP_TASK_COUNT, REQUIRED_TASK_COLUMNS, REQUIRED_SESSION_COLUMNS;
37807
+ var _drizzle4, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db3, _nativeDb3, _dbPath3, _initPromise4, MIN_BACKUP_TASK_COUNT, FIRST_OPEN_LOCK_SUFFIX, REQUIRED_TASK_COLUMNS, REQUIRED_SESSION_COLUMNS;
37494
37808
  var init_sqlite3 = __esm({
37495
37809
  "packages/core/src/store/sqlite.ts"() {
37496
37810
  "use strict";
37497
37811
  init_logger2();
37498
37812
  init_dual_scope_db();
37813
+ init_lock();
37499
37814
  init_migration_manager();
37500
37815
  init_resolve_migrations_folder();
37501
37816
  init_sqlite_backup();
@@ -37511,6 +37826,7 @@ var init_sqlite3 = __esm({
37511
37826
  _dbPath3 = null;
37512
37827
  _initPromise4 = null;
37513
37828
  MIN_BACKUP_TASK_COUNT = 10;
37829
+ FIRST_OPEN_LOCK_SUFFIX = ".exodus-on-open.lock";
37514
37830
  REQUIRED_TASK_COLUMNS = [
37515
37831
  { name: "pipeline_stage", ddl: "text" },
37516
37832
  { name: "assignee", ddl: "text" }
@@ -38655,13 +38971,13 @@ var init_sqlite_data_accessor = __esm({
38655
38971
  });
38656
38972
 
38657
38973
  // packages/core/src/sequence/index.ts
38658
- import { existsSync as existsSync19, readFileSync as readFileSync6, renameSync as renameSync3 } from "node:fs";
38659
- import { join as join19 } from "node:path";
38974
+ import { existsSync as existsSync20, readFileSync as readFileSync6, renameSync as renameSync4 } from "node:fs";
38975
+ import { join as join20 } from "node:path";
38660
38976
  function getLegacySequenceJsonPath(cwd) {
38661
- return join19(resolveOrCwd(cwd), ".cleo", ".sequence.json");
38977
+ return join20(resolveOrCwd(cwd), ".cleo", ".sequence.json");
38662
38978
  }
38663
38979
  function getLegacySequencePath(cwd) {
38664
- return join19(resolveOrCwd(cwd), ".cleo", ".sequence");
38980
+ return join20(resolveOrCwd(cwd), ".cleo", ".sequence");
38665
38981
  }
38666
38982
  function isValidSequenceState(value) {
38667
38983
  if (!value || typeof value !== "object") return false;
@@ -38672,7 +38988,7 @@ function isSeedSequence(value) {
38672
38988
  return value.counter === 0 && value.lastId === "T000" && value.checksum === "seed";
38673
38989
  }
38674
38990
  function readLegacySequenceFile(path2) {
38675
- if (!existsSync19(path2)) return null;
38991
+ if (!existsSync20(path2)) return null;
38676
38992
  try {
38677
38993
  const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
38678
38994
  return isValidSequenceState(parsed) ? parsed : null;
@@ -38681,14 +38997,14 @@ function readLegacySequenceFile(path2) {
38681
38997
  }
38682
38998
  }
38683
38999
  function renameLegacyFile(path2) {
38684
- if (!existsSync19(path2)) return;
39000
+ if (!existsSync20(path2)) return;
38685
39001
  const migratedPath = `${path2}.migrated`;
38686
39002
  try {
38687
- if (!existsSync19(migratedPath)) {
38688
- renameSync3(path2, migratedPath);
39003
+ if (!existsSync20(migratedPath)) {
39004
+ renameSync4(path2, migratedPath);
38689
39005
  return;
38690
39006
  }
38691
- renameSync3(path2, `${migratedPath}.${Date.now()}`);
39007
+ renameSync4(path2, `${migratedPath}.${Date.now()}`);
38692
39008
  } catch {
38693
39009
  }
38694
39010
  }
@@ -38847,15 +39163,15 @@ var init_sequence = __esm({
38847
39163
 
38848
39164
  // packages/core/src/store/git-checkpoint.ts
38849
39165
  import { execFile as execFile2 } from "node:child_process";
38850
- import { existsSync as existsSync20 } from "node:fs";
39166
+ import { existsSync as existsSync21 } from "node:fs";
38851
39167
  import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
38852
- import { join as join20, resolve as resolve5 } from "node:path";
39168
+ import { join as join21, resolve as resolve5 } from "node:path";
38853
39169
  import { promisify as promisify2 } from "node:util";
38854
39170
  function makeCleoGitEnv(cleoDir) {
38855
39171
  const abs = resolve5(cleoDir);
38856
39172
  return {
38857
39173
  ...process.env,
38858
- GIT_DIR: join20(abs, ".git"),
39174
+ GIT_DIR: join21(abs, ".git"),
38859
39175
  GIT_WORK_TREE: abs
38860
39176
  };
38861
39177
  }
@@ -38873,7 +39189,7 @@ async function cleoGitCommand(args, cleoDir) {
38873
39189
  }
38874
39190
  }
38875
39191
  function isCleoGitInitialized(cleoDir) {
38876
- return existsSync20(join20(cleoDir, ".git", "HEAD"));
39192
+ return existsSync21(join21(cleoDir, ".git", "HEAD"));
38877
39193
  }
38878
39194
  async function loadStateFileAllowlist(cwd) {
38879
39195
  try {
@@ -38917,25 +39233,25 @@ async function isCleoGitRepo(cleoDir) {
38917
39233
  return result.success && (result.stdout === "true" || isCleoGitInitialized(cleoDir));
38918
39234
  }
38919
39235
  function isMergeInProgress(cleoDir) {
38920
- return existsSync20(join20(cleoDir, ".git", "MERGE_HEAD"));
39236
+ return existsSync21(join21(cleoDir, ".git", "MERGE_HEAD"));
38921
39237
  }
38922
39238
  async function isDetachedHead(cleoDir) {
38923
39239
  const result = await cleoGitCommand(["symbolic-ref", "HEAD"], cleoDir);
38924
39240
  return !result.success;
38925
39241
  }
38926
39242
  function isRebaseInProgress(cleoDir) {
38927
- return existsSync20(join20(cleoDir, ".git", "rebase-merge")) || existsSync20(join20(cleoDir, ".git", "rebase-apply"));
39243
+ return existsSync21(join21(cleoDir, ".git", "rebase-merge")) || existsSync21(join21(cleoDir, ".git", "rebase-apply"));
38928
39244
  }
38929
39245
  async function recordCheckpointTime(cleoDir) {
38930
39246
  try {
38931
- const stateFile = join20(cleoDir, CHECKPOINT_STATE_FILE);
39247
+ const stateFile = join21(cleoDir, CHECKPOINT_STATE_FILE);
38932
39248
  await writeFile2(stateFile, String(Math.floor(Date.now() / 1e3)));
38933
39249
  } catch {
38934
39250
  }
38935
39251
  }
38936
39252
  async function getLastCheckpointTime(cleoDir) {
38937
39253
  try {
38938
- const stateFile = join20(cleoDir, CHECKPOINT_STATE_FILE);
39254
+ const stateFile = join21(cleoDir, CHECKPOINT_STATE_FILE);
38939
39255
  const content = await readFile3(stateFile, "utf-8");
38940
39256
  const epoch = parseInt(content.trim(), 10);
38941
39257
  return Number.isNaN(epoch) ? 0 : epoch;
@@ -38947,8 +39263,8 @@ async function getChangedStateFiles(cleoDir, cwd) {
38947
39263
  const changed = [];
38948
39264
  const allStateFiles = await getAllStateFiles(cwd);
38949
39265
  for (const stateFile of allStateFiles) {
38950
- const fullPath = join20(cleoDir, stateFile);
38951
- if (!existsSync20(fullPath)) continue;
39266
+ const fullPath = join21(cleoDir, stateFile);
39267
+ if (!existsSync21(fullPath)) continue;
38952
39268
  const diffResult = await cleoGitCommand(["diff", "--quiet", "--", stateFile], cleoDir);
38953
39269
  const cachedResult = await cleoGitCommand(
38954
39270
  ["diff", "--cached", "--quiet", "--", stateFile],
@@ -38975,7 +39291,7 @@ async function shouldCheckpoint(options) {
38975
39291
  const config = await loadCheckpointConfig(cwd);
38976
39292
  if (!config.enabled) return false;
38977
39293
  const cleoDir = getCleoDir(cwd);
38978
- if (!existsSync20(cleoDir)) return false;
39294
+ if (!existsSync21(cleoDir)) return false;
38979
39295
  if (!isCleoGitInitialized(cleoDir)) return false;
38980
39296
  if (!await isCleoGitRepo(cleoDir)) return false;
38981
39297
  if (isMergeInProgress(cleoDir)) return false;
@@ -39053,7 +39369,7 @@ async function ensureSequenceValid(cwd, options) {
39053
39369
  if (!options?.validateSequence) return;
39054
39370
  const check = await checkSequence(cwd);
39055
39371
  if (!check.valid) {
39056
- log5.warn({ counter: check.counter, maxId: check.maxIdInData }, "Sequence behind, repairing");
39372
+ log6.warn({ counter: check.counter, maxId: check.maxIdInData }, "Sequence behind, repairing");
39057
39373
  const repair = await repairSequence(cwd);
39058
39374
  if (!repair.repaired && options.strict) {
39059
39375
  throw new DataSafetyError(`Sequence repair failed: ${repair.message}`, "SEQUENCE_INVALID", {
@@ -39070,7 +39386,7 @@ async function checkpoint(context, cwd, options) {
39070
39386
  stats.checkpoints++;
39071
39387
  stats.lastCheckpoint = /* @__PURE__ */ new Date();
39072
39388
  } catch (err) {
39073
- log5.warn({ err }, "Checkpoint failed (non-fatal)");
39389
+ log6.warn({ err }, "Checkpoint failed (non-fatal)");
39074
39390
  }
39075
39391
  vacuumIntoBackup({ cwd }).catch(() => {
39076
39392
  });
@@ -39132,7 +39448,7 @@ async function safeAppendLog(accessor, entry, cwd, options) {
39132
39448
  stats.writes++;
39133
39449
  await checkpoint("log entry", cwd, opts);
39134
39450
  }
39135
- var log5, DataSafetyError, DEFAULT_SAFETY, stats;
39451
+ var log6, DataSafetyError, DEFAULT_SAFETY, stats;
39136
39452
  var init_data_safety_central = __esm({
39137
39453
  "packages/core/src/store/data-safety-central.ts"() {
39138
39454
  "use strict";
@@ -39142,7 +39458,7 @@ var init_data_safety_central = __esm({
39142
39458
  init_sqlite3();
39143
39459
  init_sqlite_backup();
39144
39460
  init_tasks_schema();
39145
- log5 = getLogger("data-safety");
39461
+ log6 = getLogger("data-safety");
39146
39462
  DataSafetyError = class extends Error {
39147
39463
  constructor(message, code, context) {
39148
39464
  super(message);
@@ -39182,7 +39498,7 @@ function isSafetyDisabled() {
39182
39498
  }
39183
39499
  function wrapWithSafety(accessor, cwd) {
39184
39500
  if (isSafetyDisabled()) {
39185
- log6.warn(
39501
+ log7.warn(
39186
39502
  "Safety disabled - emergency mode (CLEO_DISABLE_SAFETY=true). Data integrity checks bypassed."
39187
39503
  );
39188
39504
  return accessor;
@@ -39203,13 +39519,13 @@ function getSafetyStatus() {
39203
39519
  enabled: true
39204
39520
  };
39205
39521
  }
39206
- var log6, SafetyDataAccessor;
39522
+ var log7, SafetyDataAccessor;
39207
39523
  var init_safety_data_accessor = __esm({
39208
39524
  "packages/core/src/store/safety-data-accessor.ts"() {
39209
39525
  "use strict";
39210
39526
  init_logger2();
39211
39527
  init_data_safety_central();
39212
- log6 = getLogger("data-safety");
39528
+ log7 = getLogger("data-safety");
39213
39529
  SafetyDataAccessor = class {
39214
39530
  /** The underlying accessor being wrapped. */
39215
39531
  inner;
@@ -39233,7 +39549,7 @@ var init_safety_data_accessor = __esm({
39233
39549
  ...config
39234
39550
  };
39235
39551
  if (this.config.verbose) {
39236
- log6.debug({ engine: inner.engine }, "SafetyDataAccessor initialized");
39552
+ log7.debug({ engine: inner.engine }, "SafetyDataAccessor initialized");
39237
39553
  }
39238
39554
  }
39239
39555
  /** The storage engine backing this accessor. */
@@ -39245,7 +39561,7 @@ var init_safety_data_accessor = __esm({
39245
39561
  */
39246
39562
  logVerbose(message) {
39247
39563
  if (this.config.verbose) {
39248
- log6.debug(message);
39564
+ log7.debug(message);
39249
39565
  }
39250
39566
  }
39251
39567
  /**