@cleocode/core 2026.9.25 → 2026.9.26

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 (208) hide show
  1. package/dist/admin/import-tasks.d.ts.map +1 -1
  2. package/dist/admin/import-tasks.js +5 -2
  3. package/dist/admin/import-tasks.js.map +1 -1
  4. package/dist/admin/import.d.ts.map +1 -1
  5. package/dist/admin/import.js +7 -2
  6. package/dist/admin/import.js.map +1 -1
  7. package/dist/build-provenance.json +2 -2
  8. package/dist/cloud/index.d.ts +1 -0
  9. package/dist/cloud/index.d.ts.map +1 -1
  10. package/dist/cloud/index.js +1 -0
  11. package/dist/cloud/index.js.map +1 -1
  12. package/dist/cloud/nexus-auth.d.ts +26 -1
  13. package/dist/cloud/nexus-auth.d.ts.map +1 -1
  14. package/dist/cloud/nexus-auth.js +61 -32
  15. package/dist/cloud/nexus-auth.js.map +1 -1
  16. package/dist/cloud/nexus-credentials.js +2 -2
  17. package/dist/cloud/nexus-credentials.js.map +1 -1
  18. package/dist/cloud/nexus-device.d.ts +809 -0
  19. package/dist/cloud/nexus-device.d.ts.map +1 -0
  20. package/dist/cloud/nexus-device.js +1680 -0
  21. package/dist/cloud/nexus-device.js.map +1 -0
  22. package/dist/cloud/nexus-enrol.d.ts +210 -0
  23. package/dist/cloud/nexus-enrol.d.ts.map +1 -0
  24. package/dist/cloud/nexus-enrol.js +1023 -0
  25. package/dist/cloud/nexus-enrol.js.map +1 -0
  26. package/dist/cloud/nexus-link.d.ts +3 -0
  27. package/dist/cloud/nexus-link.d.ts.map +1 -1
  28. package/dist/cloud/nexus-link.js +38 -4
  29. package/dist/cloud/nexus-link.js.map +1 -1
  30. package/dist/cloud/signing.d.ts +20 -0
  31. package/dist/cloud/signing.d.ts.map +1 -1
  32. package/dist/cloud/signing.js +14 -0
  33. package/dist/cloud/signing.js.map +1 -1
  34. package/dist/crypto/credentials.d.ts +61 -0
  35. package/dist/crypto/credentials.d.ts.map +1 -1
  36. package/dist/crypto/credentials.js +154 -58
  37. package/dist/crypto/credentials.js.map +1 -1
  38. package/dist/docs/export-document.js +830 -190
  39. package/dist/docs/export-document.js.map +3 -3
  40. package/dist/doctor/dependency-cycles.d.ts +41 -0
  41. package/dist/doctor/dependency-cycles.d.ts.map +1 -0
  42. package/dist/doctor/dependency-cycles.js +39 -0
  43. package/dist/doctor/dependency-cycles.js.map +1 -0
  44. package/dist/doctor/index.d.ts +2 -0
  45. package/dist/doctor/index.d.ts.map +1 -1
  46. package/dist/doctor/index.js +1 -0
  47. package/dist/doctor/index.js.map +1 -1
  48. package/dist/doctor/row-identity.d.ts +11 -0
  49. package/dist/doctor/row-identity.d.ts.map +1 -1
  50. package/dist/doctor/row-identity.js +67 -9
  51. package/dist/doctor/row-identity.js.map +1 -1
  52. package/dist/error-catalog.d.ts.map +1 -1
  53. package/dist/error-catalog.js +4 -0
  54. package/dist/error-catalog.js.map +1 -1
  55. package/dist/error-registry.d.ts.map +1 -1
  56. package/dist/error-registry.js +8 -0
  57. package/dist/error-registry.js.map +1 -1
  58. package/dist/llm/plugin-facade.js +857 -217
  59. package/dist/llm/plugin-facade.js.map +3 -3
  60. package/dist/nexus/workspace.d.ts.map +1 -1
  61. package/dist/nexus/workspace.js +54 -4
  62. package/dist/nexus/workspace.js.map +1 -1
  63. package/dist/orchestrate/lifecycle-ops.js +1 -1
  64. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  65. package/dist/orchestrate/pivot.d.ts.map +1 -1
  66. package/dist/orchestrate/pivot.js +4 -2
  67. package/dist/orchestrate/pivot.js.map +1 -1
  68. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  69. package/dist/orchestrate/query-ops.js +3 -3
  70. package/dist/orchestrate/query-ops.js.map +1 -1
  71. package/dist/orchestrate/spawn-ops.js +1 -1
  72. package/dist/orchestrate/spawn-ops.js.map +1 -1
  73. package/dist/orchestration/index.js +1 -1
  74. package/dist/orchestration/index.js.map +1 -1
  75. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  76. package/dist/orchestration/lead-rollup.js +3 -3
  77. package/dist/orchestration/lead-rollup.js.map +1 -1
  78. package/dist/orchestration/parallel.js +1 -1
  79. package/dist/orchestration/parallel.js.map +1 -1
  80. package/dist/orchestration/wave-topic.d.ts +4 -2
  81. package/dist/orchestration/wave-topic.d.ts.map +1 -1
  82. package/dist/orchestration/wave-topic.js +6 -4
  83. package/dist/orchestration/wave-topic.js.map +1 -1
  84. package/dist/orchestration/waves.d.ts +2 -1
  85. package/dist/orchestration/waves.d.ts.map +1 -1
  86. package/dist/orchestration/waves.js +4 -3
  87. package/dist/orchestration/waves.js.map +1 -1
  88. package/dist/phases/index.d.ts.map +1 -1
  89. package/dist/phases/index.js +4 -2
  90. package/dist/phases/index.js.map +1 -1
  91. package/dist/selfimprove/fix-gen.js +856 -216
  92. package/dist/selfimprove/fix-gen.js.map +3 -3
  93. package/dist/session/engine-ops.d.ts.map +1 -1
  94. package/dist/session/engine-ops.js +77 -49
  95. package/dist/session/engine-ops.js.map +1 -1
  96. package/dist/sessions/focus-state-store.d.ts +69 -23
  97. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  98. package/dist/sessions/focus-state-store.js +113 -27
  99. package/dist/sessions/focus-state-store.js.map +1 -1
  100. package/dist/sessions/handoff.js +1 -1
  101. package/dist/sessions/handoff.js.map +1 -1
  102. package/dist/sessions/index.d.ts.map +1 -1
  103. package/dist/sessions/index.js +24 -13
  104. package/dist/sessions/index.js.map +1 -1
  105. package/dist/sessions/terminal-identity.d.ts +165 -17
  106. package/dist/sessions/terminal-identity.d.ts.map +1 -1
  107. package/dist/sessions/terminal-identity.js +341 -32
  108. package/dist/sessions/terminal-identity.js.map +1 -1
  109. package/dist/skills/orchestrator/startup.js +2 -2
  110. package/dist/skills/orchestrator/startup.js.map +1 -1
  111. package/dist/snapshot/index.d.ts +4 -0
  112. package/dist/snapshot/index.d.ts.map +1 -1
  113. package/dist/snapshot/index.js +50 -3
  114. package/dist/snapshot/index.js.map +1 -1
  115. package/dist/store/db-helpers.d.ts +15 -2
  116. package/dist/store/db-helpers.d.ts.map +1 -1
  117. package/dist/store/db-helpers.js +84 -3
  118. package/dist/store/db-helpers.js.map +1 -1
  119. package/dist/store/dependency-cycles.d.ts +164 -0
  120. package/dist/store/dependency-cycles.d.ts.map +1 -0
  121. package/dist/store/dependency-cycles.js +360 -0
  122. package/dist/store/dependency-cycles.js.map +1 -0
  123. package/dist/store/display-id-alias.d.ts +93 -37
  124. package/dist/store/display-id-alias.d.ts.map +1 -1
  125. package/dist/store/display-id-alias.js +590 -115
  126. package/dist/store/display-id-alias.js.map +1 -1
  127. package/dist/store/dual-scope-db.d.ts.map +1 -1
  128. package/dist/store/dual-scope-db.js +34 -7
  129. package/dist/store/dual-scope-db.js.map +1 -1
  130. package/dist/store/exodus/migrate.d.ts.map +1 -1
  131. package/dist/store/exodus/migrate.js +8 -1
  132. package/dist/store/exodus/migrate.js.map +1 -1
  133. package/dist/store/global-salt.d.ts +35 -0
  134. package/dist/store/global-salt.d.ts.map +1 -1
  135. package/dist/store/global-salt.js +136 -16
  136. package/dist/store/global-salt.js.map +1 -1
  137. package/dist/store/migration-sqlite.d.ts.map +1 -1
  138. package/dist/store/migration-sqlite.js +19 -11
  139. package/dist/store/migration-sqlite.js.map +1 -1
  140. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  141. package/dist/store/portable-bundle-scan.js +6 -0
  142. package/dist/store/portable-bundle-scan.js.map +1 -1
  143. package/dist/store/row-identity-registry.d.ts.map +1 -1
  144. package/dist/store/row-identity-registry.js +11 -3
  145. package/dist/store/row-identity-registry.js.map +1 -1
  146. package/dist/store/row-identity.d.ts +91 -1
  147. package/dist/store/row-identity.d.ts.map +1 -1
  148. package/dist/store/row-identity.js +276 -7
  149. package/dist/store/row-identity.js.map +1 -1
  150. package/dist/store/safety-data-accessor.d.ts +3 -2
  151. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  152. package/dist/store/safety-data-accessor.js +5 -2
  153. package/dist/store/safety-data-accessor.js.map +1 -1
  154. package/dist/store/session-store.d.ts +63 -3
  155. package/dist/store/session-store.d.ts.map +1 -1
  156. package/dist/store/session-store.js +121 -8
  157. package/dist/store/session-store.js.map +1 -1
  158. package/dist/store/split-brain-import.d.ts.map +1 -1
  159. package/dist/store/split-brain-import.js +36 -0
  160. package/dist/store/split-brain-import.js.map +1 -1
  161. package/dist/store/sqlite-data-accessor.d.ts +50 -3
  162. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  163. package/dist/store/sqlite-data-accessor.js +246 -13
  164. package/dist/store/sqlite-data-accessor.js.map +1 -1
  165. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  166. package/dist/store/tasks-sqlite.js +14 -2
  167. package/dist/store/tasks-sqlite.js.map +1 -1
  168. package/dist/store/umbrella-data-accessor.d.ts +3 -2
  169. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  170. package/dist/store/umbrella-data-accessor.js +5 -2
  171. package/dist/store/umbrella-data-accessor.js.map +1 -1
  172. package/dist/system/health.d.ts +2 -0
  173. package/dist/system/health.d.ts.map +1 -1
  174. package/dist/system/health.js +54 -0
  175. package/dist/system/health.js.map +1 -1
  176. package/dist/task-work/index.d.ts.map +1 -1
  177. package/dist/task-work/index.js +9 -6
  178. package/dist/task-work/index.js.map +1 -1
  179. package/dist/tasks/analyze.d.ts.map +1 -1
  180. package/dist/tasks/analyze.js +3 -2
  181. package/dist/tasks/analyze.js.map +1 -1
  182. package/dist/tasks/evidence.d.ts +19 -1
  183. package/dist/tasks/evidence.d.ts.map +1 -1
  184. package/dist/tasks/evidence.js +79 -18
  185. package/dist/tasks/evidence.js.map +1 -1
  186. package/dist/tasks/infer-add-params.js +1 -1
  187. package/dist/tasks/infer-add-params.js.map +1 -1
  188. package/dist/tasks/task-context.d.ts.map +1 -1
  189. package/dist/tasks/task-context.js +3 -1
  190. package/dist/tasks/task-context.js.map +1 -1
  191. package/dist/tasks/task-import.d.ts.map +1 -1
  192. package/dist/tasks/task-import.js +11 -3
  193. package/dist/tasks/task-import.js.map +1 -1
  194. package/dist/tasks/task-next.d.ts +5 -1
  195. package/dist/tasks/task-next.d.ts.map +1 -1
  196. package/dist/tasks/task-next.js +7 -2
  197. package/dist/tasks/task-next.js.map +1 -1
  198. package/dist/validation/engine-ops.d.ts.map +1 -1
  199. package/dist/validation/engine-ops.js +3 -2
  200. package/dist/validation/engine-ops.js.map +1 -1
  201. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  202. package/dist/workgraph/scaffold-apply.js +14 -5
  203. package/dist/workgraph/scaffold-apply.js.map +1 -1
  204. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/migration.sql +76 -0
  205. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/revert.sql +7 -0
  206. package/package.json +13 -13
  207. package/templates/CLEO-INJECTION.md +2 -2
  208. package/templates/CLEO-REFERENCE.md +1 -1
@@ -1874,7 +1874,7 @@ var init_output_contracts_data = __esm({
1874
1874
  // T12503: the optimistic-concurrency version agents pass to --if-match.
1875
1875
  updatedAt: {
1876
1876
  type: ["string", "null"],
1877
- description: "Task version for optimistic concurrency: pass it to `cleo update|complete --if-match`."
1877
+ description: "Task version for optimistic concurrency: pass it to `cleo update|complete --if-match`. A stale version fails with E_CONFLICT (23); E_TASK_RENAMED (26) when sync re-numbered the task you read (details.expected is its new id)."
1878
1878
  },
1879
1879
  // T12502: the human assignee and the agent claim lease are separate.
1880
1880
  assignee: {
@@ -13135,6 +13135,7 @@ var init_exit_codes = __esm({
13135
13135
  ExitCode2[ExitCode2["VERSION_CONFLICT"] = 23] = "VERSION_CONFLICT";
13136
13136
  ExitCode2[ExitCode2["SESSION_UNBOUND"] = 24] = "SESSION_UNBOUND";
13137
13137
  ExitCode2[ExitCode2["WORKTREE_LOCKED"] = 25] = "WORKTREE_LOCKED";
13138
+ ExitCode2[ExitCode2["TASK_RENAMED"] = 26] = "TASK_RENAMED";
13138
13139
  ExitCode2[ExitCode2["SESSION_EXISTS"] = 30] = "SESSION_EXISTS";
13139
13140
  ExitCode2[ExitCode2["SESSION_NOT_FOUND"] = 31] = "SESSION_NOT_FOUND";
13140
13141
  ExitCode2[ExitCode2["SCOPE_CONFLICT"] = 32] = "SCOPE_CONFLICT";
@@ -17890,6 +17891,19 @@ var init_error_catalog = __esm({
17890
17891
  `Bind a session: run 'cleo session start --scope global --name "<name>"', 'cleo session resume <id>', or set CLEO_SESSION_ID=<id>.`
17891
17892
  )
17892
17893
  ],
17894
+ [
17895
+ 26 /* TASK_RENAMED */,
17896
+ def(
17897
+ 26 /* TASK_RENAMED */,
17898
+ "TASK_RENAMED",
17899
+ "CONFLICT",
17900
+ "Task was re-numbered by sync",
17901
+ 409,
17902
+ true,
17903
+ "E_TASK_RENAMED",
17904
+ "The task you addressed now has another id (a display-id collision re-numbered it). Re-read it with 'cleo show <new id>' and retry against that id."
17905
+ )
17906
+ ],
17893
17907
  [
17894
17908
  25 /* WORKTREE_LOCKED */,
17895
17909
  def(
@@ -19837,10 +19851,10 @@ var init_table2 = __esm({
19837
19851
  [IsDrizzleTable] = true;
19838
19852
  /** @internal */
19839
19853
  [ExtraConfigBuilder] = void 0;
19840
- constructor(name15, schema, baseName) {
19854
+ constructor(name15, schema, baseName2) {
19841
19855
  this[TableName] = this[OriginalName] = name15;
19842
19856
  this[TableSchema] = schema;
19843
- this[BaseName] = baseName;
19857
+ this[BaseName] = baseName2;
19844
19858
  }
19845
19859
  };
19846
19860
  }
@@ -21770,9 +21784,9 @@ var init_casing = __esm({
21770
21784
  });
21771
21785
 
21772
21786
  // node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_@opentelemetry+api@1.9.0_@sinclair+typebox@0.34.49_@types+mssql@_b4e81f2ea693a7363d3e5dc2205249c6/node_modules/drizzle-orm/sqlite-core/table.js
21773
- function sqliteTableBase(name15, columns, extraConfig, schema, casing, baseName = name15) {
21787
+ function sqliteTableBase(name15, columns, extraConfig, schema, casing, baseName2 = name15) {
21774
21788
  const casingFn = getCasingFn(casing);
21775
- const rawTable = new SQLiteTable(name15, schema, baseName);
21789
+ const rawTable = new SQLiteTable(name15, schema, baseName2);
21776
21790
  const parsedColumns = typeof columns === "function" ? columns(getSQLiteColumnBuilders()) : columns;
21777
21791
  const builtColumns = Object.fromEntries(Object.entries(parsedColumns).map(([name16, colBuilderBase]) => {
21778
21792
  const colBuilder = colBuilderBase;
@@ -25561,6 +25575,10 @@ function decisionFor(dbPath) {
25561
25575
  if (!decisions.has(key)) decisions.set(key, schemaWriteRefusal(key));
25562
25576
  return decisions.get(key) ?? null;
25563
25577
  }
25578
+ function schemaWritesAllowed(nativeDb) {
25579
+ const dbPath = nativeDb.location();
25580
+ return !dbPath || decisionFor(dbPath) === null;
25581
+ }
25564
25582
  function isWorktreeBuildSchemaError(error48) {
25565
25583
  return error48 instanceof Error && error48.message.includes(E_WORKTREE_BUILD_SCHEMA);
25566
25584
  }
@@ -26599,7 +26617,10 @@ var init_row_identity_registry = __esm({
26599
26617
  key: ["id"],
26600
26618
  birth: "recorded_at",
26601
26619
  content: ["ac_id", "previous_text", "reason"],
26602
- birthFacts: ["ac_id", "previous_text", "reason", "@refFp:ac_uid"],
26620
+ // v2 (T12802): no @refFp: the criterion's fingerprint is unknowable when
26621
+ // it was gone before this row got its identity, so it made the value
26622
+ // depend on fill timing. ac_id is always known.
26623
+ birthFacts: ["ac_id", "previous_text", "reason"],
26603
26624
  storedRefUids: [{ column: "ac_uid", from: "ac_id", table: ACS }],
26604
26625
  task: "T12341"
26605
26626
  },
@@ -26608,7 +26629,10 @@ var init_row_identity_registry = __esm({
26608
26629
  kind: "minted",
26609
26630
  key: ["id"],
26610
26631
  birth: "created_at",
26611
- birthFacts: ["evidence_atom_id", "binding_type", "ac_text_hash", "@refFp:ac_uid"],
26632
+ // v2 (T12802): ac_id in place of @refFp:ac_uid and ac_text_hash. Both
26633
+ // were filled from the criterion, so a backfilled binding whose
26634
+ // criterion was already gone got a different value (see AC history).
26635
+ birthFacts: ["evidence_atom_id", "binding_type", "ac_id"],
26612
26636
  storedRefUids: [
26613
26637
  { column: "ac_uid", from: "ac_id", table: ACS },
26614
26638
  { column: "ac_text_hash", from: "ac_id", table: ACS, source: "text_hash" }
@@ -26650,7 +26674,9 @@ var init_row_identity_registry = __esm({
26650
26674
  // The uid IS the primary key: display-id-alias.ts computes it on write.
26651
26675
  table: "tasks_display_id_aliases",
26652
26676
  kind: "natural",
26653
- key: ["entity_table", "display_id", "entity_uid"],
26677
+ // T12800: the reason is part of the key (collision-remint and
26678
+ // remint-assigned of one id and row are distinct facts).
26679
+ key: ["entity_table", "display_id", "entity_uid", "reason"],
26654
26680
  task: "T12341"
26655
26681
  },
26656
26682
  {
@@ -27232,6 +27258,20 @@ function installRowUidTriggers(db, scope) {
27232
27258
  );
27233
27259
  }
27234
27260
  }
27261
+ function v1ReleaseBirthFp(db, table, row) {
27262
+ const col = (c) => row[c] ?? null;
27263
+ const facts = table === "tasks_task_acceptance_criteria_history" ? [col("ac_id"), col("previous_text"), col("reason")] : table === "tasks_evidence_ac_bindings" ? [col("evidence_atom_id"), col("binding_type"), col("ac_text_hash")] : [];
27264
+ if (facts.length === 0) return null;
27265
+ const acUid = col("ac_uid");
27266
+ let ref = "ref:none";
27267
+ if (acUid !== null) {
27268
+ const ac = db.prepare("SELECT birth_fp AS fp FROM main.tasks_task_acceptance_criteria WHERE uid = ?").get(acUid);
27269
+ ref = ac === void 0 ? "ref:missing" : ac.fp;
27270
+ if (ref === null) return null;
27271
+ }
27272
+ const spec = rowIdentitySpec("project", table);
27273
+ return birthFingerprint(table, spec?.birth ? col(spec.birth) : null, [...facts, ref]);
27274
+ }
27235
27275
  function hasObject(db, type, name15) {
27236
27276
  return db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = ? AND name = ?").get(type, name15) !== void 0;
27237
27277
  }
@@ -27315,24 +27355,41 @@ function preReleaseBirthFp(db, table, row) {
27315
27355
  function resetStaleIdentity(db, scope, writers) {
27316
27356
  if (scope !== "project" || !hasTable(db, ROW_IDENTITY_META_TABLE)) return "none";
27317
27357
  if (readMeta(db, ROW_IDENTITY_RECIPE_KEY) === ROW_IDENTITY_RECIPE) return "none";
27318
- const minted = ROW_IDENTITY.project.filter(
27319
- (spec) => spec.kind === "minted" && hasTable(db, spec.table) && columnsOf(db, spec.table).has(BIRTH_FP_COLUMN)
27358
+ const minted = fingerprintOrder("project", ROW_IDENTITY.project).filter(
27359
+ (spec) => hasTable(db, spec.table) && columnsOf(db, spec.table).has(BIRTH_FP_COLUMN)
27320
27360
  );
27321
27361
  const stale = [];
27362
+ const cleared = /* @__PURE__ */ new Map();
27363
+ const mark = (table, column, value) => {
27364
+ if (value === null || value === void 0) return;
27365
+ const key = `${table}.${column}`;
27366
+ const set2 = cleared.get(key) ?? /* @__PURE__ */ new Set();
27367
+ set2.add(String(value));
27368
+ cleared.set(key, set2);
27369
+ };
27322
27370
  for (const spec of minted) {
27371
+ const deps = (spec.birthFacts ?? []).map((fact) => fingerprintRef("project", spec, fact)).filter((d) => d !== null);
27372
+ const keyColumn = targetKey("project", spec.table);
27323
27373
  const rows = db.prepare(
27324
27374
  `SELECT rowid AS _rowid, * FROM main.${q(spec.table)} WHERE ${q(BIRTH_FP_COLUMN)} IS NOT NULL`
27325
27375
  ).all();
27326
27376
  for (const row of rows) {
27327
- if (row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row)) {
27377
+ const derivedFromStale = deps.some(
27378
+ (d) => cleared.get(`${d.table}.${d.key}`)?.has(String(row[d.column] ?? ""))
27379
+ );
27380
+ if (derivedFromStale || row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row) || row[BIRTH_FP_COLUMN] === v1ReleaseBirthFp(db, spec.table, row)) {
27328
27381
  stale.push({ table: spec.table, rowid: row._rowid });
27382
+ mark(spec.table, keyColumn, row[keyColumn]);
27383
+ mark(spec.table, UID_COLUMN, row[UID_COLUMN]);
27329
27384
  }
27330
27385
  }
27331
27386
  }
27332
- if (stale.length === 0) return "none";
27387
+ const graveyardRows = scope === "project" && hasTable(db, AC_UID_GRAVEYARD) ? db.prepare(`SELECT count(*) AS n FROM main.${AC_UID_GRAVEYARD}`).get().n : 0;
27388
+ if (stale.length === 0 && graveyardRows === 0) return "none";
27333
27389
  if (readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== void 0) return "refused";
27334
27390
  db.exec("SAVEPOINT row_identity_reset");
27335
27391
  try {
27392
+ if (graveyardRows > 0) writers.clearAcUidGraveyardNative(db);
27336
27393
  for (const { table, rowid } of stale) {
27337
27394
  writers.clearBirthFpNative(db, table, rowid);
27338
27395
  }
@@ -27348,6 +27405,106 @@ function writeRecipeMarker(db, writers = requireWriters()) {
27348
27405
  if (!hasTable(db, ROW_IDENTITY_META_TABLE)) return;
27349
27406
  writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_RECIPE_KEY, ROW_IDENTITY_RECIPE);
27350
27407
  }
27408
+ function rowIdentityShared(db) {
27409
+ return readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== void 0;
27410
+ }
27411
+ function healRowIdentitySchema(db, scope, writers = registeredWriters) {
27412
+ const log15 = getLogger("row-identity");
27413
+ if (ROW_IDENTITY[scope].length === 0) {
27414
+ return [];
27415
+ }
27416
+ if (!schemaWritesAllowed(db)) {
27417
+ log15.debug({ scope }, "row identity schema heal skipped: schema writes refused for this build");
27418
+ return [];
27419
+ }
27420
+ try {
27421
+ db.exec("SAVEPOINT row_identity_heal");
27422
+ try {
27423
+ const healed = [
27424
+ ...scope === "project" ? ensureIdentityTables(db) : [],
27425
+ ...ensureRowIdentitySchema(db, scope)
27426
+ ];
27427
+ if (healed.length > 0) recordHealReceipt(db, scope, healed, writers);
27428
+ db.exec("RELEASE SAVEPOINT row_identity_heal");
27429
+ if (healed.length > 0)
27430
+ log15.info({ scope, healed: healed.length }, "row identity schema healed");
27431
+ return healed;
27432
+ } catch (error48) {
27433
+ db.exec("ROLLBACK TO SAVEPOINT row_identity_heal");
27434
+ db.exec("RELEASE SAVEPOINT row_identity_heal");
27435
+ throw error48;
27436
+ }
27437
+ } catch (error48) {
27438
+ log15.error({ scope, error: error48 }, "row identity schema heal failed");
27439
+ return [];
27440
+ }
27441
+ }
27442
+ function recordHealReceipt(db, scope, healed, writers) {
27443
+ const table = HEAL_RECEIPT_TABLE[scope];
27444
+ const log15 = getLogger("row-identity");
27445
+ if (table === null || !hasTable(db, table) || !writers) {
27446
+ log15.warn({ scope, healed: healed.length }, "row identity schema healed; no receipt table");
27447
+ return;
27448
+ }
27449
+ const objects = healed.map((stmt) => {
27450
+ const col = /ALTER TABLE main\.("?)(\w+)\1 ADD COLUMN ("?)(\w+)\3/.exec(stmt);
27451
+ if (col) return `${col[2]}.${col[4]}`;
27452
+ const obj = /(TABLE|INDEX|TRIGGER) IF NOT EXISTS main\.("?)(\w+)\2/.exec(stmt);
27453
+ return obj ? `${obj[1].toLowerCase()} ${obj[3]}` : stmt.slice(0, 60);
27454
+ });
27455
+ const receipt2 = {
27456
+ at: (/* @__PURE__ */ new Date()).toISOString(),
27457
+ scope,
27458
+ statements: healed.length,
27459
+ objects
27460
+ };
27461
+ const history = [...readRowIdentityHealHistory(db), receipt2].slice(-HEAL_RECEIPTS_KEPT);
27462
+ writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_HEAL_KEY, JSON.stringify(history));
27463
+ }
27464
+ function readRowIdentityHealHistory(db) {
27465
+ const raw = readMeta(db, ROW_IDENTITY_HEAL_KEY);
27466
+ if (raw === void 0) return [];
27467
+ try {
27468
+ const parsed = JSON.parse(raw);
27469
+ return Array.isArray(parsed) ? parsed : [parsed];
27470
+ } catch {
27471
+ return [];
27472
+ }
27473
+ }
27474
+ function missingRowIdentitySchema(db) {
27475
+ if (!hasTable(db, "tasks_task_acceptance_criteria")) return [];
27476
+ const missing = [];
27477
+ for (const table of Object.keys(IDENTITY_TABLE_DDL)) {
27478
+ if (!hasTable(db, table)) missing.push(`table ${table}`);
27479
+ }
27480
+ for (const [table, column] of [
27481
+ ["tasks_display_id_aliases", "displaced_hlc"],
27482
+ ["tasks_display_id_aliases", "entity_birth_fp"],
27483
+ [AC_UID_GRAVEYARD, "birth_fp"]
27484
+ ]) {
27485
+ if (hasTable(db, table) && !columnsOf(db, table).has(column)) {
27486
+ missing.push(`column ${table}.${column}`);
27487
+ }
27488
+ }
27489
+ if (!hasObject(db, "trigger", "trg_tasks_ac_uid_graveyard")) {
27490
+ missing.push("trigger trg_tasks_ac_uid_graveyard");
27491
+ }
27492
+ for (const spec of ROW_IDENTITY.project) {
27493
+ if (!hasTable(db, spec.table)) continue;
27494
+ const cols = columnsOf(db, spec.table);
27495
+ for (const column of rowIdentityColumns("project", spec.table)) {
27496
+ if (!cols.has(column)) missing.push(`column ${spec.table}.${column}`);
27497
+ }
27498
+ if (!uidIsPrimaryKey(db, spec.table) && !hasObject(db, "index", `uq_${spec.table}_uid`)) {
27499
+ missing.push(`index uq_${spec.table}_uid`);
27500
+ }
27501
+ for (const ref of spec.storedRefUids ?? []) {
27502
+ const index2 = `idx_${spec.table}_${ref.column}`;
27503
+ if (!hasObject(db, "index", index2)) missing.push(`index ${index2}`);
27504
+ }
27505
+ }
27506
+ return missing;
27507
+ }
27351
27508
  function prepareRowIdentity(db, scope, options = {}) {
27352
27509
  if (ROW_IDENTITY[scope].length === 0) return null;
27353
27510
  if (!rowUidFillEnabled()) return null;
@@ -27386,7 +27543,7 @@ function prepareRowIdentity(db, scope, options = {}) {
27386
27543
  return null;
27387
27544
  }
27388
27545
  }
27389
- var ROW_UID_DOMAIN, ROW_BIRTH_DOMAIN, ROW_UID_SQL_FUNCTION, ROW_BIRTH_FP_SQL_FUNCTION, ROW_RANDOM_UID_SQL_FUNCTION, AC_TEXT_HASH_SQL_FUNCTION, AC_UID_GRAVEYARD, registeredWriters, STORE_TIMESTAMP, MAX_UUID_MS, ROW_IDENTITY_RECIPE, ROW_IDENTITY_META_TABLE, ROW_IDENTITY_RECIPE_KEY, ROW_IDENTITY_SYNCED_KEY, IDENTITY_TABLE_DDL, AC_UID_GRAVEYARD_TRIGGER;
27546
+ var ROW_UID_DOMAIN, ROW_BIRTH_DOMAIN, ROW_UID_SQL_FUNCTION, ROW_BIRTH_FP_SQL_FUNCTION, ROW_RANDOM_UID_SQL_FUNCTION, AC_TEXT_HASH_SQL_FUNCTION, AC_UID_GRAVEYARD, registeredWriters, STORE_TIMESTAMP, MAX_UUID_MS, ROW_IDENTITY_RECIPE, ROW_IDENTITY_META_TABLE, ROW_IDENTITY_RECIPE_KEY, ROW_IDENTITY_SYNCED_KEY, IDENTITY_TABLE_DDL, AC_UID_GRAVEYARD_TRIGGER, ROW_IDENTITY_HEAL_KEY, HEAL_RECEIPT_TABLE, HEAL_RECEIPTS_KEPT;
27390
27547
  var init_row_identity = __esm({
27391
27548
  "packages/core/src/store/row-identity.ts"() {
27392
27549
  "use strict";
@@ -27395,6 +27552,7 @@ var init_row_identity = __esm({
27395
27552
  init_ac_table();
27396
27553
  init_row_identity_flag();
27397
27554
  init_row_identity_registry();
27555
+ init_worktree_build_guard();
27398
27556
  ROW_UID_DOMAIN = "cleo/row-uid/v1";
27399
27557
  ROW_BIRTH_DOMAIN = "cleo/row-birth/v1";
27400
27558
  ROW_UID_SQL_FUNCTION = "cleo_row_uid";
@@ -27404,7 +27562,7 @@ var init_row_identity = __esm({
27404
27562
  AC_UID_GRAVEYARD = "tasks_ac_uid_graveyard";
27405
27563
  STORE_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?)?(Z|[+-]\d{2}:?\d{2})?$/;
27406
27564
  MAX_UUID_MS = 2 ** 48 - 1;
27407
- ROW_IDENTITY_RECIPE = "cleo/row-identity/v1";
27565
+ ROW_IDENTITY_RECIPE = "cleo/row-identity/v2";
27408
27566
  ROW_IDENTITY_META_TABLE = "tasks_row_identity_meta";
27409
27567
  ROW_IDENTITY_RECIPE_KEY = "row_identity_recipe";
27410
27568
  ROW_IDENTITY_SYNCED_KEY = "row_identity_synced";
@@ -27449,6 +27607,15 @@ BEGIN
27449
27607
  INSERT INTO ${AC_UID_GRAVEYARD} (ac_id, uid, task_id, ordinal, text, birth_fp, deleted_at)
27450
27608
  VALUES (OLD.id, OLD.uid, OLD.task_id, OLD.ordinal, OLD.text, OLD.birth_fp, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
27451
27609
  END`;
27610
+ ROW_IDENTITY_HEAL_KEY = "row_identity_schema_healed";
27611
+ HEAL_RECEIPT_TABLE = {
27612
+ project: ROW_IDENTITY_META_TABLE,
27613
+ // The global store declares no identity table yet (ROW_IDENTITY.global is
27614
+ // empty, so its heal never runs); the change that declares one adds its
27615
+ // meta table here.
27616
+ global: null
27617
+ };
27618
+ HEAL_RECEIPTS_KEPT = 20;
27452
27619
  }
27453
27620
  });
27454
27621
 
@@ -40995,12 +41162,12 @@ var init_safety_data_accessor = __esm({
40995
41162
  this.getSafetyOptions()
40996
41163
  );
40997
41164
  }
40998
- async insertNewTask(task) {
41165
+ async insertNewTask(task, identity) {
40999
41166
  this.logVerbose(`Inserting new task ${task.id}`);
41000
41167
  await safeSingleTaskWrite(
41001
41168
  this.inner,
41002
41169
  task.id,
41003
- () => this.inner.insertNewTask(task),
41170
+ () => this.inner.insertNewTask(task, identity),
41004
41171
  this.cwd,
41005
41172
  this.getSafetyOptions()
41006
41173
  );
@@ -41048,6 +41215,9 @@ var init_safety_data_accessor = __esm({
41048
41215
  return this.inner.findOrphanAcBindings();
41049
41216
  }
41050
41217
  // ---- Metadata (pass-through to inner) ----
41218
+ async getTaskIdentities(taskIds) {
41219
+ return await this.inner.getTaskIdentities?.(taskIds) ?? [];
41220
+ }
41051
41221
  async getMetaValue(key) {
41052
41222
  return this.inner.getMetaValue(key);
41053
41223
  }
@@ -41421,13 +41591,147 @@ var init_cross_db_cleanup = __esm({
41421
41591
  }
41422
41592
  });
41423
41593
 
41594
+ // packages/core/src/store/dependency-cycles.ts
41595
+ function formatDependencyCycle(cycle) {
41596
+ return cycle.join(" \u2192 ");
41597
+ }
41598
+ function buildAdjacency(edges) {
41599
+ const adjacency = /* @__PURE__ */ new Map();
41600
+ for (const { taskId, dependsOn } of edges) {
41601
+ const next = adjacency.get(taskId);
41602
+ if (next) next.push(dependsOn);
41603
+ else adjacency.set(taskId, [dependsOn]);
41604
+ }
41605
+ for (const next of adjacency.values()) next.sort();
41606
+ return adjacency;
41607
+ }
41608
+ function shortestPath(adjacency, from, to) {
41609
+ if (from === to) return [from];
41610
+ const previous = /* @__PURE__ */ new Map([[from, from]]);
41611
+ const queue = [from];
41612
+ for (let head = 0; head < queue.length; head++) {
41613
+ const id = queue[head];
41614
+ for (const next of adjacency.get(id) ?? []) {
41615
+ if (previous.has(next)) continue;
41616
+ previous.set(next, id);
41617
+ if (next === to) {
41618
+ const path = [to];
41619
+ let cursor = id;
41620
+ while (cursor !== from) {
41621
+ path.push(cursor);
41622
+ cursor = previous.get(cursor);
41623
+ }
41624
+ path.push(from);
41625
+ return path.reverse();
41626
+ }
41627
+ queue.push(next);
41628
+ }
41629
+ }
41630
+ return null;
41631
+ }
41632
+ function findClosedCycle(existing, edge) {
41633
+ if (edge.taskId === edge.dependsOn) return [edge.taskId, edge.taskId];
41634
+ const path = shortestPath(buildAdjacency(existing), edge.dependsOn, edge.taskId);
41635
+ return path ? [edge.taskId, ...path] : null;
41636
+ }
41637
+ function dependencyCycleError(edge, cycle) {
41638
+ const self = edge.taskId === edge.dependsOn;
41639
+ const message = self ? `${DEPENDENCY_CYCLE_CODE}: task ${edge.taskId} cannot depend on itself` : `${DEPENDENCY_CYCLE_CODE}: ${edge.taskId} depends on ${edge.dependsOn} would close a dependency cycle: ${formatDependencyCycle(cycle)}`;
41640
+ const closing = cycle.length >= 3 ? cycle[cycle.length - 2] : void 0;
41641
+ const fix = self ? `Drop ${edge.taskId} from its own --depends list` : `Drop this dependency, or first remove another edge of the cycle, e.g. cleo update ${closing} --remove-depends ${edge.taskId}`;
41642
+ return new CleoError(14 /* CIRCULAR_REFERENCE */, message, {
41643
+ fix,
41644
+ details: {
41645
+ field: "depends",
41646
+ actual: edge.dependsOn,
41647
+ taskId: edge.taskId,
41648
+ cycle: [...cycle]
41649
+ }
41650
+ });
41651
+ }
41652
+ function isDependencyCycleAbort(err) {
41653
+ for (let e = err, depth = 0; e instanceof Error && depth < 8; depth++) {
41654
+ if (e.message.includes(DEPENDENCY_CYCLE_CODE)) return true;
41655
+ e = e.cause;
41656
+ }
41657
+ return false;
41658
+ }
41659
+ function readDependencyEdges(db) {
41660
+ return db.select({
41661
+ taskId: tasksTaskDependencies.taskId,
41662
+ dependsOn: tasksTaskDependencies.dependsOn
41663
+ }).from(tasksTaskDependencies).all();
41664
+ }
41665
+ function rethrowDependencyCycle(db, err, attempted) {
41666
+ if (!isDependencyCycleAbort(err)) throw err;
41667
+ const edges = readDependencyEdges(db);
41668
+ for (const edge of attempted) {
41669
+ const cycle = findClosedCycle(edges, edge);
41670
+ if (cycle) throw dependencyCycleError(edge, cycle);
41671
+ edges.push(edge);
41672
+ }
41673
+ throw err;
41674
+ }
41675
+ var DEPENDENCY_CYCLE_CODE;
41676
+ var init_dependency_cycles = __esm({
41677
+ "packages/core/src/store/dependency-cycles.ts"() {
41678
+ "use strict";
41679
+ init_src();
41680
+ init_errors2();
41681
+ init_tasks_schema();
41682
+ DEPENDENCY_CYCLE_CODE = "E_TASK_DEPENDENCY_CYCLE";
41683
+ }
41684
+ });
41685
+
41424
41686
  // packages/core/src/store/db-helpers.ts
41425
41687
  import { and as and5, eq as eq7, inArray as inArray3, sql as sql27 } from "drizzle-orm";
41426
41688
  async function upsertTask(db, row, archiveFields, allowOrphanParent = false) {
41427
41689
  await writeTaskRow(db, row, archiveFields, allowOrphanParent, false);
41428
41690
  }
41429
- async function insertNewTask(db, row) {
41430
- await writeTaskRow(db, row, void 0, false, true);
41691
+ async function insertNewTask(db, row, identity = { origin: "new" }) {
41692
+ await writeTaskRow(
41693
+ db,
41694
+ { ...row, ...await importedIdentity(db, identity, row) },
41695
+ void 0,
41696
+ false,
41697
+ true
41698
+ );
41699
+ }
41700
+ async function tableExists2(db, table) {
41701
+ const rows = await db.all(
41702
+ sql27`SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = ${table}`
41703
+ );
41704
+ return rows.length > 0;
41705
+ }
41706
+ async function importedIdentity(db, identity, row) {
41707
+ if (identity.origin === "new" || !rowUidFillEnabled()) return {};
41708
+ const fp = identity.birthFp ?? null;
41709
+ const carried2 = identity.uid ?? null;
41710
+ if (carried2 !== null && fp !== null) {
41711
+ let uid = carried2;
41712
+ if (await tableExists2(db, "tasks_uid_aliases")) {
41713
+ for (let hop = 0; hop < 32; hop++) {
41714
+ const found = await db.all(
41715
+ sql27`SELECT new_uid AS uid FROM tasks_uid_aliases
41716
+ WHERE entity_table = 'tasks_tasks' AND old_uid = ${uid} AND old_birth_fp = ${fp}`
41717
+ );
41718
+ const next = found[0];
41719
+ if (!next) break;
41720
+ uid = next.uid;
41721
+ }
41722
+ }
41723
+ const [holder] = await db.select({ id: tasksTasks.id, birthFp: tasksTasks.birthFp }).from(tasksTasks).where(eq7(tasksTasks.uid, uid)).limit(1).all();
41724
+ if (holder && holder.birthFp === fp) throw new SameRowPresentError(row.id, holder.id);
41725
+ if (await tableExists2(db, "tasks_identity_quarantine")) {
41726
+ const [held] = await db.all(
41727
+ sql27`SELECT 1 AS x FROM tasks_identity_quarantine
41728
+ WHERE entity_table = 'tasks_tasks' AND uid = ${uid} AND birth_fp = ${fp}`
41729
+ );
41730
+ if (held) throw new SameRowPresentError(row.id, "held");
41731
+ }
41732
+ if (!holder) return { uid, birthFp: fp };
41733
+ }
41734
+ return { uid: null, birthFp: null };
41431
41735
  }
41432
41736
  async function writeTaskRow(db, row, archiveFields, allowOrphanParent, insertOnly) {
41433
41737
  if (row.parentId && row.parentId === row.id) {
@@ -41578,7 +41882,11 @@ async function batchUpdateDependencies(db, tasks2, validIds) {
41578
41882
  for (const depId of want) if (!have.has(depId)) added.push({ taskId, dependsOn: depId });
41579
41883
  }
41580
41884
  if (added.length > 0) {
41581
- await db.insert(tasksTaskDependencies).values(added).onConflictDoNothing().run();
41885
+ try {
41886
+ await db.insert(tasksTaskDependencies).values(added).onConflictDoNothing().run();
41887
+ } catch (err) {
41888
+ rethrowDependencyCycle(db, err, added);
41889
+ }
41582
41890
  }
41583
41891
  }
41584
41892
  async function loadDependenciesForTasks(db, tasks2, _validationIds) {
@@ -41623,15 +41931,29 @@ async function loadRelationsForTasks(db, tasks2) {
41623
41931
  task.relates = relations && relations.length > 0 ? relations : [];
41624
41932
  }
41625
41933
  }
41626
- var log6;
41934
+ var log6, SameRowPresentError;
41627
41935
  var init_db_helpers = __esm({
41628
41936
  "packages/core/src/store/db-helpers.ts"() {
41629
41937
  "use strict";
41630
41938
  init_src();
41631
41939
  init_errors2();
41632
41940
  init_logger2();
41941
+ init_dependency_cycles();
41942
+ init_row_identity_flag();
41633
41943
  init_tasks_schema();
41634
41944
  log6 = getLogger("db-helpers");
41945
+ SameRowPresentError = class extends CleoError {
41946
+ /** Where the row already is: its display id, or `held` when quarantined. */
41947
+ presentAs;
41948
+ constructor(taskId, presentAs) {
41949
+ super(
41950
+ 22 /* ID_COLLISION */,
41951
+ `Task ${taskId} is already in this store (${presentAs === "held" ? "held for sync" : `as ${presentAs}`}): same uid and birth fingerprint; not written again`,
41952
+ { details: { field: "taskId", actual: taskId, expected: presentAs } }
41953
+ );
41954
+ this.presentAs = presentAs;
41955
+ }
41956
+ };
41635
41957
  }
41636
41958
  });
41637
41959
 
@@ -41716,46 +42038,133 @@ function readProcessEntry(pid) {
41716
42038
  return null;
41717
42039
  }
41718
42040
  }
41719
- function resolvePpidKey(ppid, lookupProcess) {
42041
+ function readProcessArgs(pid) {
42042
+ if (process.platform === "win32" || !Number.isInteger(pid) || pid <= 1) return null;
42043
+ try {
42044
+ const out = execFileSync("ps", ["-o", "args=", "-p", String(pid)], {
42045
+ encoding: "utf8",
42046
+ timeout: 1e3,
42047
+ stdio: ["ignore", "pipe", "ignore"],
42048
+ env: { ...process.env, ...PS_STABLE_ENV }
42049
+ }).trim();
42050
+ return out === "" ? null : out;
42051
+ } catch {
42052
+ return null;
42053
+ }
42054
+ }
42055
+ function baseName(token) {
42056
+ return token.slice(token.lastIndexOf("/") + 1).replace(/^-/, "");
42057
+ }
42058
+ function programName(commandLine) {
42059
+ const tokens = commandLine.trim().split(/\s+/);
42060
+ const head = baseName(tokens[0] ?? "");
42061
+ if (!RUNTIME_PATTERN.test(head)) return head;
42062
+ for (let i = 1; i < tokens.length; i++) {
42063
+ const token = tokens[i] ?? "";
42064
+ if (token === "-m") return (tokens[i + 1] ?? head).split(".")[0] ?? head;
42065
+ if (token.startsWith("-")) continue;
42066
+ return baseName(token).replace(/\.(c|m)?(js|ts)$|\.py$/, "");
42067
+ }
42068
+ return head;
42069
+ }
42070
+ function isCommandStringShell(args) {
42071
+ const tokens = args.trim().split(/\s+/);
42072
+ const shell = programName(args);
42073
+ let first = 1;
42074
+ if (RUNTIME_PATTERN.test(baseName(tokens[0] ?? ""))) {
42075
+ while (first < tokens.length && (tokens[first] ?? "").startsWith("-")) first++;
42076
+ first++;
42077
+ }
42078
+ const rest = tokens.slice(first);
42079
+ if (shell === "pwsh" || shell === "powershell") {
42080
+ return rest.some((t) => PWSH_COMMAND_SWITCHES.has(t.toLowerCase()));
42081
+ }
42082
+ for (let i = 0; i < rest.length; i++) {
42083
+ const token = rest[i] ?? "";
42084
+ if (token === "--") return false;
42085
+ if (token === "--command" || token === "--commands") return true;
42086
+ if (SHELL_OPTIONS_WITH_ARGUMENT.has(token)) {
42087
+ i++;
42088
+ continue;
42089
+ }
42090
+ if (token.startsWith("--")) continue;
42091
+ if (!/^[-+][A-Za-z]+$/.test(token)) return false;
42092
+ if (token.startsWith("-") && token.slice(1).includes("c")) return true;
42093
+ }
42094
+ return false;
42095
+ }
42096
+ function resolveAncestor(ppid, lookupProcess, lookupArgs, agentId = null) {
41720
42097
  let pid = ppid;
41721
- for (let depth = 0; depth < PPID_CHAIN_MAX_DEPTH && pid > 1; depth++) {
42098
+ for (let depth = 0; depth < PPID_CHAIN_MAX_DEPTH; depth++) {
42099
+ if (pid <= 1) return null;
41722
42100
  const entry = lookupProcess(pid);
41723
- if (!entry) return null;
41724
- if (!PPID_CHAIN_LAUNCHERS.has(entry.command)) {
41725
- return { key: `ppid:${entry.pid}@${entry.startedAt}`, source: "ppid", kind: "ppid" };
42101
+ if (!entry || entry.pid <= 1) return null;
42102
+ const args = lookupArgs(entry.pid);
42103
+ const program = programName(args ?? entry.command);
42104
+ if (INIT_PROCESSES.has(program) || INIT_PROCESSES.has(entry.command)) return null;
42105
+ if (OWNER_PROCESS_SHELLS.has(program)) {
42106
+ if (args === null) return null;
42107
+ if (!isCommandStringShell(args)) return { entry, program, type: "shell" };
42108
+ } else if (!ANCESTOR_WRAPPERS.has(program)) {
42109
+ if (KNOWN_HARNESSES.has(program)) return { entry, program, type: "harness" };
42110
+ return agentId ? { entry, program, type: "host" } : null;
41726
42111
  }
41727
42112
  pid = entry.ppid;
41728
42113
  }
41729
42114
  return null;
41730
42115
  }
42116
+ function anchorKey(anchor, agentId, under) {
42117
+ const proc = `${anchor.entry.pid}@${anchor.entry.startedAt}`;
42118
+ if (anchor.type === "shell" && !agentId) {
42119
+ return { key: `proc:${proc}`, source: "process", kind: "process" };
42120
+ }
42121
+ const agent = agentId ? `#agent=${agentId}` : "";
42122
+ const scope = under ? `|${under.key}` : "";
42123
+ return {
42124
+ key: `harness:${anchor.program}:${proc}${agent}${scope}`,
42125
+ source: "process",
42126
+ kind: "harness"
42127
+ };
42128
+ }
41731
42129
  function resolveTerminalKeys(options = {}) {
41732
42130
  const env = options.env ?? process.env;
41733
42131
  const keys = [];
41734
42132
  for (const source of TERMINAL_KEY_SOURCES) {
41735
42133
  const value = envValue(env, source.envVar);
41736
42134
  if (value === null) continue;
41737
- const qualifier = envValue(env, source.qualifierEnvVar);
42135
+ const qualifiers = [source.qualifierEnvVar, ...source.extraQualifierEnvVars ?? []].map((name15) => envValue(env, name15)).filter((q2) => q2 !== null);
41738
42136
  keys.push({
41739
- key: `env:${source.envVar}=${qualifier === null ? value : `${qualifier}|${value}`}`,
42137
+ key: `env:${source.envVar}=${[...qualifiers, value].join("|")}`,
41740
42138
  source: source.envVar,
41741
42139
  kind: source.kind
41742
42140
  });
41743
42141
  }
41744
- if (keys.length > 0) return keys;
41745
- const live = options.ppid === void 0 && options.lookupProcess === void 0;
41746
- let ppidKey;
42142
+ if (keys.some((k) => k.kind === "provider")) return keys;
42143
+ const agentId = envValue(env, "CLEO_AGENT_ID");
42144
+ const live = options.ppid === void 0 && options.lookupProcess === void 0 && options.lookupArgs === void 0;
42145
+ let anchor;
41747
42146
  if (live) {
41748
- if (livePpidKey === void 0) livePpidKey = resolvePpidKey(process.ppid, readProcessEntry);
41749
- ppidKey = livePpidKey;
42147
+ const memoKey2 = agentId ?? "";
42148
+ if (!liveAnchors.has(memoKey2))
42149
+ liveAnchors.set(
42150
+ memoKey2,
42151
+ resolveAncestor(process.ppid, readProcessEntry, readProcessArgs, agentId)
42152
+ );
42153
+ anchor = liveAnchors.get(memoKey2) ?? null;
41750
42154
  } else {
41751
- ppidKey = resolvePpidKey(
42155
+ anchor = resolveAncestor(
41752
42156
  options.ppid ?? process.ppid,
41753
- options.lookupProcess ?? readProcessEntry
42157
+ options.lookupProcess ?? readProcessEntry,
42158
+ options.lookupArgs ?? readProcessArgs,
42159
+ agentId
41754
42160
  );
41755
42161
  }
41756
- return ppidKey ? [ppidKey] : [];
42162
+ if (!anchor) return keys;
42163
+ if (keys.length === 0) return [anchorKey(anchor, agentId, void 0)];
42164
+ if (anchor.type === "shell" && !agentId) return keys;
42165
+ return [...keys, anchorKey(anchor, agentId, keys[0])];
41757
42166
  }
41758
- var TERMINAL_KEY_SOURCES, PPID_CHAIN_LAUNCHERS, PPID_CHAIN_MAX_DEPTH, livePpidKey;
42167
+ var TERMINAL_KEY_SOURCES, PPID_CHAIN_MAX_DEPTH, KNOWN_HARNESSES, RUNTIME_PATTERN, ANCESTOR_WRAPPERS, INIT_PROCESSES, SHELL_OPTIONS_WITH_ARGUMENT, PWSH_COMMAND_SWITCHES, OWNER_PROCESS_SHELLS, liveAnchors;
41759
42168
  var init_terminal_identity = __esm({
41760
42169
  "packages/core/src/sessions/terminal-identity.ts"() {
41761
42170
  "use strict";
@@ -41787,24 +42196,100 @@ var init_terminal_identity = __esm({
41787
42196
  kind: "terminal",
41788
42197
  origin: "konsole",
41789
42198
  qualifierEnvVar: "KONSOLE_DBUS_SERVICE"
42199
+ },
42200
+ // T12864 — non-terminal callers whose platform exports a per-job / per-login id.
42201
+ // GitHub Actions: GITHUB_RUN_ID is per run and shared by its jobs, so it is
42202
+ // qualified by GITHUB_RUN_ATTEMPT and GITHUB_JOB ("Variables reference",
42203
+ // docs.github.com/en/actions/reference/workflows-and-actions/variables).
42204
+ // Every step of one job shares the key; the steps run sequentially.
42205
+ {
42206
+ envVar: "GITHUB_RUN_ID",
42207
+ kind: "terminal",
42208
+ origin: "github-actions",
42209
+ qualifierEnvVar: "GITHUB_RUN_ATTEMPT",
42210
+ extraQualifierEnvVars: ["GITHUB_JOB"]
42211
+ },
42212
+ // GitLab CI: CI_JOB_ID is unique per job across the instance ("Predefined
42213
+ // CI/CD variables reference", docs.gitlab.com/ci/variables/predefined_variables).
42214
+ { envVar: "CI_JOB_ID", kind: "terminal", origin: "gitlab-ci" },
42215
+ // OpenSSH login with a tty: SSH_TTY names the login's tty and is set only
42216
+ // when one was allocated; SSH_CONNECTION ("client-ip client-port server-ip
42217
+ // server-port", ssh(1) ENVIRONMENT) disambiguates it across connections. A
42218
+ // one-shot `ssh host 'cmd'` has no tty and falls through to the ancestor walk.
42219
+ {
42220
+ envVar: "SSH_TTY",
42221
+ kind: "terminal",
42222
+ origin: "openssh",
42223
+ qualifierEnvVar: "SSH_CONNECTION"
41790
42224
  }
41791
42225
  ];
41792
- PPID_CHAIN_LAUNCHERS = /* @__PURE__ */ new Set([
41793
- "node",
42226
+ PPID_CHAIN_MAX_DEPTH = 12;
42227
+ KNOWN_HARNESSES = /* @__PURE__ */ new Set([
42228
+ "claude",
42229
+ "codex",
42230
+ "aider",
42231
+ "gemini",
42232
+ "kimi",
42233
+ "cursor-agent",
42234
+ "amp"
42235
+ ]);
42236
+ RUNTIME_PATTERN = /^(node|nodejs|bun|deno|python[0-9.]*|pypy[0-9.]*|ruby|java|php|perl)$/;
42237
+ ANCESTOR_WRAPPERS = /* @__PURE__ */ new Set([
42238
+ "env",
42239
+ "timeout",
42240
+ "gtimeout",
42241
+ "nohup",
42242
+ "nice",
42243
+ "time",
42244
+ "xargs",
42245
+ "stdbuf",
42246
+ "sudo",
42247
+ "doas",
41794
42248
  "npm",
42249
+ "npm-cli",
41795
42250
  "npx",
42251
+ "npx-cli",
41796
42252
  "pnpm",
41797
42253
  "pnpx",
41798
42254
  "yarn",
41799
- "bun",
41800
42255
  "bunx",
41801
- "tsx",
41802
42256
  "corepack",
41803
42257
  "mise",
41804
- "env",
41805
42258
  "cleo"
41806
42259
  ]);
41807
- PPID_CHAIN_MAX_DEPTH = 4;
42260
+ INIT_PROCESSES = /* @__PURE__ */ new Set(["launchd", "systemd", "init"]);
42261
+ SHELL_OPTIONS_WITH_ARGUMENT = /* @__PURE__ */ new Set([
42262
+ "-o",
42263
+ "+o",
42264
+ "-O",
42265
+ "+O",
42266
+ "--rcfile",
42267
+ "--init-file"
42268
+ ]);
42269
+ PWSH_COMMAND_SWITCHES = /* @__PURE__ */ new Set([
42270
+ "-c",
42271
+ "-command",
42272
+ "-commandwithargs",
42273
+ "-cwa",
42274
+ "-e",
42275
+ "-ec",
42276
+ "-encodedcommand"
42277
+ ]);
42278
+ OWNER_PROCESS_SHELLS = /* @__PURE__ */ new Set([
42279
+ "sh",
42280
+ "bash",
42281
+ "zsh",
42282
+ "dash",
42283
+ "fish",
42284
+ "ksh",
42285
+ "tcsh",
42286
+ "csh",
42287
+ "pwsh",
42288
+ "powershell",
42289
+ "nu",
42290
+ "xonsh"
42291
+ ]);
42292
+ liveAnchors = /* @__PURE__ */ new Map();
41808
42293
  }
41809
42294
  });
41810
42295
 
@@ -41844,7 +42329,8 @@ var init_session_binding_schema = __esm({
41844
42329
  });
41845
42330
 
41846
42331
  // packages/core/src/store/session-store.ts
41847
- import { and as and6, desc as desc2, eq as eq8, isNull as isNull3, lte as lte2, or as or4 } from "drizzle-orm";
42332
+ import { existsSync as existsSync19 } from "node:fs";
42333
+ import { and as and6, desc as desc2, eq as eq8, inArray as inArray4, isNull as isNull3, lte as lte2, or as or4 } from "drizzle-orm";
41848
42334
  async function getSession(sessionId, cwd) {
41849
42335
  const scope = captureProjectScope(cwd ?? getProjectRoot(), worktreeScope.getStore());
41850
42336
  return worktreeScope.run(scope, async () => {
@@ -41866,8 +42352,9 @@ async function getActiveSession(cwd) {
41866
42352
  function splitTerminalKeys(keys) {
41867
42353
  return {
41868
42354
  provider: keys.find((k) => k.kind === "provider"),
41869
- pane: keys.find((k) => k.kind === "multiplexer"),
41870
- tab: keys.find((k) => k.kind === "terminal" || k.kind === "ppid")
42355
+ // A harness / CLEO_AGENT_ID agent (T12864) is more specific than a pane.
42356
+ pane: keys.find((k) => k.kind === "harness") ?? keys.find((k) => k.kind === "multiplexer"),
42357
+ tab: keys.find((k) => k.kind === "terminal" || k.kind === "process" || k.kind === "ppid")
41871
42358
  };
41872
42359
  }
41873
42360
  async function readBinding(db, key) {
@@ -41947,6 +42434,7 @@ async function resolveCurrentSession(cwd) {
41947
42434
  async function resolveBoundSession(cwd) {
41948
42435
  const scope = captureProjectScope(cwd ?? getProjectRoot(), worktreeScope.getStore());
41949
42436
  return worktreeScope.run(scope, async () => {
42437
+ if (!existsSync19(getDbPath(scope.worktreeRoot))) return null;
41950
42438
  const connId = getCurrentConnectionSessionId();
41951
42439
  if (connId) {
41952
42440
  const byConn = await getSession(connId, scope.worktreeRoot);
@@ -42098,7 +42586,7 @@ __export(registry_exports2, {
42098
42586
  updateAgentStatus: () => updateAgentStatus
42099
42587
  });
42100
42588
  import { randomBytes as randomBytes2 } from "node:crypto";
42101
- import { and as and7, eq as eq9, inArray as inArray4, lt as lt2, sql as sql29 } from "drizzle-orm";
42589
+ import { and as and7, eq as eq9, inArray as inArray5, lt as lt2, sql as sql29 } from "drizzle-orm";
42102
42590
  function generateAgentId() {
42103
42591
  const now = /* @__PURE__ */ new Date();
42104
42592
  const ts = now.toISOString().replace(/[-:T]/g, "").substring(0, 14);
@@ -42187,11 +42675,11 @@ async function listAgentInstances(filters, cwd) {
42187
42675
  const conditions = [];
42188
42676
  if (filters?.status) {
42189
42677
  const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
42190
- conditions.push(inArray4(agentInstances.status, statuses));
42678
+ conditions.push(inArray5(agentInstances.status, statuses));
42191
42679
  }
42192
42680
  if (filters?.agentType) {
42193
42681
  const types = Array.isArray(filters.agentType) ? filters.agentType : [filters.agentType];
42194
- conditions.push(inArray4(agentInstances.agentType, types));
42682
+ conditions.push(inArray5(agentInstances.agentType, types));
42195
42683
  }
42196
42684
  if (filters?.sessionId) {
42197
42685
  conditions.push(eq9(agentInstances.sessionId, filters.sessionId));
@@ -42228,7 +42716,7 @@ async function checkAgentHealth(thresholdMs = 3e4, cwd) {
42228
42716
  const cutoff = new Date(Date.now() - thresholdMs).toISOString();
42229
42717
  return db.select().from(agentInstances).where(
42230
42718
  and7(
42231
- inArray4(agentInstances.status, ["active", "idle", "starting"]),
42719
+ inArray5(agentInstances.status, ["active", "idle", "starting"]),
42232
42720
  lt2(agentInstances.lastHeartbeat, cutoff)
42233
42721
  )
42234
42722
  ).all();
@@ -42323,12 +42811,14 @@ var init_registry2 = __esm({
42323
42811
  // packages/core/src/store/sqlite-data-accessor.ts
42324
42812
  var sqlite_data_accessor_exports = {};
42325
42813
  __export(sqlite_data_accessor_exports, {
42814
+ RENAMED_RECENT_MS: () => RENAMED_RECENT_MS,
42326
42815
  addTaskLabelsNative: () => addTaskLabelsNative,
42327
42816
  advanceTaskIdSequence: () => advanceTaskIdSequence,
42328
42817
  bindTaskAccessorScope: () => bindTaskAccessorScope,
42329
42818
  captureTaskAccessorScope: () => captureProjectScope,
42330
42819
  clearAcUidGraveyardNative: () => clearAcUidGraveyardNative,
42331
42820
  clearBirthFpNative: () => clearBirthFpNative,
42821
+ clearReplacedTaskIdentityNative: () => clearReplacedTaskIdentityNative,
42332
42822
  clearRowUidNative: () => clearRowUidNative,
42333
42823
  createSqliteDataAccessor: () => createSqliteDataAccessor,
42334
42824
  deleteDisplayIdAliasNative: () => deleteDisplayIdAliasNative,
@@ -42339,6 +42829,7 @@ __export(sqlite_data_accessor_exports, {
42339
42829
  insertIdentityRowNative: () => insertIdentityRowNative,
42340
42830
  insertQuarantineNative: () => insertQuarantineNative,
42341
42831
  insertUidAliasNative: () => insertUidAliasNative,
42832
+ readReplacedTaskBirthNative: () => readReplacedTaskBirthNative,
42342
42833
  rederiveAcIdsNative: () => rederiveAcIdsNative,
42343
42834
  rekeyQuarantineNative: () => rekeyQuarantineNative,
42344
42835
  relinkAcUidNative: () => relinkAcUidNative,
@@ -42355,7 +42846,7 @@ import {
42355
42846
  and as and8,
42356
42847
  eq as eq10,
42357
42848
  getTableColumns as getTableColumns2,
42358
- inArray as inArray5,
42849
+ inArray as inArray6,
42359
42850
  isNull as isNull4,
42360
42851
  like as like2,
42361
42852
  lte as lte3,
@@ -42379,8 +42870,8 @@ async function selectAcBindings(db, acIds) {
42379
42870
  bindingType: b.bindingType,
42380
42871
  createdAt: b.createdAt
42381
42872
  };
42382
- const byUid = await db.select(columns).from(b).innerJoin(ac, eq10(ac.uid, b.acUid)).where(inArray5(ac.id, ids)).all();
42383
- const byId = await db.select(columns).from(b).leftJoin(ac, eq10(ac.uid, b.acUid)).where(inArray5(b.acId, ids)).all();
42873
+ const byUid = await db.select(columns).from(b).innerJoin(ac, eq10(ac.uid, b.acUid)).where(inArray6(ac.id, ids)).all();
42874
+ const byId = await db.select(columns).from(b).leftJoin(ac, eq10(ac.uid, b.acUid)).where(inArray6(b.acId, ids)).all();
42384
42875
  const rows = [...byUid, ...byId];
42385
42876
  const wanted = new Set(ids);
42386
42877
  const seen = /* @__PURE__ */ new Set();
@@ -42473,6 +42964,7 @@ function renameTaskDisplayIdNative(nativeDb, uid, toId, refs, guard = {}) {
42473
42964
  if (Number(changed) !== 1) {
42474
42965
  throw taskConflictError(current.id, taskVersion(current), null);
42475
42966
  }
42967
+ recordLocalRename(nativeDb, uid, current.id, toId, taskVersion(current));
42476
42968
  const rewritten = {};
42477
42969
  const q2 = (name15) => `"${name15.replaceAll('"', '""')}"`;
42478
42970
  for (const ref of refs) {
@@ -42528,23 +43020,39 @@ function rederiveAcIdsNative(nativeDb, fromId, toId) {
42528
43020
  }
42529
43021
  return count;
42530
43022
  }
42531
- function insertDisplayIdAliasNative(nativeDb, row) {
43023
+ function insertDisplayIdAliasNative(nativeDb, row, precedes) {
43024
+ const stored = nativeDb.prepare(
43025
+ `SELECT uid, entity_table AS entityTable, display_id AS displayId, entity_uid AS entityUid,
43026
+ reason, origin, displaced_hlc AS displacedHlc, created_at AS createdAt,
43027
+ entity_birth_fp AS entityBirthFp
43028
+ FROM tasks_display_id_aliases WHERE uid = ?`
43029
+ ).get(row.uid);
43030
+ if (!stored) {
43031
+ nativeDb.prepare(
43032
+ `INSERT INTO tasks_display_id_aliases
43033
+ (uid, entity_table, display_id, entity_uid, reason, origin, displaced_hlc, created_at,
43034
+ entity_birth_fp)
43035
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
43036
+ ).run(
43037
+ row.uid,
43038
+ row.entityTable,
43039
+ row.displayId,
43040
+ row.entityUid,
43041
+ row.reason,
43042
+ row.origin,
43043
+ row.displacedHlc,
43044
+ row.createdAt,
43045
+ row.entityBirthFp
43046
+ );
43047
+ return;
43048
+ }
43049
+ if (!precedes(row, stored)) return;
42532
43050
  nativeDb.prepare(
42533
- `INSERT OR IGNORE INTO tasks_display_id_aliases
42534
- (uid, entity_table, display_id, entity_uid, reason, origin, displaced_hlc, created_at,
42535
- entity_birth_fp)
42536
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
42537
- ).run(
42538
- row.uid,
42539
- row.entityTable,
42540
- row.displayId,
42541
- row.entityUid,
42542
- row.reason,
42543
- row.origin,
42544
- row.displacedHlc,
42545
- row.createdAt,
42546
- row.entityBirthFp
42547
- );
43051
+ `UPDATE tasks_display_id_aliases
43052
+ SET origin = ?, displaced_hlc = ?, created_at = ?,
43053
+ entity_birth_fp = coalesce(entity_birth_fp, ?)
43054
+ WHERE uid = ?`
43055
+ ).run(row.origin, row.displacedHlc, row.createdAt, row.entityBirthFp, row.uid);
42548
43056
  }
42549
43057
  function deleteDisplayIdAliasNative(nativeDb, uid) {
42550
43058
  nativeDb.prepare("DELETE FROM tasks_display_id_aliases WHERE uid = ?").run(uid);
@@ -42606,6 +43114,63 @@ function relinkAcUidNative(nativeDb, acId, uid, birthFp) {
42606
43114
  function clearAcUidGraveyardNative(nativeDb) {
42607
43115
  nativeDb.exec("DELETE FROM tasks_ac_uid_graveyard");
42608
43116
  }
43117
+ function readReplacedTaskBirthNative(nativeDb, taskId) {
43118
+ return nativeDb.prepare("SELECT created_at AS createdAt, uid FROM tasks_tasks WHERE id = ?").get(taskId);
43119
+ }
43120
+ function clearReplacedTaskIdentityNative(nativeDb, taskId, before) {
43121
+ const row = nativeDb.prepare("SELECT uid, birth_fp AS fp, created_at AS createdAt FROM tasks_tasks WHERE id = ?").get(taskId);
43122
+ if (!row || row.uid === null && row.fp === null) return;
43123
+ if (before) {
43124
+ const was = parseStoreTimestamp(before.createdAt);
43125
+ if (was !== null && was === parseStoreTimestamp(row.createdAt)) {
43126
+ return;
43127
+ }
43128
+ }
43129
+ if (rowIdentityShared(nativeDb)) {
43130
+ throw new CleoError(
43131
+ 6 /* VALIDATION_ERROR */,
43132
+ `Task ${taskId} cannot be overwritten by an import: its identity is shared with other devices`,
43133
+ {
43134
+ fix: "Import it under a new id (the default duplicate strategy), or edit the task instead of overwriting it.",
43135
+ details: { field: "taskId", actual: taskId }
43136
+ }
43137
+ );
43138
+ }
43139
+ const has = (table) => nativeDb.prepare("SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) !== void 0;
43140
+ nativeDb.prepare("UPDATE tasks_tasks SET uid = NULL, birth_fp = NULL WHERE id = ?").run(taskId);
43141
+ for (const spec of ROW_IDENTITY.project) {
43142
+ if (spec.kind !== "natural" || !has(spec.table)) continue;
43143
+ const cols = (spec.keyRefs ?? []).filter((r) => r.table === "tasks_tasks").map((r) => r.column);
43144
+ if (cols.length === 0) continue;
43145
+ nativeDb.prepare(
43146
+ `UPDATE main.${quoteIdent2(spec.table)} SET uid = NULL WHERE ${cols.map((c) => `${quoteIdent2(c)} = ?`).join(" OR ")}`
43147
+ ).run(...cols.map(() => taskId));
43148
+ }
43149
+ const children = ["tasks_task_acceptance_criteria_history", "tasks_evidence_ac_bindings"].filter(
43150
+ has
43151
+ );
43152
+ const criteria = has("tasks_task_acceptance_criteria") ? nativeDb.prepare("SELECT id, uid FROM tasks_task_acceptance_criteria WHERE task_id = ?").all(taskId) : [];
43153
+ if (criteria.length > 0) {
43154
+ nativeDb.prepare(
43155
+ "UPDATE tasks_task_acceptance_criteria SET uid = NULL, birth_fp = NULL WHERE task_id = ?"
43156
+ ).run(taskId);
43157
+ }
43158
+ fillRowUids(nativeDb, "project");
43159
+ const newUid = nativeDb.prepare("SELECT uid FROM tasks_task_acceptance_criteria WHERE id = ?");
43160
+ let rekeyed = 0;
43161
+ for (const ac of criteria) {
43162
+ const now = newUid.get(ac.id)?.uid ?? null;
43163
+ for (const table of children) {
43164
+ rekeyed += Number(
43165
+ nativeDb.prepare(
43166
+ `UPDATE main.${quoteIdent2(table)} SET ac_uid = ?, birth_fp = NULL
43167
+ WHERE ac_id = ? OR (? IS NOT NULL AND ac_uid = ?)`
43168
+ ).run(now, ac.id, ac.uid, ac.uid).changes
43169
+ );
43170
+ }
43171
+ }
43172
+ if (rekeyed > 0) fillRowUids(nativeDb, "project");
43173
+ }
42609
43174
  function quoteIdent2(name15) {
42610
43175
  return `"${name15.replaceAll('"', '""')}"`;
42611
43176
  }
@@ -42693,10 +43258,50 @@ function claimPredicate(guard, allowedHolders) {
42693
43258
  if (guard.mode === "force") return void 0;
42694
43259
  const mine = guard.sessionId === null ? void 0 : eq10(holder, guard.sessionId);
42695
43260
  if (guard.mode === "renew" || guard.mode === "release") return mine ?? sql30`0`;
42696
- const related = allowedHolders.length > 0 ? inArray5(holder, [...allowedHolders]) : void 0;
43261
+ const related = allowedHolders.length > 0 ? inArray6(holder, [...allowedHolders]) : void 0;
42697
43262
  const expired = guard.mode === "take-over" ? or5(isNull4(tasksTasks.leaseExpiresAt), lte3(tasksTasks.leaseExpiresAt, guard.now)) : void 0;
42698
43263
  return or5(isNull4(holder), mine, related, expired);
42699
43264
  }
43265
+ function renameRecordKey(fromId) {
43266
+ return `renamed_from:tasks_tasks:${fromId}`;
43267
+ }
43268
+ function hasIdentityMeta(nativeDb) {
43269
+ return nativeDb.prepare(
43270
+ "SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = 'tasks_row_identity_meta'"
43271
+ ).get() !== void 0;
43272
+ }
43273
+ function recordLocalRename(nativeDb, uid, fromId, toId, fromVersion) {
43274
+ if (!hasIdentityMeta(nativeDb)) return;
43275
+ const record2 = { uid, fromVersion, toId, at: (/* @__PURE__ */ new Date()).toISOString() };
43276
+ writeRowIdentityMetaNative(nativeDb, renameRecordKey(fromId), JSON.stringify(record2));
43277
+ }
43278
+ function assertNotRenamed(nativeDb, taskId, guard, current) {
43279
+ if (!nativeDb || !hasIdentityMeta(nativeDb)) return;
43280
+ const raw = nativeDb.prepare("SELECT value FROM tasks_row_identity_meta WHERE key = ?").get(renameRecordKey(taskId));
43281
+ if (!raw) return;
43282
+ let record2;
43283
+ try {
43284
+ record2 = JSON.parse(raw.value);
43285
+ } catch {
43286
+ return;
43287
+ }
43288
+ if (Date.now() - Date.parse(record2.at) > RENAMED_RECENT_MS) return;
43289
+ const renamed = nativeDb.prepare("SELECT id, claimed_by_session AS claimedBySession FROM tasks_tasks WHERE uid = ?").get(record2.uid);
43290
+ if (!renamed || renamed.id === taskId) return;
43291
+ const expected = guard.expectedUpdatedAt;
43292
+ const session = guard.claim?.sessionId ?? null;
43293
+ const holdsRenamedClaim = session !== null && (guard.claim?.mode === "renew" || guard.claim?.mode === "release") && renamed.claimedBySession === session && current?.claimedBySession !== session;
43294
+ const readRenamed = expected !== void 0 && expected === record2.fromVersion && (current === void 0 || taskVersion(current) !== expected);
43295
+ if (!(current === void 0 || holdsRenamedClaim || readRenamed)) return;
43296
+ throw new CleoError(
43297
+ 26 /* TASK_RENAMED */,
43298
+ `Task ${taskId} is now ${renamed.id}: sync re-numbered it after a display-id collision, and ${taskId} ${current ? "names another task" : "names no task"}; nothing was written`,
43299
+ {
43300
+ fix: `Re-read it with 'cleo show ${renamed.id}' and retry against ${renamed.id}.`,
43301
+ details: { field: "taskId", actual: taskId, expected: renamed.id }
43302
+ }
43303
+ );
43304
+ }
42700
43305
  function assertClaimAllowed(taskId, row, guard, allowedHolders) {
42701
43306
  const stored = claimFromColumns(row);
42702
43307
  if (claimAllows(stored, guard.mode, guard.sessionId, guard.now, allowedHolders)) return;
@@ -42871,13 +43476,13 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
42871
43476
  const db = await getDb(cwd);
42872
43477
  const conditions = [];
42873
43478
  if (query.ids && query.ids.length > 0) {
42874
- conditions.push(inArray5(tasksAuditLog.id, [...query.ids]));
43479
+ conditions.push(inArray6(tasksAuditLog.id, [...query.ids]));
42875
43480
  }
42876
43481
  if (query.taskIds && query.taskIds.length > 0) {
42877
- conditions.push(inArray5(tasksAuditLog.taskId, [...query.taskIds]));
43482
+ conditions.push(inArray6(tasksAuditLog.taskId, [...query.taskIds]));
42878
43483
  }
42879
43484
  if (query.actions && query.actions.length > 0) {
42880
- conditions.push(inArray5(tasksAuditLog.action, [...query.actions]));
43485
+ conditions.push(inArray6(tasksAuditLog.action, [...query.actions]));
42881
43486
  }
42882
43487
  if (query.since) {
42883
43488
  conditions.push(sql30`${tasksAuditLog.timestamp} >= ${query.since}`);
@@ -42910,8 +43515,13 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
42910
43515
  async upsertSingleTask(task) {
42911
43516
  await accessor.transaction((tx) => tx.upsertSingleTask(task));
42912
43517
  },
42913
- async insertNewTask(task) {
42914
- await accessor.transaction((tx) => tx.insertNewTask(task));
43518
+ async insertNewTask(task, identity) {
43519
+ await accessor.transaction((tx) => tx.insertNewTask(task, identity));
43520
+ },
43521
+ async getTaskIdentities(taskIds) {
43522
+ if (taskIds.length === 0) return [];
43523
+ const db = await getDb(cwd);
43524
+ return db.select({ id: tasksTasks.id, uid: tasksTasks.uid, birthFp: tasksTasks.birthFp }).from(tasksTasks).where(inArray6(tasksTasks.id, taskIds)).all();
42915
43525
  },
42916
43526
  async addRelation(taskId, relatedTo, relationType, reason) {
42917
43527
  return accessor.transaction(async () => {
@@ -43088,7 +43698,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43088
43698
  if (!statuses.includes("archived")) {
43089
43699
  conditions.push(ne2(tasksTasks.status, "archived"));
43090
43700
  }
43091
- conditions.push(inArray5(tasksTasks.status, statuses));
43701
+ conditions.push(inArray6(tasksTasks.status, statuses));
43092
43702
  } else if (filters.excludeStatus) {
43093
43703
  const excluded = Array.isArray(filters.excludeStatus) ? filters.excludeStatus : [filters.excludeStatus];
43094
43704
  for (const s of excluded) {
@@ -43102,11 +43712,11 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43102
43712
  if (filters.phase) conditions.push(eq10(tasksTasks.phase, filters.phase));
43103
43713
  if (filters.severity) {
43104
43714
  const severities = Array.isArray(filters.severity) ? filters.severity : [filters.severity];
43105
- conditions.push(inArray5(tasksTasks.severity, severities));
43715
+ conditions.push(inArray6(tasksTasks.severity, severities));
43106
43716
  }
43107
43717
  if (filters.kind) {
43108
43718
  const kinds = Array.isArray(filters.kind) ? filters.kind : [filters.kind];
43109
- conditions.push(inArray5(tasksTasks.kind, kinds));
43719
+ conditions.push(inArray6(tasksTasks.kind, kinds));
43110
43720
  }
43111
43721
  if (filters.parentId !== void 0) {
43112
43722
  if (filters.parentId === null) {
@@ -43127,7 +43737,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43127
43737
  }
43128
43738
  if (filters.label) {
43129
43739
  const labelMatchIds = db.select({ taskId: tasksTaskLabels.taskId }).from(tasksTaskLabels).where(eq10(tasksTaskLabels.label, filters.label));
43130
- conditions.push(inArray5(tasksTasks.id, labelMatchIds));
43740
+ conditions.push(inArray6(tasksTasks.id, labelMatchIds));
43131
43741
  }
43132
43742
  const where = conditions.length > 0 ? and8(...conditions) : void 0;
43133
43743
  const countResult = await db.select({ count: sql30`count(*)` }).from(tasksTasks).where(where).get();
@@ -43170,7 +43780,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43170
43780
  const conditions = [];
43171
43781
  if (filters?.status) {
43172
43782
  const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
43173
- conditions.push(inArray5(tasksTasks.status, statuses));
43783
+ conditions.push(inArray6(tasksTasks.status, statuses));
43174
43784
  } else {
43175
43785
  conditions.push(ne2(tasksTasks.status, "archived"));
43176
43786
  }
@@ -43235,7 +43845,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43235
43845
  if (idRows.length === 0) return [];
43236
43846
  const db = await getDb(cwd);
43237
43847
  const ids = idRows.map((r) => r.id);
43238
- const taskRows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, ids)).all();
43848
+ const taskRows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, ids)).all();
43239
43849
  const tasks2 = taskRows.map(rowToTask);
43240
43850
  if (tasks2.length > 0) {
43241
43851
  const allIds = await getAllTaskIds();
@@ -43264,7 +43874,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43264
43874
  if (idRows.length === 0) return [];
43265
43875
  const db = await getDb(cwd);
43266
43876
  const ids = idRows.map((r) => r.id);
43267
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, ids)).all();
43877
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, ids)).all();
43268
43878
  const tasks2 = rows.map(rowToTask);
43269
43879
  if (tasks2.length > 0) {
43270
43880
  const allIds = await getAllTaskIds();
@@ -43278,7 +43888,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43278
43888
  const depRows = await db.select().from(tasksTaskDependencies).where(eq10(tasksTaskDependencies.dependsOn, taskId)).all();
43279
43889
  if (depRows.length === 0) return [];
43280
43890
  const dependentIds = depRows.map((r) => r.taskId);
43281
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, dependentIds)).all();
43891
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, dependentIds)).all();
43282
43892
  const tasks2 = rows.map(rowToTask);
43283
43893
  if (tasks2.length > 0) {
43284
43894
  const allIds = await getAllTaskIds();
@@ -43308,7 +43918,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43308
43918
  async loadTasks(taskIds) {
43309
43919
  if (taskIds.length === 0) return [];
43310
43920
  const db = await getDb(cwd);
43311
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, taskIds)).all();
43921
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, taskIds)).all();
43312
43922
  const tasks2 = rows.map(rowToTask);
43313
43923
  if (tasks2.length > 0) {
43314
43924
  const allIds = await getAllTaskIds();
@@ -43362,6 +43972,9 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43362
43972
  createdAt: tasksTasks.createdAt,
43363
43973
  ...CLAIM_COLUMNS
43364
43974
  }).from(tasksTasks).where(eq10(tasksTasks.id, taskId)).limit(1).all();
43975
+ if (guard?.claim || guard?.expectedUpdatedAt !== void 0) {
43976
+ assertNotRenamed(getNativeTasksDb(cwd), taskId, guard, current);
43977
+ }
43365
43978
  if (!current) throw new Error(`Task not found: ${taskId}`);
43366
43979
  const claimGuard = guard?.claim;
43367
43980
  const allowedHolders = claimGuard ? await claimAllowedHolders(db, claimGuard) : [];
@@ -43439,24 +44052,37 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43439
44052
  nativeDb.prepare(`SAVEPOINT ${spName}`).run();
43440
44053
  }
43441
44054
  try {
44055
+ const replacedBirths = /* @__PURE__ */ new Map();
43442
44056
  const tx = {
43443
44057
  async upsertSingleTask(task) {
43444
44058
  scope.assertActive();
43445
44059
  return accessor.transaction(async () => {
43446
44060
  scope.assertActive();
44061
+ if (rowUidFillEnabled() && !replacedBirths.has(task.id)) {
44062
+ const before = readReplacedTaskBirthNative(nativeDb, task.id);
44063
+ if (before) replacedBirths.set(task.id, before);
44064
+ }
43447
44065
  const row = taskToRow(task);
43448
44066
  await upsertTask(db, row);
43449
44067
  await updateDependencies(db, task.id, task.depends ?? []);
43450
44068
  });
43451
44069
  },
43452
- async insertNewTask(task) {
44070
+ async insertNewTask(task, identity) {
43453
44071
  scope.assertActive();
43454
44072
  return accessor.transaction(async () => {
43455
44073
  scope.assertActive();
43456
- await insertNewTask(db, taskToRow(task));
44074
+ await insertNewTask(db, taskToRow(task), identity);
43457
44075
  await updateDependencies(db, task.id, task.depends ?? []);
43458
44076
  });
43459
44077
  },
44078
+ async clearTaskIdentity(taskId) {
44079
+ scope.assertActive();
44080
+ if (!rowUidFillEnabled()) return;
44081
+ return accessor.transaction(async () => {
44082
+ scope.assertActive();
44083
+ clearReplacedTaskIdentityNative(nativeDb, taskId, replacedBirths.get(taskId));
44084
+ });
44085
+ },
43460
44086
  async archiveSingleTask(taskId, fields) {
43461
44087
  scope.assertActive();
43462
44088
  return accessor.transaction(async () => {
@@ -43603,7 +44229,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43603
44229
  const owned = await db.select({ id: tasksTaskAcceptanceCriteria.id }).from(tasksTaskAcceptanceCriteria).where(
43604
44230
  and8(
43605
44231
  eq10(tasksTaskAcceptanceCriteria.taskId, taskId),
43606
- inArray5(tasksTaskAcceptanceCriteria.id, ids)
44232
+ inArray6(tasksTaskAcceptanceCriteria.id, ids)
43607
44233
  )
43608
44234
  ).all();
43609
44235
  await pruneAcBindingsForAcIds(
@@ -43616,7 +44242,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43616
44242
  await db.delete(tasksTaskAcceptanceCriteria).where(
43617
44243
  and8(
43618
44244
  eq10(tasksTaskAcceptanceCriteria.taskId, taskId),
43619
- inArray5(tasksTaskAcceptanceCriteria.id, ids)
44245
+ inArray6(tasksTaskAcceptanceCriteria.id, ids)
43620
44246
  )
43621
44247
  ).run();
43622
44248
  });
@@ -43875,7 +44501,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43875
44501
  };
43876
44502
  return accessor;
43877
44503
  }
43878
- var _txSavepointCounter, taskTransactionQueue, TaskTransactionScope, taskTransactionContext, CLAIM_COLUMNS;
44504
+ var _txSavepointCounter, taskTransactionQueue, TaskTransactionScope, taskTransactionContext, CLAIM_COLUMNS, RENAMED_RECENT_MS;
43879
44505
  var init_sqlite_data_accessor = __esm({
43880
44506
  "packages/core/src/store/sqlite-data-accessor.ts"() {
43881
44507
  "use strict";
@@ -43889,6 +44515,8 @@ var init_sqlite_data_accessor = __esm({
43889
44515
  init_cross_db_cleanup();
43890
44516
  init_db_helpers();
43891
44517
  init_row_identity();
44518
+ init_row_identity_flag();
44519
+ init_row_identity_registry();
43892
44520
  init_audit2();
43893
44521
  init_session_store();
43894
44522
  init_sqlite3();
@@ -43921,6 +44549,7 @@ var init_sqlite_data_accessor = __esm({
43921
44549
  claimedAt: tasksTasks.claimedAt,
43922
44550
  leaseExpiresAt: tasksTasks.leaseExpiresAt
43923
44551
  };
44552
+ RENAMED_RECENT_MS = 24 * 60 * 60 * 1e3;
43924
44553
  registerRowIdentityWriters({
43925
44554
  relinkAcUidNative,
43926
44555
  clearAcUidGraveyardNative,
@@ -44391,7 +45020,7 @@ __export(governor_exports, {
44391
45020
  resolveGovernorMode: () => resolveGovernorMode,
44392
45021
  slotFileCount: () => slotFileCount
44393
45022
  });
44394
- import { existsSync as existsSync19, mkdirSync as mkdirSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
45023
+ import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
44395
45024
  import { availableParallelism, totalmem } from "node:os";
44396
45025
  import { join as join25 } from "node:path";
44397
45026
  import lockfile2 from "proper-lockfile";
@@ -44465,11 +45094,11 @@ function governorSlotDir(cls) {
44465
45094
  return join25(getCleoHome(), "locks", `resource-${cls}`);
44466
45095
  }
44467
45096
  function ensureSlotFiles(dir, count) {
44468
- if (!existsSync19(dir)) mkdirSync6(dir, { recursive: true });
45097
+ if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
44469
45098
  const paths = [];
44470
45099
  for (let i = 0; i < count; i++) {
44471
45100
  const p = join25(dir, `slot-${i}.lock`);
44472
- if (!existsSync19(p)) writeFileSync5(p, "", { flag: "a" });
45101
+ if (!existsSync20(p)) writeFileSync5(p, "", { flag: "a" });
44473
45102
  paths.push(p);
44474
45103
  }
44475
45104
  return paths;
@@ -44509,7 +45138,7 @@ function shuffledIndices(n) {
44509
45138
  }
44510
45139
  async function countHeldSlots(cls, budget) {
44511
45140
  const dir = governorSlotDir(cls);
44512
- if (!existsSync19(dir)) return 0;
45141
+ if (!existsSync20(dir)) return 0;
44513
45142
  const slots = ensureSlotFiles(dir, budget);
44514
45143
  let held = 0;
44515
45144
  for (const path of slots) {
@@ -44524,7 +45153,7 @@ async function countHeldSlots(cls, budget) {
44524
45153
  }
44525
45154
  function slotFileCount(cls) {
44526
45155
  const dir = governorSlotDir(cls);
44527
- if (!existsSync19(dir)) return 0;
45156
+ if (!existsSync20(dir)) return 0;
44528
45157
  return readdirSync5(dir).filter((f) => f.startsWith("slot-") && f.endsWith(".lock")).length;
44529
45158
  }
44530
45159
  var _log2, _cachedMode2, _supervisorDegradeLogged, MB, STALE_MS, _supervisorHolderSeq, ResourceGovernor, governor;
@@ -45022,7 +45651,7 @@ var init_recovery = __esm({
45022
45651
  // packages/core/src/store/exodus/archive.ts
45023
45652
  import {
45024
45653
  copyFileSync as copyFileSync5,
45025
- existsSync as existsSync20,
45654
+ existsSync as existsSync21,
45026
45655
  mkdirSync as mkdirSync7,
45027
45656
  readFileSync as readFileSync8,
45028
45657
  renameSync as renameSync7,
@@ -45050,7 +45679,7 @@ function hasExodusCompleteMarker(scope, cwd, targetDbPath, nativeDb) {
45050
45679
  if (targetDbPath) throw error48;
45051
45680
  return false;
45052
45681
  }
45053
- if (!existsSync20(markerPath)) return false;
45682
+ if (!existsSync21(markerPath)) return false;
45054
45683
  const marker16 = markerIdentity.parse(JSON.parse(readFileSync8(markerPath, "utf8")));
45055
45684
  if (nativeDb && (!marker16.databaseIdentity || !hasExodusDatabaseIdentity(nativeDb, marker16.databaseIdentity)))
45056
45685
  throw new Error(
@@ -45080,7 +45709,7 @@ function writeExodusCompleteMarker(scope, archivedSources, cwd, targetDbPath, da
45080
45709
  function moveFileInto(srcPath, destDir) {
45081
45710
  mkdirSync7(destDir, { recursive: true });
45082
45711
  let dest = join26(destDir, basename9(srcPath));
45083
- if (existsSync20(dest)) {
45712
+ if (existsSync21(dest)) {
45084
45713
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
45085
45714
  dest = join26(destDir, `${basename9(srcPath)}.${stamp}`);
45086
45715
  }
@@ -45093,14 +45722,14 @@ function moveFileInto(srcPath, destDir) {
45093
45722
  return dest;
45094
45723
  }
45095
45724
  function archiveSourceDb(source, cwd, targetDbPath) {
45096
- if (!existsSync20(source.path)) {
45725
+ if (!existsSync21(source.path)) {
45097
45726
  return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
45098
45727
  }
45099
45728
  const destDir = exodusArchiveDir(source.targetScope, cwd, targetDbPath);
45100
45729
  const archivedTo = moveFileInto(source.path, destDir);
45101
45730
  for (const suffix of SIDECAR_SUFFIXES) {
45102
45731
  const sidecar = `${source.path}${suffix}`;
45103
- if (existsSync20(sidecar)) {
45732
+ if (existsSync21(sidecar)) {
45104
45733
  try {
45105
45734
  moveFileInto(sidecar, destDir);
45106
45735
  } catch (err) {
@@ -45148,7 +45777,7 @@ function detectStrandedResidue(sources, cwd) {
45148
45777
  const stranded = [];
45149
45778
  for (const source of sources) {
45150
45779
  if (!markedScopes.has(source.targetScope)) continue;
45151
- if (existsSync20(source.path)) {
45780
+ if (existsSync21(source.path)) {
45152
45781
  stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
45153
45782
  }
45154
45783
  }
@@ -45189,13 +45818,13 @@ var init_archive2 = __esm({
45189
45818
  });
45190
45819
 
45191
45820
  // packages/core/src/store/exodus/count-parity.ts
45192
- import { existsSync as existsSync21 } from "node:fs";
45821
+ import { existsSync as existsSync22 } from "node:fs";
45193
45822
  function listTables2(db) {
45194
45823
  return db.prepare(
45195
45824
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name"
45196
45825
  ).all().map((r) => r.name);
45197
45826
  }
45198
- function tableExists2(db, tableName) {
45827
+ function tableExists3(db, tableName) {
45199
45828
  const escaped = tableName.replace(/'/g, "''");
45200
45829
  return db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='${escaped}'`).get() !== void 0;
45201
45830
  }
@@ -45210,14 +45839,14 @@ function rowCount(db, tableName) {
45210
45839
  function computeCountParity(sources, projectDbPath, globalDbPath) {
45211
45840
  const entries = [];
45212
45841
  let skipped = 0;
45213
- if (!existsSync21(projectDbPath) || !existsSync21(globalDbPath)) {
45842
+ if (!existsSync22(projectDbPath) || !existsSync22(globalDbPath)) {
45214
45843
  return { ok: false, entries: [], deficits: [], checked: 0, skipped: 0 };
45215
45844
  }
45216
45845
  const projectSnap = openCleoDbSnapshot(projectDbPath, { readOnly: true });
45217
45846
  const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
45218
45847
  try {
45219
45848
  for (const src of sources) {
45220
- if (!existsSync21(src.path)) continue;
45849
+ if (!existsSync22(src.path)) continue;
45221
45850
  const srcSnap = openCleoDbSnapshot(src.path, { readOnly: true });
45222
45851
  try {
45223
45852
  for (const legacyTable of listTables2(srcSnap.db)) {
@@ -45234,7 +45863,7 @@ function computeCountParity(sources, projectDbPath, globalDbPath) {
45234
45863
  skipped++;
45235
45864
  continue;
45236
45865
  }
45237
- const targetCount = tableExists2(targetSnap.db, targetTable) ? rowCount(targetSnap.db, targetTable) ?? 0 : 0;
45866
+ const targetCount = tableExists3(targetSnap.db, targetTable) ? rowCount(targetSnap.db, targetTable) ?? 0 : 0;
45238
45867
  const deficit = targetCount < sourceCount ? sourceCount - targetCount : 0;
45239
45868
  entries.push({
45240
45869
  sourceDb: src.name,
@@ -45275,7 +45904,7 @@ var init_count_parity = __esm({
45275
45904
  });
45276
45905
 
45277
45906
  // packages/core/src/store/exodus/plan.ts
45278
- import { existsSync as existsSync22, readdirSync as readdirSync6, statfsSync as statfsSync2, statSync as statSync6 } from "node:fs";
45907
+ import { existsSync as existsSync23, readdirSync as readdirSync6, statfsSync as statfsSync2, statSync as statSync6 } from "node:fs";
45279
45908
  import { join as join27 } from "node:path";
45280
45909
  function computeRequiredBytes(totalSourceBytes, largestSourceBytes) {
45281
45910
  return Math.ceil(STAGING_HEADROOM_FACTOR * largestSourceBytes) + totalSourceBytes;
@@ -45377,7 +46006,7 @@ function buildExodusPlan(cwd) {
45377
46006
  };
45378
46007
  }
45379
46008
  function sourcesPresent(sources) {
45380
- return sources.some((s) => existsSync22(s.path));
46009
+ return sources.some((s) => existsSync23(s.path));
45381
46010
  }
45382
46011
  var STAGING_HEADROOM_FACTOR, STAGING_COPY_SKIP_THRESHOLD_BYTES;
45383
46012
  var init_plan2 = __esm({
@@ -45391,18 +46020,18 @@ var init_plan2 = __esm({
45391
46020
  });
45392
46021
 
45393
46022
  // packages/core/src/store/exodus/health.ts
45394
- import { existsSync as existsSync23, statSync as statSync7 } from "node:fs";
46023
+ import { existsSync as existsSync24, statSync as statSync7 } from "node:fs";
45395
46024
  function buildExodusHealth(cwd) {
45396
46025
  const plan = buildExodusPlan(cwd);
45397
- const anyLegacyPresent = plan.sources.some((s) => existsSync23(s.path));
46026
+ const anyLegacyPresent = plan.sources.some((s) => existsSync24(s.path));
45398
46027
  const parity = anyLegacyPresent ? computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath) : { ok: true, entries: [], deficits: [], checked: 0, skipped: 0 };
45399
46028
  const consolidatedExistsByScope = {
45400
- project: existsSync23(plan.projectDbPath),
45401
- global: existsSync23(plan.globalDbPath)
46029
+ project: existsSync24(plan.projectDbPath),
46030
+ global: existsSync24(plan.globalDbPath)
45402
46031
  };
45403
46032
  const buildScope = (scope) => {
45404
46033
  const sources = plan.sources.filter((s) => s.targetScope === scope).map((s) => {
45405
- const present = existsSync23(s.path);
46034
+ const present = existsSync24(s.path);
45406
46035
  let bytes = 0;
45407
46036
  if (present) {
45408
46037
  try {
@@ -45507,7 +46136,7 @@ var init_types = __esm({
45507
46136
  // packages/core/src/store/exodus/migrate.ts
45508
46137
  import {
45509
46138
  copyFileSync as copyFileSync6,
45510
- existsSync as existsSync24,
46139
+ existsSync as existsSync25,
45511
46140
  mkdirSync as mkdirSync8,
45512
46141
  readFileSync as readFileSync9,
45513
46142
  renameSync as renameSync8,
@@ -45553,7 +46182,7 @@ function writeJournal(stagingDir, journal) {
45553
46182
  }
45554
46183
  function readJournal(stagingDir) {
45555
46184
  const journalPath = join28(stagingDir, JOURNAL_FILENAME);
45556
- if (!existsSync24(journalPath)) return null;
46185
+ if (!existsSync25(journalPath)) return null;
45557
46186
  try {
45558
46187
  return JSON.parse(readFileSync9(journalPath, "utf8"));
45559
46188
  } catch {
@@ -45563,7 +46192,7 @@ function readJournal(stagingDir) {
45563
46192
  function clearExodusJournal(stagingDir) {
45564
46193
  const journalPath = join28(stagingDir, JOURNAL_FILENAME);
45565
46194
  try {
45566
- if (!existsSync24(journalPath)) return false;
46195
+ if (!existsSync25(journalPath)) return false;
45567
46196
  unlinkSync5(journalPath);
45568
46197
  log10.info(
45569
46198
  { stagingDir },
@@ -45859,7 +46488,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45859
46488
  mkdirSync8(stagingDir, { recursive: true });
45860
46489
  let sqliteVersion = "unknown";
45861
46490
  for (const src of sources) {
45862
- if (existsSync24(src.path)) {
46491
+ if (existsSync25(src.path)) {
45863
46492
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
45864
46493
  sqliteVersion = getSqliteVersion(snap.db);
45865
46494
  snap.close();
@@ -45889,7 +46518,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45889
46518
  let reseedProject = null;
45890
46519
  try {
45891
46520
  for (const src of sources) {
45892
- if (!existsSync24(src.path)) continue;
46521
+ if (!existsSync25(src.path)) continue;
45893
46522
  const backupDest = join28(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
45894
46523
  const srcBytes = safeStatBytes(src.path);
45895
46524
  const skipStagingCopy = srcBytes > plan.stagingCopyThresholdBytes;
@@ -45897,7 +46526,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45897
46526
  onProgress?.(
45898
46527
  `Skipping full staging copy of ${src.name} (${srcBytes} bytes > ${plan.stagingCopyThresholdBytes} threshold) \u2014 source is archived, not deleted, on success.`
45899
46528
  );
45900
- } else if (!existsSync24(backupDest)) {
46529
+ } else if (!existsSync25(backupDest)) {
45901
46530
  onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
45902
46531
  copyFileSync6(src.path, backupDest);
45903
46532
  backupPaths.push(backupDest);
@@ -45930,8 +46559,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45930
46559
  }
45931
46560
  prepareExodusRecovery(projectNative, stagingDir);
45932
46561
  if (globalNative !== null) prepareExodusRecovery(globalNative, stagingDir);
45933
- const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync24(s.path));
45934
- const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync24(s.path));
46562
+ const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync25(s.path));
46563
+ const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync25(s.path));
45935
46564
  if (globalNative === null && globalSources.length > 0)
45936
46565
  throw new Error("A projectOnly exodus run was given global-scope sources");
45937
46566
  await migrateScope(
@@ -46180,7 +46809,8 @@ var init_migrate = __esm({
46180
46809
  GRANDFATHERED_GUARD_TRIGGERS = [
46181
46810
  "tasks_tasks_parent_type_matrix_insert",
46182
46811
  "tasks_task_relations_non_containment_insert",
46183
- "tasks_task_acceptance_child_target_insert"
46812
+ "tasks_task_acceptance_child_target_insert",
46813
+ "tasks_task_dependencies_cycle_guard_insert"
46184
46814
  ];
46185
46815
  SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
46186
46816
  ["conduit", "seconds"],
@@ -46239,7 +46869,7 @@ __export(reconcile_exports, {
46239
46869
  reconcileSupersededStores: () => reconcileSupersededStores,
46240
46870
  unmigratedBareSources: () => unmigratedBareSources
46241
46871
  });
46242
- import { copyFileSync as copyFileSync7, existsSync as existsSync25, mkdirSync as mkdirSync9, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
46872
+ import { copyFileSync as copyFileSync7, existsSync as existsSync26, mkdirSync as mkdirSync9, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
46243
46873
  import { tmpdir as tmpdir4 } from "node:os";
46244
46874
  import { basename as basename10, join as join29 } from "node:path";
46245
46875
  function ident2(name15) {
@@ -46310,7 +46940,7 @@ function assessSupersededProjectStores(liveStorePath, sources, resolveTarget = r
46310
46940
  }
46311
46941
  function legacySourcesHoldRows(sources) {
46312
46942
  for (const src of sources) {
46313
- if (!existsSync25(src.path)) continue;
46943
+ if (!existsSync26(src.path)) continue;
46314
46944
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
46315
46945
  try {
46316
46946
  for (const table of orderTablesForCopy(snap.db)) {
@@ -46558,8 +47188,8 @@ async function reconcileSuppressed(projectRoot, options, liveStorePath) {
46558
47188
  }
46559
47189
  }
46560
47190
  async function reconcileWithScratch(projectRoot, dryRun, additive, liveStorePath, plan, resolveTarget, scratch) {
46561
- const fileSources = plan.sources.filter((s) => s.targetScope === "project" && existsSync25(s.path));
46562
- const bare = !additive && existsSync25(liveStorePath) ? await bareTaskCoreSource(liveStorePath, resolveTarget, scratch) : null;
47191
+ const fileSources = plan.sources.filter((s) => s.targetScope === "project" && existsSync26(s.path));
47192
+ const bare = !additive && existsSync26(liveStorePath) ? await bareTaskCoreSource(liveStorePath, resolveTarget, scratch) : null;
46563
47193
  const copyResolver = additive ? (sourceName, legacyTable) => {
46564
47194
  const r = resolveTarget(sourceName, legacyTable);
46565
47195
  return r.kind !== "skip" && isLiveAuthoritative(sourceName, legacyTable, r.targetName) ? { kind: "skip", reason: "additive: live-authoritative task graph" } : r;
@@ -46578,7 +47208,7 @@ async function reconcileWithScratch(projectRoot, dryRun, additive, liveStorePath
46578
47208
  stagingDir: null,
46579
47209
  receiptPath: null
46580
47210
  };
46581
- if (sources.length === 0 || !existsSync25(liveStorePath)) {
47211
+ if (sources.length === 0 || !existsSync26(liveStorePath)) {
46582
47212
  return {
46583
47213
  ...base,
46584
47214
  outcome: "nothing-to-reconcile",
@@ -46770,11 +47400,11 @@ var init_seal = __esm({
46770
47400
  });
46771
47401
 
46772
47402
  // packages/core/src/store/exodus/status.ts
46773
- import { existsSync as existsSync26, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync9 } from "node:fs";
47403
+ import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync9 } from "node:fs";
46774
47404
  import { join as join30 } from "node:path";
46775
47405
  function readJournal2(stagingDir) {
46776
47406
  const p = join30(stagingDir, JOURNAL_FILENAME2);
46777
- if (!existsSync26(p)) return null;
47407
+ if (!existsSync27(p)) return null;
46778
47408
  try {
46779
47409
  return JSON.parse(readFileSync10(p, "utf8"));
46780
47410
  } catch {
@@ -46806,15 +47436,15 @@ function runExodusStatus(cwd) {
46806
47436
  const sourcesInfo = plan.sources.map((s) => ({
46807
47437
  name: s.name,
46808
47438
  path: s.path,
46809
- exists: existsSync26(s.path),
47439
+ exists: existsSync27(s.path),
46810
47440
  bytes: safeBytes(s.path)
46811
47441
  }));
46812
47442
  return {
46813
47443
  hasStaging: latestStaging !== null,
46814
47444
  stagingDir: latestStaging,
46815
47445
  journal,
46816
- projectDbExists: existsSync26(projectDbPath),
46817
- globalDbExists: existsSync26(globalDbPath),
47446
+ projectDbExists: existsSync27(projectDbPath),
47447
+ globalDbExists: existsSync27(globalDbPath),
46818
47448
  sourcesPresent: sourcesInfo.some((s) => s.exists),
46819
47449
  sources: sourcesInfo
46820
47450
  };
@@ -46831,7 +47461,7 @@ var init_status = __esm({
46831
47461
  });
46832
47462
 
46833
47463
  // packages/core/src/store/exodus/verify-migration.ts
46834
- import { existsSync as existsSync27 } from "node:fs";
47464
+ import { existsSync as existsSync28 } from "node:fs";
46835
47465
  import { createRequire as createRequire10 } from "node:module";
46836
47466
  function orderByClause(db, tableName) {
46837
47467
  try {
@@ -46946,7 +47576,7 @@ function listTables3(db) {
46946
47576
  ).all();
46947
47577
  return rows.map((r) => r.name);
46948
47578
  }
46949
- function tableExists3(db, tableName) {
47579
+ function tableExists4(db, tableName) {
46950
47580
  try {
46951
47581
  const escaped = tableName.replace(/'/g, "''");
46952
47582
  return db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='${escaped}'`).get() !== void 0;
@@ -47096,10 +47726,10 @@ function orphanAlreadyInSource(targetDb, fk, sources, resolveTarget = resolveCon
47096
47726
  const anyFeeder = (targetTable, probe) => {
47097
47727
  for (const feeder of feedersOf(targetTable)) {
47098
47728
  const src = sources.find((s) => s.name === feeder.sourceName);
47099
- if (src === void 0 || !existsSync27(src.path)) continue;
47729
+ if (src === void 0 || !existsSync28(src.path)) continue;
47100
47730
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
47101
47731
  try {
47102
- if (!tableExists3(snap.db, feeder.legacyTable)) continue;
47732
+ if (!tableExists4(snap.db, feeder.legacyTable)) continue;
47103
47733
  const cols = new Set(
47104
47734
  snap.db.prepare(`PRAGMA table_info("${feeder.legacyTable}")`).all().map((c) => c.name)
47105
47735
  );
@@ -47151,7 +47781,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47151
47781
  const preExistingForeignKeyViolations = [];
47152
47782
  const failureLines = [];
47153
47783
  const sourceOrphanSigs = /* @__PURE__ */ new Set();
47154
- if (!existsSync27(projectDbPath)) {
47784
+ if (!existsSync28(projectDbPath)) {
47155
47785
  return {
47156
47786
  ok: false,
47157
47787
  tables: [],
@@ -47162,7 +47792,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47162
47792
  error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
47163
47793
  };
47164
47794
  }
47165
- if (!existsSync27(globalDbPath)) {
47795
+ if (!existsSync28(globalDbPath)) {
47166
47796
  return {
47167
47797
  ok: false,
47168
47798
  tables: [],
@@ -47177,7 +47807,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47177
47807
  const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
47178
47808
  try {
47179
47809
  for (const src of sources) {
47180
- if (!existsSync27(src.path)) {
47810
+ if (!existsSync28(src.path)) {
47181
47811
  onProgress?.(`Skipping ${src.name} (not present)`);
47182
47812
  continue;
47183
47813
  }
@@ -47302,7 +47932,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47302
47932
  targetOrphans.push(...foreignKeyCheck(globalSnap.db, "global"));
47303
47933
  foreignKeyViolations.push(...targetOrphans);
47304
47934
  for (const fk of targetOrphans) {
47305
- const orphanDb = tableExists3(projectSnap.db, fk.table) ? projectSnap.db : globalSnap.db;
47935
+ const orphanDb = tableExists4(projectSnap.db, fk.table) ? projectSnap.db : globalSnap.db;
47306
47936
  const sig = orphanSignature(orphanDb, fk);
47307
47937
  const preExisting = sourceOrphanSigs.has(sig) || orphanAlreadyInSource(orphanDb, fk, sources, resolveTarget);
47308
47938
  if (preExisting) {
@@ -47468,7 +48098,7 @@ __export(on_open_exports, {
47468
48098
  maybeRunExodusOnOpen: () => maybeRunExodusOnOpen,
47469
48099
  withExodusOnOpenSuppressed: () => withExodusOnOpenSuppressed
47470
48100
  });
47471
- import { existsSync as existsSync28, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
48101
+ import { existsSync as existsSync29, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
47472
48102
  import { tmpdir as tmpdir5 } from "node:os";
47473
48103
  import { join as join31, resolve as resolve13 } from "node:path";
47474
48104
  async function withExodusOnOpenSuppressed(dbPath, fn) {
@@ -47510,7 +48140,7 @@ function remedyFor(scope) {
47510
48140
  }
47511
48141
  async function strandedLegacySources(scope, cwd) {
47512
48142
  const { buildExodusPlan: buildExodusPlan2, legacySourcesHoldRows: legacySourcesHoldRows2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
47513
- return buildExodusPlan2(cwd).sources.filter((s) => s.targetScope === scope && existsSync28(s.path)).filter((s) => legacySourcesHoldRows2([s])).map((s) => s.name);
48143
+ return buildExodusPlan2(cwd).sources.filter((s) => s.targetScope === scope && existsSync29(s.path)).filter((s) => legacySourcesHoldRows2([s])).map((s) => s.name);
47514
48144
  }
47515
48145
  async function exodusStoreRewriteRefusal(dbPath, cwd) {
47516
48146
  const { assertOwnerStoreRewriteConfirmed: assertOwnerStoreRewriteConfirmed2, invocationDirectory: invocationDirectory2 } = await Promise.resolve().then(() => (init_worktree_isolation_guard(), worktree_isolation_guard_exports));
@@ -47642,7 +48272,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47642
48272
  };
47643
48273
  }
47644
48274
  const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
47645
- if (!scopeSources.some((s) => existsSync28(s.path))) {
48275
+ if (!scopeSources.some((s) => existsSync29(s.path))) {
47646
48276
  return {
47647
48277
  outcome: "skipped",
47648
48278
  reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
@@ -47667,7 +48297,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47667
48297
  {
47668
48298
  scope,
47669
48299
  dbPath,
47670
- sources: plan.sources.filter((s) => existsSync28(s.path)).map((s) => s.name)
48300
+ sources: plan.sources.filter((s) => existsSync29(s.path)).map((s) => s.name)
47671
48301
  },
47672
48302
  "exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
47673
48303
  );
@@ -47680,7 +48310,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47680
48310
  const bare = scope === "project" ? await unmigratedBareSources2(
47681
48311
  dbPath,
47682
48312
  resolveTarget,
47683
- plan.sources.find((s) => s.name === "tasks" && existsSync28(s.path))?.path,
48313
+ plan.sources.find((s) => s.name === "tasks" && existsSync29(s.path))?.path,
47684
48314
  bareScratch
47685
48315
  ) : { first: [], last: [] };
47686
48316
  const migratePlan = { ...plan, sources: [...bare.first, ...plan.sources, ...bare.last] };
@@ -47744,7 +48374,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47744
48374
  "exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
47745
48375
  );
47746
48376
  try {
47747
- const consumed = plan.sources.filter((s) => existsSync28(s.path));
48377
+ const consumed = plan.sources.filter((s) => existsSync29(s.path));
47748
48378
  const identities = await sealTargets(plan, consumed);
47749
48379
  const archiveResult = archiveMigratedSources(consumed, cwd, plan, identities);
47750
48380
  log14.info(
@@ -47819,7 +48449,7 @@ __export(dual_scope_db_exports, {
47819
48449
  upsertIdempotent: () => upsertIdempotent,
47820
48450
  withStoreSchemaOwnership: () => withStoreSchemaOwnership
47821
48451
  });
47822
- import { existsSync as existsSync29, mkdirSync as mkdirSync10 } from "node:fs";
48452
+ import { existsSync as existsSync30, mkdirSync as mkdirSync10 } from "node:fs";
47823
48453
  import { createRequire as createRequire11 } from "node:module";
47824
48454
  import { dirname as dirname20, join as join32, resolve as resolve14 } from "node:path";
47825
48455
  function getDualScopeNativeDb(handle) {
@@ -47892,11 +48522,15 @@ async function openDualScopeDb(scope, cwd, options) {
47892
48522
  open
47893
48523
  ) : open();
47894
48524
  }
48525
+ function identityWorkOnOpen(nativeDb, scope) {
48526
+ if (ROW_IDENTITY[scope].length === 0) return false;
48527
+ return rowUidFillEnabled() || missingRowIdentitySchema(nativeDb).length > 0;
48528
+ }
47895
48529
  async function openDedicatedDualScopeDb(scope, dbPath, log15, execution) {
47896
48530
  execution?.assertActive();
47897
48531
  log15.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
47898
48532
  const dir = dirname20(dbPath);
47899
- if (!existsSync29(dir)) {
48533
+ if (!existsSync30(dir)) {
47900
48534
  mkdirSync10(dir, { recursive: true });
47901
48535
  }
47902
48536
  execution?.assertActive();
@@ -47929,9 +48563,12 @@ async function openDedicatedDualScopeDb(scope, dbPath, log15, execution) {
47929
48563
  `dual-scope-db[${scope}]`
47930
48564
  );
47931
48565
  execution?.assertActive();
47932
- if (rowUidFillEnabled()) {
48566
+ if (identityWorkOnOpen(nativeDb, scope)) {
47933
48567
  await Promise.resolve().then(() => (init_sqlite_data_accessor(), sqlite_data_accessor_exports));
47934
- prepareRowIdentity(nativeDb, scope, { triggers: false });
48568
+ healRowIdentitySchema(nativeDb, scope);
48569
+ if (rowUidFillEnabled()) {
48570
+ prepareRowIdentity(nativeDb, scope, { triggers: false });
48571
+ }
47935
48572
  }
47936
48573
  execution?.assertActive();
47937
48574
  log15.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
@@ -48044,7 +48681,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
48044
48681
  execution?.assertActive();
48045
48682
  log15.debug({ scope, dbPath: normalizedPath }, "opening dual-scope cleo.db");
48046
48683
  const dir = dirname20(normalizedPath);
48047
- if (!existsSync29(dir)) {
48684
+ if (!existsSync30(dir)) {
48048
48685
  mkdirSync10(dir, { recursive: true });
48049
48686
  }
48050
48687
  execution?.assertActive();
@@ -48077,9 +48714,12 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
48077
48714
  `dual-scope-db[${scope}]`
48078
48715
  );
48079
48716
  execution?.assertActive();
48080
- if (rowUidFillEnabled()) {
48717
+ if (identityWorkOnOpen(nativeDb, scope)) {
48081
48718
  await Promise.resolve().then(() => (init_sqlite_data_accessor(), sqlite_data_accessor_exports));
48082
- prepareRowIdentity(nativeDb, scope);
48719
+ healRowIdentitySchema(nativeDb, scope);
48720
+ if (rowUidFillEnabled()) {
48721
+ prepareRowIdentity(nativeDb, scope);
48722
+ }
48083
48723
  }
48084
48724
  execution?.assertActive();
48085
48725
  log15.debug({ scope, dbPath: normalizedPath }, "dual-scope cleo.db ready");
@@ -49209,7 +49849,7 @@ __export(nexus_sqlite_exports, {
49209
49849
  resetNexusDbState: () => resetNexusDbState,
49210
49850
  resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
49211
49851
  });
49212
- import { copyFileSync as copyFileSync8, existsSync as existsSync30 } from "node:fs";
49852
+ import { copyFileSync as copyFileSync8, existsSync as existsSync31 } from "node:fs";
49213
49853
  import { join as join33 } from "node:path";
49214
49854
  function getNexusDbPath(cwd) {
49215
49855
  return resolveDualScopeDbPath("project", cwd);
@@ -49261,7 +49901,7 @@ function detectAndWarnOnNestedNexus() {
49261
49901
  } catch {
49262
49902
  return false;
49263
49903
  }
49264
- if (!existsSync30(nestedPath)) return false;
49904
+ if (!existsSync31(nestedPath)) return false;
49265
49905
  if (_warnedNestedPaths.has(nestedPath)) return false;
49266
49906
  _warnedNestedPaths.add(nestedPath);
49267
49907
  const canonicalPath = getNexusDbPath();
@@ -49281,7 +49921,7 @@ function detectAndWarnOnNestedNexus() {
49281
49921
  function _resetNestedNexusWarningGate() {
49282
49922
  _warnedNestedPaths.clear();
49283
49923
  }
49284
- function tableExists4(nativeDb, tableName) {
49924
+ function tableExists5(nativeDb, tableName) {
49285
49925
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
49286
49926
  return !!result;
49287
49927
  }
@@ -49290,7 +49930,7 @@ function objectExists(nativeDb, type, name15) {
49290
49930
  return !!result;
49291
49931
  }
49292
49932
  function ensureNexusFts5(nativeDb) {
49293
- if (!tableExists4(nativeDb, "nexus_nodes")) return;
49933
+ if (!tableExists5(nativeDb, "nexus_nodes")) return;
49294
49934
  if (!objectExists(nativeDb, "table", "nexus_symbols_fts")) {
49295
49935
  nativeDb.exec(`
49296
49936
  CREATE VIRTUAL TABLE nexus_symbols_fts USING fts5(
@@ -49336,7 +49976,7 @@ function ensureNexusFts5(nativeDb) {
49336
49976
  `);
49337
49977
  }
49338
49978
  function ensureNexusRelationWeights(nativeDb) {
49339
- if (!tableExists4(nativeDb, "nexus_relations")) return;
49979
+ if (!tableExists5(nativeDb, "nexus_relations")) return;
49340
49980
  nativeDb.exec(`
49341
49981
  CREATE TABLE IF NOT EXISTS nexus_relation_weights (
49342
49982
  relation_id TEXT PRIMARY KEY NOT NULL,
@@ -49418,9 +50058,9 @@ function ensureNexusRelationWeights(nativeDb) {
49418
50058
  }
49419
50059
  function runNexusMigrations(nativeDb, db, dbPath) {
49420
50060
  const migrationsFolder = resolveNexusMigrationsFolder();
49421
- if (tableExists4(nativeDb, "nexus_nodes")) {
50061
+ if (tableExists5(nativeDb, "nexus_nodes")) {
49422
50062
  const backupPath = dbPath.replace(/\.db$/, "-pre-cleo.db.bak");
49423
- if (!existsSync30(backupPath)) {
50063
+ if (!existsSync31(backupPath)) {
49424
50064
  try {
49425
50065
  copyFileSync8(dbPath, backupPath);
49426
50066
  } catch {
@@ -49550,13 +50190,13 @@ var init_legacy_alias = __esm({
49550
50190
 
49551
50191
  // packages/core/src/llm/stable-device-id.ts
49552
50192
  import { randomUUID as randomUUID3 } from "node:crypto";
49553
- import { existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync11, renameSync as renameSync9, writeFileSync as writeFileSync9 } from "node:fs";
50193
+ import { existsSync as existsSync32, mkdirSync as mkdirSync11, readFileSync as readFileSync11, renameSync as renameSync9, writeFileSync as writeFileSync9 } from "node:fs";
49554
50194
  import { dirname as dirname21 } from "node:path";
49555
50195
  import { resolveStableDeviceIdPath } from "@cleocode/paths";
49556
50196
  function getStableDeviceId() {
49557
50197
  if (_cachedDeviceId !== null) return _cachedDeviceId;
49558
50198
  const path = resolveStableDeviceIdPath();
49559
- if (existsSync31(path)) {
50199
+ if (existsSync32(path)) {
49560
50200
  try {
49561
50201
  const raw = readFileSync11(path, "utf-8").trim();
49562
50202
  if (raw.length > 0) {
@@ -49569,7 +50209,7 @@ function getStableDeviceId() {
49569
50209
  const fresh = randomUUID3();
49570
50210
  try {
49571
50211
  const dir = dirname21(path);
49572
- if (!existsSync31(dir)) {
50212
+ if (!existsSync32(dir)) {
49573
50213
  mkdirSync11(dir, { recursive: true });
49574
50214
  }
49575
50215
  const tmpPath = `${path}.tmp.${process.pid}`;
@@ -49645,11 +50285,11 @@ __export(path_map_exports, {
49645
50285
  supersededRegistryPath: () => supersededRegistryPath,
49646
50286
  touchProjectLocation: () => touchProjectLocation
49647
50287
  });
49648
- import { existsSync as existsSync32, lstatSync, readdirSync as readdirSync8 } from "node:fs";
50288
+ import { existsSync as existsSync33, lstatSync, readdirSync as readdirSync8 } from "node:fs";
49649
50289
  import { lstat, readdir as readdir2 } from "node:fs/promises";
49650
50290
  import { isAbsolute as isAbsolute5, join as join34, relative as relative3, sep as sep5 } from "node:path";
49651
50291
  import { canonicalizePath as canonicalizePath3, readDeclaredProjectIdentity as readDeclaredProjectIdentity4 } from "@cleocode/paths";
49652
- import { and as and9, desc as desc3, eq as eq12, inArray as inArray6, ne as ne3 } from "drizzle-orm";
50292
+ import { and as and9, desc as desc3, eq as eq12, inArray as inArray7, ne as ne3 } from "drizzle-orm";
49653
50293
  function holdingKey(projectId, path) {
49654
50294
  return `${projectId}\0${path}`;
49655
50295
  }
@@ -49797,7 +50437,7 @@ function decideEncounterBinding(db, record2) {
49797
50437
  const here = db.select({ state: projectLocations.state, checkoutNonce: projectLocations.checkoutNonce }).from(projectLocations).where(
49798
50438
  and9(
49799
50439
  eq12(projectLocations.projectId, record2.projectId),
49800
- inArray6(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
50440
+ inArray7(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
49801
50441
  eq12(projectLocations.path, record2.projectPath)
49802
50442
  )
49803
50443
  ).all();
@@ -49808,7 +50448,7 @@ function decideEncounterBinding(db, record2) {
49808
50448
  const previous = () => db.select({ checkoutNonce: projectLocations.checkoutNonce }).from(projectLocations).where(
49809
50449
  and9(
49810
50450
  eq12(projectLocations.projectId, record2.projectId),
49811
- inArray6(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
50451
+ inArray7(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
49812
50452
  eq12(projectLocations.path, oldPath)
49813
50453
  )
49814
50454
  ).all();
@@ -49856,7 +50496,7 @@ function hasOtherLiveLocation(db, record2, deviceId) {
49856
50496
  ne3(projectLocations.path, record2.projectPath)
49857
50497
  )
49858
50498
  ).all().some(
49859
- (location) => isStrictlyInside(record2.projectPath, location.path) && existsSync32(location.path) && readDeclaredProjectIdentity4(location.path)?.projectId === record2.projectId
50499
+ (location) => isStrictlyInside(record2.projectPath, location.path) && existsSync33(location.path) && readDeclaredProjectIdentity4(location.path)?.projectId === record2.projectId
49860
50500
  );
49861
50501
  }
49862
50502
  function demoteProjectLocation(db, record2, state) {
@@ -49907,7 +50547,7 @@ function rehomeDisplacedRows(db, projectId, projectPath, deviceId, probed) {
49907
50547
  ).orderBy(desc3(projectLocations.lastSeen)).all();
49908
50548
  const present = (path) => {
49909
50549
  const holding = probed?.get(holdingKey(row.projectId, path));
49910
- return holding === void 0 ? existsSync32(path) : holding === "yes";
50550
+ return holding === void 0 ? existsSync33(path) : holding === "yes";
49911
50551
  };
49912
50552
  const home = candidates.find(
49913
50553
  (c) => present(c.path) && db.select({ projectId: projectRegistry.projectId }).from(projectRegistry).where(eq12(projectRegistry.projectPath, c.path)).all().length === 0
@@ -49936,7 +50576,7 @@ async function listProjectCheckouts(projectId, opts = {}) {
49936
50576
  projectHash: generateProjectHash(row.path),
49937
50577
  firstSeen: row.firstSeen,
49938
50578
  lastSeen: row.lastSeen,
49939
- exists: local && existsSync32(row.path),
50579
+ exists: local && existsSync33(row.path),
49940
50580
  deviceId: local ? deviceId : row.deviceId,
49941
50581
  state: row.state
49942
50582
  });
@@ -49969,7 +50609,7 @@ __export(project_activity_exports, {
49969
50609
  projectLastActivitySql: () => projectLastActivitySql,
49970
50610
  projectLastActivitySqlText: () => projectLastActivitySqlText
49971
50611
  });
49972
- import { eq as eq13, inArray as inArray7, sql as sql32 } from "drizzle-orm";
50612
+ import { eq as eq13, inArray as inArray8, sql as sql32 } from "drizzle-orm";
49973
50613
  function normalizeRegistryTimestamp(value) {
49974
50614
  if (value === null || value === void 0 || value.length === 0) return null;
49975
50615
  const iso = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$/.test(value) ? `${value.replace(" ", "T")}Z` : value;
@@ -49998,7 +50638,7 @@ function markProjectsProbed(db, projectIds, now = /* @__PURE__ */ new Date()) {
49998
50638
  for (let i = 0; i < ids.length; i += PROBED_CHUNK) {
49999
50639
  const chunk = ids.slice(i, i + PROBED_CHUNK);
50000
50640
  changed += Number(
50001
- db.update(projectRegistry).set({ lastProbedAt: at }).where(inArray7(projectRegistry.projectId, chunk)).run().changes
50641
+ db.update(projectRegistry).set({ lastProbedAt: at }).where(inArray8(projectRegistry.projectId, chunk)).run().changes
50002
50642
  );
50003
50643
  }
50004
50644
  return changed;
@@ -50031,11 +50671,11 @@ __export(checkout_nonce_exports, {
50031
50671
  readCheckoutNonce: () => readCheckoutNonce
50032
50672
  });
50033
50673
  import { randomBytes as randomBytes3 } from "node:crypto";
50034
- import { existsSync as existsSync33, readFileSync as readFileSync12, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "node:fs";
50674
+ import { existsSync as existsSync34, readFileSync as readFileSync12, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "node:fs";
50035
50675
  import { join as join35 } from "node:path";
50036
50676
  function readInfo(projectRoot) {
50037
50677
  const path = join35(projectRoot, ".cleo", "project-info.json");
50038
- if (!existsSync33(path)) return null;
50678
+ if (!existsSync34(path)) return null;
50039
50679
  try {
50040
50680
  const parsed = JSON.parse(readFileSync12(path, "utf-8"));
50041
50681
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
@@ -50128,7 +50768,7 @@ __export(paths_exports, {
50128
50768
  validateProjectRoot: () => validateProjectRoot,
50129
50769
  worktreeScope: () => worktreeScope
50130
50770
  });
50131
- import { existsSync as existsSync34, readFileSync as readFileSync13, statSync as statSync10 } from "node:fs";
50771
+ import { existsSync as existsSync35, readFileSync as readFileSync13, statSync as statSync10 } from "node:fs";
50132
50772
  import { createRequire as createRequire12 } from "node:module";
50133
50773
  import { homedir as homedir2 } from "node:os";
50134
50774
  import { dirname as dirname22, join as join36, resolve as resolve15 } from "node:path";
@@ -50154,7 +50794,7 @@ function runWithWorktreeScopeFromEnv(fn) {
50154
50794
  function isProjectInitialized(projectRoot) {
50155
50795
  const root = projectRoot ?? getProjectRoot();
50156
50796
  const cleoDir = join36(root, ".cleo");
50157
- return existsSync34(cleoDir) && existsSync34(join36(cleoDir, "tasks.db"));
50797
+ return existsSync35(cleoDir) && existsSync35(join36(cleoDir, "tasks.db"));
50158
50798
  }
50159
50799
  function getCleoHome() {
50160
50800
  return getPlatformPaths().data;
@@ -50260,7 +50900,7 @@ function _resolveProjectByCwdFromNexus(cwd) {
50260
50900
  try {
50261
50901
  const cleoHome = getCleoHome();
50262
50902
  const globalDbPath = join36(cleoHome, "cleo.db");
50263
- if (!existsSync34(globalDbPath)) return null;
50903
+ if (!existsSync35(globalDbPath)) return null;
50264
50904
  const start = resolve15(cwd ?? process.cwd());
50265
50905
  let current = start;
50266
50906
  const DatabaseSync3 = _getDatabaseSyncCtor();
@@ -50423,7 +51063,7 @@ function _cwdHasGitAncestor(cwd) {
50423
51063
  while (true) {
50424
51064
  const gitMarker = join36(current, ".git");
50425
51065
  try {
50426
- if (existsSync34(gitMarker)) {
51066
+ if (existsSync35(gitMarker)) {
50427
51067
  return true;
50428
51068
  }
50429
51069
  } catch {
@@ -50501,7 +51141,7 @@ function getCleoGlobalCantAgentsDir() {
50501
51141
  function getAgentOutputsDir(cwd) {
50502
51142
  const projectRoot = getProjectRoot(cwd);
50503
51143
  const configPath = join36(projectRoot, ".cleo", "config.json");
50504
- if (existsSync34(configPath)) {
51144
+ if (existsSync35(configPath)) {
50505
51145
  try {
50506
51146
  const config2 = JSON.parse(readFileSync13(configPath, "utf-8"));
50507
51147
  if (typeof config2.agentOutputs === "object" && config2.agentOutputs?.directory) {
@@ -50534,7 +51174,7 @@ function getManifestPath(cwd) {
50534
51174
  const configPath = join36(projectRoot, ".cleo", "config.json");
50535
51175
  const legacyFileName = ["MANIFEST", "jsonl"].join(".");
50536
51176
  let manifestFile = legacyFileName;
50537
- if (existsSync34(configPath)) {
51177
+ if (existsSync35(configPath)) {
50538
51178
  try {
50539
51179
  const config2 = JSON.parse(readFileSync13(configPath, "utf-8"));
50540
51180
  const customFile = config2.agentOutputs?.manifestFile ?? config2.research?.manifestFile;
@@ -50590,7 +51230,7 @@ function resolveWorktreeRouting(cwdOverride) {
50590
51230
  while (current !== home && current !== "/" && current !== "") {
50591
51231
  const gitPath = join36(current, ".git");
50592
51232
  try {
50593
- if (existsSync34(gitPath)) {
51233
+ if (existsSync35(gitPath)) {
50594
51234
  const stat3 = statSync10(gitPath);
50595
51235
  if (stat3.isFile()) {
50596
51236
  worktreePath = current;
@@ -50629,7 +51269,7 @@ function detectStrayCleoDb(routing) {
50629
51269
  }
50630
51270
  const tasksDbPath = join36(routing.worktreePath, ".cleo", "tasks.db");
50631
51271
  try {
50632
- if (existsSync34(tasksDbPath) && statSync10(tasksDbPath).isFile()) {
51272
+ if (existsSync35(tasksDbPath) && statSync10(tasksDbPath).isFile()) {
50633
51273
  return tasksDbPath;
50634
51274
  }
50635
51275
  } catch {
@@ -50639,7 +51279,7 @@ function detectStrayCleoDb(routing) {
50639
51279
  async function resolveProjectById(projectId) {
50640
51280
  try {
50641
51281
  const { getNexusDb: getNexusDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50642
- if (!existsSync34(getNexusRegistryDbPath2())) return null;
51282
+ if (!existsSync35(getNexusRegistryDbPath2())) return null;
50643
51283
  const { eq: eq14 } = await import("drizzle-orm");
50644
51284
  const { projectRegistry: projectRegistry2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
50645
51285
  const db = await getNexusDb2();
@@ -50675,7 +51315,7 @@ function _rowToRegistryEntry(row) {
50675
51315
  }
50676
51316
  function _assertProjectInfoReadable(projectRoot) {
50677
51317
  const infoPath = join36(projectRoot, ".cleo", "project-info.json");
50678
- if (existsSync34(infoPath)) JSON.parse(readFileSync13(infoPath, "utf-8"));
51318
+ if (existsSync35(infoPath)) JSON.parse(readFileSync13(infoPath, "utf-8"));
50679
51319
  }
50680
51320
  function startProjectEncounter(key, projectRoot, infoProjectId) {
50681
51321
  const inFlight2 = _encountersInFlight.get(key);
@@ -50724,7 +51364,7 @@ async function recordProjectEncounter(cwd, options = {}) {
50724
51364
  if (!shouldAutoRegisterProject(project.projectRoot, cleoHome)) return "ephemeral";
50725
51365
  const checkout = captureProjectScope(project.projectRoot, worktreeScope.getStore()).worktreeRoot;
50726
51366
  const { getNexusRegistryDb: getNexusRegistryDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50727
- if (existsSync34(getNexusRegistryDbPath2(cleoHome))) {
51367
+ if (existsSync35(getNexusRegistryDbPath2(cleoHome))) {
50728
51368
  const { and: and10, eq: eq14 } = await import("drizzle-orm");
50729
51369
  const { projectLocations: projectLocations2, projectRegistry: projectRegistry2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
50730
51370
  const { currentDeviceId: currentDeviceId2 } = await Promise.resolve().then(() => (init_path_map(), path_map_exports));
@@ -50750,7 +51390,7 @@ async function recordProjectEncounter(cwd, options = {}) {
50750
51390
  }
50751
51391
  async function markEncounterOpened(cleoHome, projectId) {
50752
51392
  const { getNexusRegistryDb: getNexusRegistryDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50753
- if (!existsSync34(getNexusRegistryDbPath2(cleoHome))) return;
51393
+ if (!existsSync35(getNexusRegistryDbPath2(cleoHome))) return;
50754
51394
  const { markProjectOpened: markProjectOpened2 } = await Promise.resolve().then(() => (init_project_activity(), project_activity_exports));
50755
51395
  markProjectOpened2(await getNexusRegistryDb2(cleoHome), projectId);
50756
51396
  }
@@ -50912,7 +51552,7 @@ var init_paths = __esm({
50912
51552
  // packages/core/src/store/file-utils.ts
50913
51553
  import { randomBytes as randomBytes4 } from "node:crypto";
50914
51554
  import {
50915
- existsSync as existsSync35,
51555
+ existsSync as existsSync36,
50916
51556
  mkdirSync as mkdirSync12,
50917
51557
  readdirSync as readdirSync9,
50918
51558
  readFileSync as readFileSync14,
@@ -50927,7 +51567,7 @@ function rotateBackup(filePath, mode) {
50927
51567
  const name15 = basename11(filePath);
50928
51568
  const backupDir = join37(dir, ".backups");
50929
51569
  const dirMode = typeof mode === "number" ? modeToDirMode(mode) : void 0;
50930
- if (!existsSync35(backupDir)) {
51570
+ if (!existsSync36(backupDir)) {
50931
51571
  mkdirSync12(backupDir, { recursive: true, mode: dirMode });
50932
51572
  }
50933
51573
  for (let i = MAX_BACKUPS; i >= 1; i--) {
@@ -50940,7 +51580,7 @@ function rotateBackup(filePath, mode) {
50940
51580
  } else {
50941
51581
  const next = join37(backupDir, `${name15}.${i + 1}`);
50942
51582
  try {
50943
- if (existsSync35(current)) renameSync11(current, next);
51583
+ if (existsSync36(current)) renameSync11(current, next);
50944
51584
  } catch {
50945
51585
  }
50946
51586
  }
@@ -50976,7 +51616,7 @@ function writeJsonFileAtomic(filePath, data, optsOrIndent = 2) {
50976
51616
  writeFileSync11(tempPath, content, "utf-8");
50977
51617
  }
50978
51618
  try {
50979
- if (existsSync35(filePath)) {
51619
+ if (existsSync36(filePath)) {
50980
51620
  rotateBackup(filePath, mode);
50981
51621
  }
50982
51622
  renameSync11(tempPath, filePath);
@@ -51002,10 +51642,10 @@ function readJsonFile(filePath) {
51002
51642
  async function withLock2(filePath, transform2, opts = {}) {
51003
51643
  const dir = dirname23(filePath);
51004
51644
  const dirMode = typeof opts.mode === "number" ? modeToDirMode(opts.mode) : void 0;
51005
- if (!existsSync35(dir)) {
51645
+ if (!existsSync36(dir)) {
51006
51646
  mkdirSync12(dir, { recursive: true, mode: dirMode });
51007
51647
  }
51008
- if (!existsSync35(filePath)) {
51648
+ if (!existsSync36(filePath)) {
51009
51649
  if (typeof opts.mode === "number") {
51010
51650
  writeFileSync11(filePath, "", { encoding: "utf-8", mode: opts.mode });
51011
51651
  } else {
@@ -51027,10 +51667,10 @@ async function withLock2(filePath, transform2, opts = {}) {
51027
51667
  }
51028
51668
  async function withFileLock(filePath, operation) {
51029
51669
  const dir = dirname23(filePath);
51030
- if (!existsSync35(dir)) {
51670
+ if (!existsSync36(dir)) {
51031
51671
  mkdirSync12(dir, { recursive: true });
51032
51672
  }
51033
- if (!existsSync35(filePath)) {
51673
+ if (!existsSync36(filePath)) {
51034
51674
  writeFileSync11(filePath, "", "utf-8");
51035
51675
  }
51036
51676
  let release;
@@ -51402,7 +52042,7 @@ var init_cleo_pkce_seeder = __esm({
51402
52042
  });
51403
52043
 
51404
52044
  // packages/core/src/llm/credential-seeders/codex-cli-seeder.ts
51405
- import { existsSync as existsSync36, readFileSync as readFileSync17 } from "node:fs";
52045
+ import { existsSync as existsSync37, readFileSync as readFileSync17 } from "node:fs";
51406
52046
  import { homedir as homedir4 } from "node:os";
51407
52047
  import { join as join41 } from "node:path";
51408
52048
  function getCodexAuthPath() {
@@ -51427,7 +52067,7 @@ var init_codex_cli_seeder = __esm({
51427
52067
  */
51428
52068
  async seed() {
51429
52069
  const authPath = getCodexAuthPath();
51430
- if (!existsSync36(authPath)) {
52070
+ if (!existsSync37(authPath)) {
51431
52071
  return { entries: [] };
51432
52072
  }
51433
52073
  let raw;
@@ -51622,7 +52262,7 @@ var init_google_pkce = __esm({
51622
52262
  });
51623
52263
 
51624
52264
  // packages/core/src/llm/credential-seeders/gemini-cli-seeder.ts
51625
- import { existsSync as existsSync37, readFileSync as readFileSync18 } from "node:fs";
52265
+ import { existsSync as existsSync38, readFileSync as readFileSync18 } from "node:fs";
51626
52266
  import { join as join42 } from "node:path";
51627
52267
  import { getCleoHome as getCleoHome5 } from "@cleocode/paths";
51628
52268
  function getGoogleOauthPath() {
@@ -51671,7 +52311,7 @@ var init_gemini_cli_seeder = __esm({
51671
52311
  */
51672
52312
  async seed() {
51673
52313
  const path = getGoogleOauthPath();
51674
- if (!existsSync37(path)) {
52314
+ if (!existsSync38(path)) {
51675
52315
  return { entries: [] };
51676
52316
  }
51677
52317
  let raw;
@@ -51896,7 +52536,7 @@ var init_credential_seeders = __esm({
51896
52536
 
51897
52537
  // packages/core/src/llm/global-config-migration.ts
51898
52538
  import {
51899
- existsSync as existsSync38,
52539
+ existsSync as existsSync39,
51900
52540
  mkdirSync as mkdirSync13,
51901
52541
  readFileSync as readFileSync19,
51902
52542
  renameSync as renameSync12,
@@ -51922,12 +52562,12 @@ function migrateGlobalConfigToConfigDir() {
51922
52562
  const source = legacyGlobalConfigPath();
51923
52563
  const target = configDirGlobalConfigPath();
51924
52564
  const marker16 = migrationMarkerPath();
51925
- if (existsSync38(marker16)) return false;
51926
- if (!existsSync38(source)) {
52565
+ if (existsSync39(marker16)) return false;
52566
+ if (!existsSync39(source)) {
51927
52567
  stampMarker(marker16);
51928
52568
  return false;
51929
52569
  }
51930
- if (existsSync38(target)) {
52570
+ if (existsSync39(target)) {
51931
52571
  backupSourceQuiet(source);
51932
52572
  stampMarker(marker16);
51933
52573
  return false;
@@ -51960,7 +52600,7 @@ function migrateGlobalConfigToConfigDir() {
51960
52600
  } catch (err) {
51961
52601
  try {
51962
52602
  const temp = tempTargetPath();
51963
- if (existsSync38(temp)) unlinkSync8(temp);
52603
+ if (existsSync39(temp)) unlinkSync8(temp);
51964
52604
  } catch {
51965
52605
  }
51966
52606
  console.error(
@@ -51980,7 +52620,7 @@ function stampMarker(markerPath) {
51980
52620
  function backupSourceQuiet(source) {
51981
52621
  try {
51982
52622
  const backup = `${source}${BACKUP_SUFFIX}`;
51983
- if (existsSync38(backup)) return;
52623
+ if (existsSync39(backup)) return;
51984
52624
  renameSync12(source, backup);
51985
52625
  } catch {
51986
52626
  }
@@ -52003,7 +52643,7 @@ var init_global_config_migration = __esm({
52003
52643
  });
52004
52644
 
52005
52645
  // packages/core/src/llm/legacy-flat-key-import.ts
52006
- import { existsSync as existsSync39, readFileSync as readFileSync20, renameSync as renameSync13, writeFileSync as writeFileSync13 } from "node:fs";
52646
+ import { existsSync as existsSync40, readFileSync as readFileSync20, renameSync as renameSync13, writeFileSync as writeFileSync13 } from "node:fs";
52007
52647
  import { join as join44 } from "node:path";
52008
52648
  import { getCleoHome as getCleoHome7 } from "@cleocode/paths";
52009
52649
  function migrationPaths() {
@@ -52032,7 +52672,7 @@ function writeMarker(markerPath) {
52032
52672
  }
52033
52673
  function readFlatKey(flatPath) {
52034
52674
  try {
52035
- if (!existsSync39(flatPath)) return null;
52675
+ if (!existsSync40(flatPath)) return null;
52036
52676
  const raw = readFileSync20(flatPath, "utf-8").trim();
52037
52677
  return raw || null;
52038
52678
  } catch (err) {
@@ -52045,7 +52685,7 @@ function readFlatKey(flatPath) {
52045
52685
  }
52046
52686
  async function importLegacyFlatAnthropicKey() {
52047
52687
  const { flatPath, bakPath, markerPath } = migrationPaths();
52048
- if (existsSync39(markerPath)) {
52688
+ if (existsSync40(markerPath)) {
52049
52689
  return { status: "marker-present", flatPath, bakPath: null, markerPath };
52050
52690
  }
52051
52691
  let existing = null;
@@ -52062,7 +52702,7 @@ async function importLegacyFlatAnthropicKey() {
52062
52702
  writeMarker(markerPath);
52063
52703
  return { status: "already-imported", flatPath, bakPath: null, markerPath };
52064
52704
  }
52065
- if (!existsSync39(flatPath)) {
52705
+ if (!existsSync40(flatPath)) {
52066
52706
  writeMarker(markerPath);
52067
52707
  return { status: "no-flat-file", flatPath, bakPath: null, markerPath };
52068
52708
  }
@@ -52139,7 +52779,7 @@ __export(credentials_exports, {
52139
52779
  resolveProviderStatus: () => resolveProviderStatus,
52140
52780
  storeAnthropicApiKey: () => storeAnthropicApiKey
52141
52781
  });
52142
- import { existsSync as existsSync40, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
52782
+ import { existsSync as existsSync41, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
52143
52783
  import { join as join45 } from "node:path";
52144
52784
  import { getCleoHome as getCleoHome8 } from "@cleocode/paths";
52145
52785
  function globalConfigPath() {
@@ -52158,7 +52798,7 @@ function readGlobalProviderKey(provider) {
52158
52798
  function readFlatAnthropicKey() {
52159
52799
  try {
52160
52800
  const keyFile = join45(getCleoHome8(), "anthropic-key");
52161
- if (!existsSync40(keyFile)) return null;
52801
+ if (!existsSync41(keyFile)) return null;
52162
52802
  const stored = readFileSync21(keyFile, "utf-8").trim();
52163
52803
  return stored || null;
52164
52804
  } catch {
@@ -52167,7 +52807,7 @@ function readFlatAnthropicKey() {
52167
52807
  }
52168
52808
  function readProviderKeyFromConfig(configFile, provider) {
52169
52809
  try {
52170
- if (!existsSync40(configFile)) return null;
52810
+ if (!existsSync41(configFile)) return null;
52171
52811
  const raw = readFileSync21(configFile, "utf-8");
52172
52812
  const config2 = JSON.parse(raw);
52173
52813
  const llm = config2["llm"];
@@ -52340,7 +52980,7 @@ function resolveProviderStatus(provider) {
52340
52980
  }
52341
52981
  function storeAnthropicApiKey(apiKey) {
52342
52982
  const dir = getCleoHome8();
52343
- if (!existsSync40(dir)) {
52983
+ if (!existsSync41(dir)) {
52344
52984
  mkdirSync14(dir, { recursive: true });
52345
52985
  }
52346
52986
  const keyFile = join45(dir, "anthropic-key");
@@ -52408,16 +53048,16 @@ __export(credentials_store_exports, {
52408
53048
  pickCredentialForProviderSync: () => pickCredentialForProviderSync,
52409
53049
  removeCredential: () => removeCredential
52410
53050
  });
52411
- import { chmodSync as chmodSync2, existsSync as existsSync41, mkdirSync as mkdirSync15, readFileSync as readFileSync22, statSync as statSync11, writeFileSync as writeFileSync15 } from "node:fs";
53051
+ import { chmodSync as chmodSync2, existsSync as existsSync42, mkdirSync as mkdirSync15, readFileSync as readFileSync22, statSync as statSync11, writeFileSync as writeFileSync15 } from "node:fs";
52412
53052
  import { dirname as dirname24, join as join46 } from "node:path";
52413
53053
  import { getCleoHome as getCleoHome9 } from "@cleocode/paths";
52414
53054
  function credentialsStorePath() {
52415
53055
  return join46(getCleoHome9(), "llm-credentials.json");
52416
53056
  }
52417
53057
  function ensureFileInitialized(path) {
52418
- if (existsSync41(path)) return;
53058
+ if (existsSync42(path)) return;
52419
53059
  const dir = dirname24(path);
52420
- if (!existsSync41(dir)) {
53060
+ if (!existsSync42(dir)) {
52421
53061
  mkdirSync15(dir, { recursive: true, mode: 448 });
52422
53062
  } else {
52423
53063
  try {
@@ -53213,7 +53853,7 @@ var init_provider_registry = __esm({
53213
53853
  });
53214
53854
 
53215
53855
  // packages/core/src/llm/rate-limit-guard.ts
53216
- import { existsSync as existsSync42, mkdirSync as mkdirSync16, unlinkSync as unlinkSync9 } from "node:fs";
53856
+ import { existsSync as existsSync43, mkdirSync as mkdirSync16, unlinkSync as unlinkSync9 } from "node:fs";
53217
53857
  import { join as join48 } from "node:path";
53218
53858
  import { getCleoHome as getCleoHome10 } from "@cleocode/paths";
53219
53859
  function sanitize(value) {
@@ -53269,7 +53909,7 @@ async function recordRateLimit(provider, label, opts) {
53269
53909
  }
53270
53910
  const filePath = rateLimitStatePath(provider, label);
53271
53911
  const dir = join48(getCleoHome10(), "rate-limit-state");
53272
- if (!existsSync42(dir)) {
53912
+ if (!existsSync43(dir)) {
53273
53913
  mkdirSync16(dir, { recursive: true });
53274
53914
  }
53275
53915
  const state = { resetAt, recordedAt: now, source };
@@ -54170,7 +54810,7 @@ var init_error_classifier = __esm({
54170
54810
 
54171
54811
  // packages/core/src/llm/catalog-cache.ts
54172
54812
  import {
54173
- existsSync as existsSync43,
54813
+ existsSync as existsSync44,
54174
54814
  mkdirSync as mkdirSync17,
54175
54815
  readdirSync as readdirSync11,
54176
54816
  readFileSync as readFileSync23,
@@ -54188,7 +54828,7 @@ function getCatalogDir() {
54188
54828
  return join49(getCleoHome11(), "llm-catalog");
54189
54829
  }
54190
54830
  function findLatestCacheFile(dir) {
54191
- if (!existsSync43(dir)) return null;
54831
+ if (!existsSync44(dir)) return null;
54192
54832
  const files = readdirSync11(dir).filter((f) => f.endsWith("-models.json")).map((f) => ({ name: f, mtime: statSync13(join49(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
54193
54833
  return files.length > 0 ? join49(dir, files[0].name) : null;
54194
54834
  }