@cleocode/core 2026.9.25 → 2026.10.1

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 (236) 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-attach.d.ts +94 -0
  13. package/dist/cloud/nexus-attach.d.ts.map +1 -0
  14. package/dist/cloud/nexus-attach.js +192 -0
  15. package/dist/cloud/nexus-attach.js.map +1 -0
  16. package/dist/cloud/nexus-auth.d.ts +41 -4
  17. package/dist/cloud/nexus-auth.d.ts.map +1 -1
  18. package/dist/cloud/nexus-auth.js +151 -38
  19. package/dist/cloud/nexus-auth.js.map +1 -1
  20. package/dist/cloud/nexus-cloud-status.d.ts +132 -0
  21. package/dist/cloud/nexus-cloud-status.d.ts.map +1 -0
  22. package/dist/cloud/nexus-cloud-status.js +440 -0
  23. package/dist/cloud/nexus-cloud-status.js.map +1 -0
  24. package/dist/cloud/nexus-cloud.d.ts +179 -0
  25. package/dist/cloud/nexus-cloud.d.ts.map +1 -0
  26. package/dist/cloud/nexus-cloud.js +296 -0
  27. package/dist/cloud/nexus-cloud.js.map +1 -0
  28. package/dist/cloud/nexus-credentials.js +2 -2
  29. package/dist/cloud/nexus-credentials.js.map +1 -1
  30. package/dist/cloud/nexus-device.d.ts +834 -0
  31. package/dist/cloud/nexus-device.d.ts.map +1 -0
  32. package/dist/cloud/nexus-device.js +1727 -0
  33. package/dist/cloud/nexus-device.js.map +1 -0
  34. package/dist/cloud/nexus-enrol.d.ts +241 -0
  35. package/dist/cloud/nexus-enrol.d.ts.map +1 -0
  36. package/dist/cloud/nexus-enrol.js +1163 -0
  37. package/dist/cloud/nexus-enrol.js.map +1 -0
  38. package/dist/cloud/nexus-link.d.ts +10 -0
  39. package/dist/cloud/nexus-link.d.ts.map +1 -1
  40. package/dist/cloud/nexus-link.js +172 -11
  41. package/dist/cloud/nexus-link.js.map +1 -1
  42. package/dist/cloud/nexus-logout.d.ts +114 -0
  43. package/dist/cloud/nexus-logout.d.ts.map +1 -0
  44. package/dist/cloud/nexus-logout.js +429 -0
  45. package/dist/cloud/nexus-logout.js.map +1 -0
  46. package/dist/cloud/signing.d.ts +20 -0
  47. package/dist/cloud/signing.d.ts.map +1 -1
  48. package/dist/cloud/signing.js +14 -0
  49. package/dist/cloud/signing.js.map +1 -1
  50. package/dist/crypto/credentials.d.ts +61 -0
  51. package/dist/crypto/credentials.d.ts.map +1 -1
  52. package/dist/crypto/credentials.js +154 -58
  53. package/dist/crypto/credentials.js.map +1 -1
  54. package/dist/docs/export-document.js +1072 -227
  55. package/dist/docs/export-document.js.map +3 -3
  56. package/dist/doctor/dependency-cycles.d.ts +41 -0
  57. package/dist/doctor/dependency-cycles.d.ts.map +1 -0
  58. package/dist/doctor/dependency-cycles.js +39 -0
  59. package/dist/doctor/dependency-cycles.js.map +1 -0
  60. package/dist/doctor/index.d.ts +2 -0
  61. package/dist/doctor/index.d.ts.map +1 -1
  62. package/dist/doctor/index.js +1 -0
  63. package/dist/doctor/index.js.map +1 -1
  64. package/dist/doctor/row-identity.d.ts +11 -0
  65. package/dist/doctor/row-identity.d.ts.map +1 -1
  66. package/dist/doctor/row-identity.js +67 -9
  67. package/dist/doctor/row-identity.js.map +1 -1
  68. package/dist/error-catalog.d.ts.map +1 -1
  69. package/dist/error-catalog.js +4 -0
  70. package/dist/error-catalog.js.map +1 -1
  71. package/dist/error-registry.d.ts.map +1 -1
  72. package/dist/error-registry.js +8 -0
  73. package/dist/error-registry.js.map +1 -1
  74. package/dist/llm/plugin-facade.js +1200 -355
  75. package/dist/llm/plugin-facade.js.map +3 -3
  76. package/dist/nexus/workspace.d.ts.map +1 -1
  77. package/dist/nexus/workspace.js +54 -4
  78. package/dist/nexus/workspace.js.map +1 -1
  79. package/dist/orchestrate/lifecycle-ops.js +1 -1
  80. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  81. package/dist/orchestrate/pivot.d.ts.map +1 -1
  82. package/dist/orchestrate/pivot.js +4 -2
  83. package/dist/orchestrate/pivot.js.map +1 -1
  84. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  85. package/dist/orchestrate/query-ops.js +3 -3
  86. package/dist/orchestrate/query-ops.js.map +1 -1
  87. package/dist/orchestrate/spawn-ops.js +1 -1
  88. package/dist/orchestrate/spawn-ops.js.map +1 -1
  89. package/dist/orchestration/index.js +1 -1
  90. package/dist/orchestration/index.js.map +1 -1
  91. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  92. package/dist/orchestration/lead-rollup.js +3 -3
  93. package/dist/orchestration/lead-rollup.js.map +1 -1
  94. package/dist/orchestration/parallel.js +1 -1
  95. package/dist/orchestration/parallel.js.map +1 -1
  96. package/dist/orchestration/wave-topic.d.ts +4 -2
  97. package/dist/orchestration/wave-topic.d.ts.map +1 -1
  98. package/dist/orchestration/wave-topic.js +6 -4
  99. package/dist/orchestration/wave-topic.js.map +1 -1
  100. package/dist/orchestration/waves.d.ts +2 -1
  101. package/dist/orchestration/waves.d.ts.map +1 -1
  102. package/dist/orchestration/waves.js +4 -3
  103. package/dist/orchestration/waves.js.map +1 -1
  104. package/dist/phases/index.d.ts.map +1 -1
  105. package/dist/phases/index.js +4 -2
  106. package/dist/phases/index.js.map +1 -1
  107. package/dist/selfimprove/fix-gen.js +1203 -358
  108. package/dist/selfimprove/fix-gen.js.map +3 -3
  109. package/dist/session/engine-ops.d.ts.map +1 -1
  110. package/dist/session/engine-ops.js +77 -49
  111. package/dist/session/engine-ops.js.map +1 -1
  112. package/dist/sessions/focus-state-store.d.ts +69 -23
  113. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  114. package/dist/sessions/focus-state-store.js +113 -27
  115. package/dist/sessions/focus-state-store.js.map +1 -1
  116. package/dist/sessions/handoff.js +1 -1
  117. package/dist/sessions/handoff.js.map +1 -1
  118. package/dist/sessions/index.d.ts.map +1 -1
  119. package/dist/sessions/index.js +24 -13
  120. package/dist/sessions/index.js.map +1 -1
  121. package/dist/sessions/terminal-identity.d.ts +165 -17
  122. package/dist/sessions/terminal-identity.d.ts.map +1 -1
  123. package/dist/sessions/terminal-identity.js +341 -32
  124. package/dist/sessions/terminal-identity.js.map +1 -1
  125. package/dist/setup/sections/nexus-account.d.ts +8 -6
  126. package/dist/setup/sections/nexus-account.d.ts.map +1 -1
  127. package/dist/setup/sections/nexus-account.js +53 -9
  128. package/dist/setup/sections/nexus-account.js.map +1 -1
  129. package/dist/skills/orchestrator/startup.js +2 -2
  130. package/dist/skills/orchestrator/startup.js.map +1 -1
  131. package/dist/snapshot/index.d.ts +4 -0
  132. package/dist/snapshot/index.d.ts.map +1 -1
  133. package/dist/snapshot/index.js +50 -3
  134. package/dist/snapshot/index.js.map +1 -1
  135. package/dist/store/db-helpers.d.ts +15 -2
  136. package/dist/store/db-helpers.d.ts.map +1 -1
  137. package/dist/store/db-helpers.js +84 -3
  138. package/dist/store/db-helpers.js.map +1 -1
  139. package/dist/store/dependency-cycles.d.ts +164 -0
  140. package/dist/store/dependency-cycles.d.ts.map +1 -0
  141. package/dist/store/dependency-cycles.js +360 -0
  142. package/dist/store/dependency-cycles.js.map +1 -0
  143. package/dist/store/display-id-alias.d.ts +93 -37
  144. package/dist/store/display-id-alias.d.ts.map +1 -1
  145. package/dist/store/display-id-alias.js +590 -115
  146. package/dist/store/display-id-alias.js.map +1 -1
  147. package/dist/store/dual-scope-db.d.ts.map +1 -1
  148. package/dist/store/dual-scope-db.js +34 -7
  149. package/dist/store/dual-scope-db.js.map +1 -1
  150. package/dist/store/exodus/migrate.d.ts.map +1 -1
  151. package/dist/store/exodus/migrate.js +8 -1
  152. package/dist/store/exodus/migrate.js.map +1 -1
  153. package/dist/store/global-salt.d.ts +35 -0
  154. package/dist/store/global-salt.d.ts.map +1 -1
  155. package/dist/store/global-salt.js +136 -16
  156. package/dist/store/global-salt.js.map +1 -1
  157. package/dist/store/migration-sqlite.d.ts.map +1 -1
  158. package/dist/store/migration-sqlite.js +19 -11
  159. package/dist/store/migration-sqlite.js.map +1 -1
  160. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  161. package/dist/store/portable-bundle-scan.js +6 -0
  162. package/dist/store/portable-bundle-scan.js.map +1 -1
  163. package/dist/store/row-identity-registry.d.ts.map +1 -1
  164. package/dist/store/row-identity-registry.js +11 -3
  165. package/dist/store/row-identity-registry.js.map +1 -1
  166. package/dist/store/row-identity.d.ts +91 -1
  167. package/dist/store/row-identity.d.ts.map +1 -1
  168. package/dist/store/row-identity.js +276 -7
  169. package/dist/store/row-identity.js.map +1 -1
  170. package/dist/store/safety-data-accessor.d.ts +3 -2
  171. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  172. package/dist/store/safety-data-accessor.js +5 -2
  173. package/dist/store/safety-data-accessor.js.map +1 -1
  174. package/dist/store/session-store.d.ts +63 -3
  175. package/dist/store/session-store.d.ts.map +1 -1
  176. package/dist/store/session-store.js +121 -8
  177. package/dist/store/session-store.js.map +1 -1
  178. package/dist/store/split-brain-import.d.ts.map +1 -1
  179. package/dist/store/split-brain-import.js +36 -0
  180. package/dist/store/split-brain-import.js.map +1 -1
  181. package/dist/store/sqlite-data-accessor.d.ts +50 -3
  182. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  183. package/dist/store/sqlite-data-accessor.js +246 -13
  184. package/dist/store/sqlite-data-accessor.js.map +1 -1
  185. package/dist/store/sync/replica.d.ts +34 -3
  186. package/dist/store/sync/replica.d.ts.map +1 -1
  187. package/dist/store/sync/replica.js +39 -2
  188. package/dist/store/sync/replica.js.map +1 -1
  189. package/dist/store/sync/schema.d.ts +6 -3
  190. package/dist/store/sync/schema.d.ts.map +1 -1
  191. package/dist/store/sync/schema.js +6 -3
  192. package/dist/store/sync/schema.js.map +1 -1
  193. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  194. package/dist/store/tasks-sqlite.js +14 -2
  195. package/dist/store/tasks-sqlite.js.map +1 -1
  196. package/dist/store/umbrella-data-accessor.d.ts +3 -2
  197. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  198. package/dist/store/umbrella-data-accessor.js +5 -2
  199. package/dist/store/umbrella-data-accessor.js.map +1 -1
  200. package/dist/system/health.d.ts +2 -0
  201. package/dist/system/health.d.ts.map +1 -1
  202. package/dist/system/health.js +54 -0
  203. package/dist/system/health.js.map +1 -1
  204. package/dist/task-work/index.d.ts.map +1 -1
  205. package/dist/task-work/index.js +9 -6
  206. package/dist/task-work/index.js.map +1 -1
  207. package/dist/tasks/analyze.d.ts.map +1 -1
  208. package/dist/tasks/analyze.js +3 -2
  209. package/dist/tasks/analyze.js.map +1 -1
  210. package/dist/tasks/evidence.d.ts +19 -1
  211. package/dist/tasks/evidence.d.ts.map +1 -1
  212. package/dist/tasks/evidence.js +79 -18
  213. package/dist/tasks/evidence.js.map +1 -1
  214. package/dist/tasks/infer-add-params.js +1 -1
  215. package/dist/tasks/infer-add-params.js.map +1 -1
  216. package/dist/tasks/task-context.d.ts.map +1 -1
  217. package/dist/tasks/task-context.js +3 -1
  218. package/dist/tasks/task-context.js.map +1 -1
  219. package/dist/tasks/task-import.d.ts.map +1 -1
  220. package/dist/tasks/task-import.js +11 -3
  221. package/dist/tasks/task-import.js.map +1 -1
  222. package/dist/tasks/task-next.d.ts +5 -1
  223. package/dist/tasks/task-next.d.ts.map +1 -1
  224. package/dist/tasks/task-next.js +7 -2
  225. package/dist/tasks/task-next.js.map +1 -1
  226. package/dist/validation/engine-ops.d.ts.map +1 -1
  227. package/dist/validation/engine-ops.js +3 -2
  228. package/dist/validation/engine-ops.js.map +1 -1
  229. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  230. package/dist/workgraph/scaffold-apply.js +14 -5
  231. package/dist/workgraph/scaffold-apply.js.map +1 -1
  232. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/migration.sql +76 -0
  233. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/revert.sql +7 -0
  234. package/package.json +13 -13
  235. package/templates/CLEO-INJECTION.md +2 -2
  236. package/templates/CLEO-REFERENCE.md +1 -1
@@ -1850,7 +1850,7 @@ var init_output_contracts_data = __esm({
1850
1850
  // T12503: the optimistic-concurrency version agents pass to --if-match.
1851
1851
  updatedAt: {
1852
1852
  type: ["string", "null"],
1853
- description: "Task version for optimistic concurrency: pass it to `cleo update|complete --if-match`."
1853
+ 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)."
1854
1854
  },
1855
1855
  // T12502: the human assignee and the agent claim lease are separate.
1856
1856
  assignee: {
@@ -13111,6 +13111,7 @@ var init_exit_codes = __esm({
13111
13111
  ExitCode2[ExitCode2["VERSION_CONFLICT"] = 23] = "VERSION_CONFLICT";
13112
13112
  ExitCode2[ExitCode2["SESSION_UNBOUND"] = 24] = "SESSION_UNBOUND";
13113
13113
  ExitCode2[ExitCode2["WORKTREE_LOCKED"] = 25] = "WORKTREE_LOCKED";
13114
+ ExitCode2[ExitCode2["TASK_RENAMED"] = 26] = "TASK_RENAMED";
13114
13115
  ExitCode2[ExitCode2["SESSION_EXISTS"] = 30] = "SESSION_EXISTS";
13115
13116
  ExitCode2[ExitCode2["SESSION_NOT_FOUND"] = 31] = "SESSION_NOT_FOUND";
13116
13117
  ExitCode2[ExitCode2["SCOPE_CONFLICT"] = 32] = "SCOPE_CONFLICT";
@@ -17365,6 +17366,210 @@ var init_nexus_account = __esm({
17365
17366
  }
17366
17367
  });
17367
17368
 
17369
+ // packages/contracts/src/nexus-cloud.ts
17370
+ import { z as z26 } from "zod";
17371
+ var NEXUS_DEVICE_STATES, NEXUS_CLOUD_PROFILES, NEXUS_DEVICE_STATE_UNKNOWN, isoTime, profile, deviceState, nexusCloudCredentialSchema, nexusCloudDeviceSchema, nexusCloudOrganizationSchema, nexusCloudUserSchema, nexusCloudWhoamiSchema, nexusCloudDevicePageSchema, nexusCloudPresenceSchema, nexusCloudReplicaSchema, nexusCloudDeviceCountsSchema, projectFields, nexusCloudProjectListItemSchema, nexusCloudProjectPageSchema, nexusCloudProjectDetailSchema, nexusCloudReplicaPageSchema, NEXUS_STATUS_CHECK_IDS, NEXUS_STATUS_VERDICTS, CLOUD_STATUS_VERDICTS, knownCheckId, nexusCloudStatusCheckSchema, nexusCloudStatusSchema;
17372
+ var init_nexus_cloud = __esm({
17373
+ "packages/contracts/src/nexus-cloud.ts"() {
17374
+ "use strict";
17375
+ NEXUS_DEVICE_STATES = ["active", "signed-out", "revoked"];
17376
+ NEXUS_CLOUD_PROFILES = ["device", "read-only"];
17377
+ NEXUS_DEVICE_STATE_UNKNOWN = "unknown";
17378
+ isoTime = z26.iso.datetime({ offset: true });
17379
+ profile = z26.enum(NEXUS_CLOUD_PROFILES).nullable().catch(null);
17380
+ deviceState = z26.enum([...NEXUS_DEVICE_STATES, NEXUS_DEVICE_STATE_UNKNOWN]).catch(NEXUS_DEVICE_STATE_UNKNOWN);
17381
+ nexusCloudCredentialSchema = z26.object({
17382
+ kind: z26.enum(["device", "session"]),
17383
+ credentialId: z26.string().nullable().optional(),
17384
+ profile: profile.optional(),
17385
+ scopes: z26.array(z26.string()).default([]),
17386
+ createdAt: isoTime.nullable().optional(),
17387
+ lastUsedAt: isoTime.nullable().optional(),
17388
+ /** When the credential expires if it is not used again; null for sessions. */
17389
+ idleExpiresAt: isoTime.nullable().optional()
17390
+ });
17391
+ nexusCloudDeviceSchema = z26.object({
17392
+ deviceId: z26.string(),
17393
+ name: z26.string(),
17394
+ platform: z26.string().optional(),
17395
+ arch: z26.string().nullable().optional(),
17396
+ cliVersion: z26.string().nullable().optional(),
17397
+ createdAt: isoTime.optional(),
17398
+ lastSeenAt: isoTime.nullable().optional(),
17399
+ state: deviceState,
17400
+ signedOutAt: isoTime.nullable().optional(),
17401
+ /** Base64 device certificate; null until the device holds a key grant. */
17402
+ certificate: z26.string().nullable().optional(),
17403
+ /** The current credential's profile, or null with none (signed out or revoked). */
17404
+ profile: profile.optional(),
17405
+ /** True for the device whose credential made this request. */
17406
+ current: z26.boolean().default(false),
17407
+ projects: z26.number().int().nonnegative().optional(),
17408
+ replicas: z26.number().int().nonnegative().optional(),
17409
+ lastSyncAt: isoTime.nullable().optional(),
17410
+ lastPresenceAt: isoTime.nullable().optional()
17411
+ });
17412
+ nexusCloudOrganizationSchema = z26.object({
17413
+ id: z26.string(),
17414
+ name: z26.string(),
17415
+ slug: z26.string().nullable().optional(),
17416
+ role: z26.string().optional(),
17417
+ personal: z26.boolean().optional()
17418
+ });
17419
+ nexusCloudUserSchema = z26.object({
17420
+ id: z26.string(),
17421
+ email: z26.string(),
17422
+ name: z26.string().nullable().optional()
17423
+ });
17424
+ nexusCloudWhoamiSchema = z26.object({
17425
+ serverTime: isoTime.optional(),
17426
+ user: nexusCloudUserSchema,
17427
+ organizations: z26.array(nexusCloudOrganizationSchema).default([]),
17428
+ credential: nexusCloudCredentialSchema,
17429
+ /** The calling device; null for a session caller. */
17430
+ device: nexusCloudDeviceSchema.nullable()
17431
+ });
17432
+ nexusCloudDevicePageSchema = z26.object({
17433
+ devices: z26.array(nexusCloudDeviceSchema),
17434
+ nextCursor: z26.string().max(512).nullable().default(null),
17435
+ truncated: z26.boolean().default(false)
17436
+ });
17437
+ nexusCloudPresenceSchema = z26.looseObject({
17438
+ observedAt: isoTime.optional(),
17439
+ sync: z26.object({ enabled: z26.boolean() }).optional()
17440
+ });
17441
+ nexusCloudReplicaSchema = z26.object({
17442
+ projectId: z26.string(),
17443
+ replicaId: z26.string(),
17444
+ deviceId: z26.string(),
17445
+ deviceName: z26.string(),
17446
+ deviceState: deviceState.optional(),
17447
+ attachedAt: isoTime.optional(),
17448
+ lastSyncAt: isoTime.nullable(),
17449
+ presence: nexusCloudPresenceSchema.nullable(),
17450
+ presenceAt: isoTime.nullable()
17451
+ });
17452
+ nexusCloudDeviceCountsSchema = z26.object({
17453
+ active: z26.number().int().nonnegative(),
17454
+ total: z26.number().int().nonnegative()
17455
+ });
17456
+ projectFields = {
17457
+ projectId: z26.string(),
17458
+ label: z26.string().nullable(),
17459
+ organizationId: z26.string(),
17460
+ organizationName: z26.string().optional(),
17461
+ remoteUrl: z26.string().nullable().optional(),
17462
+ createdAt: isoTime.optional()
17463
+ };
17464
+ nexusCloudProjectListItemSchema = z26.object({
17465
+ ...projectFields,
17466
+ role: z26.string(),
17467
+ streamId: z26.string().optional(),
17468
+ headSeq: z26.number().int().nonnegative().nullable().optional(),
17469
+ openConflicts: z26.number().int().nonnegative().optional(),
17470
+ replicas: z26.array(nexusCloudReplicaSchema).default([]),
17471
+ devices: nexusCloudDeviceCountsSchema.optional(),
17472
+ replicaCount: z26.number().int().nonnegative().optional(),
17473
+ lastSyncAt: isoTime.nullable().optional(),
17474
+ lastPresenceAt: isoTime.nullable().optional(),
17475
+ /** This project's replica list was cut at 50 (T12858). */
17476
+ replicasTruncated: z26.boolean().default(false)
17477
+ });
17478
+ nexusCloudProjectPageSchema = z26.object({
17479
+ projects: z26.array(nexusCloudProjectListItemSchema),
17480
+ nextCursor: z26.string().max(512).nullable().default(null),
17481
+ /** Anything in the page was cut (the project list or a replica list). */
17482
+ truncated: z26.boolean().default(false),
17483
+ /** The project list itself was cut at 1000 (T12858). */
17484
+ projectsTruncated: z26.boolean().default(false)
17485
+ });
17486
+ nexusCloudProjectDetailSchema = z26.object({
17487
+ project: z26.object({ ...projectFields, deletedAt: isoTime.nullable().optional() }),
17488
+ role: z26.string(),
17489
+ canTrash: z26.boolean().optional(),
17490
+ openConflicts: z26.number().int().nonnegative(),
17491
+ /** At most 50, oldest attachment first. */
17492
+ replicas: z26.array(nexusCloudReplicaSchema),
17493
+ devices: nexusCloudDeviceCountsSchema,
17494
+ replicaCount: z26.number().int().nonnegative().optional(),
17495
+ lastSyncAt: isoTime.nullable().optional(),
17496
+ /** `replicas` was cut at 50. */
17497
+ truncated: z26.boolean().default(false),
17498
+ stream: z26.object({
17499
+ streamId: z26.string(),
17500
+ headSeq: z26.number().int().nonnegative(),
17501
+ headCheckpointId: z26.string().nullable()
17502
+ }).nullable()
17503
+ });
17504
+ nexusCloudReplicaPageSchema = z26.object({
17505
+ replicas: z26.array(nexusCloudReplicaSchema),
17506
+ devices: nexusCloudDeviceCountsSchema.optional(),
17507
+ nextCursor: z26.string().max(512).nullable().default(null),
17508
+ truncated: z26.boolean().default(false)
17509
+ });
17510
+ NEXUS_STATUS_CHECK_IDS = [
17511
+ "credential.valid",
17512
+ "device.registered",
17513
+ "device.active",
17514
+ "device.certified",
17515
+ "project.registered",
17516
+ "project.writable",
17517
+ "replica.attached",
17518
+ "presence.fresh",
17519
+ "replica.synced",
17520
+ "conflicts.none"
17521
+ ];
17522
+ NEXUS_STATUS_VERDICTS = ["ok", "attention", "not-linked", "not-registered"];
17523
+ CLOUD_STATUS_VERDICTS = [...NEXUS_STATUS_VERDICTS, "not-signed-in"];
17524
+ knownCheckId = z26.enum(NEXUS_STATUS_CHECK_IDS);
17525
+ nexusCloudStatusCheckSchema = z26.object({
17526
+ id: z26.enum(NEXUS_STATUS_CHECK_IDS),
17527
+ ok: z26.boolean(),
17528
+ required: z26.boolean(),
17529
+ detail: z26.string()
17530
+ });
17531
+ nexusCloudStatusSchema = z26.object({
17532
+ serverTime: isoTime.optional(),
17533
+ apiVersion: z26.string().optional(),
17534
+ user: nexusCloudUserSchema,
17535
+ credential: nexusCloudCredentialSchema,
17536
+ device: nexusCloudDeviceSchema.nullable(),
17537
+ project: z26.object({
17538
+ projectId: z26.string(),
17539
+ registered: z26.boolean(),
17540
+ organizationId: z26.string().nullable(),
17541
+ organizationName: z26.string().nullable(),
17542
+ role: z26.string().nullable()
17543
+ }).nullable(),
17544
+ replica: z26.object({
17545
+ replicaId: z26.string(),
17546
+ attached: z26.boolean(),
17547
+ attachedElsewhere: z26.boolean(),
17548
+ attachedAt: isoTime.nullable(),
17549
+ lastSyncAt: isoTime.nullable(),
17550
+ presenceAt: isoTime.nullable(),
17551
+ lastReplicaSeq: z26.number().int().nonnegative().nullable().optional()
17552
+ }).nullable(),
17553
+ stream: z26.object({
17554
+ streamId: z26.string(),
17555
+ headSeq: z26.number().int().nonnegative(),
17556
+ headCheckpointId: z26.string().nullable(),
17557
+ openConflicts: z26.number().int().nonnegative(),
17558
+ devices: nexusCloudDeviceCountsSchema
17559
+ }).nullable(),
17560
+ /** Check ids added by a newer server are dropped, never a parse failure. */
17561
+ checks: z26.array(nexusCloudStatusCheckSchema.extend({ id: z26.string() })).transform(
17562
+ (list) => list.flatMap((c) => {
17563
+ const id = knownCheckId.safeParse(c.id);
17564
+ return id.success ? [{ ...c, id: id.data }] : [];
17565
+ })
17566
+ ),
17567
+ /** A verdict added by a newer server reads as `attention`: never `ok` by accident. */
17568
+ verdict: z26.enum(NEXUS_STATUS_VERDICTS).catch("attention")
17569
+ });
17570
+ }
17571
+ });
17572
+
17368
17573
  // packages/contracts/src/task-id.ts
17369
17574
  var init_task_id = __esm({
17370
17575
  "packages/contracts/src/task-id.ts"() {
@@ -17449,6 +17654,7 @@ var init_src = __esm({
17449
17654
  init_decide();
17450
17655
  init_decide_sites();
17451
17656
  init_nexus_account();
17657
+ init_nexus_cloud();
17452
17658
  init_task_id();
17453
17659
  }
17454
17660
  });
@@ -17771,6 +17977,19 @@ var init_error_catalog = __esm({
17771
17977
  `Bind a session: run 'cleo session start --scope global --name "<name>"', 'cleo session resume <id>', or set CLEO_SESSION_ID=<id>.`
17772
17978
  )
17773
17979
  ],
17980
+ [
17981
+ 26 /* TASK_RENAMED */,
17982
+ def(
17983
+ 26 /* TASK_RENAMED */,
17984
+ "TASK_RENAMED",
17985
+ "CONFLICT",
17986
+ "Task was re-numbered by sync",
17987
+ 409,
17988
+ true,
17989
+ "E_TASK_RENAMED",
17990
+ "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."
17991
+ )
17992
+ ],
17774
17993
  [
17775
17994
  25 /* WORKTREE_LOCKED */,
17776
17995
  def(
@@ -19718,10 +19937,10 @@ var init_table2 = __esm({
19718
19937
  [IsDrizzleTable] = true;
19719
19938
  /** @internal */
19720
19939
  [ExtraConfigBuilder] = void 0;
19721
- constructor(name2, schema, baseName) {
19940
+ constructor(name2, schema, baseName2) {
19722
19941
  this[TableName] = this[OriginalName] = name2;
19723
19942
  this[TableSchema] = schema;
19724
- this[BaseName] = baseName;
19943
+ this[BaseName] = baseName2;
19725
19944
  }
19726
19945
  };
19727
19946
  }
@@ -21651,9 +21870,9 @@ var init_casing = __esm({
21651
21870
  });
21652
21871
 
21653
21872
  // 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
21654
- function sqliteTableBase(name2, columns, extraConfig, schema, casing, baseName = name2) {
21873
+ function sqliteTableBase(name2, columns, extraConfig, schema, casing, baseName2 = name2) {
21655
21874
  const casingFn = getCasingFn(casing);
21656
- const rawTable = new SQLiteTable(name2, schema, baseName);
21875
+ const rawTable = new SQLiteTable(name2, schema, baseName2);
21657
21876
  const parsedColumns = typeof columns === "function" ? columns(getSQLiteColumnBuilders()) : columns;
21658
21877
  const builtColumns = Object.fromEntries(Object.entries(parsedColumns).map(([name3, colBuilderBase]) => {
21659
21878
  const colBuilder = colBuilderBase;
@@ -25450,6 +25669,10 @@ function decisionFor(dbPath) {
25450
25669
  if (!decisions.has(key)) decisions.set(key, schemaWriteRefusal(key));
25451
25670
  return decisions.get(key) ?? null;
25452
25671
  }
25672
+ function schemaWritesAllowed(nativeDb) {
25673
+ const dbPath = nativeDb.location();
25674
+ return !dbPath || decisionFor(dbPath) === null;
25675
+ }
25453
25676
  function isWorktreeBuildSchemaError(error) {
25454
25677
  return error instanceof Error && error.message.includes(E_WORKTREE_BUILD_SCHEMA);
25455
25678
  }
@@ -26488,7 +26711,10 @@ var init_row_identity_registry = __esm({
26488
26711
  key: ["id"],
26489
26712
  birth: "recorded_at",
26490
26713
  content: ["ac_id", "previous_text", "reason"],
26491
- birthFacts: ["ac_id", "previous_text", "reason", "@refFp:ac_uid"],
26714
+ // v2 (T12802): no @refFp: the criterion's fingerprint is unknowable when
26715
+ // it was gone before this row got its identity, so it made the value
26716
+ // depend on fill timing. ac_id is always known.
26717
+ birthFacts: ["ac_id", "previous_text", "reason"],
26492
26718
  storedRefUids: [{ column: "ac_uid", from: "ac_id", table: ACS }],
26493
26719
  task: "T12341"
26494
26720
  },
@@ -26497,7 +26723,10 @@ var init_row_identity_registry = __esm({
26497
26723
  kind: "minted",
26498
26724
  key: ["id"],
26499
26725
  birth: "created_at",
26500
- birthFacts: ["evidence_atom_id", "binding_type", "ac_text_hash", "@refFp:ac_uid"],
26726
+ // v2 (T12802): ac_id in place of @refFp:ac_uid and ac_text_hash. Both
26727
+ // were filled from the criterion, so a backfilled binding whose
26728
+ // criterion was already gone got a different value (see AC history).
26729
+ birthFacts: ["evidence_atom_id", "binding_type", "ac_id"],
26501
26730
  storedRefUids: [
26502
26731
  { column: "ac_uid", from: "ac_id", table: ACS },
26503
26732
  { column: "ac_text_hash", from: "ac_id", table: ACS, source: "text_hash" }
@@ -26539,7 +26768,9 @@ var init_row_identity_registry = __esm({
26539
26768
  // The uid IS the primary key: display-id-alias.ts computes it on write.
26540
26769
  table: "tasks_display_id_aliases",
26541
26770
  kind: "natural",
26542
- key: ["entity_table", "display_id", "entity_uid"],
26771
+ // T12800: the reason is part of the key (collision-remint and
26772
+ // remint-assigned of one id and row are distinct facts).
26773
+ key: ["entity_table", "display_id", "entity_uid", "reason"],
26543
26774
  task: "T12341"
26544
26775
  },
26545
26776
  {
@@ -27121,6 +27352,20 @@ function installRowUidTriggers(db, scope) {
27121
27352
  );
27122
27353
  }
27123
27354
  }
27355
+ function v1ReleaseBirthFp(db, table, row) {
27356
+ const col = (c) => row[c] ?? null;
27357
+ 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")] : [];
27358
+ if (facts.length === 0) return null;
27359
+ const acUid = col("ac_uid");
27360
+ let ref = "ref:none";
27361
+ if (acUid !== null) {
27362
+ const ac = db.prepare("SELECT birth_fp AS fp FROM main.tasks_task_acceptance_criteria WHERE uid = ?").get(acUid);
27363
+ ref = ac === void 0 ? "ref:missing" : ac.fp;
27364
+ if (ref === null) return null;
27365
+ }
27366
+ const spec = rowIdentitySpec("project", table);
27367
+ return birthFingerprint(table, spec?.birth ? col(spec.birth) : null, [...facts, ref]);
27368
+ }
27124
27369
  function hasObject(db, type, name2) {
27125
27370
  return db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = ? AND name = ?").get(type, name2) !== void 0;
27126
27371
  }
@@ -27204,24 +27449,41 @@ function preReleaseBirthFp(db, table, row) {
27204
27449
  function resetStaleIdentity(db, scope, writers) {
27205
27450
  if (scope !== "project" || !hasTable(db, ROW_IDENTITY_META_TABLE)) return "none";
27206
27451
  if (readMeta(db, ROW_IDENTITY_RECIPE_KEY) === ROW_IDENTITY_RECIPE) return "none";
27207
- const minted = ROW_IDENTITY.project.filter(
27208
- (spec) => spec.kind === "minted" && hasTable(db, spec.table) && columnsOf(db, spec.table).has(BIRTH_FP_COLUMN)
27452
+ const minted = fingerprintOrder("project", ROW_IDENTITY.project).filter(
27453
+ (spec) => hasTable(db, spec.table) && columnsOf(db, spec.table).has(BIRTH_FP_COLUMN)
27209
27454
  );
27210
27455
  const stale = [];
27456
+ const cleared = /* @__PURE__ */ new Map();
27457
+ const mark = (table, column, value) => {
27458
+ if (value === null || value === void 0) return;
27459
+ const key = `${table}.${column}`;
27460
+ const set = cleared.get(key) ?? /* @__PURE__ */ new Set();
27461
+ set.add(String(value));
27462
+ cleared.set(key, set);
27463
+ };
27211
27464
  for (const spec of minted) {
27465
+ const deps = (spec.birthFacts ?? []).map((fact) => fingerprintRef("project", spec, fact)).filter((d) => d !== null);
27466
+ const keyColumn = targetKey("project", spec.table);
27212
27467
  const rows = db.prepare(
27213
27468
  `SELECT rowid AS _rowid, * FROM main.${q(spec.table)} WHERE ${q(BIRTH_FP_COLUMN)} IS NOT NULL`
27214
27469
  ).all();
27215
27470
  for (const row of rows) {
27216
- if (row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row)) {
27471
+ const derivedFromStale = deps.some(
27472
+ (d) => cleared.get(`${d.table}.${d.key}`)?.has(String(row[d.column] ?? ""))
27473
+ );
27474
+ if (derivedFromStale || row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row) || row[BIRTH_FP_COLUMN] === v1ReleaseBirthFp(db, spec.table, row)) {
27217
27475
  stale.push({ table: spec.table, rowid: row._rowid });
27476
+ mark(spec.table, keyColumn, row[keyColumn]);
27477
+ mark(spec.table, UID_COLUMN, row[UID_COLUMN]);
27218
27478
  }
27219
27479
  }
27220
27480
  }
27221
- if (stale.length === 0) return "none";
27481
+ const graveyardRows = scope === "project" && hasTable(db, AC_UID_GRAVEYARD) ? db.prepare(`SELECT count(*) AS n FROM main.${AC_UID_GRAVEYARD}`).get().n : 0;
27482
+ if (stale.length === 0 && graveyardRows === 0) return "none";
27222
27483
  if (readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== void 0) return "refused";
27223
27484
  db.exec("SAVEPOINT row_identity_reset");
27224
27485
  try {
27486
+ if (graveyardRows > 0) writers.clearAcUidGraveyardNative(db);
27225
27487
  for (const { table, rowid } of stale) {
27226
27488
  writers.clearBirthFpNative(db, table, rowid);
27227
27489
  }
@@ -27237,6 +27499,106 @@ function writeRecipeMarker(db, writers = requireWriters()) {
27237
27499
  if (!hasTable(db, ROW_IDENTITY_META_TABLE)) return;
27238
27500
  writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_RECIPE_KEY, ROW_IDENTITY_RECIPE);
27239
27501
  }
27502
+ function rowIdentityShared(db) {
27503
+ return readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== void 0;
27504
+ }
27505
+ function healRowIdentitySchema(db, scope, writers = registeredWriters) {
27506
+ const log15 = getLogger("row-identity");
27507
+ if (ROW_IDENTITY[scope].length === 0) {
27508
+ return [];
27509
+ }
27510
+ if (!schemaWritesAllowed(db)) {
27511
+ log15.debug({ scope }, "row identity schema heal skipped: schema writes refused for this build");
27512
+ return [];
27513
+ }
27514
+ try {
27515
+ db.exec("SAVEPOINT row_identity_heal");
27516
+ try {
27517
+ const healed = [
27518
+ ...scope === "project" ? ensureIdentityTables(db) : [],
27519
+ ...ensureRowIdentitySchema(db, scope)
27520
+ ];
27521
+ if (healed.length > 0) recordHealReceipt(db, scope, healed, writers);
27522
+ db.exec("RELEASE SAVEPOINT row_identity_heal");
27523
+ if (healed.length > 0)
27524
+ log15.info({ scope, healed: healed.length }, "row identity schema healed");
27525
+ return healed;
27526
+ } catch (error) {
27527
+ db.exec("ROLLBACK TO SAVEPOINT row_identity_heal");
27528
+ db.exec("RELEASE SAVEPOINT row_identity_heal");
27529
+ throw error;
27530
+ }
27531
+ } catch (error) {
27532
+ log15.error({ scope, error }, "row identity schema heal failed");
27533
+ return [];
27534
+ }
27535
+ }
27536
+ function recordHealReceipt(db, scope, healed, writers) {
27537
+ const table = HEAL_RECEIPT_TABLE[scope];
27538
+ const log15 = getLogger("row-identity");
27539
+ if (table === null || !hasTable(db, table) || !writers) {
27540
+ log15.warn({ scope, healed: healed.length }, "row identity schema healed; no receipt table");
27541
+ return;
27542
+ }
27543
+ const objects = healed.map((stmt) => {
27544
+ const col = /ALTER TABLE main\.("?)(\w+)\1 ADD COLUMN ("?)(\w+)\3/.exec(stmt);
27545
+ if (col) return `${col[2]}.${col[4]}`;
27546
+ const obj = /(TABLE|INDEX|TRIGGER) IF NOT EXISTS main\.("?)(\w+)\2/.exec(stmt);
27547
+ return obj ? `${obj[1].toLowerCase()} ${obj[3]}` : stmt.slice(0, 60);
27548
+ });
27549
+ const receipt2 = {
27550
+ at: (/* @__PURE__ */ new Date()).toISOString(),
27551
+ scope,
27552
+ statements: healed.length,
27553
+ objects
27554
+ };
27555
+ const history = [...readRowIdentityHealHistory(db), receipt2].slice(-HEAL_RECEIPTS_KEPT);
27556
+ writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_HEAL_KEY, JSON.stringify(history));
27557
+ }
27558
+ function readRowIdentityHealHistory(db) {
27559
+ const raw = readMeta(db, ROW_IDENTITY_HEAL_KEY);
27560
+ if (raw === void 0) return [];
27561
+ try {
27562
+ const parsed = JSON.parse(raw);
27563
+ return Array.isArray(parsed) ? parsed : [parsed];
27564
+ } catch {
27565
+ return [];
27566
+ }
27567
+ }
27568
+ function missingRowIdentitySchema(db) {
27569
+ if (!hasTable(db, "tasks_task_acceptance_criteria")) return [];
27570
+ const missing = [];
27571
+ for (const table of Object.keys(IDENTITY_TABLE_DDL)) {
27572
+ if (!hasTable(db, table)) missing.push(`table ${table}`);
27573
+ }
27574
+ for (const [table, column] of [
27575
+ ["tasks_display_id_aliases", "displaced_hlc"],
27576
+ ["tasks_display_id_aliases", "entity_birth_fp"],
27577
+ [AC_UID_GRAVEYARD, "birth_fp"]
27578
+ ]) {
27579
+ if (hasTable(db, table) && !columnsOf(db, table).has(column)) {
27580
+ missing.push(`column ${table}.${column}`);
27581
+ }
27582
+ }
27583
+ if (!hasObject(db, "trigger", "trg_tasks_ac_uid_graveyard")) {
27584
+ missing.push("trigger trg_tasks_ac_uid_graveyard");
27585
+ }
27586
+ for (const spec of ROW_IDENTITY.project) {
27587
+ if (!hasTable(db, spec.table)) continue;
27588
+ const cols = columnsOf(db, spec.table);
27589
+ for (const column of rowIdentityColumns("project", spec.table)) {
27590
+ if (!cols.has(column)) missing.push(`column ${spec.table}.${column}`);
27591
+ }
27592
+ if (!uidIsPrimaryKey(db, spec.table) && !hasObject(db, "index", `uq_${spec.table}_uid`)) {
27593
+ missing.push(`index uq_${spec.table}_uid`);
27594
+ }
27595
+ for (const ref of spec.storedRefUids ?? []) {
27596
+ const index2 = `idx_${spec.table}_${ref.column}`;
27597
+ if (!hasObject(db, "index", index2)) missing.push(`index ${index2}`);
27598
+ }
27599
+ }
27600
+ return missing;
27601
+ }
27240
27602
  function prepareRowIdentity(db, scope, options = {}) {
27241
27603
  if (ROW_IDENTITY[scope].length === 0) return null;
27242
27604
  if (!rowUidFillEnabled()) return null;
@@ -27275,7 +27637,7 @@ function prepareRowIdentity(db, scope, options = {}) {
27275
27637
  return null;
27276
27638
  }
27277
27639
  }
27278
- 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;
27640
+ 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;
27279
27641
  var init_row_identity = __esm({
27280
27642
  "packages/core/src/store/row-identity.ts"() {
27281
27643
  "use strict";
@@ -27284,6 +27646,7 @@ var init_row_identity = __esm({
27284
27646
  init_ac_table();
27285
27647
  init_row_identity_flag();
27286
27648
  init_row_identity_registry();
27649
+ init_worktree_build_guard();
27287
27650
  ROW_UID_DOMAIN = "cleo/row-uid/v1";
27288
27651
  ROW_BIRTH_DOMAIN = "cleo/row-birth/v1";
27289
27652
  ROW_UID_SQL_FUNCTION = "cleo_row_uid";
@@ -27293,7 +27656,7 @@ var init_row_identity = __esm({
27293
27656
  AC_UID_GRAVEYARD = "tasks_ac_uid_graveyard";
27294
27657
  STORE_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?)?(Z|[+-]\d{2}:?\d{2})?$/;
27295
27658
  MAX_UUID_MS = 2 ** 48 - 1;
27296
- ROW_IDENTITY_RECIPE = "cleo/row-identity/v1";
27659
+ ROW_IDENTITY_RECIPE = "cleo/row-identity/v2";
27297
27660
  ROW_IDENTITY_META_TABLE = "tasks_row_identity_meta";
27298
27661
  ROW_IDENTITY_RECIPE_KEY = "row_identity_recipe";
27299
27662
  ROW_IDENTITY_SYNCED_KEY = "row_identity_synced";
@@ -27338,6 +27701,15 @@ BEGIN
27338
27701
  INSERT INTO ${AC_UID_GRAVEYARD} (ac_id, uid, task_id, ordinal, text, birth_fp, deleted_at)
27339
27702
  VALUES (OLD.id, OLD.uid, OLD.task_id, OLD.ordinal, OLD.text, OLD.birth_fp, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
27340
27703
  END`;
27704
+ ROW_IDENTITY_HEAL_KEY = "row_identity_schema_healed";
27705
+ HEAL_RECEIPT_TABLE = {
27706
+ project: ROW_IDENTITY_META_TABLE,
27707
+ // The global store declares no identity table yet (ROW_IDENTITY.global is
27708
+ // empty, so its heal never runs); the change that declares one adds its
27709
+ // meta table here.
27710
+ global: null
27711
+ };
27712
+ HEAL_RECEIPTS_KEPT = 20;
27341
27713
  }
27342
27714
  });
27343
27715
 
@@ -33031,7 +33403,7 @@ var init_task_claim = __esm({
33031
33403
  });
33032
33404
 
33033
33405
  // packages/core/src/store/converters.ts
33034
- import { z as z26 } from "zod";
33406
+ import { z as z27 } from "zod";
33035
33407
  function readStoredAcceptance(row) {
33036
33408
  if (row.acceptanceJson === null || row.acceptanceJson === void 0) return void 0;
33037
33409
  try {
@@ -33189,7 +33561,7 @@ var init_converters = __esm({
33189
33561
  init_src();
33190
33562
  init_parsers();
33191
33563
  init_task_claim();
33192
- storedAcceptanceSchema = z26.custom(
33564
+ storedAcceptanceSchema = z27.custom(
33193
33565
  (value) => Array.isArray(value) && value.every((item) => acceptanceItemSchema.safeParse(item).success)
33194
33566
  );
33195
33567
  }
@@ -35852,7 +36224,7 @@ import {
35852
36224
  } from "node:fs";
35853
36225
  import { createRequire as createRequire5 } from "node:module";
35854
36226
  import { basename as basename7, dirname as dirname12, join as join13 } from "node:path";
35855
- import { z as z27 } from "zod";
36227
+ import { z as z28 } from "zod";
35856
36228
  function formatBackupTimestamp(d) {
35857
36229
  const pad = (n, len = 2) => String(n).padStart(len, "0");
35858
36230
  return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
@@ -35989,16 +36361,16 @@ var init_backup_sidecar = __esm({
35989
36361
  init_logger2();
35990
36362
  PRE_COLLAPSE_BACKUP_TYPE = "migration";
35991
36363
  MARKER_PREFIX = "twin_collapse:";
35992
- backupSidecarSchema = z27.object({
35993
- backupId: z27.string().min(1),
35994
- type: z27.string().min(1),
35995
- timestamp: z27.string().min(1),
35996
- note: z27.string().optional(),
35997
- files: z27.array(z27.string()),
35998
- pinned: z27.boolean().optional(),
35999
- pinnedReason: z27.string().optional(),
36000
- releasedAt: z27.string().optional(),
36001
- releasedReason: z27.string().optional()
36364
+ backupSidecarSchema = z28.object({
36365
+ backupId: z28.string().min(1),
36366
+ type: z28.string().min(1),
36367
+ timestamp: z28.string().min(1),
36368
+ note: z28.string().optional(),
36369
+ files: z28.array(z28.string()),
36370
+ pinned: z28.boolean().optional(),
36371
+ pinnedReason: z28.string().optional(),
36372
+ releasedAt: z28.string().optional(),
36373
+ releasedReason: z28.string().optional()
36002
36374
  }).passthrough();
36003
36375
  }
36004
36376
  });
@@ -36170,7 +36542,7 @@ var init_pre_repair_snapshot = __esm({
36170
36542
  import { createHash as createHash5 } from "node:crypto";
36171
36543
  import { basename as basename8, dirname as dirname15 } from "node:path";
36172
36544
  import { fileURLToPath as fileURLToPath4 } from "node:url";
36173
- import { z as z28 } from "zod";
36545
+ import { z as z29 } from "zod";
36174
36546
  function hasMainTable(db, name2) {
36175
36547
  return db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = 'table' AND name = ?").get(name2) !== void 0;
36176
36548
  }
@@ -37303,20 +37675,20 @@ var init_twin_collapse = __esm({
37303
37675
  SHADOW_WRITE_REFUSED = "E_TWIN_COLLAPSE_FAILED: store is read-only until the twin collapse succeeds; run cleo doctor twin-collapse";
37304
37676
  SHADOW_WRITE_OPS = ["INSERT", "UPDATE", "DELETE"];
37305
37677
  STICKY_ARCHIVE_KEY = `${TWIN_COLLAPSE_ARCHIVE_PREFIX}sticky_tags`;
37306
- FocusNotesShape = z28.object({ sessionNotes: z28.array(z28.unknown()) }).passthrough();
37307
- StickyArchiveShape = z28.array(z28.string());
37308
- RecoveryReceiptShape = z28.object({
37309
- task: z28.literal("T12727"),
37310
- id: z28.string(),
37311
- recoveredAt: z28.string(),
37312
- snapshot: z28.string().nullable(),
37313
- archived: z28.array(z28.object({ key: z28.string(), archiveKey: z28.string(), sha: z28.string() })),
37314
- focusNotesAdded: z28.number(),
37315
- stickyArchived: z28.array(z28.string()),
37316
- stickyArchiveKey: z28.string(),
37317
- before: z28.object({ focusState: z28.string().nullable(), stickyArchive: z28.string().nullable() }),
37318
- after: z28.object({ focusState: z28.string().nullable(), stickyArchive: z28.string().nullable() }),
37319
- rolledBackAt: z28.string().nullable()
37678
+ FocusNotesShape = z29.object({ sessionNotes: z29.array(z29.unknown()) }).passthrough();
37679
+ StickyArchiveShape = z29.array(z29.string());
37680
+ RecoveryReceiptShape = z29.object({
37681
+ task: z29.literal("T12727"),
37682
+ id: z29.string(),
37683
+ recoveredAt: z29.string(),
37684
+ snapshot: z29.string().nullable(),
37685
+ archived: z29.array(z29.object({ key: z29.string(), archiveKey: z29.string(), sha: z29.string() })),
37686
+ focusNotesAdded: z29.number(),
37687
+ stickyArchived: z29.array(z29.string()),
37688
+ stickyArchiveKey: z29.string(),
37689
+ before: z29.object({ focusState: z29.string().nullable(), stickyArchive: z29.string().nullable() }),
37690
+ after: z29.object({ focusState: z29.string().nullable(), stickyArchive: z29.string().nullable() }),
37691
+ rolledBackAt: z29.string().nullable()
37320
37692
  });
37321
37693
  }
37322
37694
  });
@@ -40898,12 +41270,12 @@ var init_safety_data_accessor = __esm({
40898
41270
  this.getSafetyOptions()
40899
41271
  );
40900
41272
  }
40901
- async insertNewTask(task) {
41273
+ async insertNewTask(task, identity) {
40902
41274
  this.logVerbose(`Inserting new task ${task.id}`);
40903
41275
  await safeSingleTaskWrite(
40904
41276
  this.inner,
40905
41277
  task.id,
40906
- () => this.inner.insertNewTask(task),
41278
+ () => this.inner.insertNewTask(task, identity),
40907
41279
  this.cwd,
40908
41280
  this.getSafetyOptions()
40909
41281
  );
@@ -40951,6 +41323,9 @@ var init_safety_data_accessor = __esm({
40951
41323
  return this.inner.findOrphanAcBindings();
40952
41324
  }
40953
41325
  // ---- Metadata (pass-through to inner) ----
41326
+ async getTaskIdentities(taskIds) {
41327
+ return await this.inner.getTaskIdentities?.(taskIds) ?? [];
41328
+ }
40954
41329
  async getMetaValue(key) {
40955
41330
  return this.inner.getMetaValue(key);
40956
41331
  }
@@ -41324,13 +41699,147 @@ var init_cross_db_cleanup = __esm({
41324
41699
  }
41325
41700
  });
41326
41701
 
41702
+ // packages/core/src/store/dependency-cycles.ts
41703
+ function formatDependencyCycle(cycle) {
41704
+ return cycle.join(" \u2192 ");
41705
+ }
41706
+ function buildAdjacency(edges) {
41707
+ const adjacency = /* @__PURE__ */ new Map();
41708
+ for (const { taskId, dependsOn } of edges) {
41709
+ const next = adjacency.get(taskId);
41710
+ if (next) next.push(dependsOn);
41711
+ else adjacency.set(taskId, [dependsOn]);
41712
+ }
41713
+ for (const next of adjacency.values()) next.sort();
41714
+ return adjacency;
41715
+ }
41716
+ function shortestPath(adjacency, from, to) {
41717
+ if (from === to) return [from];
41718
+ const previous = /* @__PURE__ */ new Map([[from, from]]);
41719
+ const queue = [from];
41720
+ for (let head = 0; head < queue.length; head++) {
41721
+ const id = queue[head];
41722
+ for (const next of adjacency.get(id) ?? []) {
41723
+ if (previous.has(next)) continue;
41724
+ previous.set(next, id);
41725
+ if (next === to) {
41726
+ const path2 = [to];
41727
+ let cursor = id;
41728
+ while (cursor !== from) {
41729
+ path2.push(cursor);
41730
+ cursor = previous.get(cursor);
41731
+ }
41732
+ path2.push(from);
41733
+ return path2.reverse();
41734
+ }
41735
+ queue.push(next);
41736
+ }
41737
+ }
41738
+ return null;
41739
+ }
41740
+ function findClosedCycle(existing, edge) {
41741
+ if (edge.taskId === edge.dependsOn) return [edge.taskId, edge.taskId];
41742
+ const path2 = shortestPath(buildAdjacency(existing), edge.dependsOn, edge.taskId);
41743
+ return path2 ? [edge.taskId, ...path2] : null;
41744
+ }
41745
+ function dependencyCycleError(edge, cycle) {
41746
+ const self = edge.taskId === edge.dependsOn;
41747
+ 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)}`;
41748
+ const closing = cycle.length >= 3 ? cycle[cycle.length - 2] : void 0;
41749
+ 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}`;
41750
+ return new CleoError(14 /* CIRCULAR_REFERENCE */, message, {
41751
+ fix,
41752
+ details: {
41753
+ field: "depends",
41754
+ actual: edge.dependsOn,
41755
+ taskId: edge.taskId,
41756
+ cycle: [...cycle]
41757
+ }
41758
+ });
41759
+ }
41760
+ function isDependencyCycleAbort(err) {
41761
+ for (let e = err, depth = 0; e instanceof Error && depth < 8; depth++) {
41762
+ if (e.message.includes(DEPENDENCY_CYCLE_CODE)) return true;
41763
+ e = e.cause;
41764
+ }
41765
+ return false;
41766
+ }
41767
+ function readDependencyEdges(db) {
41768
+ return db.select({
41769
+ taskId: tasksTaskDependencies.taskId,
41770
+ dependsOn: tasksTaskDependencies.dependsOn
41771
+ }).from(tasksTaskDependencies).all();
41772
+ }
41773
+ function rethrowDependencyCycle(db, err, attempted) {
41774
+ if (!isDependencyCycleAbort(err)) throw err;
41775
+ const edges = readDependencyEdges(db);
41776
+ for (const edge of attempted) {
41777
+ const cycle = findClosedCycle(edges, edge);
41778
+ if (cycle) throw dependencyCycleError(edge, cycle);
41779
+ edges.push(edge);
41780
+ }
41781
+ throw err;
41782
+ }
41783
+ var DEPENDENCY_CYCLE_CODE;
41784
+ var init_dependency_cycles = __esm({
41785
+ "packages/core/src/store/dependency-cycles.ts"() {
41786
+ "use strict";
41787
+ init_src();
41788
+ init_errors2();
41789
+ init_tasks_schema();
41790
+ DEPENDENCY_CYCLE_CODE = "E_TASK_DEPENDENCY_CYCLE";
41791
+ }
41792
+ });
41793
+
41327
41794
  // packages/core/src/store/db-helpers.ts
41328
41795
  import { and as and5, eq as eq7, inArray as inArray3, sql as sql27 } from "drizzle-orm";
41329
41796
  async function upsertTask(db, row, archiveFields, allowOrphanParent = false) {
41330
41797
  await writeTaskRow(db, row, archiveFields, allowOrphanParent, false);
41331
41798
  }
41332
- async function insertNewTask(db, row) {
41333
- await writeTaskRow(db, row, void 0, false, true);
41799
+ async function insertNewTask(db, row, identity = { origin: "new" }) {
41800
+ await writeTaskRow(
41801
+ db,
41802
+ { ...row, ...await importedIdentity(db, identity, row) },
41803
+ void 0,
41804
+ false,
41805
+ true
41806
+ );
41807
+ }
41808
+ async function tableExists2(db, table) {
41809
+ const rows = await db.all(
41810
+ sql27`SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = ${table}`
41811
+ );
41812
+ return rows.length > 0;
41813
+ }
41814
+ async function importedIdentity(db, identity, row) {
41815
+ if (identity.origin === "new" || !rowUidFillEnabled()) return {};
41816
+ const fp = identity.birthFp ?? null;
41817
+ const carried2 = identity.uid ?? null;
41818
+ if (carried2 !== null && fp !== null) {
41819
+ let uid = carried2;
41820
+ if (await tableExists2(db, "tasks_uid_aliases")) {
41821
+ for (let hop = 0; hop < 32; hop++) {
41822
+ const found = await db.all(
41823
+ sql27`SELECT new_uid AS uid FROM tasks_uid_aliases
41824
+ WHERE entity_table = 'tasks_tasks' AND old_uid = ${uid} AND old_birth_fp = ${fp}`
41825
+ );
41826
+ const next = found[0];
41827
+ if (!next) break;
41828
+ uid = next.uid;
41829
+ }
41830
+ }
41831
+ const [holder] = await db.select({ id: tasksTasks.id, birthFp: tasksTasks.birthFp }).from(tasksTasks).where(eq7(tasksTasks.uid, uid)).limit(1).all();
41832
+ if (holder && holder.birthFp === fp) throw new SameRowPresentError(row.id, holder.id);
41833
+ if (await tableExists2(db, "tasks_identity_quarantine")) {
41834
+ const [held] = await db.all(
41835
+ sql27`SELECT 1 AS x FROM tasks_identity_quarantine
41836
+ WHERE entity_table = 'tasks_tasks' AND uid = ${uid} AND birth_fp = ${fp}`
41837
+ );
41838
+ if (held) throw new SameRowPresentError(row.id, "held");
41839
+ }
41840
+ if (!holder) return { uid, birthFp: fp };
41841
+ }
41842
+ return { uid: null, birthFp: null };
41334
41843
  }
41335
41844
  async function writeTaskRow(db, row, archiveFields, allowOrphanParent, insertOnly) {
41336
41845
  if (row.parentId && row.parentId === row.id) {
@@ -41481,7 +41990,11 @@ async function batchUpdateDependencies(db, tasks2, validIds) {
41481
41990
  for (const depId of want) if (!have.has(depId)) added.push({ taskId, dependsOn: depId });
41482
41991
  }
41483
41992
  if (added.length > 0) {
41484
- await db.insert(tasksTaskDependencies).values(added).onConflictDoNothing().run();
41993
+ try {
41994
+ await db.insert(tasksTaskDependencies).values(added).onConflictDoNothing().run();
41995
+ } catch (err) {
41996
+ rethrowDependencyCycle(db, err, added);
41997
+ }
41485
41998
  }
41486
41999
  }
41487
42000
  async function loadDependenciesForTasks(db, tasks2, _validationIds) {
@@ -41526,15 +42039,29 @@ async function loadRelationsForTasks(db, tasks2) {
41526
42039
  task.relates = relations && relations.length > 0 ? relations : [];
41527
42040
  }
41528
42041
  }
41529
- var log6;
42042
+ var log6, SameRowPresentError;
41530
42043
  var init_db_helpers = __esm({
41531
42044
  "packages/core/src/store/db-helpers.ts"() {
41532
42045
  "use strict";
41533
42046
  init_src();
41534
42047
  init_errors2();
41535
42048
  init_logger2();
42049
+ init_dependency_cycles();
42050
+ init_row_identity_flag();
41536
42051
  init_tasks_schema();
41537
42052
  log6 = getLogger("db-helpers");
42053
+ SameRowPresentError = class extends CleoError {
42054
+ /** Where the row already is: its display id, or `held` when quarantined. */
42055
+ presentAs;
42056
+ constructor(taskId, presentAs) {
42057
+ super(
42058
+ 22 /* ID_COLLISION */,
42059
+ `Task ${taskId} is already in this store (${presentAs === "held" ? "held for sync" : `as ${presentAs}`}): same uid and birth fingerprint; not written again`,
42060
+ { details: { field: "taskId", actual: taskId, expected: presentAs } }
42061
+ );
42062
+ this.presentAs = presentAs;
42063
+ }
42064
+ };
41538
42065
  }
41539
42066
  });
41540
42067
 
@@ -41619,46 +42146,133 @@ function readProcessEntry(pid) {
41619
42146
  return null;
41620
42147
  }
41621
42148
  }
41622
- function resolvePpidKey(ppid, lookupProcess) {
42149
+ function readProcessArgs(pid) {
42150
+ if (process.platform === "win32" || !Number.isInteger(pid) || pid <= 1) return null;
42151
+ try {
42152
+ const out = execFileSync("ps", ["-o", "args=", "-p", String(pid)], {
42153
+ encoding: "utf8",
42154
+ timeout: 1e3,
42155
+ stdio: ["ignore", "pipe", "ignore"],
42156
+ env: { ...process.env, ...PS_STABLE_ENV }
42157
+ }).trim();
42158
+ return out === "" ? null : out;
42159
+ } catch {
42160
+ return null;
42161
+ }
42162
+ }
42163
+ function baseName(token) {
42164
+ return token.slice(token.lastIndexOf("/") + 1).replace(/^-/, "");
42165
+ }
42166
+ function programName(commandLine) {
42167
+ const tokens = commandLine.trim().split(/\s+/);
42168
+ const head = baseName(tokens[0] ?? "");
42169
+ if (!RUNTIME_PATTERN.test(head)) return head;
42170
+ for (let i = 1; i < tokens.length; i++) {
42171
+ const token = tokens[i] ?? "";
42172
+ if (token === "-m") return (tokens[i + 1] ?? head).split(".")[0] ?? head;
42173
+ if (token.startsWith("-")) continue;
42174
+ return baseName(token).replace(/\.(c|m)?(js|ts)$|\.py$/, "");
42175
+ }
42176
+ return head;
42177
+ }
42178
+ function isCommandStringShell(args) {
42179
+ const tokens = args.trim().split(/\s+/);
42180
+ const shell = programName(args);
42181
+ let first = 1;
42182
+ if (RUNTIME_PATTERN.test(baseName(tokens[0] ?? ""))) {
42183
+ while (first < tokens.length && (tokens[first] ?? "").startsWith("-")) first++;
42184
+ first++;
42185
+ }
42186
+ const rest = tokens.slice(first);
42187
+ if (shell === "pwsh" || shell === "powershell") {
42188
+ return rest.some((t) => PWSH_COMMAND_SWITCHES.has(t.toLowerCase()));
42189
+ }
42190
+ for (let i = 0; i < rest.length; i++) {
42191
+ const token = rest[i] ?? "";
42192
+ if (token === "--") return false;
42193
+ if (token === "--command" || token === "--commands") return true;
42194
+ if (SHELL_OPTIONS_WITH_ARGUMENT.has(token)) {
42195
+ i++;
42196
+ continue;
42197
+ }
42198
+ if (token.startsWith("--")) continue;
42199
+ if (!/^[-+][A-Za-z]+$/.test(token)) return false;
42200
+ if (token.startsWith("-") && token.slice(1).includes("c")) return true;
42201
+ }
42202
+ return false;
42203
+ }
42204
+ function resolveAncestor(ppid, lookupProcess, lookupArgs, agentId = null) {
41623
42205
  let pid = ppid;
41624
- for (let depth = 0; depth < PPID_CHAIN_MAX_DEPTH && pid > 1; depth++) {
42206
+ for (let depth = 0; depth < PPID_CHAIN_MAX_DEPTH; depth++) {
42207
+ if (pid <= 1) return null;
41625
42208
  const entry = lookupProcess(pid);
41626
- if (!entry) return null;
41627
- if (!PPID_CHAIN_LAUNCHERS.has(entry.command)) {
41628
- return { key: `ppid:${entry.pid}@${entry.startedAt}`, source: "ppid", kind: "ppid" };
42209
+ if (!entry || entry.pid <= 1) return null;
42210
+ const args = lookupArgs(entry.pid);
42211
+ const program = programName(args ?? entry.command);
42212
+ if (INIT_PROCESSES.has(program) || INIT_PROCESSES.has(entry.command)) return null;
42213
+ if (OWNER_PROCESS_SHELLS.has(program)) {
42214
+ if (args === null) return null;
42215
+ if (!isCommandStringShell(args)) return { entry, program, type: "shell" };
42216
+ } else if (!ANCESTOR_WRAPPERS.has(program)) {
42217
+ if (KNOWN_HARNESSES.has(program)) return { entry, program, type: "harness" };
42218
+ return agentId ? { entry, program, type: "host" } : null;
41629
42219
  }
41630
42220
  pid = entry.ppid;
41631
42221
  }
41632
42222
  return null;
41633
42223
  }
42224
+ function anchorKey(anchor, agentId, under) {
42225
+ const proc = `${anchor.entry.pid}@${anchor.entry.startedAt}`;
42226
+ if (anchor.type === "shell" && !agentId) {
42227
+ return { key: `proc:${proc}`, source: "process", kind: "process" };
42228
+ }
42229
+ const agent = agentId ? `#agent=${agentId}` : "";
42230
+ const scope = under ? `|${under.key}` : "";
42231
+ return {
42232
+ key: `harness:${anchor.program}:${proc}${agent}${scope}`,
42233
+ source: "process",
42234
+ kind: "harness"
42235
+ };
42236
+ }
41634
42237
  function resolveTerminalKeys(options = {}) {
41635
42238
  const env = options.env ?? process.env;
41636
42239
  const keys = [];
41637
42240
  for (const source of TERMINAL_KEY_SOURCES) {
41638
42241
  const value = envValue(env, source.envVar);
41639
42242
  if (value === null) continue;
41640
- const qualifier = envValue(env, source.qualifierEnvVar);
42243
+ const qualifiers = [source.qualifierEnvVar, ...source.extraQualifierEnvVars ?? []].map((name2) => envValue(env, name2)).filter((q2) => q2 !== null);
41641
42244
  keys.push({
41642
- key: `env:${source.envVar}=${qualifier === null ? value : `${qualifier}|${value}`}`,
42245
+ key: `env:${source.envVar}=${[...qualifiers, value].join("|")}`,
41643
42246
  source: source.envVar,
41644
42247
  kind: source.kind
41645
42248
  });
41646
42249
  }
41647
- if (keys.length > 0) return keys;
41648
- const live = options.ppid === void 0 && options.lookupProcess === void 0;
41649
- let ppidKey;
42250
+ if (keys.some((k) => k.kind === "provider")) return keys;
42251
+ const agentId = envValue(env, "CLEO_AGENT_ID");
42252
+ const live = options.ppid === void 0 && options.lookupProcess === void 0 && options.lookupArgs === void 0;
42253
+ let anchor;
41650
42254
  if (live) {
41651
- if (livePpidKey === void 0) livePpidKey = resolvePpidKey(process.ppid, readProcessEntry);
41652
- ppidKey = livePpidKey;
42255
+ const memoKey2 = agentId ?? "";
42256
+ if (!liveAnchors.has(memoKey2))
42257
+ liveAnchors.set(
42258
+ memoKey2,
42259
+ resolveAncestor(process.ppid, readProcessEntry, readProcessArgs, agentId)
42260
+ );
42261
+ anchor = liveAnchors.get(memoKey2) ?? null;
41653
42262
  } else {
41654
- ppidKey = resolvePpidKey(
42263
+ anchor = resolveAncestor(
41655
42264
  options.ppid ?? process.ppid,
41656
- options.lookupProcess ?? readProcessEntry
42265
+ options.lookupProcess ?? readProcessEntry,
42266
+ options.lookupArgs ?? readProcessArgs,
42267
+ agentId
41657
42268
  );
41658
42269
  }
41659
- return ppidKey ? [ppidKey] : [];
42270
+ if (!anchor) return keys;
42271
+ if (keys.length === 0) return [anchorKey(anchor, agentId, void 0)];
42272
+ if (anchor.type === "shell" && !agentId) return keys;
42273
+ return [...keys, anchorKey(anchor, agentId, keys[0])];
41660
42274
  }
41661
- var TERMINAL_KEY_SOURCES, PPID_CHAIN_LAUNCHERS, PPID_CHAIN_MAX_DEPTH, livePpidKey;
42275
+ 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;
41662
42276
  var init_terminal_identity = __esm({
41663
42277
  "packages/core/src/sessions/terminal-identity.ts"() {
41664
42278
  "use strict";
@@ -41690,24 +42304,100 @@ var init_terminal_identity = __esm({
41690
42304
  kind: "terminal",
41691
42305
  origin: "konsole",
41692
42306
  qualifierEnvVar: "KONSOLE_DBUS_SERVICE"
42307
+ },
42308
+ // T12864 — non-terminal callers whose platform exports a per-job / per-login id.
42309
+ // GitHub Actions: GITHUB_RUN_ID is per run and shared by its jobs, so it is
42310
+ // qualified by GITHUB_RUN_ATTEMPT and GITHUB_JOB ("Variables reference",
42311
+ // docs.github.com/en/actions/reference/workflows-and-actions/variables).
42312
+ // Every step of one job shares the key; the steps run sequentially.
42313
+ {
42314
+ envVar: "GITHUB_RUN_ID",
42315
+ kind: "terminal",
42316
+ origin: "github-actions",
42317
+ qualifierEnvVar: "GITHUB_RUN_ATTEMPT",
42318
+ extraQualifierEnvVars: ["GITHUB_JOB"]
42319
+ },
42320
+ // GitLab CI: CI_JOB_ID is unique per job across the instance ("Predefined
42321
+ // CI/CD variables reference", docs.gitlab.com/ci/variables/predefined_variables).
42322
+ { envVar: "CI_JOB_ID", kind: "terminal", origin: "gitlab-ci" },
42323
+ // OpenSSH login with a tty: SSH_TTY names the login's tty and is set only
42324
+ // when one was allocated; SSH_CONNECTION ("client-ip client-port server-ip
42325
+ // server-port", ssh(1) ENVIRONMENT) disambiguates it across connections. A
42326
+ // one-shot `ssh host 'cmd'` has no tty and falls through to the ancestor walk.
42327
+ {
42328
+ envVar: "SSH_TTY",
42329
+ kind: "terminal",
42330
+ origin: "openssh",
42331
+ qualifierEnvVar: "SSH_CONNECTION"
41693
42332
  }
41694
42333
  ];
41695
- PPID_CHAIN_LAUNCHERS = /* @__PURE__ */ new Set([
41696
- "node",
42334
+ PPID_CHAIN_MAX_DEPTH = 12;
42335
+ KNOWN_HARNESSES = /* @__PURE__ */ new Set([
42336
+ "claude",
42337
+ "codex",
42338
+ "aider",
42339
+ "gemini",
42340
+ "kimi",
42341
+ "cursor-agent",
42342
+ "amp"
42343
+ ]);
42344
+ RUNTIME_PATTERN = /^(node|nodejs|bun|deno|python[0-9.]*|pypy[0-9.]*|ruby|java|php|perl)$/;
42345
+ ANCESTOR_WRAPPERS = /* @__PURE__ */ new Set([
42346
+ "env",
42347
+ "timeout",
42348
+ "gtimeout",
42349
+ "nohup",
42350
+ "nice",
42351
+ "time",
42352
+ "xargs",
42353
+ "stdbuf",
42354
+ "sudo",
42355
+ "doas",
41697
42356
  "npm",
42357
+ "npm-cli",
41698
42358
  "npx",
42359
+ "npx-cli",
41699
42360
  "pnpm",
41700
42361
  "pnpx",
41701
42362
  "yarn",
41702
- "bun",
41703
42363
  "bunx",
41704
- "tsx",
41705
42364
  "corepack",
41706
42365
  "mise",
41707
- "env",
41708
42366
  "cleo"
41709
42367
  ]);
41710
- PPID_CHAIN_MAX_DEPTH = 4;
42368
+ INIT_PROCESSES = /* @__PURE__ */ new Set(["launchd", "systemd", "init"]);
42369
+ SHELL_OPTIONS_WITH_ARGUMENT = /* @__PURE__ */ new Set([
42370
+ "-o",
42371
+ "+o",
42372
+ "-O",
42373
+ "+O",
42374
+ "--rcfile",
42375
+ "--init-file"
42376
+ ]);
42377
+ PWSH_COMMAND_SWITCHES = /* @__PURE__ */ new Set([
42378
+ "-c",
42379
+ "-command",
42380
+ "-commandwithargs",
42381
+ "-cwa",
42382
+ "-e",
42383
+ "-ec",
42384
+ "-encodedcommand"
42385
+ ]);
42386
+ OWNER_PROCESS_SHELLS = /* @__PURE__ */ new Set([
42387
+ "sh",
42388
+ "bash",
42389
+ "zsh",
42390
+ "dash",
42391
+ "fish",
42392
+ "ksh",
42393
+ "tcsh",
42394
+ "csh",
42395
+ "pwsh",
42396
+ "powershell",
42397
+ "nu",
42398
+ "xonsh"
42399
+ ]);
42400
+ liveAnchors = /* @__PURE__ */ new Map();
41711
42401
  }
41712
42402
  });
41713
42403
 
@@ -41747,7 +42437,8 @@ var init_session_binding_schema = __esm({
41747
42437
  });
41748
42438
 
41749
42439
  // packages/core/src/store/session-store.ts
41750
- import { and as and6, desc as desc2, eq as eq8, isNull as isNull3, lte as lte2, or as or4 } from "drizzle-orm";
42440
+ import { existsSync as existsSync19 } from "node:fs";
42441
+ 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";
41751
42442
  async function getSession(sessionId, cwd) {
41752
42443
  const scope = captureProjectScope(cwd ?? getProjectRoot(), worktreeScope.getStore());
41753
42444
  return worktreeScope.run(scope, async () => {
@@ -41769,8 +42460,9 @@ async function getActiveSession(cwd) {
41769
42460
  function splitTerminalKeys(keys) {
41770
42461
  return {
41771
42462
  provider: keys.find((k) => k.kind === "provider"),
41772
- pane: keys.find((k) => k.kind === "multiplexer"),
41773
- tab: keys.find((k) => k.kind === "terminal" || k.kind === "ppid")
42463
+ // A harness / CLEO_AGENT_ID agent (T12864) is more specific than a pane.
42464
+ pane: keys.find((k) => k.kind === "harness") ?? keys.find((k) => k.kind === "multiplexer"),
42465
+ tab: keys.find((k) => k.kind === "terminal" || k.kind === "process" || k.kind === "ppid")
41774
42466
  };
41775
42467
  }
41776
42468
  async function readBinding(db, key) {
@@ -41850,6 +42542,7 @@ async function resolveCurrentSession(cwd) {
41850
42542
  async function resolveBoundSession(cwd) {
41851
42543
  const scope = captureProjectScope(cwd ?? getProjectRoot(), worktreeScope.getStore());
41852
42544
  return worktreeScope.run(scope, async () => {
42545
+ if (!existsSync19(getDbPath(scope.worktreeRoot))) return null;
41853
42546
  const connId = getCurrentConnectionSessionId();
41854
42547
  if (connId) {
41855
42548
  const byConn = await getSession(connId, scope.worktreeRoot);
@@ -42001,7 +42694,7 @@ __export(registry_exports2, {
42001
42694
  updateAgentStatus: () => updateAgentStatus
42002
42695
  });
42003
42696
  import { randomBytes as randomBytes2 } from "node:crypto";
42004
- import { and as and7, eq as eq9, inArray as inArray4, lt as lt2, sql as sql29 } from "drizzle-orm";
42697
+ import { and as and7, eq as eq9, inArray as inArray5, lt as lt2, sql as sql29 } from "drizzle-orm";
42005
42698
  function generateAgentId() {
42006
42699
  const now = /* @__PURE__ */ new Date();
42007
42700
  const ts = now.toISOString().replace(/[-:T]/g, "").substring(0, 14);
@@ -42090,11 +42783,11 @@ async function listAgentInstances(filters, cwd) {
42090
42783
  const conditions = [];
42091
42784
  if (filters?.status) {
42092
42785
  const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
42093
- conditions.push(inArray4(agentInstances.status, statuses));
42786
+ conditions.push(inArray5(agentInstances.status, statuses));
42094
42787
  }
42095
42788
  if (filters?.agentType) {
42096
42789
  const types = Array.isArray(filters.agentType) ? filters.agentType : [filters.agentType];
42097
- conditions.push(inArray4(agentInstances.agentType, types));
42790
+ conditions.push(inArray5(agentInstances.agentType, types));
42098
42791
  }
42099
42792
  if (filters?.sessionId) {
42100
42793
  conditions.push(eq9(agentInstances.sessionId, filters.sessionId));
@@ -42131,7 +42824,7 @@ async function checkAgentHealth(thresholdMs = 3e4, cwd) {
42131
42824
  const cutoff = new Date(Date.now() - thresholdMs).toISOString();
42132
42825
  return db.select().from(agentInstances).where(
42133
42826
  and7(
42134
- inArray4(agentInstances.status, ["active", "idle", "starting"]),
42827
+ inArray5(agentInstances.status, ["active", "idle", "starting"]),
42135
42828
  lt2(agentInstances.lastHeartbeat, cutoff)
42136
42829
  )
42137
42830
  ).all();
@@ -42226,12 +42919,14 @@ var init_registry2 = __esm({
42226
42919
  // packages/core/src/store/sqlite-data-accessor.ts
42227
42920
  var sqlite_data_accessor_exports = {};
42228
42921
  __export(sqlite_data_accessor_exports, {
42922
+ RENAMED_RECENT_MS: () => RENAMED_RECENT_MS,
42229
42923
  addTaskLabelsNative: () => addTaskLabelsNative,
42230
42924
  advanceTaskIdSequence: () => advanceTaskIdSequence,
42231
42925
  bindTaskAccessorScope: () => bindTaskAccessorScope,
42232
42926
  captureTaskAccessorScope: () => captureProjectScope,
42233
42927
  clearAcUidGraveyardNative: () => clearAcUidGraveyardNative,
42234
42928
  clearBirthFpNative: () => clearBirthFpNative,
42929
+ clearReplacedTaskIdentityNative: () => clearReplacedTaskIdentityNative,
42235
42930
  clearRowUidNative: () => clearRowUidNative,
42236
42931
  createSqliteDataAccessor: () => createSqliteDataAccessor,
42237
42932
  deleteDisplayIdAliasNative: () => deleteDisplayIdAliasNative,
@@ -42242,6 +42937,7 @@ __export(sqlite_data_accessor_exports, {
42242
42937
  insertIdentityRowNative: () => insertIdentityRowNative,
42243
42938
  insertQuarantineNative: () => insertQuarantineNative,
42244
42939
  insertUidAliasNative: () => insertUidAliasNative,
42940
+ readReplacedTaskBirthNative: () => readReplacedTaskBirthNative,
42245
42941
  rederiveAcIdsNative: () => rederiveAcIdsNative,
42246
42942
  rekeyQuarantineNative: () => rekeyQuarantineNative,
42247
42943
  relinkAcUidNative: () => relinkAcUidNative,
@@ -42258,7 +42954,7 @@ import {
42258
42954
  and as and8,
42259
42955
  eq as eq10,
42260
42956
  getTableColumns as getTableColumns2,
42261
- inArray as inArray5,
42957
+ inArray as inArray6,
42262
42958
  isNull as isNull4,
42263
42959
  like as like2,
42264
42960
  lte as lte3,
@@ -42282,8 +42978,8 @@ async function selectAcBindings(db, acIds) {
42282
42978
  bindingType: b.bindingType,
42283
42979
  createdAt: b.createdAt
42284
42980
  };
42285
- const byUid = await db.select(columns).from(b).innerJoin(ac, eq10(ac.uid, b.acUid)).where(inArray5(ac.id, ids)).all();
42286
- const byId = await db.select(columns).from(b).leftJoin(ac, eq10(ac.uid, b.acUid)).where(inArray5(b.acId, ids)).all();
42981
+ const byUid = await db.select(columns).from(b).innerJoin(ac, eq10(ac.uid, b.acUid)).where(inArray6(ac.id, ids)).all();
42982
+ const byId = await db.select(columns).from(b).leftJoin(ac, eq10(ac.uid, b.acUid)).where(inArray6(b.acId, ids)).all();
42287
42983
  const rows = [...byUid, ...byId];
42288
42984
  const wanted = new Set(ids);
42289
42985
  const seen = /* @__PURE__ */ new Set();
@@ -42376,6 +43072,7 @@ function renameTaskDisplayIdNative(nativeDb, uid, toId, refs, guard = {}) {
42376
43072
  if (Number(changed) !== 1) {
42377
43073
  throw taskConflictError(current.id, taskVersion(current), null);
42378
43074
  }
43075
+ recordLocalRename(nativeDb, uid, current.id, toId, taskVersion(current));
42379
43076
  const rewritten = {};
42380
43077
  const q2 = (name2) => `"${name2.replaceAll('"', '""')}"`;
42381
43078
  for (const ref of refs) {
@@ -42431,23 +43128,39 @@ function rederiveAcIdsNative(nativeDb, fromId, toId) {
42431
43128
  }
42432
43129
  return count;
42433
43130
  }
42434
- function insertDisplayIdAliasNative(nativeDb, row) {
43131
+ function insertDisplayIdAliasNative(nativeDb, row, precedes) {
43132
+ const stored = nativeDb.prepare(
43133
+ `SELECT uid, entity_table AS entityTable, display_id AS displayId, entity_uid AS entityUid,
43134
+ reason, origin, displaced_hlc AS displacedHlc, created_at AS createdAt,
43135
+ entity_birth_fp AS entityBirthFp
43136
+ FROM tasks_display_id_aliases WHERE uid = ?`
43137
+ ).get(row.uid);
43138
+ if (!stored) {
43139
+ nativeDb.prepare(
43140
+ `INSERT INTO tasks_display_id_aliases
43141
+ (uid, entity_table, display_id, entity_uid, reason, origin, displaced_hlc, created_at,
43142
+ entity_birth_fp)
43143
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
43144
+ ).run(
43145
+ row.uid,
43146
+ row.entityTable,
43147
+ row.displayId,
43148
+ row.entityUid,
43149
+ row.reason,
43150
+ row.origin,
43151
+ row.displacedHlc,
43152
+ row.createdAt,
43153
+ row.entityBirthFp
43154
+ );
43155
+ return;
43156
+ }
43157
+ if (!precedes(row, stored)) return;
42435
43158
  nativeDb.prepare(
42436
- `INSERT OR IGNORE INTO tasks_display_id_aliases
42437
- (uid, entity_table, display_id, entity_uid, reason, origin, displaced_hlc, created_at,
42438
- entity_birth_fp)
42439
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
42440
- ).run(
42441
- row.uid,
42442
- row.entityTable,
42443
- row.displayId,
42444
- row.entityUid,
42445
- row.reason,
42446
- row.origin,
42447
- row.displacedHlc,
42448
- row.createdAt,
42449
- row.entityBirthFp
42450
- );
43159
+ `UPDATE tasks_display_id_aliases
43160
+ SET origin = ?, displaced_hlc = ?, created_at = ?,
43161
+ entity_birth_fp = coalesce(entity_birth_fp, ?)
43162
+ WHERE uid = ?`
43163
+ ).run(row.origin, row.displacedHlc, row.createdAt, row.entityBirthFp, row.uid);
42451
43164
  }
42452
43165
  function deleteDisplayIdAliasNative(nativeDb, uid) {
42453
43166
  nativeDb.prepare("DELETE FROM tasks_display_id_aliases WHERE uid = ?").run(uid);
@@ -42509,6 +43222,63 @@ function relinkAcUidNative(nativeDb, acId, uid, birthFp) {
42509
43222
  function clearAcUidGraveyardNative(nativeDb) {
42510
43223
  nativeDb.exec("DELETE FROM tasks_ac_uid_graveyard");
42511
43224
  }
43225
+ function readReplacedTaskBirthNative(nativeDb, taskId) {
43226
+ return nativeDb.prepare("SELECT created_at AS createdAt, uid FROM tasks_tasks WHERE id = ?").get(taskId);
43227
+ }
43228
+ function clearReplacedTaskIdentityNative(nativeDb, taskId, before) {
43229
+ const row = nativeDb.prepare("SELECT uid, birth_fp AS fp, created_at AS createdAt FROM tasks_tasks WHERE id = ?").get(taskId);
43230
+ if (!row || row.uid === null && row.fp === null) return;
43231
+ if (before) {
43232
+ const was = parseStoreTimestamp(before.createdAt);
43233
+ if (was !== null && was === parseStoreTimestamp(row.createdAt)) {
43234
+ return;
43235
+ }
43236
+ }
43237
+ if (rowIdentityShared(nativeDb)) {
43238
+ throw new CleoError(
43239
+ 6 /* VALIDATION_ERROR */,
43240
+ `Task ${taskId} cannot be overwritten by an import: its identity is shared with other devices`,
43241
+ {
43242
+ fix: "Import it under a new id (the default duplicate strategy), or edit the task instead of overwriting it.",
43243
+ details: { field: "taskId", actual: taskId }
43244
+ }
43245
+ );
43246
+ }
43247
+ const has = (table) => nativeDb.prepare("SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) !== void 0;
43248
+ nativeDb.prepare("UPDATE tasks_tasks SET uid = NULL, birth_fp = NULL WHERE id = ?").run(taskId);
43249
+ for (const spec of ROW_IDENTITY.project) {
43250
+ if (spec.kind !== "natural" || !has(spec.table)) continue;
43251
+ const cols = (spec.keyRefs ?? []).filter((r) => r.table === "tasks_tasks").map((r) => r.column);
43252
+ if (cols.length === 0) continue;
43253
+ nativeDb.prepare(
43254
+ `UPDATE main.${quoteIdent2(spec.table)} SET uid = NULL WHERE ${cols.map((c) => `${quoteIdent2(c)} = ?`).join(" OR ")}`
43255
+ ).run(...cols.map(() => taskId));
43256
+ }
43257
+ const children = ["tasks_task_acceptance_criteria_history", "tasks_evidence_ac_bindings"].filter(
43258
+ has
43259
+ );
43260
+ const criteria = has("tasks_task_acceptance_criteria") ? nativeDb.prepare("SELECT id, uid FROM tasks_task_acceptance_criteria WHERE task_id = ?").all(taskId) : [];
43261
+ if (criteria.length > 0) {
43262
+ nativeDb.prepare(
43263
+ "UPDATE tasks_task_acceptance_criteria SET uid = NULL, birth_fp = NULL WHERE task_id = ?"
43264
+ ).run(taskId);
43265
+ }
43266
+ fillRowUids(nativeDb, "project");
43267
+ const newUid = nativeDb.prepare("SELECT uid FROM tasks_task_acceptance_criteria WHERE id = ?");
43268
+ let rekeyed = 0;
43269
+ for (const ac of criteria) {
43270
+ const now = newUid.get(ac.id)?.uid ?? null;
43271
+ for (const table of children) {
43272
+ rekeyed += Number(
43273
+ nativeDb.prepare(
43274
+ `UPDATE main.${quoteIdent2(table)} SET ac_uid = ?, birth_fp = NULL
43275
+ WHERE ac_id = ? OR (? IS NOT NULL AND ac_uid = ?)`
43276
+ ).run(now, ac.id, ac.uid, ac.uid).changes
43277
+ );
43278
+ }
43279
+ }
43280
+ if (rekeyed > 0) fillRowUids(nativeDb, "project");
43281
+ }
42512
43282
  function quoteIdent2(name2) {
42513
43283
  return `"${name2.replaceAll('"', '""')}"`;
42514
43284
  }
@@ -42596,10 +43366,50 @@ function claimPredicate(guard, allowedHolders) {
42596
43366
  if (guard.mode === "force") return void 0;
42597
43367
  const mine = guard.sessionId === null ? void 0 : eq10(holder, guard.sessionId);
42598
43368
  if (guard.mode === "renew" || guard.mode === "release") return mine ?? sql30`0`;
42599
- const related = allowedHolders.length > 0 ? inArray5(holder, [...allowedHolders]) : void 0;
43369
+ const related = allowedHolders.length > 0 ? inArray6(holder, [...allowedHolders]) : void 0;
42600
43370
  const expired = guard.mode === "take-over" ? or5(isNull4(tasksTasks.leaseExpiresAt), lte3(tasksTasks.leaseExpiresAt, guard.now)) : void 0;
42601
43371
  return or5(isNull4(holder), mine, related, expired);
42602
43372
  }
43373
+ function renameRecordKey(fromId) {
43374
+ return `renamed_from:tasks_tasks:${fromId}`;
43375
+ }
43376
+ function hasIdentityMeta(nativeDb) {
43377
+ return nativeDb.prepare(
43378
+ "SELECT 1 AS x FROM sqlite_master WHERE type = 'table' AND name = 'tasks_row_identity_meta'"
43379
+ ).get() !== void 0;
43380
+ }
43381
+ function recordLocalRename(nativeDb, uid, fromId, toId, fromVersion) {
43382
+ if (!hasIdentityMeta(nativeDb)) return;
43383
+ const record = { uid, fromVersion, toId, at: (/* @__PURE__ */ new Date()).toISOString() };
43384
+ writeRowIdentityMetaNative(nativeDb, renameRecordKey(fromId), JSON.stringify(record));
43385
+ }
43386
+ function assertNotRenamed(nativeDb, taskId, guard, current) {
43387
+ if (!nativeDb || !hasIdentityMeta(nativeDb)) return;
43388
+ const raw = nativeDb.prepare("SELECT value FROM tasks_row_identity_meta WHERE key = ?").get(renameRecordKey(taskId));
43389
+ if (!raw) return;
43390
+ let record;
43391
+ try {
43392
+ record = JSON.parse(raw.value);
43393
+ } catch {
43394
+ return;
43395
+ }
43396
+ if (Date.now() - Date.parse(record.at) > RENAMED_RECENT_MS) return;
43397
+ const renamed = nativeDb.prepare("SELECT id, claimed_by_session AS claimedBySession FROM tasks_tasks WHERE uid = ?").get(record.uid);
43398
+ if (!renamed || renamed.id === taskId) return;
43399
+ const expected = guard.expectedUpdatedAt;
43400
+ const session = guard.claim?.sessionId ?? null;
43401
+ const holdsRenamedClaim = session !== null && (guard.claim?.mode === "renew" || guard.claim?.mode === "release") && renamed.claimedBySession === session && current?.claimedBySession !== session;
43402
+ const readRenamed = expected !== void 0 && expected === record.fromVersion && (current === void 0 || taskVersion(current) !== expected);
43403
+ if (!(current === void 0 || holdsRenamedClaim || readRenamed)) return;
43404
+ throw new CleoError(
43405
+ 26 /* TASK_RENAMED */,
43406
+ `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`,
43407
+ {
43408
+ fix: `Re-read it with 'cleo show ${renamed.id}' and retry against ${renamed.id}.`,
43409
+ details: { field: "taskId", actual: taskId, expected: renamed.id }
43410
+ }
43411
+ );
43412
+ }
42603
43413
  function assertClaimAllowed(taskId, row, guard, allowedHolders) {
42604
43414
  const stored = claimFromColumns(row);
42605
43415
  if (claimAllows(stored, guard.mode, guard.sessionId, guard.now, allowedHolders)) return;
@@ -42774,13 +43584,13 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
42774
43584
  const db = await getDb(cwd);
42775
43585
  const conditions = [];
42776
43586
  if (query.ids && query.ids.length > 0) {
42777
- conditions.push(inArray5(tasksAuditLog.id, [...query.ids]));
43587
+ conditions.push(inArray6(tasksAuditLog.id, [...query.ids]));
42778
43588
  }
42779
43589
  if (query.taskIds && query.taskIds.length > 0) {
42780
- conditions.push(inArray5(tasksAuditLog.taskId, [...query.taskIds]));
43590
+ conditions.push(inArray6(tasksAuditLog.taskId, [...query.taskIds]));
42781
43591
  }
42782
43592
  if (query.actions && query.actions.length > 0) {
42783
- conditions.push(inArray5(tasksAuditLog.action, [...query.actions]));
43593
+ conditions.push(inArray6(tasksAuditLog.action, [...query.actions]));
42784
43594
  }
42785
43595
  if (query.since) {
42786
43596
  conditions.push(sql30`${tasksAuditLog.timestamp} >= ${query.since}`);
@@ -42813,8 +43623,13 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
42813
43623
  async upsertSingleTask(task) {
42814
43624
  await accessor.transaction((tx) => tx.upsertSingleTask(task));
42815
43625
  },
42816
- async insertNewTask(task) {
42817
- await accessor.transaction((tx) => tx.insertNewTask(task));
43626
+ async insertNewTask(task, identity) {
43627
+ await accessor.transaction((tx) => tx.insertNewTask(task, identity));
43628
+ },
43629
+ async getTaskIdentities(taskIds) {
43630
+ if (taskIds.length === 0) return [];
43631
+ const db = await getDb(cwd);
43632
+ return db.select({ id: tasksTasks.id, uid: tasksTasks.uid, birthFp: tasksTasks.birthFp }).from(tasksTasks).where(inArray6(tasksTasks.id, taskIds)).all();
42818
43633
  },
42819
43634
  async addRelation(taskId, relatedTo, relationType, reason) {
42820
43635
  return accessor.transaction(async () => {
@@ -42991,7 +43806,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
42991
43806
  if (!statuses.includes("archived")) {
42992
43807
  conditions.push(ne2(tasksTasks.status, "archived"));
42993
43808
  }
42994
- conditions.push(inArray5(tasksTasks.status, statuses));
43809
+ conditions.push(inArray6(tasksTasks.status, statuses));
42995
43810
  } else if (filters.excludeStatus) {
42996
43811
  const excluded = Array.isArray(filters.excludeStatus) ? filters.excludeStatus : [filters.excludeStatus];
42997
43812
  for (const s of excluded) {
@@ -43005,11 +43820,11 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43005
43820
  if (filters.phase) conditions.push(eq10(tasksTasks.phase, filters.phase));
43006
43821
  if (filters.severity) {
43007
43822
  const severities = Array.isArray(filters.severity) ? filters.severity : [filters.severity];
43008
- conditions.push(inArray5(tasksTasks.severity, severities));
43823
+ conditions.push(inArray6(tasksTasks.severity, severities));
43009
43824
  }
43010
43825
  if (filters.kind) {
43011
43826
  const kinds = Array.isArray(filters.kind) ? filters.kind : [filters.kind];
43012
- conditions.push(inArray5(tasksTasks.kind, kinds));
43827
+ conditions.push(inArray6(tasksTasks.kind, kinds));
43013
43828
  }
43014
43829
  if (filters.parentId !== void 0) {
43015
43830
  if (filters.parentId === null) {
@@ -43030,7 +43845,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43030
43845
  }
43031
43846
  if (filters.label) {
43032
43847
  const labelMatchIds = db.select({ taskId: tasksTaskLabels.taskId }).from(tasksTaskLabels).where(eq10(tasksTaskLabels.label, filters.label));
43033
- conditions.push(inArray5(tasksTasks.id, labelMatchIds));
43848
+ conditions.push(inArray6(tasksTasks.id, labelMatchIds));
43034
43849
  }
43035
43850
  const where = conditions.length > 0 ? and8(...conditions) : void 0;
43036
43851
  const countResult = await db.select({ count: sql30`count(*)` }).from(tasksTasks).where(where).get();
@@ -43073,7 +43888,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43073
43888
  const conditions = [];
43074
43889
  if (filters?.status) {
43075
43890
  const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
43076
- conditions.push(inArray5(tasksTasks.status, statuses));
43891
+ conditions.push(inArray6(tasksTasks.status, statuses));
43077
43892
  } else {
43078
43893
  conditions.push(ne2(tasksTasks.status, "archived"));
43079
43894
  }
@@ -43138,7 +43953,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43138
43953
  if (idRows.length === 0) return [];
43139
43954
  const db = await getDb(cwd);
43140
43955
  const ids = idRows.map((r) => r.id);
43141
- const taskRows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, ids)).all();
43956
+ const taskRows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, ids)).all();
43142
43957
  const tasks2 = taskRows.map(rowToTask);
43143
43958
  if (tasks2.length > 0) {
43144
43959
  const allIds = await getAllTaskIds();
@@ -43167,7 +43982,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43167
43982
  if (idRows.length === 0) return [];
43168
43983
  const db = await getDb(cwd);
43169
43984
  const ids = idRows.map((r) => r.id);
43170
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, ids)).all();
43985
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, ids)).all();
43171
43986
  const tasks2 = rows.map(rowToTask);
43172
43987
  if (tasks2.length > 0) {
43173
43988
  const allIds = await getAllTaskIds();
@@ -43181,7 +43996,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43181
43996
  const depRows = await db.select().from(tasksTaskDependencies).where(eq10(tasksTaskDependencies.dependsOn, taskId)).all();
43182
43997
  if (depRows.length === 0) return [];
43183
43998
  const dependentIds = depRows.map((r) => r.taskId);
43184
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, dependentIds)).all();
43999
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, dependentIds)).all();
43185
44000
  const tasks2 = rows.map(rowToTask);
43186
44001
  if (tasks2.length > 0) {
43187
44002
  const allIds = await getAllTaskIds();
@@ -43211,7 +44026,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43211
44026
  async loadTasks(taskIds) {
43212
44027
  if (taskIds.length === 0) return [];
43213
44028
  const db = await getDb(cwd);
43214
- const rows = await db.select().from(tasksTasks).where(inArray5(tasksTasks.id, taskIds)).all();
44029
+ const rows = await db.select().from(tasksTasks).where(inArray6(tasksTasks.id, taskIds)).all();
43215
44030
  const tasks2 = rows.map(rowToTask);
43216
44031
  if (tasks2.length > 0) {
43217
44032
  const allIds = await getAllTaskIds();
@@ -43265,6 +44080,9 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43265
44080
  createdAt: tasksTasks.createdAt,
43266
44081
  ...CLAIM_COLUMNS
43267
44082
  }).from(tasksTasks).where(eq10(tasksTasks.id, taskId)).limit(1).all();
44083
+ if (guard?.claim || guard?.expectedUpdatedAt !== void 0) {
44084
+ assertNotRenamed(getNativeTasksDb(cwd), taskId, guard, current);
44085
+ }
43268
44086
  if (!current) throw new Error(`Task not found: ${taskId}`);
43269
44087
  const claimGuard = guard?.claim;
43270
44088
  const allowedHolders = claimGuard ? await claimAllowedHolders(db, claimGuard) : [];
@@ -43342,24 +44160,37 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43342
44160
  nativeDb.prepare(`SAVEPOINT ${spName}`).run();
43343
44161
  }
43344
44162
  try {
44163
+ const replacedBirths = /* @__PURE__ */ new Map();
43345
44164
  const tx = {
43346
44165
  async upsertSingleTask(task) {
43347
44166
  scope.assertActive();
43348
44167
  return accessor.transaction(async () => {
43349
44168
  scope.assertActive();
44169
+ if (rowUidFillEnabled() && !replacedBirths.has(task.id)) {
44170
+ const before = readReplacedTaskBirthNative(nativeDb, task.id);
44171
+ if (before) replacedBirths.set(task.id, before);
44172
+ }
43350
44173
  const row = taskToRow(task);
43351
44174
  await upsertTask(db, row);
43352
44175
  await updateDependencies(db, task.id, task.depends ?? []);
43353
44176
  });
43354
44177
  },
43355
- async insertNewTask(task) {
44178
+ async insertNewTask(task, identity) {
43356
44179
  scope.assertActive();
43357
44180
  return accessor.transaction(async () => {
43358
44181
  scope.assertActive();
43359
- await insertNewTask(db, taskToRow(task));
44182
+ await insertNewTask(db, taskToRow(task), identity);
43360
44183
  await updateDependencies(db, task.id, task.depends ?? []);
43361
44184
  });
43362
44185
  },
44186
+ async clearTaskIdentity(taskId) {
44187
+ scope.assertActive();
44188
+ if (!rowUidFillEnabled()) return;
44189
+ return accessor.transaction(async () => {
44190
+ scope.assertActive();
44191
+ clearReplacedTaskIdentityNative(nativeDb, taskId, replacedBirths.get(taskId));
44192
+ });
44193
+ },
43363
44194
  async archiveSingleTask(taskId, fields) {
43364
44195
  scope.assertActive();
43365
44196
  return accessor.transaction(async () => {
@@ -43506,7 +44337,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43506
44337
  const owned = await db.select({ id: tasksTaskAcceptanceCriteria.id }).from(tasksTaskAcceptanceCriteria).where(
43507
44338
  and8(
43508
44339
  eq10(tasksTaskAcceptanceCriteria.taskId, taskId),
43509
- inArray5(tasksTaskAcceptanceCriteria.id, ids)
44340
+ inArray6(tasksTaskAcceptanceCriteria.id, ids)
43510
44341
  )
43511
44342
  ).all();
43512
44343
  await pruneAcBindingsForAcIds(
@@ -43519,7 +44350,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43519
44350
  await db.delete(tasksTaskAcceptanceCriteria).where(
43520
44351
  and8(
43521
44352
  eq10(tasksTaskAcceptanceCriteria.taskId, taskId),
43522
- inArray5(tasksTaskAcceptanceCriteria.id, ids)
44353
+ inArray6(tasksTaskAcceptanceCriteria.id, ids)
43523
44354
  )
43524
44355
  ).run();
43525
44356
  });
@@ -43778,7 +44609,7 @@ async function createOwnedSqliteDataAccessor(cwd, accessorScope) {
43778
44609
  };
43779
44610
  return accessor;
43780
44611
  }
43781
- var _txSavepointCounter, taskTransactionQueue, TaskTransactionScope, taskTransactionContext, CLAIM_COLUMNS;
44612
+ var _txSavepointCounter, taskTransactionQueue, TaskTransactionScope, taskTransactionContext, CLAIM_COLUMNS, RENAMED_RECENT_MS;
43782
44613
  var init_sqlite_data_accessor = __esm({
43783
44614
  "packages/core/src/store/sqlite-data-accessor.ts"() {
43784
44615
  "use strict";
@@ -43792,6 +44623,8 @@ var init_sqlite_data_accessor = __esm({
43792
44623
  init_cross_db_cleanup();
43793
44624
  init_db_helpers();
43794
44625
  init_row_identity();
44626
+ init_row_identity_flag();
44627
+ init_row_identity_registry();
43795
44628
  init_audit2();
43796
44629
  init_session_store();
43797
44630
  init_sqlite3();
@@ -43824,6 +44657,7 @@ var init_sqlite_data_accessor = __esm({
43824
44657
  claimedAt: tasksTasks.claimedAt,
43825
44658
  leaseExpiresAt: tasksTasks.leaseExpiresAt
43826
44659
  };
44660
+ RENAMED_RECENT_MS = 24 * 60 * 60 * 1e3;
43827
44661
  registerRowIdentityWriters({
43828
44662
  relinkAcUidNative,
43829
44663
  clearAcUidGraveyardNative,
@@ -44294,7 +45128,7 @@ __export(governor_exports, {
44294
45128
  resolveGovernorMode: () => resolveGovernorMode,
44295
45129
  slotFileCount: () => slotFileCount
44296
45130
  });
44297
- import { existsSync as existsSync19, mkdirSync as mkdirSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
45131
+ import { existsSync as existsSync20, mkdirSync as mkdirSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
44298
45132
  import { availableParallelism, totalmem } from "node:os";
44299
45133
  import { join as join25 } from "node:path";
44300
45134
  import lockfile2 from "proper-lockfile";
@@ -44368,11 +45202,11 @@ function governorSlotDir(cls) {
44368
45202
  return join25(getCleoHome(), "locks", `resource-${cls}`);
44369
45203
  }
44370
45204
  function ensureSlotFiles(dir, count) {
44371
- if (!existsSync19(dir)) mkdirSync6(dir, { recursive: true });
45205
+ if (!existsSync20(dir)) mkdirSync6(dir, { recursive: true });
44372
45206
  const paths = [];
44373
45207
  for (let i = 0; i < count; i++) {
44374
45208
  const p = join25(dir, `slot-${i}.lock`);
44375
- if (!existsSync19(p)) writeFileSync5(p, "", { flag: "a" });
45209
+ if (!existsSync20(p)) writeFileSync5(p, "", { flag: "a" });
44376
45210
  paths.push(p);
44377
45211
  }
44378
45212
  return paths;
@@ -44412,7 +45246,7 @@ function shuffledIndices(n) {
44412
45246
  }
44413
45247
  async function countHeldSlots(cls, budget) {
44414
45248
  const dir = governorSlotDir(cls);
44415
- if (!existsSync19(dir)) return 0;
45249
+ if (!existsSync20(dir)) return 0;
44416
45250
  const slots = ensureSlotFiles(dir, budget);
44417
45251
  let held = 0;
44418
45252
  for (const path2 of slots) {
@@ -44427,7 +45261,7 @@ async function countHeldSlots(cls, budget) {
44427
45261
  }
44428
45262
  function slotFileCount(cls) {
44429
45263
  const dir = governorSlotDir(cls);
44430
- if (!existsSync19(dir)) return 0;
45264
+ if (!existsSync20(dir)) return 0;
44431
45265
  return readdirSync5(dir).filter((f) => f.startsWith("slot-") && f.endsWith(".lock")).length;
44432
45266
  }
44433
45267
  var _log2, _cachedMode2, _supervisorDegradeLogged, MB, STALE_MS, _supervisorHolderSeq, ResourceGovernor, governor;
@@ -44578,7 +45412,7 @@ var init_governor = __esm({
44578
45412
  // packages/core/src/store/exodus/recovery.ts
44579
45413
  import { randomUUID as randomUUID2 } from "node:crypto";
44580
45414
  import { constants } from "node:sqlite";
44581
- import { z as z29 } from "zod";
45415
+ import { z as z30 } from "zod";
44582
45416
  function identityValues(encoded) {
44583
45417
  return identitySchema.parse(JSON.parse(encoded)).map(([kind, value]) => {
44584
45418
  if (kind === "null") return null;
@@ -44914,8 +45748,8 @@ var init_recovery = __esm({
44914
45748
  "use strict";
44915
45749
  init_row_identity_registry();
44916
45750
  RECEIPTS = "_exodus_recovery_rows";
44917
- identitySchema = z29.array(
44918
- z29.tuple([z29.enum(["null", "integer", "real", "text", "blob"]), z29.string()])
45751
+ identitySchema = z30.array(
45752
+ z30.tuple([z30.enum(["null", "integer", "real", "text", "blob"]), z30.string()])
44919
45753
  );
44920
45754
  ExodusRecoveryError = class extends Error {
44921
45755
  };
@@ -44925,7 +45759,7 @@ var init_recovery = __esm({
44925
45759
  // packages/core/src/store/exodus/archive.ts
44926
45760
  import {
44927
45761
  copyFileSync as copyFileSync5,
44928
- existsSync as existsSync20,
45762
+ existsSync as existsSync21,
44929
45763
  mkdirSync as mkdirSync7,
44930
45764
  readFileSync as readFileSync8,
44931
45765
  renameSync as renameSync7,
@@ -44933,7 +45767,7 @@ import {
44933
45767
  writeFileSync as writeFileSync6
44934
45768
  } from "node:fs";
44935
45769
  import { basename as basename9, dirname as dirname19, join as join26, resolve as resolve12 } from "node:path";
44936
- import { z as z30 } from "zod";
45770
+ import { z as z31 } from "zod";
44937
45771
  function scopeBaseDir(scope, cwd, targetDbPath) {
44938
45772
  if (targetDbPath !== void 0) return dirname19(targetDbPath);
44939
45773
  return scope === "project" ? resolveCleoDir(cwd) : getCleoHome();
@@ -44953,7 +45787,7 @@ function hasExodusCompleteMarker(scope, cwd, targetDbPath, nativeDb) {
44953
45787
  if (targetDbPath) throw error;
44954
45788
  return false;
44955
45789
  }
44956
- if (!existsSync20(markerPath)) return false;
45790
+ if (!existsSync21(markerPath)) return false;
44957
45791
  const marker = markerIdentity.parse(JSON.parse(readFileSync8(markerPath, "utf8")));
44958
45792
  if (nativeDb && (!marker.databaseIdentity || !hasExodusDatabaseIdentity(nativeDb, marker.databaseIdentity)))
44959
45793
  throw new Error(
@@ -44983,7 +45817,7 @@ function writeExodusCompleteMarker(scope, archivedSources, cwd, targetDbPath, da
44983
45817
  function moveFileInto(srcPath, destDir) {
44984
45818
  mkdirSync7(destDir, { recursive: true });
44985
45819
  let dest = join26(destDir, basename9(srcPath));
44986
- if (existsSync20(dest)) {
45820
+ if (existsSync21(dest)) {
44987
45821
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
44988
45822
  dest = join26(destDir, `${basename9(srcPath)}.${stamp}`);
44989
45823
  }
@@ -44996,14 +45830,14 @@ function moveFileInto(srcPath, destDir) {
44996
45830
  return dest;
44997
45831
  }
44998
45832
  function archiveSourceDb(source, cwd, targetDbPath) {
44999
- if (!existsSync20(source.path)) {
45833
+ if (!existsSync21(source.path)) {
45000
45834
  return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
45001
45835
  }
45002
45836
  const destDir = exodusArchiveDir(source.targetScope, cwd, targetDbPath);
45003
45837
  const archivedTo = moveFileInto(source.path, destDir);
45004
45838
  for (const suffix of SIDECAR_SUFFIXES) {
45005
45839
  const sidecar = `${source.path}${suffix}`;
45006
- if (existsSync20(sidecar)) {
45840
+ if (existsSync21(sidecar)) {
45007
45841
  try {
45008
45842
  moveFileInto(sidecar, destDir);
45009
45843
  } catch (err) {
@@ -45051,7 +45885,7 @@ function detectStrandedResidue(sources, cwd) {
45051
45885
  const stranded = [];
45052
45886
  for (const source of sources) {
45053
45887
  if (!markedScopes.has(source.targetScope)) continue;
45054
- if (existsSync20(source.path)) {
45888
+ if (existsSync21(source.path)) {
45055
45889
  stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
45056
45890
  }
45057
45891
  }
@@ -45076,11 +45910,11 @@ var init_archive2 = __esm({
45076
45910
  init_ensure_config();
45077
45911
  init_recovery();
45078
45912
  log8 = getLogger("exodus-archive");
45079
- markerIdentity = z30.object({
45080
- version: z30.literal(1),
45081
- scope: z30.enum(["project", "global"]),
45082
- targetDbPath: z30.string().optional(),
45083
- databaseIdentity: z30.string().optional()
45913
+ markerIdentity = z31.object({
45914
+ version: z31.literal(1),
45915
+ scope: z31.enum(["project", "global"]),
45916
+ targetDbPath: z31.string().optional(),
45917
+ databaseIdentity: z31.string().optional()
45084
45918
  });
45085
45919
  ARCHIVE_DIR_NAME = "_archive";
45086
45920
  MARKER_FILENAME_BY_SCOPE = {
@@ -45092,13 +45926,13 @@ var init_archive2 = __esm({
45092
45926
  });
45093
45927
 
45094
45928
  // packages/core/src/store/exodus/count-parity.ts
45095
- import { existsSync as existsSync21 } from "node:fs";
45929
+ import { existsSync as existsSync22 } from "node:fs";
45096
45930
  function listTables2(db) {
45097
45931
  return db.prepare(
45098
45932
  "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name"
45099
45933
  ).all().map((r) => r.name);
45100
45934
  }
45101
- function tableExists2(db, tableName) {
45935
+ function tableExists3(db, tableName) {
45102
45936
  const escaped = tableName.replace(/'/g, "''");
45103
45937
  return db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='${escaped}'`).get() !== void 0;
45104
45938
  }
@@ -45113,14 +45947,14 @@ function rowCount(db, tableName) {
45113
45947
  function computeCountParity(sources, projectDbPath, globalDbPath) {
45114
45948
  const entries = [];
45115
45949
  let skipped = 0;
45116
- if (!existsSync21(projectDbPath) || !existsSync21(globalDbPath)) {
45950
+ if (!existsSync22(projectDbPath) || !existsSync22(globalDbPath)) {
45117
45951
  return { ok: false, entries: [], deficits: [], checked: 0, skipped: 0 };
45118
45952
  }
45119
45953
  const projectSnap = openCleoDbSnapshot(projectDbPath, { readOnly: true });
45120
45954
  const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
45121
45955
  try {
45122
45956
  for (const src of sources) {
45123
- if (!existsSync21(src.path)) continue;
45957
+ if (!existsSync22(src.path)) continue;
45124
45958
  const srcSnap = openCleoDbSnapshot(src.path, { readOnly: true });
45125
45959
  try {
45126
45960
  for (const legacyTable of listTables2(srcSnap.db)) {
@@ -45137,7 +45971,7 @@ function computeCountParity(sources, projectDbPath, globalDbPath) {
45137
45971
  skipped++;
45138
45972
  continue;
45139
45973
  }
45140
- const targetCount = tableExists2(targetSnap.db, targetTable) ? rowCount(targetSnap.db, targetTable) ?? 0 : 0;
45974
+ const targetCount = tableExists3(targetSnap.db, targetTable) ? rowCount(targetSnap.db, targetTable) ?? 0 : 0;
45141
45975
  const deficit = targetCount < sourceCount ? sourceCount - targetCount : 0;
45142
45976
  entries.push({
45143
45977
  sourceDb: src.name,
@@ -45178,7 +46012,7 @@ var init_count_parity = __esm({
45178
46012
  });
45179
46013
 
45180
46014
  // packages/core/src/store/exodus/plan.ts
45181
- import { existsSync as existsSync22, readdirSync as readdirSync6, statfsSync as statfsSync2, statSync as statSync6 } from "node:fs";
46015
+ import { existsSync as existsSync23, readdirSync as readdirSync6, statfsSync as statfsSync2, statSync as statSync6 } from "node:fs";
45182
46016
  import { join as join27 } from "node:path";
45183
46017
  function computeRequiredBytes(totalSourceBytes, largestSourceBytes) {
45184
46018
  return Math.ceil(STAGING_HEADROOM_FACTOR * largestSourceBytes) + totalSourceBytes;
@@ -45280,7 +46114,7 @@ function buildExodusPlan(cwd) {
45280
46114
  };
45281
46115
  }
45282
46116
  function sourcesPresent(sources) {
45283
- return sources.some((s) => existsSync22(s.path));
46117
+ return sources.some((s) => existsSync23(s.path));
45284
46118
  }
45285
46119
  var STAGING_HEADROOM_FACTOR, STAGING_COPY_SKIP_THRESHOLD_BYTES;
45286
46120
  var init_plan2 = __esm({
@@ -45294,18 +46128,18 @@ var init_plan2 = __esm({
45294
46128
  });
45295
46129
 
45296
46130
  // packages/core/src/store/exodus/health.ts
45297
- import { existsSync as existsSync23, statSync as statSync7 } from "node:fs";
46131
+ import { existsSync as existsSync24, statSync as statSync7 } from "node:fs";
45298
46132
  function buildExodusHealth(cwd) {
45299
46133
  const plan = buildExodusPlan(cwd);
45300
- const anyLegacyPresent = plan.sources.some((s) => existsSync23(s.path));
46134
+ const anyLegacyPresent = plan.sources.some((s) => existsSync24(s.path));
45301
46135
  const parity = anyLegacyPresent ? computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath) : { ok: true, entries: [], deficits: [], checked: 0, skipped: 0 };
45302
46136
  const consolidatedExistsByScope = {
45303
- project: existsSync23(plan.projectDbPath),
45304
- global: existsSync23(plan.globalDbPath)
46137
+ project: existsSync24(plan.projectDbPath),
46138
+ global: existsSync24(plan.globalDbPath)
45305
46139
  };
45306
46140
  const buildScope = (scope) => {
45307
46141
  const sources = plan.sources.filter((s) => s.targetScope === scope).map((s) => {
45308
- const present = existsSync23(s.path);
46142
+ const present = existsSync24(s.path);
45309
46143
  let bytes = 0;
45310
46144
  if (present) {
45311
46145
  try {
@@ -45410,7 +46244,7 @@ var init_types = __esm({
45410
46244
  // packages/core/src/store/exodus/migrate.ts
45411
46245
  import {
45412
46246
  copyFileSync as copyFileSync6,
45413
- existsSync as existsSync24,
46247
+ existsSync as existsSync25,
45414
46248
  mkdirSync as mkdirSync8,
45415
46249
  readFileSync as readFileSync9,
45416
46250
  renameSync as renameSync8,
@@ -45456,7 +46290,7 @@ function writeJournal(stagingDir, journal) {
45456
46290
  }
45457
46291
  function readJournal(stagingDir) {
45458
46292
  const journalPath = join28(stagingDir, JOURNAL_FILENAME);
45459
- if (!existsSync24(journalPath)) return null;
46293
+ if (!existsSync25(journalPath)) return null;
45460
46294
  try {
45461
46295
  return JSON.parse(readFileSync9(journalPath, "utf8"));
45462
46296
  } catch {
@@ -45466,7 +46300,7 @@ function readJournal(stagingDir) {
45466
46300
  function clearExodusJournal(stagingDir) {
45467
46301
  const journalPath = join28(stagingDir, JOURNAL_FILENAME);
45468
46302
  try {
45469
- if (!existsSync24(journalPath)) return false;
46303
+ if (!existsSync25(journalPath)) return false;
45470
46304
  unlinkSync5(journalPath);
45471
46305
  log10.info(
45472
46306
  { stagingDir },
@@ -45762,7 +46596,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45762
46596
  mkdirSync8(stagingDir, { recursive: true });
45763
46597
  let sqliteVersion = "unknown";
45764
46598
  for (const src of sources) {
45765
- if (existsSync24(src.path)) {
46599
+ if (existsSync25(src.path)) {
45766
46600
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
45767
46601
  sqliteVersion = getSqliteVersion(snap.db);
45768
46602
  snap.close();
@@ -45792,7 +46626,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45792
46626
  let reseedProject = null;
45793
46627
  try {
45794
46628
  for (const src of sources) {
45795
- if (!existsSync24(src.path)) continue;
46629
+ if (!existsSync25(src.path)) continue;
45796
46630
  const backupDest = join28(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
45797
46631
  const srcBytes = safeStatBytes(src.path);
45798
46632
  const skipStagingCopy = srcBytes > plan.stagingCopyThresholdBytes;
@@ -45800,7 +46634,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45800
46634
  onProgress?.(
45801
46635
  `Skipping full staging copy of ${src.name} (${srcBytes} bytes > ${plan.stagingCopyThresholdBytes} threshold) \u2014 source is archived, not deleted, on success.`
45802
46636
  );
45803
- } else if (!existsSync24(backupDest)) {
46637
+ } else if (!existsSync25(backupDest)) {
45804
46638
  onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
45805
46639
  copyFileSync6(src.path, backupDest);
45806
46640
  backupPaths.push(backupDest);
@@ -45833,8 +46667,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress, opt
45833
46667
  }
45834
46668
  prepareExodusRecovery(projectNative, stagingDir);
45835
46669
  if (globalNative !== null) prepareExodusRecovery(globalNative, stagingDir);
45836
- const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync24(s.path));
45837
- const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync24(s.path));
46670
+ const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync25(s.path));
46671
+ const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync25(s.path));
45838
46672
  if (globalNative === null && globalSources.length > 0)
45839
46673
  throw new Error("A projectOnly exodus run was given global-scope sources");
45840
46674
  await migrateScope(
@@ -46083,7 +46917,8 @@ var init_migrate = __esm({
46083
46917
  GRANDFATHERED_GUARD_TRIGGERS = [
46084
46918
  "tasks_tasks_parent_type_matrix_insert",
46085
46919
  "tasks_task_relations_non_containment_insert",
46086
- "tasks_task_acceptance_child_target_insert"
46920
+ "tasks_task_acceptance_child_target_insert",
46921
+ "tasks_task_dependencies_cycle_guard_insert"
46087
46922
  ];
46088
46923
  SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
46089
46924
  ["conduit", "seconds"],
@@ -46142,7 +46977,7 @@ __export(reconcile_exports, {
46142
46977
  reconcileSupersededStores: () => reconcileSupersededStores,
46143
46978
  unmigratedBareSources: () => unmigratedBareSources
46144
46979
  });
46145
- import { copyFileSync as copyFileSync7, existsSync as existsSync25, mkdirSync as mkdirSync9, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
46980
+ import { copyFileSync as copyFileSync7, existsSync as existsSync26, mkdirSync as mkdirSync9, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
46146
46981
  import { tmpdir as tmpdir4 } from "node:os";
46147
46982
  import { basename as basename10, join as join29 } from "node:path";
46148
46983
  function ident2(name2) {
@@ -46213,7 +47048,7 @@ function assessSupersededProjectStores(liveStorePath, sources, resolveTarget = r
46213
47048
  }
46214
47049
  function legacySourcesHoldRows(sources) {
46215
47050
  for (const src of sources) {
46216
- if (!existsSync25(src.path)) continue;
47051
+ if (!existsSync26(src.path)) continue;
46217
47052
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
46218
47053
  try {
46219
47054
  for (const table of orderTablesForCopy(snap.db)) {
@@ -46461,8 +47296,8 @@ async function reconcileSuppressed(projectRoot, options, liveStorePath) {
46461
47296
  }
46462
47297
  }
46463
47298
  async function reconcileWithScratch(projectRoot, dryRun, additive, liveStorePath, plan, resolveTarget, scratch) {
46464
- const fileSources = plan.sources.filter((s) => s.targetScope === "project" && existsSync25(s.path));
46465
- const bare = !additive && existsSync25(liveStorePath) ? await bareTaskCoreSource(liveStorePath, resolveTarget, scratch) : null;
47299
+ const fileSources = plan.sources.filter((s) => s.targetScope === "project" && existsSync26(s.path));
47300
+ const bare = !additive && existsSync26(liveStorePath) ? await bareTaskCoreSource(liveStorePath, resolveTarget, scratch) : null;
46466
47301
  const copyResolver = additive ? (sourceName, legacyTable) => {
46467
47302
  const r = resolveTarget(sourceName, legacyTable);
46468
47303
  return r.kind !== "skip" && isLiveAuthoritative(sourceName, legacyTable, r.targetName) ? { kind: "skip", reason: "additive: live-authoritative task graph" } : r;
@@ -46481,7 +47316,7 @@ async function reconcileWithScratch(projectRoot, dryRun, additive, liveStorePath
46481
47316
  stagingDir: null,
46482
47317
  receiptPath: null
46483
47318
  };
46484
- if (sources.length === 0 || !existsSync25(liveStorePath)) {
47319
+ if (sources.length === 0 || !existsSync26(liveStorePath)) {
46485
47320
  return {
46486
47321
  ...base,
46487
47322
  outcome: "nothing-to-reconcile",
@@ -46673,11 +47508,11 @@ var init_seal = __esm({
46673
47508
  });
46674
47509
 
46675
47510
  // packages/core/src/store/exodus/status.ts
46676
- import { existsSync as existsSync26, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync9 } from "node:fs";
47511
+ import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync9 } from "node:fs";
46677
47512
  import { join as join30 } from "node:path";
46678
47513
  function readJournal2(stagingDir) {
46679
47514
  const p = join30(stagingDir, JOURNAL_FILENAME2);
46680
- if (!existsSync26(p)) return null;
47515
+ if (!existsSync27(p)) return null;
46681
47516
  try {
46682
47517
  return JSON.parse(readFileSync10(p, "utf8"));
46683
47518
  } catch {
@@ -46709,15 +47544,15 @@ function runExodusStatus(cwd) {
46709
47544
  const sourcesInfo = plan.sources.map((s) => ({
46710
47545
  name: s.name,
46711
47546
  path: s.path,
46712
- exists: existsSync26(s.path),
47547
+ exists: existsSync27(s.path),
46713
47548
  bytes: safeBytes(s.path)
46714
47549
  }));
46715
47550
  return {
46716
47551
  hasStaging: latestStaging !== null,
46717
47552
  stagingDir: latestStaging,
46718
47553
  journal,
46719
- projectDbExists: existsSync26(projectDbPath),
46720
- globalDbExists: existsSync26(globalDbPath),
47554
+ projectDbExists: existsSync27(projectDbPath),
47555
+ globalDbExists: existsSync27(globalDbPath),
46721
47556
  sourcesPresent: sourcesInfo.some((s) => s.exists),
46722
47557
  sources: sourcesInfo
46723
47558
  };
@@ -46734,7 +47569,7 @@ var init_status = __esm({
46734
47569
  });
46735
47570
 
46736
47571
  // packages/core/src/store/exodus/verify-migration.ts
46737
- import { existsSync as existsSync27 } from "node:fs";
47572
+ import { existsSync as existsSync28 } from "node:fs";
46738
47573
  import { createRequire as createRequire10 } from "node:module";
46739
47574
  function orderByClause(db, tableName) {
46740
47575
  try {
@@ -46849,7 +47684,7 @@ function listTables3(db) {
46849
47684
  ).all();
46850
47685
  return rows.map((r) => r.name);
46851
47686
  }
46852
- function tableExists3(db, tableName) {
47687
+ function tableExists4(db, tableName) {
46853
47688
  try {
46854
47689
  const escaped = tableName.replace(/'/g, "''");
46855
47690
  return db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='${escaped}'`).get() !== void 0;
@@ -46999,10 +47834,10 @@ function orphanAlreadyInSource(targetDb, fk, sources, resolveTarget = resolveCon
46999
47834
  const anyFeeder = (targetTable, probe) => {
47000
47835
  for (const feeder of feedersOf(targetTable)) {
47001
47836
  const src = sources.find((s) => s.name === feeder.sourceName);
47002
- if (src === void 0 || !existsSync27(src.path)) continue;
47837
+ if (src === void 0 || !existsSync28(src.path)) continue;
47003
47838
  const snap = openCleoDbSnapshot(src.path, { readOnly: true });
47004
47839
  try {
47005
- if (!tableExists3(snap.db, feeder.legacyTable)) continue;
47840
+ if (!tableExists4(snap.db, feeder.legacyTable)) continue;
47006
47841
  const cols = new Set(
47007
47842
  snap.db.prepare(`PRAGMA table_info("${feeder.legacyTable}")`).all().map((c) => c.name)
47008
47843
  );
@@ -47054,7 +47889,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47054
47889
  const preExistingForeignKeyViolations = [];
47055
47890
  const failureLines = [];
47056
47891
  const sourceOrphanSigs = /* @__PURE__ */ new Set();
47057
- if (!existsSync27(projectDbPath)) {
47892
+ if (!existsSync28(projectDbPath)) {
47058
47893
  return {
47059
47894
  ok: false,
47060
47895
  tables: [],
@@ -47065,7 +47900,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47065
47900
  error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
47066
47901
  };
47067
47902
  }
47068
- if (!existsSync27(globalDbPath)) {
47903
+ if (!existsSync28(globalDbPath)) {
47069
47904
  return {
47070
47905
  ok: false,
47071
47906
  tables: [],
@@ -47080,7 +47915,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47080
47915
  const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
47081
47916
  try {
47082
47917
  for (const src of sources) {
47083
- if (!existsSync27(src.path)) {
47918
+ if (!existsSync28(src.path)) {
47084
47919
  onProgress?.(`Skipping ${src.name} (not present)`);
47085
47920
  continue;
47086
47921
  }
@@ -47205,7 +48040,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress, resol
47205
48040
  targetOrphans.push(...foreignKeyCheck(globalSnap.db, "global"));
47206
48041
  foreignKeyViolations.push(...targetOrphans);
47207
48042
  for (const fk of targetOrphans) {
47208
- const orphanDb = tableExists3(projectSnap.db, fk.table) ? projectSnap.db : globalSnap.db;
48043
+ const orphanDb = tableExists4(projectSnap.db, fk.table) ? projectSnap.db : globalSnap.db;
47209
48044
  const sig = orphanSignature(orphanDb, fk);
47210
48045
  const preExisting = sourceOrphanSigs.has(sig) || orphanAlreadyInSource(orphanDb, fk, sources, resolveTarget);
47211
48046
  if (preExisting) {
@@ -47371,7 +48206,7 @@ __export(on_open_exports, {
47371
48206
  maybeRunExodusOnOpen: () => maybeRunExodusOnOpen,
47372
48207
  withExodusOnOpenSuppressed: () => withExodusOnOpenSuppressed
47373
48208
  });
47374
- import { existsSync as existsSync28, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
48209
+ import { existsSync as existsSync29, mkdtempSync as mkdtempSync2, rmSync as rmSync4 } from "node:fs";
47375
48210
  import { tmpdir as tmpdir5 } from "node:os";
47376
48211
  import { join as join31, resolve as resolve13 } from "node:path";
47377
48212
  async function withExodusOnOpenSuppressed(dbPath, fn) {
@@ -47413,7 +48248,7 @@ function remedyFor(scope) {
47413
48248
  }
47414
48249
  async function strandedLegacySources(scope, cwd) {
47415
48250
  const { buildExodusPlan: buildExodusPlan2, legacySourcesHoldRows: legacySourcesHoldRows2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
47416
- return buildExodusPlan2(cwd).sources.filter((s) => s.targetScope === scope && existsSync28(s.path)).filter((s) => legacySourcesHoldRows2([s])).map((s) => s.name);
48251
+ return buildExodusPlan2(cwd).sources.filter((s) => s.targetScope === scope && existsSync29(s.path)).filter((s) => legacySourcesHoldRows2([s])).map((s) => s.name);
47417
48252
  }
47418
48253
  async function exodusStoreRewriteRefusal(dbPath, cwd) {
47419
48254
  const { assertOwnerStoreRewriteConfirmed: assertOwnerStoreRewriteConfirmed2, invocationDirectory: invocationDirectory2 } = await Promise.resolve().then(() => (init_worktree_isolation_guard(), worktree_isolation_guard_exports));
@@ -47545,7 +48380,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47545
48380
  };
47546
48381
  }
47547
48382
  const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
47548
- if (!scopeSources.some((s) => existsSync28(s.path))) {
48383
+ if (!scopeSources.some((s) => existsSync29(s.path))) {
47549
48384
  return {
47550
48385
  outcome: "skipped",
47551
48386
  reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
@@ -47570,7 +48405,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47570
48405
  {
47571
48406
  scope,
47572
48407
  dbPath,
47573
- sources: plan.sources.filter((s) => existsSync28(s.path)).map((s) => s.name)
48408
+ sources: plan.sources.filter((s) => existsSync29(s.path)).map((s) => s.name)
47574
48409
  },
47575
48410
  "exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
47576
48411
  );
@@ -47583,7 +48418,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47583
48418
  const bare = scope === "project" ? await unmigratedBareSources2(
47584
48419
  dbPath,
47585
48420
  resolveTarget,
47586
- plan.sources.find((s) => s.name === "tasks" && existsSync28(s.path))?.path,
48421
+ plan.sources.find((s) => s.name === "tasks" && existsSync29(s.path))?.path,
47587
48422
  bareScratch
47588
48423
  ) : { first: [], last: [] };
47589
48424
  const migratePlan = { ...plan, sources: [...bare.first, ...plan.sources, ...bare.last] };
@@ -47647,7 +48482,7 @@ async function runExodusOnOpen(scope, dbPath, nativeDb, cwd) {
47647
48482
  "exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
47648
48483
  );
47649
48484
  try {
47650
- const consumed = plan.sources.filter((s) => existsSync28(s.path));
48485
+ const consumed = plan.sources.filter((s) => existsSync29(s.path));
47651
48486
  const identities = await sealTargets(plan, consumed);
47652
48487
  const archiveResult = archiveMigratedSources(consumed, cwd, plan, identities);
47653
48488
  log14.info(
@@ -47722,7 +48557,7 @@ __export(dual_scope_db_exports, {
47722
48557
  upsertIdempotent: () => upsertIdempotent,
47723
48558
  withStoreSchemaOwnership: () => withStoreSchemaOwnership
47724
48559
  });
47725
- import { existsSync as existsSync29, mkdirSync as mkdirSync10 } from "node:fs";
48560
+ import { existsSync as existsSync30, mkdirSync as mkdirSync10 } from "node:fs";
47726
48561
  import { createRequire as createRequire11 } from "node:module";
47727
48562
  import { dirname as dirname20, join as join32, resolve as resolve14 } from "node:path";
47728
48563
  function getDualScopeNativeDb(handle) {
@@ -47795,11 +48630,15 @@ async function openDualScopeDb(scope, cwd, options) {
47795
48630
  open
47796
48631
  ) : open();
47797
48632
  }
48633
+ function identityWorkOnOpen(nativeDb, scope) {
48634
+ if (ROW_IDENTITY[scope].length === 0) return false;
48635
+ return rowUidFillEnabled() || missingRowIdentitySchema(nativeDb).length > 0;
48636
+ }
47798
48637
  async function openDedicatedDualScopeDb(scope, dbPath, log15, execution) {
47799
48638
  execution?.assertActive();
47800
48639
  log15.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
47801
48640
  const dir = dirname20(dbPath);
47802
- if (!existsSync29(dir)) {
48641
+ if (!existsSync30(dir)) {
47803
48642
  mkdirSync10(dir, { recursive: true });
47804
48643
  }
47805
48644
  execution?.assertActive();
@@ -47832,9 +48671,12 @@ async function openDedicatedDualScopeDb(scope, dbPath, log15, execution) {
47832
48671
  `dual-scope-db[${scope}]`
47833
48672
  );
47834
48673
  execution?.assertActive();
47835
- if (rowUidFillEnabled()) {
48674
+ if (identityWorkOnOpen(nativeDb, scope)) {
47836
48675
  await Promise.resolve().then(() => (init_sqlite_data_accessor(), sqlite_data_accessor_exports));
47837
- prepareRowIdentity(nativeDb, scope, { triggers: false });
48676
+ healRowIdentitySchema(nativeDb, scope);
48677
+ if (rowUidFillEnabled()) {
48678
+ prepareRowIdentity(nativeDb, scope, { triggers: false });
48679
+ }
47838
48680
  }
47839
48681
  execution?.assertActive();
47840
48682
  log15.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
@@ -47947,7 +48789,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
47947
48789
  execution?.assertActive();
47948
48790
  log15.debug({ scope, dbPath: normalizedPath }, "opening dual-scope cleo.db");
47949
48791
  const dir = dirname20(normalizedPath);
47950
- if (!existsSync29(dir)) {
48792
+ if (!existsSync30(dir)) {
47951
48793
  mkdirSync10(dir, { recursive: true });
47952
48794
  }
47953
48795
  execution?.assertActive();
@@ -47980,9 +48822,12 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
47980
48822
  `dual-scope-db[${scope}]`
47981
48823
  );
47982
48824
  execution?.assertActive();
47983
- if (rowUidFillEnabled()) {
48825
+ if (identityWorkOnOpen(nativeDb, scope)) {
47984
48826
  await Promise.resolve().then(() => (init_sqlite_data_accessor(), sqlite_data_accessor_exports));
47985
- prepareRowIdentity(nativeDb, scope);
48827
+ healRowIdentitySchema(nativeDb, scope);
48828
+ if (rowUidFillEnabled()) {
48829
+ prepareRowIdentity(nativeDb, scope);
48830
+ }
47986
48831
  }
47987
48832
  execution?.assertActive();
47988
48833
  log15.debug({ scope, dbPath: normalizedPath }, "dual-scope cleo.db ready");
@@ -49112,7 +49957,7 @@ __export(nexus_sqlite_exports, {
49112
49957
  resetNexusDbState: () => resetNexusDbState,
49113
49958
  resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
49114
49959
  });
49115
- import { copyFileSync as copyFileSync8, existsSync as existsSync30 } from "node:fs";
49960
+ import { copyFileSync as copyFileSync8, existsSync as existsSync31 } from "node:fs";
49116
49961
  import { join as join33 } from "node:path";
49117
49962
  function getNexusDbPath(cwd) {
49118
49963
  return resolveDualScopeDbPath("project", cwd);
@@ -49164,7 +50009,7 @@ function detectAndWarnOnNestedNexus() {
49164
50009
  } catch {
49165
50010
  return false;
49166
50011
  }
49167
- if (!existsSync30(nestedPath)) return false;
50012
+ if (!existsSync31(nestedPath)) return false;
49168
50013
  if (_warnedNestedPaths.has(nestedPath)) return false;
49169
50014
  _warnedNestedPaths.add(nestedPath);
49170
50015
  const canonicalPath = getNexusDbPath();
@@ -49184,7 +50029,7 @@ function detectAndWarnOnNestedNexus() {
49184
50029
  function _resetNestedNexusWarningGate() {
49185
50030
  _warnedNestedPaths.clear();
49186
50031
  }
49187
- function tableExists4(nativeDb, tableName) {
50032
+ function tableExists5(nativeDb, tableName) {
49188
50033
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
49189
50034
  return !!result;
49190
50035
  }
@@ -49193,7 +50038,7 @@ function objectExists(nativeDb, type, name2) {
49193
50038
  return !!result;
49194
50039
  }
49195
50040
  function ensureNexusFts5(nativeDb) {
49196
- if (!tableExists4(nativeDb, "nexus_nodes")) return;
50041
+ if (!tableExists5(nativeDb, "nexus_nodes")) return;
49197
50042
  if (!objectExists(nativeDb, "table", "nexus_symbols_fts")) {
49198
50043
  nativeDb.exec(`
49199
50044
  CREATE VIRTUAL TABLE nexus_symbols_fts USING fts5(
@@ -49239,7 +50084,7 @@ function ensureNexusFts5(nativeDb) {
49239
50084
  `);
49240
50085
  }
49241
50086
  function ensureNexusRelationWeights(nativeDb) {
49242
- if (!tableExists4(nativeDb, "nexus_relations")) return;
50087
+ if (!tableExists5(nativeDb, "nexus_relations")) return;
49243
50088
  nativeDb.exec(`
49244
50089
  CREATE TABLE IF NOT EXISTS nexus_relation_weights (
49245
50090
  relation_id TEXT PRIMARY KEY NOT NULL,
@@ -49321,9 +50166,9 @@ function ensureNexusRelationWeights(nativeDb) {
49321
50166
  }
49322
50167
  function runNexusMigrations(nativeDb, db, dbPath) {
49323
50168
  const migrationsFolder = resolveNexusMigrationsFolder();
49324
- if (tableExists4(nativeDb, "nexus_nodes")) {
50169
+ if (tableExists5(nativeDb, "nexus_nodes")) {
49325
50170
  const backupPath = dbPath.replace(/\.db$/, "-pre-cleo.db.bak");
49326
- if (!existsSync30(backupPath)) {
50171
+ if (!existsSync31(backupPath)) {
49327
50172
  try {
49328
50173
  copyFileSync8(dbPath, backupPath);
49329
50174
  } catch {
@@ -49453,13 +50298,13 @@ var init_legacy_alias = __esm({
49453
50298
 
49454
50299
  // packages/core/src/llm/stable-device-id.ts
49455
50300
  import { randomUUID as randomUUID3 } from "node:crypto";
49456
- import { existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync11, renameSync as renameSync9, writeFileSync as writeFileSync9 } from "node:fs";
50301
+ import { existsSync as existsSync32, mkdirSync as mkdirSync11, readFileSync as readFileSync11, renameSync as renameSync9, writeFileSync as writeFileSync9 } from "node:fs";
49457
50302
  import { dirname as dirname21 } from "node:path";
49458
50303
  import { resolveStableDeviceIdPath } from "@cleocode/paths";
49459
50304
  function getStableDeviceId() {
49460
50305
  if (_cachedDeviceId !== null) return _cachedDeviceId;
49461
50306
  const path2 = resolveStableDeviceIdPath();
49462
- if (existsSync31(path2)) {
50307
+ if (existsSync32(path2)) {
49463
50308
  try {
49464
50309
  const raw = readFileSync11(path2, "utf-8").trim();
49465
50310
  if (raw.length > 0) {
@@ -49472,7 +50317,7 @@ function getStableDeviceId() {
49472
50317
  const fresh = randomUUID3();
49473
50318
  try {
49474
50319
  const dir = dirname21(path2);
49475
- if (!existsSync31(dir)) {
50320
+ if (!existsSync32(dir)) {
49476
50321
  mkdirSync11(dir, { recursive: true });
49477
50322
  }
49478
50323
  const tmpPath = `${path2}.tmp.${process.pid}`;
@@ -49548,11 +50393,11 @@ __export(path_map_exports, {
49548
50393
  supersededRegistryPath: () => supersededRegistryPath,
49549
50394
  touchProjectLocation: () => touchProjectLocation
49550
50395
  });
49551
- import { existsSync as existsSync32, lstatSync, readdirSync as readdirSync8 } from "node:fs";
50396
+ import { existsSync as existsSync33, lstatSync, readdirSync as readdirSync8 } from "node:fs";
49552
50397
  import { lstat, readdir as readdir2 } from "node:fs/promises";
49553
50398
  import { isAbsolute as isAbsolute5, join as join34, relative as relative3, sep as sep5 } from "node:path";
49554
50399
  import { canonicalizePath as canonicalizePath3, readDeclaredProjectIdentity as readDeclaredProjectIdentity4 } from "@cleocode/paths";
49555
- import { and as and9, desc as desc3, eq as eq12, inArray as inArray6, ne as ne3 } from "drizzle-orm";
50400
+ import { and as and9, desc as desc3, eq as eq12, inArray as inArray7, ne as ne3 } from "drizzle-orm";
49556
50401
  function holdingKey(projectId, path2) {
49557
50402
  return `${projectId}\0${path2}`;
49558
50403
  }
@@ -49700,7 +50545,7 @@ function decideEncounterBinding(db, record) {
49700
50545
  const here = db.select({ state: projectLocations.state, checkoutNonce: projectLocations.checkoutNonce }).from(projectLocations).where(
49701
50546
  and9(
49702
50547
  eq12(projectLocations.projectId, record.projectId),
49703
- inArray6(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
50548
+ inArray7(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
49704
50549
  eq12(projectLocations.path, record.projectPath)
49705
50550
  )
49706
50551
  ).all();
@@ -49711,7 +50556,7 @@ function decideEncounterBinding(db, record) {
49711
50556
  const previous = () => db.select({ checkoutNonce: projectLocations.checkoutNonce }).from(projectLocations).where(
49712
50557
  and9(
49713
50558
  eq12(projectLocations.projectId, record.projectId),
49714
- inArray6(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
50559
+ inArray7(projectLocations.deviceId, [deviceId, LOCAL_DEVICE_SENTINEL]),
49715
50560
  eq12(projectLocations.path, oldPath)
49716
50561
  )
49717
50562
  ).all();
@@ -49759,7 +50604,7 @@ function hasOtherLiveLocation(db, record, deviceId) {
49759
50604
  ne3(projectLocations.path, record.projectPath)
49760
50605
  )
49761
50606
  ).all().some(
49762
- (location) => isStrictlyInside(record.projectPath, location.path) && existsSync32(location.path) && readDeclaredProjectIdentity4(location.path)?.projectId === record.projectId
50607
+ (location) => isStrictlyInside(record.projectPath, location.path) && existsSync33(location.path) && readDeclaredProjectIdentity4(location.path)?.projectId === record.projectId
49763
50608
  );
49764
50609
  }
49765
50610
  function demoteProjectLocation(db, record, state) {
@@ -49810,7 +50655,7 @@ function rehomeDisplacedRows(db, projectId, projectPath, deviceId, probed) {
49810
50655
  ).orderBy(desc3(projectLocations.lastSeen)).all();
49811
50656
  const present = (path2) => {
49812
50657
  const holding = probed?.get(holdingKey(row.projectId, path2));
49813
- return holding === void 0 ? existsSync32(path2) : holding === "yes";
50658
+ return holding === void 0 ? existsSync33(path2) : holding === "yes";
49814
50659
  };
49815
50660
  const home = candidates.find(
49816
50661
  (c) => present(c.path) && db.select({ projectId: projectRegistry.projectId }).from(projectRegistry).where(eq12(projectRegistry.projectPath, c.path)).all().length === 0
@@ -49839,7 +50684,7 @@ async function listProjectCheckouts(projectId, opts = {}) {
49839
50684
  projectHash: generateProjectHash(row.path),
49840
50685
  firstSeen: row.firstSeen,
49841
50686
  lastSeen: row.lastSeen,
49842
- exists: local && existsSync32(row.path),
50687
+ exists: local && existsSync33(row.path),
49843
50688
  deviceId: local ? deviceId : row.deviceId,
49844
50689
  state: row.state
49845
50690
  });
@@ -49872,7 +50717,7 @@ __export(project_activity_exports, {
49872
50717
  projectLastActivitySql: () => projectLastActivitySql,
49873
50718
  projectLastActivitySqlText: () => projectLastActivitySqlText
49874
50719
  });
49875
- import { eq as eq13, inArray as inArray7, sql as sql32 } from "drizzle-orm";
50720
+ import { eq as eq13, inArray as inArray8, sql as sql32 } from "drizzle-orm";
49876
50721
  function normalizeRegistryTimestamp(value) {
49877
50722
  if (value === null || value === void 0 || value.length === 0) return null;
49878
50723
  const iso = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$/.test(value) ? `${value.replace(" ", "T")}Z` : value;
@@ -49901,7 +50746,7 @@ function markProjectsProbed(db, projectIds, now = /* @__PURE__ */ new Date()) {
49901
50746
  for (let i = 0; i < ids.length; i += PROBED_CHUNK) {
49902
50747
  const chunk = ids.slice(i, i + PROBED_CHUNK);
49903
50748
  changed += Number(
49904
- db.update(projectRegistry).set({ lastProbedAt: at }).where(inArray7(projectRegistry.projectId, chunk)).run().changes
50749
+ db.update(projectRegistry).set({ lastProbedAt: at }).where(inArray8(projectRegistry.projectId, chunk)).run().changes
49905
50750
  );
49906
50751
  }
49907
50752
  return changed;
@@ -49934,11 +50779,11 @@ __export(checkout_nonce_exports, {
49934
50779
  readCheckoutNonce: () => readCheckoutNonce
49935
50780
  });
49936
50781
  import { randomBytes as randomBytes3 } from "node:crypto";
49937
- import { existsSync as existsSync33, readFileSync as readFileSync12, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "node:fs";
50782
+ import { existsSync as existsSync34, readFileSync as readFileSync12, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "node:fs";
49938
50783
  import { join as join35 } from "node:path";
49939
50784
  function readInfo(projectRoot) {
49940
50785
  const path2 = join35(projectRoot, ".cleo", "project-info.json");
49941
- if (!existsSync33(path2)) return null;
50786
+ if (!existsSync34(path2)) return null;
49942
50787
  try {
49943
50788
  const parsed = JSON.parse(readFileSync12(path2, "utf-8"));
49944
50789
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
@@ -50031,7 +50876,7 @@ __export(paths_exports, {
50031
50876
  validateProjectRoot: () => validateProjectRoot,
50032
50877
  worktreeScope: () => worktreeScope
50033
50878
  });
50034
- import { existsSync as existsSync34, readFileSync as readFileSync13, statSync as statSync10 } from "node:fs";
50879
+ import { existsSync as existsSync35, readFileSync as readFileSync13, statSync as statSync10 } from "node:fs";
50035
50880
  import { createRequire as createRequire12 } from "node:module";
50036
50881
  import { homedir as homedir2 } from "node:os";
50037
50882
  import { dirname as dirname22, join as join36, resolve as resolve15 } from "node:path";
@@ -50057,7 +50902,7 @@ function runWithWorktreeScopeFromEnv(fn) {
50057
50902
  function isProjectInitialized(projectRoot) {
50058
50903
  const root = projectRoot ?? getProjectRoot();
50059
50904
  const cleoDir = join36(root, ".cleo");
50060
- return existsSync34(cleoDir) && existsSync34(join36(cleoDir, "tasks.db"));
50905
+ return existsSync35(cleoDir) && existsSync35(join36(cleoDir, "tasks.db"));
50061
50906
  }
50062
50907
  function getCleoHome() {
50063
50908
  return getPlatformPaths().data;
@@ -50163,7 +51008,7 @@ function _resolveProjectByCwdFromNexus(cwd) {
50163
51008
  try {
50164
51009
  const cleoHome = getCleoHome();
50165
51010
  const globalDbPath = join36(cleoHome, "cleo.db");
50166
- if (!existsSync34(globalDbPath)) return null;
51011
+ if (!existsSync35(globalDbPath)) return null;
50167
51012
  const start = resolve15(cwd ?? process.cwd());
50168
51013
  let current = start;
50169
51014
  const DatabaseSync3 = _getDatabaseSyncCtor();
@@ -50326,7 +51171,7 @@ function _cwdHasGitAncestor(cwd) {
50326
51171
  while (true) {
50327
51172
  const gitMarker = join36(current, ".git");
50328
51173
  try {
50329
- if (existsSync34(gitMarker)) {
51174
+ if (existsSync35(gitMarker)) {
50330
51175
  return true;
50331
51176
  }
50332
51177
  } catch {
@@ -50404,7 +51249,7 @@ function getCleoGlobalCantAgentsDir() {
50404
51249
  function getAgentOutputsDir(cwd) {
50405
51250
  const projectRoot = getProjectRoot(cwd);
50406
51251
  const configPath = join36(projectRoot, ".cleo", "config.json");
50407
- if (existsSync34(configPath)) {
51252
+ if (existsSync35(configPath)) {
50408
51253
  try {
50409
51254
  const config = JSON.parse(readFileSync13(configPath, "utf-8"));
50410
51255
  if (typeof config.agentOutputs === "object" && config.agentOutputs?.directory) {
@@ -50437,7 +51282,7 @@ function getManifestPath(cwd) {
50437
51282
  const configPath = join36(projectRoot, ".cleo", "config.json");
50438
51283
  const legacyFileName = ["MANIFEST", "jsonl"].join(".");
50439
51284
  let manifestFile = legacyFileName;
50440
- if (existsSync34(configPath)) {
51285
+ if (existsSync35(configPath)) {
50441
51286
  try {
50442
51287
  const config = JSON.parse(readFileSync13(configPath, "utf-8"));
50443
51288
  const customFile = config.agentOutputs?.manifestFile ?? config.research?.manifestFile;
@@ -50493,7 +51338,7 @@ function resolveWorktreeRouting(cwdOverride) {
50493
51338
  while (current !== home && current !== "/" && current !== "") {
50494
51339
  const gitPath = join36(current, ".git");
50495
51340
  try {
50496
- if (existsSync34(gitPath)) {
51341
+ if (existsSync35(gitPath)) {
50497
51342
  const stat3 = statSync10(gitPath);
50498
51343
  if (stat3.isFile()) {
50499
51344
  worktreePath = current;
@@ -50532,7 +51377,7 @@ function detectStrayCleoDb(routing) {
50532
51377
  }
50533
51378
  const tasksDbPath = join36(routing.worktreePath, ".cleo", "tasks.db");
50534
51379
  try {
50535
- if (existsSync34(tasksDbPath) && statSync10(tasksDbPath).isFile()) {
51380
+ if (existsSync35(tasksDbPath) && statSync10(tasksDbPath).isFile()) {
50536
51381
  return tasksDbPath;
50537
51382
  }
50538
51383
  } catch {
@@ -50542,7 +51387,7 @@ function detectStrayCleoDb(routing) {
50542
51387
  async function resolveProjectById(projectId) {
50543
51388
  try {
50544
51389
  const { getNexusDb: getNexusDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50545
- if (!existsSync34(getNexusRegistryDbPath2())) return null;
51390
+ if (!existsSync35(getNexusRegistryDbPath2())) return null;
50546
51391
  const { eq: eq16 } = await import("drizzle-orm");
50547
51392
  const { projectRegistry: projectRegistry2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
50548
51393
  const db = await getNexusDb2();
@@ -50578,7 +51423,7 @@ function _rowToRegistryEntry(row) {
50578
51423
  }
50579
51424
  function _assertProjectInfoReadable(projectRoot) {
50580
51425
  const infoPath = join36(projectRoot, ".cleo", "project-info.json");
50581
- if (existsSync34(infoPath)) JSON.parse(readFileSync13(infoPath, "utf-8"));
51426
+ if (existsSync35(infoPath)) JSON.parse(readFileSync13(infoPath, "utf-8"));
50582
51427
  }
50583
51428
  function startProjectEncounter(key, projectRoot, infoProjectId) {
50584
51429
  const inFlight2 = _encountersInFlight.get(key);
@@ -50627,7 +51472,7 @@ async function recordProjectEncounter(cwd, options = {}) {
50627
51472
  if (!shouldAutoRegisterProject(project.projectRoot, cleoHome)) return "ephemeral";
50628
51473
  const checkout = captureProjectScope(project.projectRoot, worktreeScope.getStore()).worktreeRoot;
50629
51474
  const { getNexusRegistryDb: getNexusRegistryDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50630
- if (existsSync34(getNexusRegistryDbPath2(cleoHome))) {
51475
+ if (existsSync35(getNexusRegistryDbPath2(cleoHome))) {
50631
51476
  const { and: and12, eq: eq16 } = await import("drizzle-orm");
50632
51477
  const { projectLocations: projectLocations2, projectRegistry: projectRegistry2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
50633
51478
  const { currentDeviceId: currentDeviceId2 } = await Promise.resolve().then(() => (init_path_map(), path_map_exports));
@@ -50653,7 +51498,7 @@ async function recordProjectEncounter(cwd, options = {}) {
50653
51498
  }
50654
51499
  async function markEncounterOpened(cleoHome, projectId) {
50655
51500
  const { getNexusRegistryDb: getNexusRegistryDb2, getNexusRegistryDbPath: getNexusRegistryDbPath2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
50656
- if (!existsSync34(getNexusRegistryDbPath2(cleoHome))) return;
51501
+ if (!existsSync35(getNexusRegistryDbPath2(cleoHome))) return;
50657
51502
  const { markProjectOpened: markProjectOpened2 } = await Promise.resolve().then(() => (init_project_activity(), project_activity_exports));
50658
51503
  markProjectOpened2(await getNexusRegistryDb2(cleoHome), projectId);
50659
51504
  }
@@ -50826,7 +51671,7 @@ var init_eligibility = __esm({
50826
51671
 
50827
51672
  // packages/core/src/store/background-jobs.ts
50828
51673
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
50829
- import { and as and10, asc as asc2, eq as eq14, getTableColumns as getTableColumns3, gt as gt2, inArray as inArray8, isNull as isNull5, ne as ne4, or as or6, sql as sql33 } from "drizzle-orm";
51674
+ import { and as and10, asc as asc2, eq as eq14, getTableColumns as getTableColumns3, gt as gt2, inArray as inArray9, isNull as isNull5, ne as ne4, or as or6, sql as sql33 } from "drizzle-orm";
50830
51675
  function assertOperationWriteFence(db, execution) {
50831
51676
  assertOperationJobState(db, execution, "running");
50832
51677
  }
@@ -51801,7 +52646,7 @@ __export(memory_accessor_exports, {
51801
52646
  logConsolidationComplete: () => logConsolidationComplete,
51802
52647
  logConsolidationStart: () => logConsolidationStart
51803
52648
  });
51804
- import { and as and11, asc as asc3, desc as desc4, eq as eq15, gt as gt3, gte as gte2, inArray as inArray9, isNull as isNull6, lt as lt3, ne as ne5, or as or7, sql as sql35 } from "drizzle-orm";
52649
+ import { and as and11, asc as asc3, desc as desc4, eq as eq15, gt as gt3, gte as gte2, inArray as inArray10, isNull as isNull6, lt as lt3, ne as ne5, or as or7, sql as sql35 } from "drizzle-orm";
51805
52650
  async function getBrainAccessor(cwd) {
51806
52651
  const db = await getBrainDb(cwd);
51807
52652
  return new BrainDataAccessor(db);
@@ -52230,8 +53075,8 @@ var init_memory_accessor = __esm({
52230
53075
  }
52231
53076
  if (params.tags && params.tags.length > 0) {
52232
53077
  const wantedTags = [...new Set(params.tags)];
52233
- const matchingIds = this.db.select({ stickyId: brainStickyTags.stickyId }).from(brainStickyTags).where(inArray9(brainStickyTags.tag, wantedTags)).groupBy(brainStickyTags.stickyId).having(sql35`count(distinct ${brainStickyTags.tag}) = ${wantedTags.length}`);
52234
- conditions.push(inArray9(brainStickyNotes.id, matchingIds));
53078
+ const matchingIds = this.db.select({ stickyId: brainStickyTags.stickyId }).from(brainStickyTags).where(inArray10(brainStickyTags.tag, wantedTags)).groupBy(brainStickyTags.stickyId).having(sql35`count(distinct ${brainStickyTags.tag}) = ${wantedTags.length}`);
53079
+ conditions.push(inArray10(brainStickyNotes.id, matchingIds));
52235
53080
  }
52236
53081
  let query = this.db.select().from(brainStickyNotes).orderBy(desc4(brainStickyNotes.createdAt));
52237
53082
  if (conditions.length > 0) {
@@ -52330,8 +53175,8 @@ var init_memory_accessor = __esm({
52330
53175
  }
52331
53176
  if (params.tags && params.tags.length > 0) {
52332
53177
  const wanted = [...new Set(params.tags)];
52333
- const matchingIds = this.db.select({ id: sql35`a.id` }).from(sql35`${t} AS a, json_each(a.tags) AS je`).where(inArray9(sql35`je.value`, wanted)).groupBy(sql35`a.id`).having(sql35`count(distinct je.value) = ${wanted.length}`);
52334
- conditions.push(inArray9(t.id, matchingIds));
53178
+ const matchingIds = this.db.select({ id: sql35`a.id` }).from(sql35`${t} AS a, json_each(a.tags) AS je`).where(inArray10(sql35`je.value`, wanted)).groupBy(sql35`a.id`).having(sql35`count(distinct je.value) = ${wanted.length}`);
53179
+ conditions.push(inArray10(t.id, matchingIds));
52335
53180
  }
52336
53181
  const where = conditions.length > 0 ? and11(...conditions) : void 0;
52337
53182
  return this.selectAttention(where, params.limit);