@cleocode/core 2026.5.134 → 2026.6.0

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 (548) hide show
  1. package/binaries/README.md +49 -27
  2. package/dist/agents/{agent-registry.d.ts → agent-capacity-tracker.d.ts} +2 -2
  3. package/dist/agents/agent-capacity-tracker.d.ts.map +1 -0
  4. package/dist/agents/{agent-registry.js → agent-capacity-tracker.js} +2 -2
  5. package/dist/agents/agent-capacity-tracker.js.map +1 -0
  6. package/dist/agents/index.d.ts +1 -1
  7. package/dist/agents/index.d.ts.map +1 -1
  8. package/dist/agents/index.js +4 -2
  9. package/dist/agents/index.js.map +1 -1
  10. package/dist/agents/seed-install.d.ts +1 -1
  11. package/dist/agents/seed-install.d.ts.map +1 -1
  12. package/dist/agents/seed-install.js +42 -36
  13. package/dist/agents/seed-install.js.map +1 -1
  14. package/dist/caamp-export.d.ts +18 -0
  15. package/dist/caamp-export.d.ts.map +1 -0
  16. package/dist/caamp-export.js +18 -0
  17. package/dist/caamp-export.js.map +1 -0
  18. package/dist/conduit/local-transport.d.ts +1 -1
  19. package/dist/conduit/local-transport.d.ts.map +1 -1
  20. package/dist/conduit/local-transport.js +69 -43
  21. package/dist/conduit/local-transport.js.map +1 -1
  22. package/dist/dispatch/mutate-projection.d.ts.map +1 -1
  23. package/dist/dispatch/mutate-projection.js +11 -0
  24. package/dist/dispatch/mutate-projection.js.map +1 -1
  25. package/dist/docs/docs-read-model.d.ts +7 -0
  26. package/dist/docs/docs-read-model.d.ts.map +1 -1
  27. package/dist/docs/docs-read-model.js +5 -0
  28. package/dist/docs/docs-read-model.js.map +1 -1
  29. package/dist/docs/supersede.d.ts.map +1 -1
  30. package/dist/docs/supersede.js +12 -7
  31. package/dist/docs/supersede.js.map +1 -1
  32. package/dist/doctor/db-substrate.d.ts.map +1 -1
  33. package/dist/doctor/db-substrate.js +10 -9
  34. package/dist/doctor/db-substrate.js.map +1 -1
  35. package/dist/git-shim-export.d.ts +18 -0
  36. package/dist/git-shim-export.d.ts.map +1 -0
  37. package/dist/git-shim-export.js +18 -0
  38. package/dist/git-shim-export.js.map +1 -0
  39. package/dist/hooks/payload-schemas.d.ts +2 -2
  40. package/dist/init.d.ts.map +1 -1
  41. package/dist/init.js +39 -32
  42. package/dist/init.js.map +1 -1
  43. package/dist/internal.d.ts +11 -3
  44. package/dist/internal.d.ts.map +1 -1
  45. package/dist/internal.js +14 -5
  46. package/dist/internal.js.map +1 -1
  47. package/dist/lafs-export.d.ts +18 -0
  48. package/dist/lafs-export.d.ts.map +1 -0
  49. package/dist/lafs-export.js +18 -0
  50. package/dist/lafs-export.js.map +1 -0
  51. package/dist/lifecycle/effective-stage.js +1 -1
  52. package/dist/lifecycle/index.js +1 -1
  53. package/dist/lifecycle/index.js.map +1 -1
  54. package/dist/lifecycle/rollup.js +1 -1
  55. package/dist/llm/credential-pool.d.ts +17 -0
  56. package/dist/llm/credential-pool.d.ts.map +1 -1
  57. package/dist/llm/credential-pool.js +40 -1
  58. package/dist/llm/credential-pool.js.map +1 -1
  59. package/dist/llm/plugin-facade.d.ts.map +1 -1
  60. package/dist/llm/plugin-facade.js +11 -19
  61. package/dist/llm/plugin-facade.js.map +1 -1
  62. package/dist/llm/role-executor.d.ts +8 -0
  63. package/dist/llm/role-executor.d.ts.map +1 -1
  64. package/dist/llm/role-executor.js +96 -4
  65. package/dist/llm/role-executor.js.map +1 -1
  66. package/dist/llm/role-resolver.d.ts.map +1 -1
  67. package/dist/llm/role-resolver.js +56 -1
  68. package/dist/llm/role-resolver.js.map +1 -1
  69. package/dist/llm/transports/codex-oauth-headers.d.ts +51 -0
  70. package/dist/llm/transports/codex-oauth-headers.d.ts.map +1 -0
  71. package/dist/llm/transports/codex-oauth-headers.js +89 -0
  72. package/dist/llm/transports/codex-oauth-headers.js.map +1 -0
  73. package/dist/memory/claude-mem-migration.d.ts.map +1 -1
  74. package/dist/memory/claude-mem-migration.js +1 -3
  75. package/dist/memory/claude-mem-migration.js.map +1 -1
  76. package/dist/memory/decisions.d.ts.map +1 -1
  77. package/dist/memory/decisions.js +77 -23
  78. package/dist/memory/decisions.js.map +1 -1
  79. package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
  80. package/dist/memory/graph-memory-bridge.js +12 -6
  81. package/dist/memory/graph-memory-bridge.js.map +1 -1
  82. package/dist/memory/learnings.d.ts +2 -2
  83. package/dist/memory/nexus-plasticity.d.ts +21 -9
  84. package/dist/memory/nexus-plasticity.d.ts.map +1 -1
  85. package/dist/memory/nexus-plasticity.js +44 -22
  86. package/dist/memory/nexus-plasticity.js.map +1 -1
  87. package/dist/memory/patterns.d.ts +2 -2
  88. package/dist/memory/redaction.d.ts +19 -3
  89. package/dist/memory/redaction.d.ts.map +1 -1
  90. package/dist/memory/redaction.js +22 -94
  91. package/dist/memory/redaction.js.map +1 -1
  92. package/dist/metrics/token-service.d.ts +8 -2
  93. package/dist/metrics/token-service.d.ts.map +1 -1
  94. package/dist/metrics/token-service.js +1 -1
  95. package/dist/metrics/token-service.js.map +1 -1
  96. package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
  97. package/dist/nexus/analyze-orchestrator.js +6 -8
  98. package/dist/nexus/analyze-orchestrator.js.map +1 -1
  99. package/dist/nexus/api-extractors/http-extractor.d.ts.map +1 -1
  100. package/dist/nexus/api-extractors/http-extractor.js +3 -3
  101. package/dist/nexus/api-extractors/http-extractor.js.map +1 -1
  102. package/dist/nexus/clusters.d.ts.map +1 -1
  103. package/dist/nexus/clusters.js +3 -2
  104. package/dist/nexus/clusters.js.map +1 -1
  105. package/dist/nexus/context.d.ts.map +1 -1
  106. package/dist/nexus/context.js +10 -16
  107. package/dist/nexus/context.js.map +1 -1
  108. package/dist/nexus/diff.d.ts.map +1 -1
  109. package/dist/nexus/diff.js +6 -4
  110. package/dist/nexus/diff.js.map +1 -1
  111. package/dist/nexus/export.d.ts.map +1 -1
  112. package/dist/nexus/export.js +7 -4
  113. package/dist/nexus/export.js.map +1 -1
  114. package/dist/nexus/flows.d.ts.map +1 -1
  115. package/dist/nexus/flows.js +3 -1
  116. package/dist/nexus/flows.js.map +1 -1
  117. package/dist/nexus/impact.d.ts +1 -1
  118. package/dist/nexus/impact.d.ts.map +1 -1
  119. package/dist/nexus/impact.js +31 -17
  120. package/dist/nexus/impact.js.map +1 -1
  121. package/dist/nexus/living-brain.d.ts.map +1 -1
  122. package/dist/nexus/living-brain.js +27 -15
  123. package/dist/nexus/living-brain.js.map +1 -1
  124. package/dist/nexus/nexus-bridge.d.ts.map +1 -1
  125. package/dist/nexus/nexus-bridge.js +28 -29
  126. package/dist/nexus/nexus-bridge.js.map +1 -1
  127. package/dist/nexus/plasticity-queries.d.ts +4 -2
  128. package/dist/nexus/plasticity-queries.d.ts.map +1 -1
  129. package/dist/nexus/plasticity-queries.js +27 -15
  130. package/dist/nexus/plasticity-queries.js.map +1 -1
  131. package/dist/nexus/query.d.ts.map +1 -1
  132. package/dist/nexus/query.js +6 -2
  133. package/dist/nexus/query.js.map +1 -1
  134. package/dist/nexus/registry.d.ts.map +1 -1
  135. package/dist/nexus/registry.js +65 -30
  136. package/dist/nexus/registry.js.map +1 -1
  137. package/dist/nexus/route-analysis.d.ts +3 -2
  138. package/dist/nexus/route-analysis.d.ts.map +1 -1
  139. package/dist/nexus/route-analysis.js +11 -10
  140. package/dist/nexus/route-analysis.js.map +1 -1
  141. package/dist/nexus/sigil.d.ts.map +1 -1
  142. package/dist/nexus/sigil.js +60 -13
  143. package/dist/nexus/sigil.js.map +1 -1
  144. package/dist/nexus/user-profile.d.ts +2 -1
  145. package/dist/nexus/user-profile.d.ts.map +1 -1
  146. package/dist/nexus/user-profile.js +8 -4
  147. package/dist/nexus/user-profile.js.map +1 -1
  148. package/dist/orchestrate/index.d.ts +1 -1
  149. package/dist/orchestrate/index.d.ts.map +1 -1
  150. package/dist/orchestrate/index.js +1 -1
  151. package/dist/orchestrate/index.js.map +1 -1
  152. package/dist/orchestrate/plan.d.ts +3 -3
  153. package/dist/orchestrate/plan.d.ts.map +1 -1
  154. package/dist/orchestrate/plan.js +7 -7
  155. package/dist/orchestrate/plan.js.map +1 -1
  156. package/dist/orchestrate/spawn-ops.js +2 -2
  157. package/dist/orchestrate/spawn-ops.js.map +1 -1
  158. package/dist/orchestration/classify.d.ts +2 -2
  159. package/dist/orchestration/classify.js +3 -3
  160. package/dist/orchestration/classify.js.map +1 -1
  161. package/dist/orchestration/validate-spawn.d.ts.map +1 -1
  162. package/dist/orchestration/validate-spawn.js +5 -5
  163. package/dist/orchestration/validate-spawn.js.map +1 -1
  164. package/dist/paths-export.d.ts +18 -0
  165. package/dist/paths-export.d.ts.map +1 -0
  166. package/dist/paths-export.js +18 -0
  167. package/dist/paths-export.js.map +1 -0
  168. package/dist/paths.d.ts.map +1 -1
  169. package/dist/paths.js +24 -11
  170. package/dist/paths.js.map +1 -1
  171. package/dist/playbooks/index.d.ts +1 -0
  172. package/dist/playbooks/index.d.ts.map +1 -1
  173. package/dist/playbooks/index.js +4 -0
  174. package/dist/playbooks/index.js.map +1 -1
  175. package/dist/playbooks/skill-node-executor.d.ts +155 -0
  176. package/dist/playbooks/skill-node-executor.d.ts.map +1 -0
  177. package/dist/playbooks/skill-node-executor.js +156 -0
  178. package/dist/playbooks/skill-node-executor.js.map +1 -0
  179. package/dist/repair.d.ts +3 -7
  180. package/dist/repair.d.ts.map +1 -1
  181. package/dist/repair.js +5 -43
  182. package/dist/repair.js.map +1 -1
  183. package/dist/sagas/migrate-containment.js +7 -7
  184. package/dist/sagas/migrate-containment.js.map +1 -1
  185. package/dist/scaffold/ensure-dirs.d.ts +8 -2
  186. package/dist/scaffold/ensure-dirs.d.ts.map +1 -1
  187. package/dist/scaffold/ensure-dirs.js +24 -11
  188. package/dist/scaffold/ensure-dirs.js.map +1 -1
  189. package/dist/scaffold/project-detection.d.ts +5 -1
  190. package/dist/scaffold/project-detection.d.ts.map +1 -1
  191. package/dist/scaffold/project-detection.js +9 -5
  192. package/dist/scaffold/project-detection.js.map +1 -1
  193. package/dist/sentient/hygiene-scan.js +6 -6
  194. package/dist/sentient/hygiene-scan.js.map +1 -1
  195. package/dist/sentient/proposal-dedup.js +2 -2
  196. package/dist/sentient/proposal-rate-limiter.js +1 -1
  197. package/dist/sentient/propose-tick.js +5 -5
  198. package/dist/sentient/propose-tick.js.map +1 -1
  199. package/dist/sentient/stage-drift-tick.js +3 -3
  200. package/dist/sentient/stage-drift-tick.js.map +1 -1
  201. package/dist/sequence/index.d.ts.map +1 -1
  202. package/dist/sequence/index.js +6 -2
  203. package/dist/sequence/index.js.map +1 -1
  204. package/dist/setup/sections/verification.js +2 -2
  205. package/dist/setup/sections/verification.js.map +1 -1
  206. package/dist/shutdown.d.ts +81 -0
  207. package/dist/shutdown.d.ts.map +1 -0
  208. package/dist/shutdown.js +105 -0
  209. package/dist/shutdown.js.map +1 -0
  210. package/dist/skills/index.d.ts +2 -0
  211. package/dist/skills/index.d.ts.map +1 -1
  212. package/dist/skills/index.js +1 -0
  213. package/dist/skills/index.js.map +1 -1
  214. package/dist/skills/skill-executor-adapter.d.ts +136 -0
  215. package/dist/skills/skill-executor-adapter.d.ts.map +1 -0
  216. package/dist/skills/skill-executor-adapter.js +137 -0
  217. package/dist/skills/skill-executor-adapter.js.map +1 -0
  218. package/dist/skills/usage-recorder.d.ts.map +1 -1
  219. package/dist/skills/usage-recorder.js +30 -0
  220. package/dist/skills/usage-recorder.js.map +1 -1
  221. package/dist/skills-export.d.ts +23 -0
  222. package/dist/skills-export.d.ts.map +1 -0
  223. package/dist/skills-export.js +23 -0
  224. package/dist/skills-export.js.map +1 -0
  225. package/dist/stats/index.d.ts.map +1 -1
  226. package/dist/stats/index.js +8 -3
  227. package/dist/stats/index.js.map +1 -1
  228. package/dist/store/agent-doctor.d.ts +3 -3
  229. package/dist/store/agent-doctor.d.ts.map +1 -1
  230. package/dist/store/agent-doctor.js +18 -13
  231. package/dist/store/agent-doctor.js.map +1 -1
  232. package/dist/store/agent-install.d.ts +6 -5
  233. package/dist/store/agent-install.d.ts.map +1 -1
  234. package/dist/store/agent-install.js +20 -16
  235. package/dist/store/agent-install.js.map +1 -1
  236. package/dist/store/agent-registry-accessor.d.ts +66 -28
  237. package/dist/store/agent-registry-accessor.d.ts.map +1 -1
  238. package/dist/store/agent-registry-accessor.js +248 -167
  239. package/dist/store/agent-registry-accessor.js.map +1 -1
  240. package/dist/store/agent-registry-store.d.ts +242 -0
  241. package/dist/store/agent-registry-store.d.ts.map +1 -0
  242. package/dist/store/agent-registry-store.js +501 -0
  243. package/dist/store/agent-registry-store.js.map +1 -0
  244. package/dist/store/agent-resolver.d.ts +8 -8
  245. package/dist/store/agent-resolver.d.ts.map +1 -1
  246. package/dist/store/agent-resolver.js +19 -17
  247. package/dist/store/agent-resolver.js.map +1 -1
  248. package/dist/store/backup-pack.d.ts.map +1 -1
  249. package/dist/store/backup-pack.js +24 -8
  250. package/dist/store/backup-pack.js.map +1 -1
  251. package/dist/store/conduit-sqlite.d.ts +181 -74
  252. package/dist/store/conduit-sqlite.d.ts.map +1 -1
  253. package/dist/store/conduit-sqlite.js +307 -528
  254. package/dist/store/conduit-sqlite.js.map +1 -1
  255. package/dist/store/cross-db-cleanup.d.ts +5 -5
  256. package/dist/store/cross-db-cleanup.d.ts.map +1 -1
  257. package/dist/store/cross-db-cleanup.js +12 -10
  258. package/dist/store/cross-db-cleanup.js.map +1 -1
  259. package/dist/store/data-accessor.d.ts +4 -4
  260. package/dist/store/data-accessor.js +5 -5
  261. package/dist/store/data-accessor.js.map +1 -1
  262. package/dist/store/data-safety-central.d.ts +83 -1
  263. package/dist/store/data-safety-central.d.ts.map +1 -1
  264. package/dist/store/data-safety-central.js +257 -0
  265. package/dist/store/data-safety-central.js.map +1 -1
  266. package/dist/store/db-helpers.d.ts +8 -2
  267. package/dist/store/db-helpers.d.ts.map +1 -1
  268. package/dist/store/db-helpers.js +6 -2
  269. package/dist/store/db-helpers.js.map +1 -1
  270. package/dist/store/dual-scope-db.d.ts +46 -4
  271. package/dist/store/dual-scope-db.d.ts.map +1 -1
  272. package/dist/store/dual-scope-db.js +103 -9
  273. package/dist/store/dual-scope-db.js.map +1 -1
  274. package/dist/store/exodus/__fixtures__/representative-fixture.d.ts +116 -0
  275. package/dist/store/exodus/__fixtures__/representative-fixture.d.ts.map +1 -0
  276. package/dist/store/exodus/__fixtures__/representative-fixture.js +274 -0
  277. package/dist/store/exodus/__fixtures__/representative-fixture.js.map +1 -0
  278. package/dist/store/exodus/index.d.ts +3 -1
  279. package/dist/store/exodus/index.d.ts.map +1 -1
  280. package/dist/store/exodus/index.js +3 -1
  281. package/dist/store/exodus/index.js.map +1 -1
  282. package/dist/store/exodus/migrate.d.ts +120 -1
  283. package/dist/store/exodus/migrate.d.ts.map +1 -1
  284. package/dist/store/exodus/migrate.js +923 -119
  285. package/dist/store/exodus/migrate.js.map +1 -1
  286. package/dist/store/exodus/on-open.d.ts +189 -0
  287. package/dist/store/exodus/on-open.d.ts.map +1 -0
  288. package/dist/store/exodus/on-open.js +464 -0
  289. package/dist/store/exodus/on-open.js.map +1 -0
  290. package/dist/store/exodus/table-name-map.d.ts +173 -0
  291. package/dist/store/exodus/table-name-map.d.ts.map +1 -0
  292. package/dist/store/exodus/table-name-map.js +660 -0
  293. package/dist/store/exodus/table-name-map.js.map +1 -0
  294. package/dist/store/exodus/verify-migration.d.ts +72 -0
  295. package/dist/store/exodus/verify-migration.d.ts.map +1 -0
  296. package/dist/store/exodus/verify-migration.js +678 -0
  297. package/dist/store/exodus/verify-migration.js.map +1 -0
  298. package/dist/store/exodus/verify.d.ts +32 -8
  299. package/dist/store/exodus/verify.d.ts.map +1 -1
  300. package/dist/store/exodus/verify.js +48 -142
  301. package/dist/store/exodus/verify.js.map +1 -1
  302. package/dist/store/index.d.ts +2 -3
  303. package/dist/store/index.d.ts.map +1 -1
  304. package/dist/store/index.js +2 -3
  305. package/dist/store/index.js.map +1 -1
  306. package/dist/store/memory-accessor.d.ts +31 -0
  307. package/dist/store/memory-accessor.d.ts.map +1 -1
  308. package/dist/store/memory-accessor.js +38 -0
  309. package/dist/store/memory-accessor.js.map +1 -1
  310. package/dist/store/memory-sqlite.d.ts +86 -13
  311. package/dist/store/memory-sqlite.d.ts.map +1 -1
  312. package/dist/store/memory-sqlite.js +326 -528
  313. package/dist/store/memory-sqlite.js.map +1 -1
  314. package/dist/store/migrate-signaldock-to-conduit.d.ts +1 -1
  315. package/dist/store/migrate-signaldock-to-conduit.d.ts.map +1 -1
  316. package/dist/store/migrate-signaldock-to-conduit.js +126 -35
  317. package/dist/store/migrate-signaldock-to-conduit.js.map +1 -1
  318. package/dist/store/migration-manager.d.ts +49 -0
  319. package/dist/store/migration-manager.d.ts.map +1 -1
  320. package/dist/store/migration-manager.js +167 -67
  321. package/dist/store/migration-manager.js.map +1 -1
  322. package/dist/store/migration-sqlite.d.ts +1 -1
  323. package/dist/store/migration-sqlite.d.ts.map +1 -1
  324. package/dist/store/migration-sqlite.js +32 -3
  325. package/dist/store/migration-sqlite.js.map +1 -1
  326. package/dist/store/nexus-sqlite.d.ts +152 -29
  327. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  328. package/dist/store/nexus-sqlite.js +496 -177
  329. package/dist/store/nexus-sqlite.js.map +1 -1
  330. package/dist/store/nexus-validation-schemas.d.ts +32 -32
  331. package/dist/store/open-cleo-db.d.ts +37 -40
  332. package/dist/store/open-cleo-db.d.ts.map +1 -1
  333. package/dist/store/open-cleo-db.js +76 -153
  334. package/dist/store/open-cleo-db.js.map +1 -1
  335. package/dist/store/role-accessors-impl.d.ts +4 -4
  336. package/dist/store/role-accessors-impl.d.ts.map +1 -1
  337. package/dist/store/role-accessors-impl.js +18 -15
  338. package/dist/store/role-accessors-impl.js.map +1 -1
  339. package/dist/store/schema/{signaldock-schema.d.ts → agent-registry-schema.d.ts} +15 -5
  340. package/dist/store/schema/agent-registry-schema.d.ts.map +1 -0
  341. package/dist/store/schema/{signaldock-schema.js → agent-registry-schema.js} +15 -5
  342. package/dist/store/schema/agent-registry-schema.js.map +1 -0
  343. package/dist/store/schema/agent-schema.d.ts +1 -1
  344. package/dist/store/schema/agent-schema.js +4 -4
  345. package/dist/store/schema/agent-schema.js.map +1 -1
  346. package/dist/store/schema/attachments.d.ts +1 -1
  347. package/dist/store/schema/audit.d.ts +15 -5
  348. package/dist/store/schema/audit.d.ts.map +1 -1
  349. package/dist/store/schema/audit.js +12 -2
  350. package/dist/store/schema/audit.js.map +1 -1
  351. package/dist/store/schema/background-jobs.d.ts +1 -1
  352. package/dist/store/schema/cleo-global/{signaldock.d.ts → agent-registry.d.ts} +277 -271
  353. package/dist/store/schema/cleo-global/agent-registry.d.ts.map +1 -0
  354. package/dist/store/schema/cleo-global/{signaldock.js → agent-registry.js} +136 -125
  355. package/dist/store/schema/cleo-global/agent-registry.js.map +1 -0
  356. package/dist/store/schema/cleo-global/index.d.ts +29 -22
  357. package/dist/store/schema/cleo-global/index.d.ts.map +1 -1
  358. package/dist/store/schema/cleo-global/index.js +29 -22
  359. package/dist/store/schema/cleo-global/index.js.map +1 -1
  360. package/dist/store/schema/cleo-global/nexus.d.ts +36 -1034
  361. package/dist/store/schema/cleo-global/nexus.d.ts.map +1 -1
  362. package/dist/store/schema/cleo-global/nexus.js +32 -337
  363. package/dist/store/schema/cleo-global/nexus.js.map +1 -1
  364. package/dist/store/schema/cleo-global/skills.d.ts +16 -0
  365. package/dist/store/schema/cleo-global/skills.d.ts.map +1 -1
  366. package/dist/store/schema/cleo-global/skills.js +11 -0
  367. package/dist/store/schema/cleo-global/skills.js.map +1 -1
  368. package/dist/store/schema/{cleo-project → cleo-global}/telemetry.d.ts +33 -17
  369. package/dist/store/schema/cleo-global/telemetry.d.ts.map +1 -0
  370. package/dist/store/schema/{cleo-project → cleo-global}/telemetry.js +30 -18
  371. package/dist/store/schema/cleo-global/telemetry.js.map +1 -0
  372. package/dist/store/schema/cleo-project/audit.d.ts +8 -8
  373. package/dist/store/schema/cleo-project/audit.d.ts.map +1 -1
  374. package/dist/store/schema/cleo-project/audit.js +2 -6
  375. package/dist/store/schema/cleo-project/audit.js.map +1 -1
  376. package/dist/store/schema/cleo-project/docs.d.ts +1 -1
  377. package/dist/store/schema/cleo-project/index.d.ts +29 -12
  378. package/dist/store/schema/cleo-project/index.d.ts.map +1 -1
  379. package/dist/store/schema/cleo-project/index.js +29 -12
  380. package/dist/store/schema/cleo-project/index.js.map +1 -1
  381. package/dist/store/schema/cleo-project/lifecycle.d.ts +2 -2
  382. package/dist/store/schema/cleo-project/nexus-graph.d.ts +1067 -0
  383. package/dist/store/schema/cleo-project/nexus-graph.d.ts.map +1 -0
  384. package/dist/store/schema/cleo-project/nexus-graph.js +407 -0
  385. package/dist/store/schema/cleo-project/nexus-graph.js.map +1 -0
  386. package/dist/store/schema/cleo-project/provenance-orphans.d.ts +385 -0
  387. package/dist/store/schema/cleo-project/provenance-orphans.d.ts.map +1 -0
  388. package/dist/store/schema/cleo-project/provenance-orphans.js +142 -0
  389. package/dist/store/schema/cleo-project/provenance-orphans.js.map +1 -0
  390. package/dist/store/schema/cleo-project/provenance-rest.d.ts +1 -1
  391. package/dist/store/schema/cleo-project/runtime.d.ts +1 -1
  392. package/dist/store/schema/cleo-project/tasks-core-batch2.d.ts +1 -1
  393. package/dist/store/schema/cleo-project/tasks-core.d.ts +3 -3
  394. package/dist/store/schema/cleo-shared/brain.d.ts +711 -494
  395. package/dist/store/schema/cleo-shared/brain.d.ts.map +1 -1
  396. package/dist/store/schema/cleo-shared/brain.js +215 -134
  397. package/dist/store/schema/cleo-shared/brain.js.map +1 -1
  398. package/dist/store/schema/conduit-schema.d.ts +63 -51
  399. package/dist/store/schema/conduit-schema.d.ts.map +1 -1
  400. package/dist/store/schema/conduit-schema.js +23 -11
  401. package/dist/store/schema/conduit-schema.js.map +1 -1
  402. package/dist/store/schema/goal.d.ts +3 -2
  403. package/dist/store/schema/goal.d.ts.map +1 -1
  404. package/dist/store/schema/goal.js +3 -2
  405. package/dist/store/schema/goal.js.map +1 -1
  406. package/dist/store/schema/index.d.ts +1 -0
  407. package/dist/store/schema/index.d.ts.map +1 -1
  408. package/dist/store/schema/index.js +1 -0
  409. package/dist/store/schema/index.js.map +1 -1
  410. package/dist/store/schema/lifecycle.d.ts +2 -2
  411. package/dist/store/schema/memory-schema.d.ts +2 -2
  412. package/dist/store/schema/nexus-schema.d.ts +174 -115
  413. package/dist/store/schema/nexus-schema.d.ts.map +1 -1
  414. package/dist/store/schema/nexus-schema.js +175 -55
  415. package/dist/store/schema/nexus-schema.js.map +1 -1
  416. package/dist/store/schema/provenance/releases.d.ts +1 -1
  417. package/dist/store/schema/schema-utils.d.ts +78 -0
  418. package/dist/store/schema/schema-utils.d.ts.map +1 -0
  419. package/dist/store/schema/schema-utils.js +49 -0
  420. package/dist/store/schema/schema-utils.js.map +1 -0
  421. package/dist/store/schema/skills-schema.d.ts +81 -44
  422. package/dist/store/schema/skills-schema.d.ts.map +1 -1
  423. package/dist/store/schema/skills-schema.js +49 -16
  424. package/dist/store/schema/skills-schema.js.map +1 -1
  425. package/dist/store/schema/tasks.d.ts +3 -3
  426. package/dist/store/skills-db.d.ts +90 -50
  427. package/dist/store/skills-db.d.ts.map +1 -1
  428. package/dist/store/skills-db.js +132 -146
  429. package/dist/store/skills-db.js.map +1 -1
  430. package/dist/store/sqlite-backup.d.ts +2 -2
  431. package/dist/store/sqlite-backup.d.ts.map +1 -1
  432. package/dist/store/sqlite-backup.js +11 -10
  433. package/dist/store/sqlite-backup.js.map +1 -1
  434. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  435. package/dist/store/sqlite-data-accessor.js +25 -18
  436. package/dist/store/sqlite-data-accessor.js.map +1 -1
  437. package/dist/store/sqlite.d.ts +72 -12
  438. package/dist/store/sqlite.d.ts.map +1 -1
  439. package/dist/store/sqlite.js +153 -89
  440. package/dist/store/sqlite.js.map +1 -1
  441. package/dist/store/tasks-schema.d.ts +4 -0
  442. package/dist/store/tasks-schema.d.ts.map +1 -1
  443. package/dist/store/tasks-schema.js +60 -0
  444. package/dist/store/tasks-schema.js.map +1 -1
  445. package/dist/store/tasks-sqlite.d.ts +2 -2
  446. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  447. package/dist/store/tasks-sqlite.js +10 -5
  448. package/dist/store/tasks-sqlite.js.map +1 -1
  449. package/dist/store/umbrella-data-accessor.d.ts +17 -6
  450. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  451. package/dist/store/umbrella-data-accessor.js +8 -8
  452. package/dist/store/umbrella-data-accessor.js.map +1 -1
  453. package/dist/store/validation-schemas.d.ts +241 -208
  454. package/dist/store/validation-schemas.d.ts.map +1 -1
  455. package/dist/system/health.d.ts.map +1 -1
  456. package/dist/system/health.js +11 -6
  457. package/dist/system/health.js.map +1 -1
  458. package/dist/system/project-health.d.ts.map +1 -1
  459. package/dist/system/project-health.js +58 -12
  460. package/dist/system/project-health.js.map +1 -1
  461. package/dist/tasks/add.d.ts +8 -0
  462. package/dist/tasks/add.d.ts.map +1 -1
  463. package/dist/tasks/add.js +101 -0
  464. package/dist/tasks/add.js.map +1 -1
  465. package/dist/tasks/cancelled-child-waiver-audit.d.ts +47 -0
  466. package/dist/tasks/cancelled-child-waiver-audit.d.ts.map +1 -0
  467. package/dist/tasks/cancelled-child-waiver-audit.js +34 -0
  468. package/dist/tasks/cancelled-child-waiver-audit.js.map +1 -0
  469. package/dist/tasks/complete.d.ts +22 -2
  470. package/dist/tasks/complete.d.ts.map +1 -1
  471. package/dist/tasks/complete.js +71 -6
  472. package/dist/tasks/complete.js.map +1 -1
  473. package/dist/tasks/compute-task-view.js +1 -1
  474. package/dist/tasks/session-scope.d.ts +5 -0
  475. package/dist/tasks/session-scope.d.ts.map +1 -1
  476. package/dist/tasks/session-scope.js +4 -0
  477. package/dist/tasks/session-scope.js.map +1 -1
  478. package/dist/tools/guard.d.ts +71 -1
  479. package/dist/tools/guard.d.ts.map +1 -1
  480. package/dist/tools/guard.js +73 -1
  481. package/dist/tools/guard.js.map +1 -1
  482. package/dist/tools/index.d.ts +21 -0
  483. package/dist/tools/index.d.ts.map +1 -1
  484. package/dist/tools/index.js +25 -0
  485. package/dist/tools/index.js.map +1 -1
  486. package/dist/upgrade.d.ts.map +1 -1
  487. package/dist/upgrade.js +22 -13
  488. package/dist/upgrade.js.map +1 -1
  489. package/dist/workgraph/containment.js +18 -18
  490. package/dist/workgraph/relations.js +2 -2
  491. package/dist/worktree/list.d.ts +1 -1
  492. package/dist/worktree/list.d.ts.map +1 -1
  493. package/dist/worktree/list.js +19 -21
  494. package/dist/worktree/list.js.map +1 -1
  495. package/dist/worktree-export.d.ts +18 -0
  496. package/dist/worktree-export.d.ts.map +1 -0
  497. package/dist/worktree-export.js +18 -0
  498. package/dist/worktree-export.js.map +1 -0
  499. package/migrations/drizzle-agent-registry/20260412000000_initial-global-agent-registry/migration.sql +29 -0
  500. package/migrations/drizzle-brain/20260601000001_t11522-brain-task-observations/migration.sql +28 -0
  501. package/migrations/drizzle-brain/20260601000002_t11522-inline-only-brain-tables/migration.sql +75 -0
  502. package/migrations/drizzle-cleo-global/20260531000001_t11363-consolidation-cleo-global/migration.sql +49 -144
  503. package/migrations/drizzle-cleo-global/20260531000001_t11363-consolidation-cleo-global/snapshot.json +8 -8
  504. package/migrations/drizzle-cleo-global/20260531000002_t11546-brain-usage-log/migration.sql +16 -0
  505. package/migrations/drizzle-cleo-global/20260601000001_t11544-skills-usage-project-id/migration.sql +12 -0
  506. package/migrations/drizzle-cleo-global/20260602000001_t11622-agent-registry-rename/migration.sql +80 -0
  507. package/migrations/drizzle-cleo-project/20260531000001_t11363-consolidation-cleo-project/migration.sql +26 -167
  508. package/migrations/drizzle-cleo-project/20260531000001_t11363-consolidation-cleo-project/snapshot.json +8 -8
  509. package/migrations/drizzle-cleo-project/20260531000002_t11546-brain-usage-log/migration.sql +21 -0
  510. package/migrations/drizzle-cleo-project/20260601000001_t11549-agent-credentials-brain-release-links/migration.sql +49 -0
  511. package/migrations/drizzle-cleo-project/20260601000002_t11538-project-nexus-graph/migration.sql +140 -0
  512. package/migrations/drizzle-cleo-project/20260602000002_t11649-token-usage-transport-mcp/migration.sql +146 -0
  513. package/migrations/drizzle-conduit/20260601000003_t11523-conduit-inline-schema/migration.sql +82 -0
  514. package/migrations/drizzle-nexus/20260421200001_t1165-baseline-reset/migration.sql +26 -8
  515. package/migrations/drizzle-nexus/20260601000001_t11545-nexus-relation-weights-partition/migration.sql +97 -0
  516. package/package.json +44 -12
  517. package/scripts/install-supervisor-binary.mjs +50 -201
  518. package/scripts/napi-binary-picker.mjs +267 -0
  519. package/dist/agents/agent-registry.d.ts.map +0 -1
  520. package/dist/agents/agent-registry.js.map +0 -1
  521. package/dist/store/data-safety.d.ts +0 -92
  522. package/dist/store/data-safety.d.ts.map +0 -1
  523. package/dist/store/data-safety.js +0 -274
  524. package/dist/store/data-safety.js.map +0 -1
  525. package/dist/store/schema/cleo-global/signaldock.d.ts.map +0 -1
  526. package/dist/store/schema/cleo-global/signaldock.js.map +0 -1
  527. package/dist/store/schema/cleo-project/telemetry.d.ts.map +0 -1
  528. package/dist/store/schema/cleo-project/telemetry.js.map +0 -1
  529. package/dist/store/schema/signaldock-schema.d.ts.map +0 -1
  530. package/dist/store/schema/signaldock-schema.js.map +0 -1
  531. package/dist/store/signaldock-sqlite.d.ts +0 -173
  532. package/dist/store/signaldock-sqlite.d.ts.map +0 -1
  533. package/dist/store/signaldock-sqlite.js +0 -445
  534. package/dist/store/signaldock-sqlite.js.map +0 -1
  535. package/migrations/drizzle-nexus/20260318205558_initial/migration.sql +0 -46
  536. package/migrations/drizzle-nexus/20260412000001_t529-nexus-graph-tables/migration.sql +0 -49
  537. package/migrations/drizzle-nexus/20260415000001_t622-project-registry-paths/migration.sql +0 -12
  538. package/migrations/drizzle-nexus/20260419000001_t998-nexus-plasticity/migration.sql +0 -13
  539. package/migrations/drizzle-nexus/20260423052640_t1077-add-user-profile-table/migration.sql +0 -16
  540. package/migrations/drizzle-nexus/20260423052640_t1077-add-user-profile-table/snapshot.json +0 -1531
  541. package/migrations/drizzle-nexus/20260424140538_t1148-add-sigils-table/migration.sql +0 -13
  542. package/migrations/drizzle-nexus/20260424140538_t1148-add-sigils-table/snapshot.json +0 -1652
  543. package/migrations/drizzle-nexus/20260504000001_t1839-fts5-nexus-symbols/migration.sql +0 -68
  544. package/migrations/drizzle-nexus/20260507135519_t9163-nexus-is-external/migration.sql +0 -20
  545. package/migrations/drizzle-nexus/20260507135519_t9163-nexus-is-external/snapshot.json +0 -1652
  546. package/migrations/drizzle-nexus/20260526222449_t11025-project-id-aliases/migration.sql +0 -16
  547. package/migrations/drizzle-signaldock/20260412000000_initial-global-signaldock/migration.sql +0 -209
  548. package/migrations/drizzle-signaldock/20260412000000_initial-global-signaldock/snapshot.json +0 -2060
@@ -1,66 +1,229 @@
1
1
  /**
2
- * SQLite store for nexus.db via drizzle-orm/node-sqlite + node:sqlite (DatabaseSync).
2
+ * SQLite store for the NEXUS domain via drizzle-orm/node-sqlite + node:sqlite
3
+ * (DatabaseSync).
3
4
  *
4
- * Separate database from tasks.db and brain.db for cross-project registry
5
- * and audit infrastructure. Follows the same singleton + WAL + migration
6
- * pattern as memory-sqlite.ts.
5
+ * ## Dual-scope residency split (ADR-090 · T11648 runtime read half)
7
6
  *
8
- * nexus.db lives in ~/.cleo/ (global home) rather than per-project .cleo/,
9
- * since it stores cross-project data.
7
+ * The nexus domain is now SPLIT across the two consolidated `cleo.db` files:
10
8
  *
9
+ * - **GRAPH (project scope):** the four code-graph tables (`nexus_nodes`,
10
+ * `nexus_relations`, `nexus_contracts`, `nexus_code_index`) + the
11
+ * `nexus_relation_weights` plasticity sibling live in
12
+ * `<projectRoot>/.cleo/cleo.db` — the portable per-project living brain.
13
+ * This is where exodus WRITES them (`resolveTableTargetScope` in
14
+ * `store/exodus/table-name-map.ts`), so the runtime READS them from there too.
15
+ * - **REGISTRY/identity (global scope):** the six cross-project tables
16
+ * (`nexus_project_registry`, `nexus_project_id_aliases`, `nexus_user_profile`,
17
+ * `nexus_sigils`, `nexus_audit_log`, `nexus_schema_meta`) stay in the GLOBAL
18
+ * `cleo.db` under {@link getCleoHome} (ADR-090 §2.2).
19
+ *
20
+ * `getNexusDb()` opens the PROJECT scope as `main` (via {@link openDualScopeDb}
21
+ * ('project')) and ATTACHes the GLOBAL `cleo.db` under
22
+ * {@link NEXUS_GLOBAL_ATTACH_ALIAS}. SQLite's bare-name resolution then routes
23
+ * each query to the correct file with ZERO accessor changes: graph tables exist
24
+ * in `main` (project) and resolve there; registry/identity tables are absent
25
+ * from `main` and fall through to the attached GLOBAL db. The GLOBAL db also
26
+ * carries empty graph tables (frozen T11363 migration leftovers), correctly
27
+ * SHADOWED by `main`.
28
+ *
29
+ * ### Why this fix (T11648)
30
+ *
31
+ * T11538/T11539 moved the graph schema to PROJECT scope and routed exodus there,
32
+ * but the runtime READ accessors still opened the GLOBAL handle — so after a
33
+ * `cleo exodus migrate` the project held 24k+ `nexus_nodes` while the runtime
34
+ * read 0 from global (`cleo nexus search-code` / `context` returned empty). This
35
+ * module is the deferred runtime half: it reads the graph from the SAME scope
36
+ * exodus writes it.
37
+ *
38
+ * This preserves the prior guarantees:
39
+ * - Every nexus-domain open flows through the single pragma SSoT (ADR-068/069).
40
+ * - DB Open Guard Gate 3 (`scripts/lint-no-direct-db-open.mjs`) stays green: the
41
+ * only native open is inside `dual-scope-db.ts` (the ATTACH below is a SQL
42
+ * statement on an already-chokepointed handle, not a `new DatabaseSync(`).
43
+ *
44
+ * ## COMPLETE-CUTOVER to prefixed `nexus_*` tables (T11578 · AC3)
45
+ *
46
+ * The nexus runtime READ + WRITE path targets PREFIXED consolidated tables. The
47
+ * five PROJECT graph tables are owned by the consolidated cleo-project migration;
48
+ * the six GLOBAL registry/identity tables by the consolidated cleo-global
49
+ * migration. The former legacy drop/rebuild (`establishLegacyNexusSchema`) and
50
+ * BARE registry tables (`project_registry`, …) are GONE. The runtime schema
51
+ * barrel `schema/nexus-schema.ts` maps every export symbol to its prefixed
52
+ * physical name — accessors need ZERO change.
53
+ *
54
+ * The `drizzle-nexus` migration set carries ONLY the delta the consolidated
55
+ * migration cannot model: the `nexus_symbols_fts` FTS5 virtual table + its three
56
+ * `nexus_nodes` triggers, the `nexus_relation_weights` plasticity-partition
57
+ * sibling (T11545), and the `_nexus_meta` health-probe table (the reconcile
58
+ * sentinel). The destructive half of the plasticity partition is applied
59
+ * idempotently at open by `ensureNexusRelationWeights` (a no-op for the
60
+ * already-narrow project-scope `nexus_relations`).
61
+ *
62
+ * @adr ADR-036 — registry/identity is global-only (relaxed for the 4 graph
63
+ * tables by ADR-090 §2.4).
64
+ * @adr ADR-090 — nexus code-graph residency global→project scope split.
11
65
  * @task T5365
66
+ * @task T11524 - E6-L4: route getNexusDb through the dual-scope chokepoint
67
+ * @task T11578 - AC3: COMPLETE-CUTOVER nexus runtime → prefixed nexus_* tables
68
+ * @task T11648 - ADR-090 runtime read half: route graph reads to project scope
12
69
  */
13
- import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
14
- import { dirname, join } from 'node:path';
15
- import { readMigrationFiles } from 'drizzle-orm/migrator';
70
+ import { copyFileSync, existsSync } from 'node:fs';
71
+ import { join } from 'node:path';
16
72
  import { drizzle } from 'drizzle-orm/node-sqlite';
17
73
  import { getLogger } from '../logger.js';
18
74
  import { getCleoHome } from '../paths.js';
75
+ // E6-L4 (T11524): dual-scope chokepoint — the nexus domain now opens the
76
+ // consolidated GLOBAL `cleo.db` through here. openDualScopeDb manages the
77
+ // DatabaseSync lifecycle, pragmas, and consolidated migrations. We extract the
78
+ // native handle and re-wrap it with the legacy nexus-schema drizzle instance so
79
+ // existing callers (nexusSchema.* queries) compile and run without change.
80
+ import { openDualScopeDb, resolveDualScopeDbPath } from './dual-scope-db.js';
19
81
  import { ensureColumns, migrateWithRetry, reconcileJournal } from './migration-manager.js';
20
82
  import { resolveCorePackageMigrationsFolder } from './resolve-migrations-folder.js';
21
83
  import * as nexusSchema from './schema/nexus-schema.js';
22
- import { isSqliteBusy, openNativeDatabase } from './sqlite.js';
23
- /** Database file name within ~/.cleo/ directory. */
24
- const DB_FILENAME = 'nexus.db';
84
+ // isSqliteBusy is a pure predicate with no node:sqlite dependency — import it
85
+ // from its canonical leaf home (with-retry.ts) rather than sqlite.ts so this
86
+ // module no longer pulls the native open path.
87
+ import { isSqliteBusy } from './with-retry.js';
25
88
  /** Schema version for newly created nexus databases. Single source of truth. */
26
89
  export const NEXUS_SCHEMA_VERSION = '1.0.0';
27
90
  /** Singleton state for lazy initialization. */
28
91
  let _nexusDb = null;
29
92
  let _nexusNativeDb = null;
30
93
  let _nexusDbPath = null;
94
+ /**
95
+ * The GLOBAL registry path the current singleton's ATTACH targets (ADR-090 ·
96
+ * T11648). When `getCleoHome()` changes (e.g. tests mutating `CLEO_HOME`), the
97
+ * attached global differs AND must be migrated via `openDualScopeDb('global')` —
98
+ * which only runs on the init path. So a registry-path change forces a full
99
+ * singleton reset rather than a cheap re-attach to an unmigrated file.
100
+ */
101
+ let _nexusRegistryPath = null;
31
102
  /** Guard against concurrent initialization (async migration). */
32
103
  let _nexusInitPromise = null;
33
104
  /**
34
- * Returns the global-tier nexus.db path. ALWAYS under `getCleoHome()`.
105
+ * SQLite ATTACH alias under which the GLOBAL consolidated `cleo.db` is mounted
106
+ * into the PROJECT-scope nexus handle so the cross-project registry/identity
107
+ * tables (`nexus_project_registry`, `nexus_user_profile`, `nexus_sigils`,
108
+ * `nexus_project_id_aliases`, `nexus_audit_log`, `nexus_schema_meta`) stay
109
+ * reachable by their BARE names (ADR-090 · T11648).
110
+ *
111
+ * SQLite resolves a bare table name against `main` first, then attached
112
+ * databases in attach order. The PROJECT `cleo.db` (`main`) physically carries
113
+ * ONLY the five graph tables (`nexus_nodes`, `nexus_relations`,
114
+ * `nexus_contracts`, `nexus_code_index`, `nexus_relation_weights`), so those
115
+ * bare names resolve to the populated project graph; the registry/identity
116
+ * tables — absent from `main` — fall through to this attached GLOBAL db. The
117
+ * GLOBAL db ALSO carries empty graph tables (frozen T11363 migration leftovers),
118
+ * but those are correctly SHADOWED by `main` and never read or written.
119
+ */
120
+ const NEXUS_GLOBAL_ATTACH_ALIAS = 'nexus_global';
121
+ /**
122
+ * Returns the nexus GRAPH DB path — the consolidated PROJECT `cleo.db`
123
+ * (`<projectRoot>/.cleo/cleo.db`), where the four code-graph tables
124
+ * (`nexus_nodes`, `nexus_relations`, `nexus_contracts`, `nexus_code_index`) and
125
+ * the `nexus_relation_weights` plasticity sibling physically reside post the
126
+ * ADR-090 residency split (T11538/T11539).
127
+ *
128
+ * ## ADR-090 residency move — runtime read half (T11648)
35
129
  *
36
- * nexus.db is a cross-project registry and must live in the global CLEO
37
- * home directory (`~/.local/share/cleo/` on Linux via XDG). It is NEVER
38
- * written to a per-project `.cleo/` directory.
130
+ * Exodus WRITES the graph tables to PROJECT scope (per `resolveTableTargetScope`
131
+ * in `store/exodus/table-name-map.ts`); this resolver routes the runtime READ
132
+ * path to the SAME scope so the migrated graph is visible. Previously this
133
+ * returned the GLOBAL `cleo.db` (`resolveDualScopeDbPath('global')`), which left
134
+ * `cleo nexus search-code` / `context` reading the empty global graph tables —
135
+ * the T11538/T11539 runtime-half gap this fix closes. The cross-project
136
+ * registry/identity tables stay GLOBAL and are reached via the
137
+ * {@link NEXUS_GLOBAL_ATTACH_ALIAS} attach (see {@link getNexusRegistryDbPath}).
39
138
  *
139
+ * @param cwd - Optional working directory used to resolve the owning project
140
+ * root (forwarded to {@link resolveDualScopeDbPath}('project', cwd)).
40
141
  * @task T307
41
142
  * @epic T299
42
- * @why ADR-036 §Decision/Global-Tier: nexus.db is global-only. This guard
43
- * throws immediately if path resolution ever drifts outside getCleoHome(),
44
- * preventing silent creation of project-tier stray nexus.db files.
45
- * @throws {Error} If the resolved path is not under `getCleoHome()` — this
46
- * indicates a code path that bypasses canonical path resolution and is a
47
- * bug that must be fixed rather than silently tolerated.
143
+ * @task T11648 (ADR-090 runtime read half route graph reads to project scope)
144
+ * @why ADR-090 §2.1/§2.4 supersedes ADR-036's global-only assertion FOR THE
145
+ * GRAPH TABLES ONLY. The graph is per-project and must live in the portable
146
+ * `.cleo/cleo.db`; the registry/identity tables remain global-asserted in
147
+ * {@link getNexusRegistryDbPath}.
48
148
  */
49
- export function getNexusDbPath() {
149
+ export function getNexusDbPath(cwd) {
150
+ return resolveDualScopeDbPath('project', cwd);
151
+ }
152
+ /**
153
+ * Returns the cross-project nexus REGISTRY/identity DB path — the consolidated
154
+ * GLOBAL `cleo.db` under `getCleoHome()`.
155
+ *
156
+ * The registry/identity tables (`nexus_project_registry`,
157
+ * `nexus_project_id_aliases`, `nexus_user_profile`, `nexus_sigils`,
158
+ * `nexus_audit_log`, `nexus_schema_meta`) are genuinely global (ADR-090 §2.2)
159
+ * and MUST stay under `getCleoHome()`. The ADR-036 global-only assertion is
160
+ * retained here as defence-in-depth (it was relaxed only for the four graph
161
+ * tables, now homed in PROJECT scope via {@link getNexusDbPath}).
162
+ *
163
+ * @task T11648 (ADR-090 — registry stays global-asserted)
164
+ * @adr ADR-036 — registry/identity is global-only.
165
+ * @throws {Error} If the resolved path is not under `getCleoHome()`.
166
+ */
167
+ export function getNexusRegistryDbPath() {
50
168
  const cleoHome = getCleoHome();
51
- const nexusPath = join(cleoHome, DB_FILENAME);
52
- // Guard: the resolved path MUST be under the global tier.
53
- // Under normal operation this invariant is always satisfied because we
54
- // build nexusPath from cleoHome above. The assertion catches hypothetical
55
- // future regressions where getCleoHome() is monkey-patched or join()
56
- // produces an unexpected result on exotic platforms.
57
- if (!nexusPath.startsWith(cleoHome)) {
58
- throw new Error(`BUG: getNexusDbPath() resolved to "${nexusPath}" which is NOT under ` +
59
- `getCleoHome() ("${cleoHome}"). nexus.db is global-only per ADR-036. ` +
60
- `This indicates a code path that bypasses canonical path resolution — ` +
61
- `fix the caller, do not suppress this error.`);
169
+ const registryPath = resolveDualScopeDbPath('global');
170
+ // Guard: the registry/identity home MUST be under the global tier (ADR-036).
171
+ if (!registryPath.startsWith(cleoHome)) {
172
+ throw new Error(`BUG: getNexusRegistryDbPath() resolved to "${registryPath}" which is NOT ` +
173
+ `under getCleoHome() ("${cleoHome}"). The nexus registry/identity tables ` +
174
+ `are global-only per ADR-036/ADR-090 §2.2. This indicates a code path that ` +
175
+ `bypasses canonical path resolution — fix the caller, do not suppress this error.`);
176
+ }
177
+ return registryPath;
178
+ }
179
+ /**
180
+ * Idempotently ATTACH the GLOBAL consolidated `cleo.db` into a PROJECT-scope
181
+ * nexus handle under {@link NEXUS_GLOBAL_ATTACH_ALIAS}, so the cross-project
182
+ * registry/identity tables resolve by their bare names (ADR-090 · T11648).
183
+ *
184
+ * Safe to call repeatedly: if the alias is already attached (a sibling domain
185
+ * opened the same shared project handle first, or a prior nexus open ran this),
186
+ * the duplicate `ATTACH` throws and is swallowed. The global `cleo.db` is the
187
+ * canonical registry home; we ensure its directory exists via the global open
188
+ * resolver before attaching.
189
+ *
190
+ * @param nativeDb - The open PROJECT-scope `DatabaseSync` handle.
191
+ * @task T11648
192
+ */
193
+ function ensureGlobalRegistryAttached(nativeDb) {
194
+ const globalPath = getNexusRegistryDbPath();
195
+ const escaped = globalPath.replace(/'/g, "''");
196
+ // Inspect the current attach (if any). `PRAGMA database_list` reports the alias
197
+ // + the file each schema is bound to. The registry home is resolved fresh from
198
+ // `getNexusRegistryDbPath()` (which reads `getCleoHome()`), so it can change
199
+ // between opens — e.g. tests that mutate `CLEO_HOME` while sharing the cwd-keyed
200
+ // project handle, or a sibling domain that re-opened the shared project DB. A
201
+ // stale ATTACH would silently read a prior registry. To stay deterministic we
202
+ // DETACH any existing `nexus_global` (unless it is provably the same file) and
203
+ // re-ATTACH the current registry path.
204
+ const existing = nativeDb.prepare('PRAGMA database_list').all().find((row) => row.name === NEXUS_GLOBAL_ATTACH_ALIAS);
205
+ if (existing) {
206
+ // Fast path: the alias is already bound to exactly the current registry file.
207
+ if (existing.file === globalPath)
208
+ return;
209
+ // Otherwise re-bind. DETACH is best-effort; the subsequent ATTACH surfaces a
210
+ // clear error rather than silently leaving a stale binding.
211
+ try {
212
+ nativeDb.exec(`DETACH DATABASE ${NEXUS_GLOBAL_ATTACH_ALIAS}`);
213
+ }
214
+ catch {
215
+ // Alias still bound (e.g. an open statement) — the ATTACH below would throw
216
+ // "database nexus_global is already in use"; treat the existing binding as
217
+ // authoritative only when its file matches, which we already returned for.
218
+ return;
219
+ }
62
220
  }
63
- return nexusPath;
221
+ // The global cleo.db is created/migrated by the global dual-scope open path
222
+ // (sibling domains: skills/agent-registry/global-brain). ATTACH only needs the
223
+ // file to exist; SQLite creates it if absent, but it will then lack the
224
+ // consolidated registry schema. Registry readers tolerate a missing table
225
+ // (try/catch), so a bare ATTACH is sufficient and non-fatal here.
226
+ nativeDb.exec(`ATTACH DATABASE '${escaped}' AS ${NEXUS_GLOBAL_ATTACH_ALIAS}`);
64
227
  }
65
228
  /**
66
229
  * Resolve the absolute path to the drizzle-nexus migrations folder inside
@@ -109,9 +272,8 @@ export function getNestedNexusSentinelPath() {
109
272
  * warning via the canonical pino logger if found.
110
273
  *
111
274
  * The function is non-blocking and non-throwing — it never alters the
112
- * outcome of the surrounding `getNexusDb()` open. The canonical flat
113
- * `nexus.db` open proceeds normally regardless of whether the nested debris
114
- * is present.
275
+ * outcome of the surrounding `getNexusDb()` open. The canonical consolidated
276
+ * open proceeds normally regardless of whether the nested debris is present.
115
277
  *
116
278
  * Idempotency: the first call for a given nested path emits the warning;
117
279
  * subsequent calls within the same process are no-ops. Tests can reset the
@@ -145,7 +307,7 @@ export function detectAndWarnOnNestedNexus() {
145
307
  migrationCommand: 'node scripts/migrate-nested-nexus.mjs',
146
308
  adr: 'ADR-086',
147
309
  task: 'T10321',
148
- }, 'Detected nested-nexus structural bug — canonical flat nexus.db is in use; ' +
310
+ }, 'Detected nested-nexus structural bug — canonical consolidated cleo.db is in use; ' +
149
311
  'nested duplicates at <cleoHome>/nexus/ are migration debris. Run the ' +
150
312
  'migration script to remove them.');
151
313
  return true;
@@ -254,17 +416,151 @@ function ensureNexusFts5(nativeDb) {
254
416
  `);
255
417
  }
256
418
  /**
257
- * Run drizzle migrations to create/update nexus.db tables.
419
+ * Idempotent safety net + partition completer for `nexus_relation_weights`
420
+ * (T11545 · ADR-090 §5.3 · T11578 · AC3).
421
+ *
422
+ * The Hebbian plasticity columns were partitioned out of `nexus_relations` into
423
+ * this sibling 1:1 table. After the AC3 cutover the consolidated cleo-global
424
+ * migration (T11363) owns `nexus_relations` but still creates it with the THREE
425
+ * inline plasticity columns (`weight`, `last_accessed_at`, `co_accessed_count`)
426
+ * — the T11363 shape predates the partition. This safety net completes the
427
+ * partition idempotently at runtime (the destructive column-DROP is kept OUT of
428
+ * the journaled migration SQL, where re-runs would throw):
429
+ *
430
+ * 1. CREATE the sibling table (+ indexes) — covers fresh + pre-partition DBs.
431
+ * 2. When the inline columns are still present on `nexus_relations`, BACKFILL
432
+ * any non-default plasticity state into the sibling table so no weights are
433
+ * lost, then DROP the three inline columns so the structural graph row is
434
+ * narrow (matching `nexus-schema.ts` + the must-pass fresh-init test).
435
+ *
436
+ * Every statement is guarded by IF-NOT-EXISTS / column-presence probes; safe to
437
+ * re-run on every open (the DROP only fires while the inline columns exist).
438
+ *
439
+ * @task T11545
440
+ * @task T11578
441
+ */
442
+ function ensureNexusRelationWeights(nativeDb) {
443
+ // Only meaningful once the parent graph table exists.
444
+ if (!tableExists(nativeDb, 'nexus_relations'))
445
+ return;
446
+ nativeDb.exec(`
447
+ CREATE TABLE IF NOT EXISTS nexus_relation_weights (
448
+ relation_id TEXT PRIMARY KEY NOT NULL,
449
+ weight REAL DEFAULT 0.0 NOT NULL,
450
+ last_accessed_at TEXT,
451
+ co_accessed_count INTEGER DEFAULT 0 NOT NULL
452
+ )
453
+ `);
454
+ nativeDb.exec(`CREATE INDEX IF NOT EXISTS idx_nexus_relation_weights_last_accessed ON nexus_relation_weights (last_accessed_at)`);
455
+ nativeDb.exec(`CREATE INDEX IF NOT EXISTS idx_nexus_relation_weights_weight ON nexus_relation_weights (weight)`);
456
+ // Detect the inline plasticity columns the consolidated T11363 `nexus_relations`
457
+ // still carries. When absent, the partition is already complete — nothing to do.
458
+ const relCols = nativeDb.prepare('PRAGMA table_info(nexus_relations)').all();
459
+ const hasInlinePlasticity = relCols.some((c) => c.name === 'weight');
460
+ if (!hasInlinePlasticity)
461
+ return;
462
+ // Backfill any non-default plasticity state before dropping the columns
463
+ // (pristine rows are intentionally NOT copied — absence == weight 0.0).
464
+ nativeDb.exec(`
465
+ INSERT OR IGNORE INTO nexus_relation_weights (relation_id, weight, last_accessed_at, co_accessed_count)
466
+ SELECT id, COALESCE(weight, 0.0), last_accessed_at, COALESCE(co_accessed_count, 0)
467
+ FROM nexus_relations
468
+ WHERE COALESCE(weight, 0.0) > 0.0
469
+ OR last_accessed_at IS NOT NULL
470
+ OR COALESCE(co_accessed_count, 0) > 0
471
+ `);
472
+ // Complete the partition: rebuild `nexus_relations` to the narrow structural
473
+ // shape (T11578 · AC3). A plain `ALTER TABLE ... DROP COLUMN last_accessed_at`
474
+ // is REJECTED by SQLite because the consolidated T11363 table carries
475
+ // `CHECK ("last_accessed_at" GLOB …)` — a column referenced by a CHECK cannot
476
+ // be dropped without a full table rebuild (the T11545 migration note). So we
477
+ // CREATE the narrow table, copy the structural columns, drop the old table, and
478
+ // rename. The `type`/`indexed_at` CHECKs + indexes are preserved; only the three
479
+ // plasticity columns + their CHECK + the plasticity index are removed.
480
+ //
481
+ // The FTS triggers fire on `nexus_nodes` (not `nexus_relations`), so they are
482
+ // unaffected. FKs are disabled for the rebuild then restored to the dual-scope
483
+ // SSoT state (T10314 idempotent-pragma contract).
484
+ const fkRow = nativeDb.prepare('PRAGMA foreign_keys').get();
485
+ const fkWasOn = fkRow?.foreign_keys === 1;
486
+ nativeDb.exec('PRAGMA foreign_keys=OFF');
487
+ try {
488
+ nativeDb.exec('DROP INDEX IF EXISTS idx_nexus_relations_last_accessed');
489
+ nativeDb.exec(`
490
+ CREATE TABLE nexus_relations__narrow (
491
+ id text PRIMARY KEY,
492
+ project_id text NOT NULL,
493
+ source_id text NOT NULL,
494
+ target_id text NOT NULL,
495
+ type text NOT NULL,
496
+ confidence real NOT NULL,
497
+ reason text,
498
+ step integer,
499
+ indexed_at text DEFAULT (datetime('now')) NOT NULL,
500
+ CHECK ("type" IN ('contains', 'defines', 'imports', 'accesses', 'calls', 'extends', 'implements', 'method_overrides', 'method_implements', 'has_method', 'has_property', 'member_of', 'step_in_process', 'handles_route', 'fetches', 'handles_tool', 'entry_point_of', 'wraps', 'queries', 'documents', 'applies_to', 'co_changed', 'co_cited_in_task')),
501
+ CHECK ("indexed_at" IS NULL OR "indexed_at" GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*')
502
+ )
503
+ `);
504
+ nativeDb.exec(`
505
+ INSERT INTO nexus_relations__narrow
506
+ (id, project_id, source_id, target_id, type, confidence, reason, step, indexed_at)
507
+ SELECT id, project_id, source_id, target_id, type, confidence, reason, step, indexed_at
508
+ FROM nexus_relations
509
+ `);
510
+ nativeDb.exec('DROP TABLE nexus_relations');
511
+ nativeDb.exec('ALTER TABLE nexus_relations__narrow RENAME TO nexus_relations');
512
+ // Recreate the structural indexes the rebuild dropped (match T11363 names).
513
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_project ON nexus_relations (project_id)');
514
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_source ON nexus_relations (source_id)');
515
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_target ON nexus_relations (target_id)');
516
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_type ON nexus_relations (type)');
517
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_project_type ON nexus_relations (project_id, type)');
518
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_source_type ON nexus_relations (source_id, type)');
519
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_target_type ON nexus_relations (target_id, type)');
520
+ nativeDb.exec('CREATE INDEX IF NOT EXISTS idx_nexus_relations_confidence ON nexus_relations (confidence)');
521
+ }
522
+ finally {
523
+ nativeDb.exec(`PRAGMA foreign_keys=${fkWasOn ? 'ON' : 'OFF'}`);
524
+ }
525
+ }
526
+ /**
527
+ * Apply the nexus-domain DELTA migration + idempotent safety nets on top of the
528
+ * PREFIXED consolidated graph tables the PROJECT dual-scope chokepoint already
529
+ * created (T11578 · AC3 · ADR-090 T11648).
258
530
  *
259
- * Uses IMMEDIATE transactions to prevent concurrent migration races.
260
- * Follows the same pattern as memory-sqlite.ts runBrainMigrations().
531
+ * ## COMPLETE-CUTOVER (T11578 · AC3) + residency move (ADR-090 · T11648)
532
+ *
533
+ * The consolidated cleo-project migration OWNS the five PREFIXED graph tables
534
+ * in the PROJECT `cleo.db` `main` (`nexus_nodes`, `nexus_relations`,
535
+ * `nexus_contracts`, `nexus_code_index`, `nexus_relation_weights`); the
536
+ * cross-project registry/identity tables are owned by the cleo-global migration
537
+ * and reached through the {@link NEXUS_GLOBAL_ATTACH_ALIAS} attach. The runtime
538
+ * no longer drops/rebuilds a legacy shape (former `establishLegacyNexusSchema`).
539
+ * The `drizzle-nexus` set carries ONLY the delta the consolidated migration
540
+ * cannot model:
541
+ * - the `nexus_relation_weights` sibling (plasticity partition, T11545),
542
+ * - the `nexus_symbols_fts` FTS5 virtual table + its three triggers (over the
543
+ * project-scope `nexus_nodes`), and
544
+ * - the `_nexus_meta` health-probe table (also the reconcile sentinel).
545
+ *
546
+ * The reconcile sentinel is `_nexus_meta` (a table the nexus migration ITSELF
547
+ * creates, NOT a consolidated-owned table). This keeps `reconcileJournal`
548
+ * Scenario 2 (orphan deletion) dormant until the nexus migration set is journaled
549
+ * — otherwise the consolidated PROJECT migration's journal entries (written FIRST
550
+ * by `openDualScopeDb`) would look like orphans and be deleted, corrupting the
551
+ * shared journal (mirrors the conduit `_conduit_meta` sentinel, AC4).
261
552
  *
262
553
  * @task T5365
554
+ * @task T11578
555
+ * @task T11648
263
556
  */
264
557
  function runNexusMigrations(nativeDb, db) {
265
558
  const migrationsFolder = resolveNexusMigrationsFolder();
266
- // If existing DB with pending migrations, create safety backup (cleo compat)
267
- if (tableExists(nativeDb, 'project_registry') && _nexusDbPath) {
559
+ // If existing DB with populated graph, create a safety backup (cleo compat).
560
+ // Sentinel is `nexus_nodes` the canonical project-scope graph table in
561
+ // `main` (ADR-090 · T11648); the former `nexus_project_registry` sentinel now
562
+ // lives in the attached GLOBAL db, not this project handle.
563
+ if (tableExists(nativeDb, 'nexus_nodes') && _nexusDbPath) {
268
564
  const backupPath = _nexusDbPath.replace(/\.db$/, '-pre-cleo.db.bak');
269
565
  if (!existsSync(backupPath)) {
270
566
  try {
@@ -275,103 +571,24 @@ function runNexusMigrations(nativeDb, db) {
275
571
  }
276
572
  }
277
573
  }
278
- // Bootstrap existing databases that predate drizzle migrations.
279
- // Mark baseline migration as already applied if tables exist but
280
- // __drizzle_migrations doesn't.
281
- if (tableExists(nativeDb, 'project_registry') && !tableExists(nativeDb, '__drizzle_migrations')) {
282
- const migrations = readMigrationFiles({ migrationsFolder });
283
- const baseline = migrations[0];
284
- if (baseline) {
285
- nativeDb
286
- .prepare(`CREATE TABLE IF NOT EXISTS "__drizzle_migrations" (id INTEGER PRIMARY KEY AUTOINCREMENT, hash text NOT NULL, created_at numeric)`)
287
- .run();
288
- nativeDb
289
- .prepare(`INSERT INTO "__drizzle_migrations" ("hash", "created_at") VALUES ('${baseline.hash}', ${baseline.folderMillis})`)
290
- .run();
291
- }
292
- }
293
- // T998: idempotent safety net for plasticity columns — covers pre-migration
294
- // nexus.db instances that were created before the drizzle migration runs.
295
- // ensureColumns is a no-op when the columns already exist.
296
- ensureColumns(nativeDb, 'nexus_relations', [
297
- { name: 'weight', ddl: 'real DEFAULT 0.0' },
298
- { name: 'last_accessed_at', ddl: 'text' },
299
- { name: 'co_accessed_count', ddl: 'integer DEFAULT 0' },
300
- ], 'nexus');
301
- // T1062: idempotent safety net for external module nodes — covers pre-migration
302
- // nexus.db instances that were created before the drizzle migration runs.
303
- // Unresolved imports are persisted as ExternalModule nodes with is_external=true.
304
- ensureColumns(nativeDb, 'nexus_nodes', [{ name: 'is_external', ddl: 'integer DEFAULT 0' }], 'nexus');
305
- // T1065: idempotent safety net for contracts table — covers pre-migration
306
- // nexus.db instances that were created before contracts extraction.
307
- // If the table doesn't exist after migrations, it will be created here.
308
- if (!tableExists(nativeDb, 'nexus_contracts')) {
309
- nativeDb
310
- .prepare(`CREATE TABLE IF NOT EXISTS nexus_contracts (
311
- contract_id TEXT PRIMARY KEY,
312
- project_id TEXT NOT NULL,
313
- type TEXT NOT NULL CHECK (type IN ('http', 'grpc', 'topic')),
314
- path TEXT NOT NULL,
315
- method TEXT,
316
- request_schema_json TEXT NOT NULL DEFAULT '{}',
317
- response_schema_json TEXT NOT NULL DEFAULT '{}',
318
- source_symbol_id TEXT,
319
- route_node_id TEXT,
320
- confidence REAL NOT NULL DEFAULT 1.0,
321
- description TEXT,
322
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
323
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
324
- )`)
325
- .run();
326
- // Create indexes
327
- nativeDb
328
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_project ON nexus_contracts(project_id)')
329
- .run();
330
- nativeDb
331
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_type ON nexus_contracts(type)')
332
- .run();
333
- nativeDb
334
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_path ON nexus_contracts(path)')
335
- .run();
336
- nativeDb
337
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_method ON nexus_contracts(method)')
338
- .run();
339
- nativeDb
340
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_project_type ON nexus_contracts(project_id, type)')
341
- .run();
342
- nativeDb
343
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_source_symbol ON nexus_contracts(source_symbol_id)')
344
- .run();
345
- nativeDb
346
- .prepare('CREATE INDEX IF NOT EXISTS idx_nexus_contracts_created ON nexus_contracts(created_at)')
347
- .run();
348
- }
349
- // T1839: idempotent safety net for FTS5 virtual table + triggers.
350
- // Covers existing nexus.db instances that were created before this migration.
351
- // Each statement uses nativeDb.exec() (not prepare().run()) so that the entire
352
- // DDL block executes atomically without the node:sqlite first-statement-only limit.
353
- ensureNexusFts5(nativeDb);
354
- // T9183: Reconcile partial migrations before running migrate (matches brain.db
355
- // pattern in memory-sqlite.ts). When a legacy nexus.db has columns from prior
356
- // ensureColumns() repair but no journal entries, reconcileJournal Scenario 3
357
- // marks those migrations applied via DDL probe so migrateWithRetry doesn't
358
- // hit duplicate-column errors on the legacy-upgrade path.
359
- reconcileJournal(nativeDb, migrationsFolder, 'project_registry', 'nexus');
360
- // Run pending migrations via migrateWithRetry which catches duplicate-column
361
- // errors and triggers Scenario 3 reconciliation as a belt-and-suspenders
362
- // safety net (T9183, matches memory-sqlite.ts:99 brain pattern).
574
+ // Reconcile the Drizzle journal first so existing DBs don't try to re-run the
575
+ // comment-only baseline marker, and so removed legacy per-table migrations are
576
+ // treated as true orphans (Sub-case B) rather than re-run. Sentinel =
577
+ // `_nexus_meta` (created by the nexus delta migration itself — NOT a
578
+ // consolidated-owned table; see the doc note above).
579
+ reconcileJournal(nativeDb, migrationsFolder, '_nexus_meta', 'nexus');
580
+ // Run the nexus DELTA migration (FTS5 quartet + nexus_relation_weights +
581
+ // `_nexus_meta`). Wrapped in a busy retry — the GLOBAL `cleo.db` handle is
582
+ // shared with sibling domains, so a concurrent open may briefly hold a lock.
363
583
  const MAX_RETRIES = 5;
364
584
  const BASE_DELAY_MS = 100;
365
585
  const MAX_DELAY_MS = 2000;
366
586
  let lastError;
367
587
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
368
588
  try {
369
- migrateWithRetry(db, migrationsFolder, nativeDb, 'project_registry', 'nexus');
370
- // T1062: post-migration safety net for is_external — ensures the column exists
371
- // on fresh DBs where ensureColumns ran before the table was created, and on
372
- // old DBs where this column was added after initial schema creation.
373
- ensureColumns(nativeDb, 'nexus_nodes', [{ name: 'is_external', ddl: 'integer DEFAULT 0' }], 'nexus');
374
- return;
589
+ migrateWithRetry(db, migrationsFolder, nativeDb, '_nexus_meta', 'nexus');
590
+ lastError = undefined;
591
+ break;
375
592
  }
376
593
  catch (err) {
377
594
  if (!isSqliteBusy(err) || attempt === MAX_RETRIES)
@@ -381,42 +598,141 @@ function runNexusMigrations(nativeDb, db) {
381
598
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Math.round(delay));
382
599
  }
383
600
  }
384
- /* c8 ignore next */
385
- throw lastError;
601
+ /* c8 ignore next 1 */
602
+ if (lastError)
603
+ throw lastError;
604
+ // Complete the plasticity partition: create the sibling table (the migration
605
+ // also creates it idempotently), backfill, then DROP the inline plasticity
606
+ // columns the consolidated T11363 `nexus_relations` still carries (T11578 · AC3).
607
+ ensureNexusRelationWeights(nativeDb);
608
+ // T1062 safety net: ensure `nexus_nodes.is_external` exists (the consolidated
609
+ // T11363 shape already includes it; this is a belt-and-suspenders no-op).
610
+ ensureColumns(nativeDb, 'nexus_nodes', [{ name: 'is_external', ddl: 'integer DEFAULT 0' }], 'nexus');
611
+ // T1839 safety net: (re)build the FTS5 virtual table + triggers and backfill
612
+ // existing `nexus_nodes` rows. Idempotent — a no-op once the index exists.
613
+ ensureNexusFts5(nativeDb);
386
614
  }
387
615
  /**
388
- * Initialize the nexus.db SQLite database (lazy, singleton).
389
- * Creates the database file and tables if they don't exist.
390
- * Returns the drizzle ORM instance (async via sqlite-proxy).
616
+ * Initialize the consolidated PROJECT `cleo.db` (the nexus GRAPH home) with the
617
+ * GLOBAL `cleo.db` ATTACHed for registry/identity reads, plus the nexus DELTA
618
+ * schema within it (lazy, singleton).
619
+ *
620
+ * ## ADR-090 residency move — runtime read half (T11648)
621
+ *
622
+ * The four code-graph tables (`nexus_nodes`, `nexus_relations`, `nexus_contracts`,
623
+ * `nexus_code_index`) + the `nexus_relation_weights` plasticity sibling now live
624
+ * in PROJECT scope (`<projectRoot>/.cleo/cleo.db`) — that is where exodus WRITES
625
+ * them. The runtime READ path therefore opens the PROJECT scope as `main` so the
626
+ * graph is visible (previously it opened GLOBAL and read empty graph tables —
627
+ * the T11538/T11539 gap). The cross-project registry/identity tables stay GLOBAL;
628
+ * we open GLOBAL first (so its consolidated migration creates them) then ATTACH
629
+ * it under {@link NEXUS_GLOBAL_ATTACH_ALIAS} so the registry/identity accessors —
630
+ * which reference bare names (`nexus_project_registry`, `nexus_user_profile`,
631
+ * `nexus_sigils`, `nexus_project_id_aliases`, `nexus_audit_log`,
632
+ * `nexus_schema_meta`) — resolve via SQLite's bare-name fall-through to the
633
+ * attached GLOBAL db. ADR-036's global-only assertion is relaxed for the four
634
+ * graph tables ONLY (registry stays global-asserted in {@link getNexusRegistryDbPath}).
391
635
  *
392
- * Uses a promise guard so concurrent callers wait for the same
393
- * initialization to complete (migrations are async).
636
+ * Uses a promise guard so concurrent callers wait for the same initialization to
637
+ * complete (migrations are async).
638
+ *
639
+ * @task T307
640
+ * @task T11524 (E6-L4 — dual-scope chokepoint delegation)
641
+ * @task T11578 (AC3 — prefixed `nexus_*` tables)
642
+ * @task T11648 (ADR-090 runtime read half — project-scope graph + global attach)
394
643
  */
395
644
  export async function getNexusDb() {
396
645
  const requestedPath = getNexusDbPath();
397
- // If singleton exists but points to different path, reset it
646
+ const requestedRegistryPath = getNexusRegistryDbPath();
647
+ // If singleton exists but points to a different PROJECT graph path (e.g.
648
+ // CLEO_DIR changed between tests), reset it.
398
649
  if (_nexusDb && _nexusDbPath !== requestedPath) {
399
650
  resetNexusDbState();
400
651
  }
401
- if (_nexusDb)
402
- return _nexusDb;
652
+ // If the GLOBAL registry path changed (e.g. CLEO_HOME changed), reset fully so
653
+ // the init path runs `openDualScopeDb('global')` to MIGRATE the new registry
654
+ // file before the ATTACH — a cheap re-attach alone would bind an unmigrated
655
+ // global (→ "no such table: nexus_project_registry"). (ADR-090 · T11648)
656
+ if (_nexusDb && _nexusRegistryPath !== requestedRegistryPath) {
657
+ resetNexusDbState();
658
+ }
659
+ // Liveness guard (T11524): nexus shares the consolidated PROJECT `cleo.db`
660
+ // handle with the other project-tier domains (tasks/brain/conduit). Another
661
+ // domain may have closed + re-opened the shared `DatabaseSync` while our nexus
662
+ // singleton still references the now-closed handle. Detect a stale (closed)
663
+ // handle and drop the singleton so we re-derive from the live openDualScopeDb
664
+ // cache below.
665
+ if (_nexusDb && (_nexusNativeDb === null || !_nexusNativeDb.isOpen)) {
666
+ resetNexusDbState();
667
+ }
668
+ if (_nexusDb) {
669
+ // Re-validate the GLOBAL registry ATTACH on every singleton hit (ADR-090 ·
670
+ // T11648). The registry home (`getCleoHome()`) can change between calls —
671
+ // tests that mutate `CLEO_HOME` while the cwd-keyed project handle stays
672
+ // cached, or a sibling domain that re-opened the shared project handle and
673
+ // dropped the attach. `ensureGlobalRegistryAttached` early-returns when the
674
+ // current attach already points at the right file, so this is cheap.
675
+ if (_nexusNativeDb) {
676
+ try {
677
+ ensureGlobalRegistryAttached(_nexusNativeDb);
678
+ }
679
+ catch {
680
+ // A failed re-attach means the shared handle is unusable — drop the
681
+ // singleton so the next call re-derives a fresh handle + attach.
682
+ resetNexusDbState();
683
+ }
684
+ }
685
+ if (_nexusDb)
686
+ return _nexusDb;
687
+ }
403
688
  // If already initializing, wait for the in-flight init
404
689
  if (_nexusInitPromise)
405
690
  return _nexusInitPromise;
406
691
  _nexusInitPromise = (async () => {
407
692
  const dbPath = requestedPath;
408
693
  _nexusDbPath = dbPath;
694
+ _nexusRegistryPath = requestedRegistryPath;
409
695
  // ADR-086 / T10321 — warn (one-shot, non-blocking) if the install still
410
696
  // carries the nested-nexus migration debris. Does not alter the open.
411
697
  detectAndWarnOnNestedNexus();
412
- // Ensure directory exists
413
- mkdirSync(dirname(dbPath), { recursive: true });
414
- // Open file-backed SQLite via node:sqlite with WAL mode.
415
- const nativeDb = openNativeDatabase(dbPath);
698
+ // ── Registry home: open GLOBAL first (T11648) ──────────────────────────
699
+ // The registry/identity tables are global (ADR-090 §2.2). Opening the GLOBAL
700
+ // scope through the dual-scope chokepoint runs its consolidated migration,
701
+ // guaranteeing those tables (and their schema) physically exist before we
702
+ // ATTACH the global file into the project handle below. This is the same
703
+ // shared handle the global-tier siblings (skills/agent-registry) hold; we do
704
+ // NOT keep a reference to it — we only need its schema materialised on disk.
705
+ await openDualScopeDb('global');
706
+ // ── Graph home: open PROJECT scope as `main` (T11648 · ADR-090 §2.1/§2.4) ─
707
+ // openDualScopeDb('project') applies the pragma SSoT, creates the directory,
708
+ // runs the consolidated cleo-project migrations (which OWN the five PREFIXED
709
+ // graph tables), and manages the singleton cache. We pass NO cwd: the
710
+ // dual-scope resolver resolves the canonical project root via the
711
+ // `resolveCleoDir()` SSoT (CWD-walk / CLEO_DIR / worktree scope) — never a
712
+ // bare `process.cwd()` (T9584). Omitting the cwd also keeps the exodus-on-open
713
+ // hook un-armed, which is correct for the runtime READ path (exodus is a
714
+ // separate explicit step).
715
+ const dualHandle = await openDualScopeDb('project');
716
+ // Extract the underlying DatabaseSync. Drizzle exposes it via `$client`.
717
+ const nativeDb = dualHandle.db.$client ?? null;
718
+ if (!nativeDb) {
719
+ throw new Error('T11648: openDualScopeDb returned a handle without $client — ' +
720
+ 'cannot extract DatabaseSync for the nexus-schema drizzle wrapping.');
721
+ }
416
722
  _nexusNativeDb = nativeDb;
417
- // Create drizzle ORM wrapper via node-sqlite
723
+ // ATTACH the GLOBAL `cleo.db` so the registry/identity tables resolve by
724
+ // their bare names via SQLite's fall-through (ADR-090 · T11648). Idempotent.
725
+ ensureGlobalRegistryAttached(nativeDb);
726
+ // Wrap the native handle with the `nexusSchema` drizzle instance so existing
727
+ // accessors (nexusSchema.* queries) run unchanged: graph tables resolve to
728
+ // the project `main`; registry/identity tables fall through to the attach.
418
729
  const db = drizzle({ client: nativeDb, schema: nexusSchema });
419
- // Run drizzle migrations (creates/updates tables)
730
+ // Apply the nexus DELTA migration + idempotent safety nets on top of the
731
+ // consolidated prefixed tables openDualScopeDb already created: the FTS5
732
+ // quartet, the `nexus_relation_weights` plasticity partition (incl. the
733
+ // inline-column DROP), and the `_nexus_meta` health-probe sentinel. The
734
+ // legacy drop/rebuild (`establishLegacyNexusSchema`) is GONE — the
735
+ // consolidated migration is the single SSoT for the base tables (T11578 · AC3).
420
736
  runNexusMigrations(nativeDb, db);
421
737
  // Seed schema version for new databases (no-op if already set)
422
738
  nativeDb
@@ -434,46 +750,49 @@ export async function getNexusDb() {
434
750
  }
435
751
  }
436
752
  /**
437
- * Close the nexus.db database connection and release resources.
753
+ * Close the nexus-domain database connection and release resources.
754
+ *
755
+ * ## E6-L4 (T11524) · ADR-090 runtime read half (T11648)
756
+ *
757
+ * The nexus GRAPH domain now SHARES the consolidated PROJECT `cleo.db` handle
758
+ * with the other project-tier domains (tasks/brain/conduit — all open it via
759
+ * {@link openDualScopeDb}('project'), same cache key), with the GLOBAL `cleo.db`
760
+ * ATTACHed for registry/identity reads. This function therefore must NOT close
761
+ * the underlying `DatabaseSync` nor evict the dual-scope cache — doing so would
762
+ * break in-flight queries from those siblings with "database is not open". It
763
+ * only drops the nexus-domain singleton references; the shared handle's lifecycle
764
+ * (and the global ATTACH) is owned by `openDualScopeDb` and torn down by a
765
+ * coordinated reset (`closeAllDatabases` → `_resetDualScopeDbCache`).
438
766
  */
439
767
  export function closeNexusDb() {
440
- if (_nexusNativeDb) {
441
- try {
442
- if (_nexusNativeDb.isOpen) {
443
- _nexusNativeDb.close();
444
- }
445
- }
446
- catch {
447
- // Ignore close errors
448
- }
449
- _nexusNativeDb = null;
450
- }
768
+ // Drop only the nexus singleton references. Do NOT close `_nexusNativeDb` — it
769
+ // is the shared dual-scope project handle, possibly still in use by siblings.
770
+ _nexusNativeDb = null;
451
771
  _nexusDb = null;
452
772
  _nexusDbPath = null;
773
+ _nexusRegistryPath = null;
774
+ _nexusInitPromise = null;
453
775
  }
454
776
  /**
455
- * Reset nexus.db singleton state without saving.
456
- * Used during tests or when database file is recreated.
777
+ * Reset nexus singleton state without saving.
778
+ * Used during tests or when the database file is recreated.
457
779
  * Safe to call multiple times.
780
+ *
781
+ * ## E6-L4 (T11524) · ADR-090 runtime read half (T11648)
782
+ *
783
+ * Drops only the nexus-domain singleton references — does NOT close the shared
784
+ * dual-scope PROJECT `cleo.db` handle nor evict the dual-scope cache (that handle
785
+ * is shared with the other project-tier domains). Mirrors {@link closeNexusDb}.
458
786
  */
459
787
  export function resetNexusDbState() {
460
- if (_nexusNativeDb) {
461
- try {
462
- if (_nexusNativeDb.isOpen) {
463
- _nexusNativeDb.close();
464
- }
465
- }
466
- catch {
467
- // Ignore close errors
468
- }
469
- _nexusNativeDb = null;
470
- }
788
+ _nexusNativeDb = null;
471
789
  _nexusDb = null;
472
790
  _nexusDbPath = null;
791
+ _nexusRegistryPath = null;
473
792
  _nexusInitPromise = null;
474
793
  }
475
794
  /**
476
- * Get the underlying node:sqlite DatabaseSync instance for nexus.db.
795
+ * Get the underlying node:sqlite DatabaseSync instance for the nexus domain.
477
796
  * Useful for direct PRAGMA calls or raw SQL operations.
478
797
  * Returns null if the database hasn't been initialized.
479
798
  */