@cleocode/core 2026.8.1 → 2026.8.3

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 (323) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +57 -27
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/check/pr-gate.d.ts.map +1 -1
  5. package/dist/check/pr-gate.js +9 -0
  6. package/dist/check/pr-gate.js.map +1 -1
  7. package/dist/db/index.d.ts +13 -0
  8. package/dist/db/index.d.ts.map +1 -1
  9. package/dist/db/index.js +13 -0
  10. package/dist/db/index.js.map +1 -1
  11. package/dist/docs/display-alias.js +1 -1
  12. package/dist/docs/display-alias.js.map +1 -1
  13. package/dist/docs/export-document.js +499 -380
  14. package/dist/docs/export-document.js.map +3 -3
  15. package/dist/docs/numbering.js +1 -1
  16. package/dist/docs/numbering.js.map +1 -1
  17. package/dist/docs/supersede.js +1 -1
  18. package/dist/docs/supersede.js.map +1 -1
  19. package/dist/hooks/registry.d.ts +2 -2
  20. package/dist/hooks/registry.d.ts.map +1 -1
  21. package/dist/hooks/registry.js +5 -0
  22. package/dist/hooks/registry.js.map +1 -1
  23. package/dist/hooks/types.d.ts +59 -0
  24. package/dist/hooks/types.d.ts.map +1 -1
  25. package/dist/init/scaffold-workflows.d.ts +12 -0
  26. package/dist/init/scaffold-workflows.d.ts.map +1 -1
  27. package/dist/init/scaffold-workflows.js +16 -0
  28. package/dist/init/scaffold-workflows.js.map +1 -1
  29. package/dist/init/upgrade-workflows.d.ts.map +1 -1
  30. package/dist/init/upgrade-workflows.js +16 -0
  31. package/dist/init/upgrade-workflows.js.map +1 -1
  32. package/dist/init.d.ts.map +1 -1
  33. package/dist/init.js +16 -1
  34. package/dist/init.js.map +1 -1
  35. package/dist/injection.d.ts.map +1 -1
  36. package/dist/injection.js +44 -3
  37. package/dist/injection.js.map +1 -1
  38. package/dist/internal.d.ts +1 -0
  39. package/dist/internal.d.ts.map +1 -1
  40. package/dist/internal.js +2 -0
  41. package/dist/internal.js.map +1 -1
  42. package/dist/lifecycle/index.js +1 -1
  43. package/dist/lifecycle/index.js.map +1 -1
  44. package/dist/llm/credentials.d.ts +16 -1
  45. package/dist/llm/credentials.d.ts.map +1 -1
  46. package/dist/llm/credentials.js.map +1 -1
  47. package/dist/llm/cross-provider-selector.d.ts +25 -0
  48. package/dist/llm/cross-provider-selector.d.ts.map +1 -1
  49. package/dist/llm/cross-provider-selector.js +76 -3
  50. package/dist/llm/cross-provider-selector.js.map +1 -1
  51. package/dist/llm/local-inference-probe.d.ts +101 -0
  52. package/dist/llm/local-inference-probe.d.ts.map +1 -0
  53. package/dist/llm/local-inference-probe.js +174 -0
  54. package/dist/llm/local-inference-probe.js.map +1 -0
  55. package/dist/llm/plugin-facade.js +429 -115
  56. package/dist/llm/plugin-facade.js.map +3 -3
  57. package/dist/llm/role-executor.d.ts.map +1 -1
  58. package/dist/llm/role-executor.js +95 -9
  59. package/dist/llm/role-executor.js.map +1 -1
  60. package/dist/llm/role-resolver.d.ts +10 -0
  61. package/dist/llm/role-resolver.d.ts.map +1 -1
  62. package/dist/llm/role-resolver.js +92 -1
  63. package/dist/llm/role-resolver.js.map +1 -1
  64. package/dist/llm/system-resolver.d.ts.map +1 -1
  65. package/dist/llm/system-resolver.js +2 -0
  66. package/dist/llm/system-resolver.js.map +1 -1
  67. package/dist/memory/brain-backfill.js +3 -3
  68. package/dist/memory/brain-backfill.js.map +1 -1
  69. package/dist/memory/brain-consolidator.js +2 -2
  70. package/dist/memory/brain-consolidator.js.map +1 -1
  71. package/dist/memory/brain-doctor.js +1 -1
  72. package/dist/memory/brain-doctor.js.map +1 -1
  73. package/dist/memory/brain-health-dashboard.js +1 -1
  74. package/dist/memory/brain-health-dashboard.js.map +1 -1
  75. package/dist/memory/brain-lifecycle.js +10 -10
  76. package/dist/memory/brain-lifecycle.js.map +1 -1
  77. package/dist/memory/brain-maintenance.js +1 -1
  78. package/dist/memory/brain-maintenance.js.map +1 -1
  79. package/dist/memory/brain-page-nodes.js +4 -4
  80. package/dist/memory/brain-page-nodes.js.map +1 -1
  81. package/dist/memory/brain-reasoning.js +1 -1
  82. package/dist/memory/brain-reasoning.js.map +1 -1
  83. package/dist/memory/brain-search.d.ts +109 -0
  84. package/dist/memory/brain-search.d.ts.map +1 -1
  85. package/dist/memory/brain-search.js +150 -38
  86. package/dist/memory/brain-search.js.map +1 -1
  87. package/dist/memory/brain-similarity.js +1 -1
  88. package/dist/memory/brain-similarity.js.map +1 -1
  89. package/dist/memory/brain-stdp.js +6 -6
  90. package/dist/memory/brain-stdp.js.map +1 -1
  91. package/dist/memory/brain-stub-prune.d.ts +109 -0
  92. package/dist/memory/brain-stub-prune.d.ts.map +1 -0
  93. package/dist/memory/brain-stub-prune.js +155 -0
  94. package/dist/memory/brain-stub-prune.js.map +1 -0
  95. package/dist/memory/brain-sweep-executor.js +1 -1
  96. package/dist/memory/brain-sweep-executor.js.map +1 -1
  97. package/dist/memory/brain-writer-handlers.js +2 -2
  98. package/dist/memory/brain-writer-handlers.js.map +1 -1
  99. package/dist/memory/claude-mem-migration.d.ts.map +1 -1
  100. package/dist/memory/claude-mem-migration.js +2 -2
  101. package/dist/memory/claude-mem-migration.js.map +1 -1
  102. package/dist/memory/engine-compat.d.ts.map +1 -1
  103. package/dist/memory/engine-compat.js +65 -7
  104. package/dist/memory/engine-compat.js.map +1 -1
  105. package/dist/memory/extraction-gate.js +3 -3
  106. package/dist/memory/extraction-gate.js.map +1 -1
  107. package/dist/memory/graph-memory-bridge.js +9 -9
  108. package/dist/memory/graph-memory-bridge.js.map +1 -1
  109. package/dist/memory/graph-queries.js +4 -4
  110. package/dist/memory/graph-queries.js.map +1 -1
  111. package/dist/memory/llm-extraction.js +1 -1
  112. package/dist/memory/llm-extraction.js.map +1 -1
  113. package/dist/memory/memory-bridge.js +1 -1
  114. package/dist/memory/memory-bridge.js.map +1 -1
  115. package/dist/memory/nexus-plasticity.js +1 -1
  116. package/dist/memory/nexus-plasticity.js.map +1 -1
  117. package/dist/memory/observer-reflector.js +1 -1
  118. package/dist/memory/observer-reflector.js.map +1 -1
  119. package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
  120. package/dist/memory/pipeline-manifest-sqlite.js +13 -3
  121. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
  122. package/dist/memory/quality-feedback.js +4 -4
  123. package/dist/memory/quality-feedback.js.map +1 -1
  124. package/dist/memory/retrieval/increment-citation-counts.js +1 -1
  125. package/dist/memory/retrieval/increment-citation-counts.js.map +1 -1
  126. package/dist/memory/retrieval/log-retrieval.js +1 -1
  127. package/dist/memory/retrieval/log-retrieval.js.map +1 -1
  128. package/dist/memory/retrieval/observe.js +1 -1
  129. package/dist/memory/retrieval/observe.js.map +1 -1
  130. package/dist/memory/retrieval/search.js +1 -1
  131. package/dist/memory/retrieval/search.js.map +1 -1
  132. package/dist/memory/retrieval/timeline.js +1 -1
  133. package/dist/memory/retrieval/timeline.js.map +1 -1
  134. package/dist/memory/sleep-consolidation.js +4 -4
  135. package/dist/memory/sleep-consolidation.js.map +1 -1
  136. package/dist/memory/temporal-supersession.js +5 -5
  137. package/dist/memory/temporal-supersession.js.map +1 -1
  138. package/dist/memory/transcript-extractor.js +1 -1
  139. package/dist/memory/transcript-extractor.js.map +1 -1
  140. package/dist/memory/transcript-ingestor.js +1 -1
  141. package/dist/memory/transcript-ingestor.js.map +1 -1
  142. package/dist/memory/transcript-scanner.js +1 -1
  143. package/dist/memory/transcript-scanner.js.map +1 -1
  144. package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
  145. package/dist/nexus/analyze-orchestrator.js +28 -0
  146. package/dist/nexus/analyze-orchestrator.js.map +1 -1
  147. package/dist/nexus/context.d.ts.map +1 -1
  148. package/dist/nexus/context.js +3 -3
  149. package/dist/nexus/context.js.map +1 -1
  150. package/dist/nexus/impact.d.ts.map +1 -1
  151. package/dist/nexus/impact.js +3 -3
  152. package/dist/nexus/impact.js.map +1 -1
  153. package/dist/nexus/living-brain.js +7 -7
  154. package/dist/nexus/living-brain.js.map +1 -1
  155. package/dist/nexus/symbol-miss.d.ts +132 -0
  156. package/dist/nexus/symbol-miss.d.ts.map +1 -0
  157. package/dist/nexus/symbol-miss.js +168 -0
  158. package/dist/nexus/symbol-miss.js.map +1 -0
  159. package/dist/nexus/tasks-bridge.js +4 -4
  160. package/dist/nexus/tasks-bridge.js.map +1 -1
  161. package/dist/orchestrate/spawn-ops.d.ts +21 -1
  162. package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
  163. package/dist/orchestrate/spawn-ops.js +39 -2
  164. package/dist/orchestrate/spawn-ops.js.map +1 -1
  165. package/dist/orchestrate/worker-verify.d.ts.map +1 -1
  166. package/dist/orchestrate/worker-verify.js +22 -0
  167. package/dist/orchestrate/worker-verify.js.map +1 -1
  168. package/dist/release/changesets-aggregator.d.ts.map +1 -1
  169. package/dist/release/changesets-aggregator.js +15 -1
  170. package/dist/release/changesets-aggregator.js.map +1 -1
  171. package/dist/release/open.d.ts +29 -2
  172. package/dist/release/open.d.ts.map +1 -1
  173. package/dist/release/open.js +48 -11
  174. package/dist/release/open.js.map +1 -1
  175. package/dist/release/reconcile.d.ts.map +1 -1
  176. package/dist/release/reconcile.js +5 -1
  177. package/dist/release/reconcile.js.map +1 -1
  178. package/dist/resources/governor.d.ts +12 -2
  179. package/dist/resources/governor.d.ts.map +1 -1
  180. package/dist/resources/governor.js +12 -3
  181. package/dist/resources/governor.js.map +1 -1
  182. package/dist/sagas/migrate-containment.d.ts.map +1 -1
  183. package/dist/sagas/migrate-containment.js +4 -2
  184. package/dist/sagas/migrate-containment.js.map +1 -1
  185. package/dist/scaffold/ensure-dirs.d.ts.map +1 -1
  186. package/dist/scaffold/ensure-dirs.js +15 -3
  187. package/dist/scaffold/ensure-dirs.js.map +1 -1
  188. package/dist/scaffold/project-detection.d.ts.map +1 -1
  189. package/dist/scaffold/project-detection.js +2 -1
  190. package/dist/scaffold/project-detection.js.map +1 -1
  191. package/dist/selfimprove/draft-pr.d.ts +14 -1
  192. package/dist/selfimprove/draft-pr.d.ts.map +1 -1
  193. package/dist/selfimprove/draft-pr.js.map +1 -1
  194. package/dist/selfimprove/fix-gen.d.ts +0 -21
  195. package/dist/selfimprove/fix-gen.d.ts.map +1 -1
  196. package/dist/selfimprove/fix-gen.js +509 -173
  197. package/dist/selfimprove/fix-gen.js.map +3 -3
  198. package/dist/selfimprove/run-loop.d.ts.map +1 -1
  199. package/dist/selfimprove/run-loop.js +50 -19
  200. package/dist/selfimprove/run-loop.js.map +1 -1
  201. package/dist/selfimprove/scenarios/dhq-replay-find/golden.json +114 -4
  202. package/dist/selfimprove/scenarios/dhq-replay-find/scenario.json +3 -3
  203. package/dist/sentient/dream-cycle.d.ts +20 -19
  204. package/dist/sentient/dream-cycle.d.ts.map +1 -1
  205. package/dist/sentient/dream-cycle.js +323 -12
  206. package/dist/sentient/dream-cycle.js.map +1 -1
  207. package/dist/sentient/dream-watermark.d.ts +134 -0
  208. package/dist/sentient/dream-watermark.d.ts.map +1 -0
  209. package/dist/sentient/dream-watermark.js +156 -0
  210. package/dist/sentient/dream-watermark.js.map +1 -0
  211. package/dist/sentient/hygiene-scan.js +1 -1
  212. package/dist/sentient/hygiene-scan.js.map +1 -1
  213. package/dist/sentient/propose-tick.js +3 -3
  214. package/dist/sentient/propose-tick.js.map +1 -1
  215. package/dist/sentient/stage-drift-tick.js +1 -1
  216. package/dist/sentient/stage-drift-tick.js.map +1 -1
  217. package/dist/sentient/state.d.ts +17 -0
  218. package/dist/sentient/state.d.ts.map +1 -1
  219. package/dist/sentient/state.js +22 -3
  220. package/dist/sentient/state.js.map +1 -1
  221. package/dist/sentient/tick.d.ts.map +1 -1
  222. package/dist/sentient/tick.js +79 -2
  223. package/dist/sentient/tick.js.map +1 -1
  224. package/dist/sequence/index.js +1 -1
  225. package/dist/sequence/index.js.map +1 -1
  226. package/dist/sessions/index.d.ts.map +1 -1
  227. package/dist/sessions/index.js +5 -1
  228. package/dist/sessions/index.js.map +1 -1
  229. package/dist/sessions/snapshot.d.ts.map +1 -1
  230. package/dist/sessions/snapshot.js +6 -3
  231. package/dist/sessions/snapshot.js.map +1 -1
  232. package/dist/store/agent-registry-store.d.ts.map +1 -1
  233. package/dist/store/agent-registry-store.js +71 -28
  234. package/dist/store/agent-registry-store.js.map +1 -1
  235. package/dist/store/attachment-store.js +3 -3
  236. package/dist/store/attachment-store.js.map +1 -1
  237. package/dist/store/checkpoint-git-dir.d.ts +95 -0
  238. package/dist/store/checkpoint-git-dir.d.ts.map +1 -0
  239. package/dist/store/checkpoint-git-dir.js +111 -0
  240. package/dist/store/checkpoint-git-dir.js.map +1 -0
  241. package/dist/store/conduit-sqlite.d.ts +21 -1
  242. package/dist/store/conduit-sqlite.d.ts.map +1 -1
  243. package/dist/store/conduit-sqlite.js +42 -56
  244. package/dist/store/conduit-sqlite.js.map +1 -1
  245. package/dist/store/dual-scope-db.d.ts +39 -0
  246. package/dist/store/dual-scope-db.d.ts.map +1 -1
  247. package/dist/store/dual-scope-db.js +26 -8
  248. package/dist/store/dual-scope-db.js.map +1 -1
  249. package/dist/store/git-checkpoint.d.ts.map +1 -1
  250. package/dist/store/git-checkpoint.js +6 -5
  251. package/dist/store/git-checkpoint.js.map +1 -1
  252. package/dist/store/memory-sqlite.d.ts +57 -3
  253. package/dist/store/memory-sqlite.d.ts.map +1 -1
  254. package/dist/store/memory-sqlite.js +70 -89
  255. package/dist/store/memory-sqlite.js.map +1 -1
  256. package/dist/store/nexus-sqlite.d.ts +42 -0
  257. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  258. package/dist/store/nexus-sqlite.js +79 -115
  259. package/dist/store/nexus-sqlite.js.map +1 -1
  260. package/dist/store/ports/domain-binding.d.ts +233 -0
  261. package/dist/store/ports/domain-binding.d.ts.map +1 -0
  262. package/dist/store/ports/domain-binding.js +500 -0
  263. package/dist/store/ports/domain-binding.js.map +1 -0
  264. package/dist/store/ports/fk-integrity.d.ts +155 -0
  265. package/dist/store/ports/fk-integrity.d.ts.map +1 -0
  266. package/dist/store/ports/fk-integrity.js +182 -0
  267. package/dist/store/ports/fk-integrity.js.map +1 -0
  268. package/dist/store/role-accessors-impl.js +2 -2
  269. package/dist/store/role-accessors-impl.js.map +1 -1
  270. package/dist/store/skills-db.d.ts +19 -0
  271. package/dist/store/skills-db.d.ts.map +1 -1
  272. package/dist/store/skills-db.js +80 -72
  273. package/dist/store/skills-db.js.map +1 -1
  274. package/dist/store/sqlite-backup.js +2 -2
  275. package/dist/store/sqlite-backup.js.map +1 -1
  276. package/dist/store/sqlite-data-accessor.js +1 -1
  277. package/dist/store/sqlite-data-accessor.js.map +1 -1
  278. package/dist/store/sqlite.d.ts +72 -10
  279. package/dist/store/sqlite.d.ts.map +1 -1
  280. package/dist/store/sqlite.js +173 -160
  281. package/dist/store/sqlite.js.map +1 -1
  282. package/dist/store/tasks-sqlite.js +2 -2
  283. package/dist/store/tasks-sqlite.js.map +1 -1
  284. package/dist/system/health.d.ts +0 -4
  285. package/dist/system/health.d.ts.map +1 -1
  286. package/dist/system/health.js +60 -3
  287. package/dist/system/health.js.map +1 -1
  288. package/dist/tasks/add.d.ts.map +1 -1
  289. package/dist/tasks/add.js +40 -4
  290. package/dist/tasks/add.js.map +1 -1
  291. package/dist/tasks/collection.d.ts +51 -0
  292. package/dist/tasks/collection.d.ts.map +1 -0
  293. package/dist/tasks/collection.js +81 -0
  294. package/dist/tasks/collection.js.map +1 -0
  295. package/dist/tasks/complete.d.ts.map +1 -1
  296. package/dist/tasks/complete.js +8 -3
  297. package/dist/tasks/complete.js.map +1 -1
  298. package/dist/tasks/evidence.d.ts.map +1 -1
  299. package/dist/tasks/evidence.js +17 -0
  300. package/dist/tasks/evidence.js.map +1 -1
  301. package/dist/tasks/find.d.ts +50 -0
  302. package/dist/tasks/find.d.ts.map +1 -1
  303. package/dist/tasks/find.js +63 -2
  304. package/dist/tasks/find.js.map +1 -1
  305. package/dist/tasks/tool-resolver.d.ts +1 -1
  306. package/dist/tasks/tool-resolver.d.ts.map +1 -1
  307. package/dist/tasks/tool-resolver.js +83 -0
  308. package/dist/tasks/tool-resolver.js.map +1 -1
  309. package/dist/tasks/tool-semaphore.d.ts +82 -12
  310. package/dist/tasks/tool-semaphore.d.ts.map +1 -1
  311. package/dist/tasks/tool-semaphore.js +79 -17
  312. package/dist/tasks/tool-semaphore.js.map +1 -1
  313. package/dist/upgrade.js +2 -2
  314. package/dist/upgrade.js.map +1 -1
  315. package/dist/validation/doctor/checks.d.ts +23 -1
  316. package/dist/validation/doctor/checks.d.ts.map +1 -1
  317. package/dist/validation/doctor/checks.js +79 -19
  318. package/dist/validation/doctor/checks.js.map +1 -1
  319. package/dist/worktree/list.js +1 -1
  320. package/dist/worktree/list.js.map +1 -1
  321. package/package.json +23 -13
  322. package/templates/CLEO-INJECTION.md +40 -25
  323. package/templates/workflows/release-prepare.yml.tmpl +73 -16
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Path-keyed domain binding registry — the single owner of every per-domain
3
+ * Drizzle wrapper in the CLEO runtime.
4
+ *
5
+ * ## Why this module exists (E6-L13 · T12037)
6
+ *
7
+ * Before this leaf, each store facade (`sqlite.ts`, `memory-sqlite.ts`,
8
+ * `conduit-sqlite.ts`, `nexus-sqlite.ts`, `skills-db.ts`) owned its OWN
9
+ * process-global singleton quartet:
10
+ *
11
+ * ```ts
12
+ * let _db: NodeSQLiteDatabase | null = null;
13
+ * let _nativeDb: DatabaseSync | null = null;
14
+ * let _dbPath: string | null = null;
15
+ * let _initPromise: Promise<NodeSQLiteDatabase> | null = null;
16
+ * ```
17
+ *
18
+ * Five independent caches over ONE shared `DatabaseSync` produced a documented
19
+ * class of defects:
20
+ *
21
+ * - **Last-project-wins** — `_dbPath` held a single project. Touching project B
22
+ * reset the singleton, so an interleaved project-A query re-opened and
23
+ * re-migrated on every alternation (thrash, not just slowness).
24
+ * - **Cross-domain staleness** — the tasks singleton could reference a
25
+ * `DatabaseSync` that the brain domain had already closed (T12019/T12020),
26
+ * surfacing as `database is not open` or, worse, a silently-nulled
27
+ * `sourceSessionId`.
28
+ * - **Band-aid retry loops** — both `getDb` and `getBrainDb` grew bounded
29
+ * re-acquisition loops (T12035) purely to paper over the above.
30
+ *
31
+ * This module replaces all five with ONE registry keyed by
32
+ * `${scope}::${canonical dbPath}::${domain}`, layered on the
33
+ * {@link CleoRuntime} store registry (T12036). A binding is valid only while
34
+ * the {@link ProjectStore} / {@link GlobalStore} instance it was established
35
+ * against is still the one the runtime hands out AND its native handle is
36
+ * open. Any eviction — a `close()`, a `_resetDualScopeDbCache`, an external
37
+ * teardown — yields a fresh store object, which invalidates every binding
38
+ * derived from it by identity comparison. No liveness polling, no retry loop,
39
+ * no "last opened project".
40
+ *
41
+ * ## What a domain owns after this leaf
42
+ *
43
+ * A domain owns its `establish` function — the schema reconciliation it must
44
+ * run against a native handle (legacy Drizzle wrapping, `runMigrations`,
45
+ * vec0 extension loading, seed rows). It no longer owns caching, path
46
+ * resolution, single-flight, or liveness. Those are this module's job.
47
+ *
48
+ * @packageDocumentation
49
+ * @task T12037 (E6-L13)
50
+ * @epic T11249 (E6)
51
+ * @saga T11242 (SG-DB-SUBSTRATE-V2)
52
+ */
53
+ import { resolve } from 'node:path';
54
+ import { createCleoRuntime, resolveDualScopeDbPath, } from '../dual-scope-db.js';
55
+ /**
56
+ * The process-wide {@link CleoRuntime} instance backing every domain port.
57
+ *
58
+ * A single runtime is correct here: the runtime registry is itself path-keyed,
59
+ * so one instance serves every project the process touches. It is lazily
60
+ * constructed so importing this module has no side effects (a hard constraint
61
+ * — the Gate-13 chokepoint audit rejects import-time DB work).
62
+ */
63
+ let _runtime = null;
64
+ /**
65
+ * Get (or lazily create) the process-wide {@link CleoRuntime}.
66
+ *
67
+ * @returns The shared runtime store registry.
68
+ */
69
+ export function getCleoRuntime() {
70
+ if (!_runtime) {
71
+ _runtime = createCleoRuntime();
72
+ }
73
+ return _runtime;
74
+ }
75
+ /**
76
+ * Tear down the process-wide runtime: close every registry entry and drop
77
+ * every domain binding. The next {@link getCleoRuntime} call builds a fresh
78
+ * registry.
79
+ *
80
+ * Used by full-teardown paths (`closeAllDatabases`) and by tests that need a
81
+ * pristine process state between cases.
82
+ */
83
+ export function resetCleoRuntime() {
84
+ releaseAllDomainBindings();
85
+ if (_runtime) {
86
+ try {
87
+ _runtime.closeAll();
88
+ }
89
+ catch {
90
+ // Best-effort teardown — the registry is discarded either way.
91
+ }
92
+ _runtime = null;
93
+ }
94
+ }
95
+ /**
96
+ * Established bindings, keyed `${scope}::${canonical dbPath}::${domain}`.
97
+ * Type-erased; {@link bindProjectDomain} re-applies the caller's type at the
98
+ * boundary, which is sound because the key includes the domain id and each
99
+ * domain passes exactly one `establish` shape.
100
+ */
101
+ const _bindings = new Map();
102
+ /**
103
+ * In-flight `establish` calls, same keying as {@link _bindings}. Concurrent
104
+ * binders for one key share a single establishment (single-flight).
105
+ */
106
+ const _inflight = new Map();
107
+ /**
108
+ * Build the composite registry key for a domain binding.
109
+ *
110
+ * @param scope - The dual-scope discriminator.
111
+ * @param dbPath - Canonical (already `path.resolve`d) database path.
112
+ * @param domain - Stable domain id (e.g. `'tasks'`, `'brain'`).
113
+ */
114
+ function bindingKey(scope, dbPath, domain) {
115
+ return `${scope}::${dbPath}::${domain}`;
116
+ }
117
+ /**
118
+ * Extract the underlying `DatabaseSync` from a store's Drizzle handle.
119
+ *
120
+ * Drizzle's `node:sqlite` driver exposes the native connection as `$client`.
121
+ * This is the ONLY place that reach-through happens — domains receive the
122
+ * already-extracted handle.
123
+ *
124
+ * @param store - The runtime store to unwrap.
125
+ * @returns The live native connection.
126
+ * @throws If the Drizzle handle exposes no `$client`.
127
+ */
128
+ function nativeOf(store) {
129
+ const native = store.db.$client;
130
+ if (!native) {
131
+ throw new Error(`T12037: CleoRuntime store for ${store.scope}::${store.dbPath} exposed no $client — ` +
132
+ 'cannot bind a domain schema to it.');
133
+ }
134
+ return native;
135
+ }
136
+ /**
137
+ * Reuse the cached binding for `key` if it is still valid.
138
+ *
139
+ * A binding is valid iff it was established against the SAME store instance
140
+ * the runtime just returned (identity, not path — a reopened store is a new
141
+ * object) and that store's native connection is still open.
142
+ *
143
+ * @param key - Composite registry key.
144
+ * @param store - The store the runtime returned for this open.
145
+ * @returns The still-valid row, or `undefined` if it must be re-established.
146
+ */
147
+ function reuseValid(key, store) {
148
+ const row = _bindings.get(key);
149
+ if (!row)
150
+ return undefined;
151
+ if (row.store !== store || !store.isOpen || !row.native.isOpen) {
152
+ _bindings.delete(key);
153
+ return undefined;
154
+ }
155
+ return row;
156
+ }
157
+ /**
158
+ * Establish (or reuse) `domain`'s binding against a store, single-flighted.
159
+ *
160
+ * @param key - Composite registry key.
161
+ * @param store - The store to bind against.
162
+ * @param establish - Domain schema reconciliation.
163
+ */
164
+ async function bindAgainst(key, store, establish) {
165
+ const cached = reuseValid(key, store);
166
+ if (cached)
167
+ return cached;
168
+ const pending = _inflight.get(key);
169
+ if (pending) {
170
+ const row = await pending;
171
+ // The in-flight establishment may have raced an eviction; re-validate.
172
+ if (row.store === store && row.native.isOpen)
173
+ return row;
174
+ return bindAgainst(key, store, establish);
175
+ }
176
+ const native = nativeOf(store);
177
+ const flight = (async () => {
178
+ const db = await establish(native, store);
179
+ const row = { store, native, db };
180
+ // Publish only if this store is still the live one for the key. A close
181
+ // that landed during `establish` must not resurrect a dead binding.
182
+ if (store.isOpen && native.isOpen) {
183
+ _bindings.set(key, row);
184
+ }
185
+ return row;
186
+ })();
187
+ _inflight.set(key, flight);
188
+ try {
189
+ return await flight;
190
+ }
191
+ finally {
192
+ if (_inflight.get(key) === flight) {
193
+ _inflight.delete(key);
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Maximum consecutive attempts to obtain a binding over a LIVE connection.
199
+ *
200
+ * The shared consolidated `DatabaseSync` can be closed in the microtask gap
201
+ * between the runtime resolving a store and `establish` finishing against it
202
+ * (another domain's `closeDb()`, a `_resetDualScopeDbCache`, a fire-and-forget
203
+ * brain write crossing a test boundary). One reacquisition is enough: closing
204
+ * the dead store evicts it from BOTH the runtime registry and the dual-scope
205
+ * chokepoint cache, so the retry necessarily opens a fresh connection.
206
+ *
207
+ * Before this module each facade carried its own copy of this loop
208
+ * (T12020 · T12035). It lives here now — one implementation, every domain.
209
+ */
210
+ const MAX_LIVENESS_ATTEMPTS = 2;
211
+ /**
212
+ * Obtain a binding whose connection is guaranteed live, with bounded
213
+ * reacquisition.
214
+ *
215
+ * @param scope - Dual-scope discriminator, for the eviction filter.
216
+ * @param key - Composite registry key.
217
+ * @param dbPath - Canonical database path, for the eviction filter.
218
+ * @param openStore - Opens (or reuses) the store for this key.
219
+ * @param establish - Domain schema reconciliation.
220
+ * @returns A row whose `native` was open at publication.
221
+ * @throws When every attempt yielded a dead connection.
222
+ */
223
+ async function bindLive(scope, key, dbPath, openStore, establish) {
224
+ let lastError;
225
+ for (let attempt = 0; attempt < MAX_LIVENESS_ATTEMPTS; attempt += 1) {
226
+ let store;
227
+ try {
228
+ store = await openStore();
229
+ }
230
+ catch (err) {
231
+ // The runtime's OWN bounded reacquisition can exhaust when a sibling
232
+ // keeps closing the shared handle across a microtask boundary (a
233
+ // fire-and-forget brain write crossing a test teardown is the usual
234
+ // culprit). That is transient, not terminal: drop our bindings for the
235
+ // path so the chokepoint cache is not holding a corpse, and take one
236
+ // more pass. A genuinely broken open fails again and rethrows.
237
+ lastError = err;
238
+ releaseDomainBindings({ scope, dbPath });
239
+ if (attempt + 1 >= MAX_LIVENESS_ATTEMPTS)
240
+ throw err;
241
+ continue;
242
+ }
243
+ try {
244
+ const row = await bindAgainst(key, store, establish);
245
+ if (row.store.isOpen && row.native.isOpen)
246
+ return row;
247
+ }
248
+ catch (err) {
249
+ // A connection closed mid-`establish` surfaces as "database is not
250
+ // open" from the migration/DDL path. Retry once against a fresh
251
+ // handle; a genuine schema error will fail again and rethrow below.
252
+ lastError = err;
253
+ if (store.isOpen)
254
+ throw err;
255
+ }
256
+ // Dead handle. Closing the store evicts the runtime entry AND the
257
+ // chokepoint cache entry, so the next attempt opens a fresh connection.
258
+ releaseDomainBindings({ scope, dbPath });
259
+ try {
260
+ store.close();
261
+ }
262
+ catch {
263
+ // Already closed — the eviction is what matters.
264
+ }
265
+ }
266
+ if (lastError !== undefined)
267
+ throw lastError;
268
+ throw new Error(`T12037: could not bind ${key} over a live connection after ` +
269
+ `${MAX_LIVENESS_ATTEMPTS} attempts — the consolidated handle was closed each time.`);
270
+ }
271
+ /**
272
+ * Bind a project-scope domain schema to the {@link ProjectStore} for `cwd`.
273
+ *
274
+ * This is the replacement for every `getXxxDb(cwd)` project-domain facade.
275
+ * The returned binding carries the store itself, so callers that need a
276
+ * cross-domain transaction hold an explicit handle rather than reaching for a
277
+ * process global.
278
+ *
279
+ * @typeParam TDb - The domain's Drizzle handle type.
280
+ * @param domain - Stable domain id; part of the registry key.
281
+ * @param cwd - Project working directory. Resolved to the canonical
282
+ * `cleo.db` path and forwarded as `exodusCwd` so a port open arms the
283
+ * exodus-on-open auto-migration exactly like the facade it replaces.
284
+ * @param establish - Reconcile the domain's schema against the native handle
285
+ * and return the domain-typed Drizzle instance. Called once per
286
+ * (path, domain, store instance); MUST be idempotent because a store
287
+ * eviction re-runs it.
288
+ * @returns The live {@link DomainBinding}.
289
+ */
290
+ export async function bindProjectDomain(domain, cwd, establish) {
291
+ const dbPath = resolve(resolveDualScopeDbPath('project', cwd));
292
+ const row = await bindLive('project', bindingKey('project', dbPath, domain), dbPath, () => getCleoRuntime().openProject(dbPath, { exodusCwd: cwd }), establish);
293
+ return row;
294
+ }
295
+ /**
296
+ * Bind a project-scope domain schema to an EXPLICIT database path.
297
+ *
298
+ * Used by domains whose legacy lifecycle API accepted an on-disk path (test
299
+ * fixtures, snapshot inspection). Never arms exodus-on-open — an explicit
300
+ * path must not auto-migrate a legacy fleet into a fixture.
301
+ *
302
+ * @typeParam TDb - The domain's Drizzle handle type.
303
+ * @param domain - Stable domain id.
304
+ * @param dbPath - Absolute path to a consolidated `cleo.db`.
305
+ * @param establish - See {@link bindProjectDomain}.
306
+ */
307
+ export async function bindProjectDomainAtPath(domain, dbPath, establish) {
308
+ const normalized = resolve(dbPath);
309
+ const row = await bindLive('project', bindingKey('project', normalized, domain), normalized, () => getCleoRuntime().openProject(normalized), establish);
310
+ return row;
311
+ }
312
+ /**
313
+ * Bind a global-scope domain schema to the {@link GlobalStore}.
314
+ *
315
+ * This is the replacement for every `getXxxDb()` global-domain facade
316
+ * (nexus, skills, agents, telemetry).
317
+ *
318
+ * @typeParam TDb - The domain's Drizzle handle type.
319
+ * @param domain - Stable domain id; part of the registry key.
320
+ * @param establish - See {@link bindProjectDomain}.
321
+ * @param cwd - Optional cwd forwarded as `exodusCwd` for the global scope's
322
+ * own legacy-fleet auto-migration.
323
+ */
324
+ export async function bindGlobalDomain(domain, establish, cwd) {
325
+ const dbPath = resolve(resolveDualScopeDbPath('global'));
326
+ const row = await bindLive('global', bindingKey('global', dbPath, domain), dbPath, () => getCleoRuntime().openGlobal({ exodusCwd: cwd }), establish);
327
+ return row;
328
+ }
329
+ /**
330
+ * Bind a global-scope domain schema to an EXPLICIT database path.
331
+ *
332
+ * The path-aware sibling of {@link bindGlobalDomain}, for domains whose
333
+ * lifecycle API accepts an on-disk path — notably the skills registry's
334
+ * test-sandbox `{ path }` override. Never arms exodus-on-open.
335
+ *
336
+ * @typeParam TDb - The domain's Drizzle handle type.
337
+ * @param domain - Stable domain id.
338
+ * @param dbPath - Absolute path to a global-scope `cleo.db`.
339
+ * @param establish - See {@link bindProjectDomain}.
340
+ */
341
+ export async function bindGlobalDomainAtPath(domain, dbPath, establish) {
342
+ const normalized = resolve(dbPath);
343
+ const row = await bindLive('global', bindingKey('global', normalized, domain), normalized, () => getCleoRuntime().openGlobalAt(normalized), establish);
344
+ return row;
345
+ }
346
+ /**
347
+ * Synchronously read an ALREADY-established binding at an EXPLICIT path.
348
+ *
349
+ * @typeParam TDb - The domain's Drizzle handle type.
350
+ * @param scope - Dual-scope discriminator.
351
+ * @param dbPath - Absolute database path.
352
+ * @param domain - Stable domain id.
353
+ */
354
+ export function peekDomainAtPath(scope, dbPath, domain) {
355
+ const row = _bindings.get(bindingKey(scope, resolve(dbPath), domain));
356
+ if (!row?.native.isOpen)
357
+ return null;
358
+ return row;
359
+ }
360
+ /**
361
+ * Drop cached bindings.
362
+ *
363
+ * Dropping a binding does NOT close any connection — the runtime registry and
364
+ * the dual-scope chokepoint own connection lifecycle. This only forces the
365
+ * next bind to re-run the domain's `establish`.
366
+ *
367
+ * @param filter - Restrict the drop. Omit any field to match all values of it.
368
+ * `{ scope: 'project' }` drops every project-domain binding;
369
+ * `{ domain: 'brain' }` drops the brain binding for every project.
370
+ */
371
+ export function releaseDomainBindings(filter) {
372
+ if (!filter) {
373
+ _bindings.clear();
374
+ return;
375
+ }
376
+ const wantPath = filter.dbPath === undefined ? undefined : resolve(filter.dbPath);
377
+ for (const key of [..._bindings.keys()]) {
378
+ const sep = key.indexOf('::');
379
+ const lastSep = key.lastIndexOf('::');
380
+ const scope = key.slice(0, sep);
381
+ const dbPath = key.slice(sep + 2, lastSep);
382
+ const domain = key.slice(lastSep + 2);
383
+ if (filter.scope !== undefined && scope !== filter.scope)
384
+ continue;
385
+ if (wantPath !== undefined && dbPath !== wantPath)
386
+ continue;
387
+ if (filter.domain !== undefined && domain !== filter.domain)
388
+ continue;
389
+ _bindings.delete(key);
390
+ }
391
+ }
392
+ /**
393
+ * Drop every cached binding in every scope. Equivalent to
394
+ * `releaseDomainBindings()` with no filter; named for call-site clarity in
395
+ * teardown paths.
396
+ */
397
+ export function releaseAllDomainBindings() {
398
+ _bindings.clear();
399
+ }
400
+ /**
401
+ * Snapshot of currently-established binding keys.
402
+ *
403
+ * Read-only diagnostic surface for `cleo health` / architecture tests that
404
+ * assert a process holds exactly one binding per (path, domain).
405
+ */
406
+ export function boundDomainKeys() {
407
+ return new Set(_bindings.keys());
408
+ }
409
+ /**
410
+ * Synchronously look up the native `DatabaseSync` of an ALREADY-established
411
+ * project-domain binding.
412
+ *
413
+ * This exists solely to keep the legacy synchronous native getters
414
+ * (`getNativeDb`, `getNativeTasksDb`, `getBrainNativeDb`) working while their
415
+ * call sites migrate to the async binding. Unlike the module-global they
416
+ * replace, this lookup is **path-keyed** — passing the caller's `cwd` returns
417
+ * that project's handle rather than "whichever project was opened last".
418
+ *
419
+ * Returns `null` when nothing is bound yet or the connection has since
420
+ * closed; callers must have awaited the domain's async binder first.
421
+ *
422
+ * @param domain - Stable domain id.
423
+ * @param cwd - Project working directory; resolved to the canonical path.
424
+ * @returns The live native connection, or `null`.
425
+ *
426
+ * @deprecated Read `.native` from the {@link DomainBinding} instead. Removed
427
+ * when the last synchronous native getter is retired (T12040 · E6-L16).
428
+ */
429
+ export function boundProjectNative(domain, cwd) {
430
+ return peekProjectDomain(domain, cwd)?.native ?? null;
431
+ }
432
+ /**
433
+ * Synchronously read an ALREADY-established project-domain binding without
434
+ * opening anything.
435
+ *
436
+ * Returns `null` when the domain is unbound for that project or its
437
+ * connection has closed. Unlike {@link bindProjectDomain} this never triggers
438
+ * an open, so it is safe on synchronous code paths (accessor predicates,
439
+ * health probes) that must not force a migration.
440
+ *
441
+ * @typeParam TDb - The domain's Drizzle handle type, as passed to
442
+ * {@link bindProjectDomain}. Unchecked at runtime — the caller is
443
+ * responsible for using the same type it bound with.
444
+ * @param domain - Stable domain id.
445
+ * @param cwd - Project working directory.
446
+ */
447
+ export function peekProjectDomain(domain, cwd) {
448
+ const dbPath = resolve(resolveDualScopeDbPath('project', cwd));
449
+ const row = _bindings.get(bindingKey('project', dbPath, domain));
450
+ if (row?.native.isOpen)
451
+ return row;
452
+ // An EXPLICIT `cwd` is a precise request: if that project is not bound, say
453
+ // so. Never silently answer with a different project's connection — that is
454
+ // the cross-project hazard this cutover exists to remove.
455
+ if (cwd !== undefined)
456
+ return null;
457
+ // No `cwd` means "the project". Ambient resolution can still miss when the
458
+ // caller bound an EXPLICIT project root that differs from the ambient one
459
+ // (a test fixture, a tool operating on another checkout). Fall back ONLY
460
+ // when the answer is unambiguous — exactly one project bound for this
461
+ // domain. With two or more we return null, forcing the caller to pass a
462
+ // `cwd`, which is precisely the situation where it must.
463
+ const suffix = `::${domain}`;
464
+ let sole;
465
+ for (const [key, candidate] of _bindings) {
466
+ if (!key.startsWith('project::') || !key.endsWith(suffix))
467
+ continue;
468
+ if (!candidate.native.isOpen)
469
+ continue;
470
+ if (sole)
471
+ return null; // ambiguous — the caller must disambiguate
472
+ sole = candidate;
473
+ }
474
+ return sole ?? null;
475
+ }
476
+ /**
477
+ * Synchronous sibling of {@link peekProjectDomain} for the global scope.
478
+ *
479
+ * @typeParam TDb - The domain's Drizzle handle type.
480
+ * @param domain - Stable domain id.
481
+ */
482
+ export function peekGlobalDomain(domain) {
483
+ const dbPath = resolve(resolveDualScopeDbPath('global'));
484
+ const row = _bindings.get(bindingKey('global', dbPath, domain));
485
+ if (!row || !row.native.isOpen)
486
+ return null;
487
+ return row;
488
+ }
489
+ /**
490
+ * Synchronous sibling of {@link boundProjectNative} for the global scope.
491
+ *
492
+ * @param domain - Stable domain id.
493
+ * @returns The live native connection, or `null`.
494
+ *
495
+ * @deprecated See {@link boundProjectNative}.
496
+ */
497
+ export function boundGlobalNative(domain) {
498
+ return peekGlobalDomain(domain)?.native ?? null;
499
+ }
500
+ //# sourceMappingURL=domain-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-binding.js","sourceRoot":"","sources":["../../../src/store/ports/domain-binding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Foreign-key integrity verification for the consolidated dual-scope
3
+ * `cleo.db` files.
4
+ *
5
+ * ## Why this exists (E6-L10 · T11530)
6
+ *
7
+ * The E6 cutover consolidated eight standalone databases into two files
8
+ * (project `cleo.db` and global `cleo.db`) and re-pointed every cross-domain
9
+ * foreign key at the PREFIXED consolidated tables. Two properties must hold
10
+ * afterwards and neither is self-evident:
11
+ *
12
+ * 1. **Referential integrity survived the move.** Exodus copies tables in
13
+ * dependency order, but a partially-applied migration, a hand-edited row,
14
+ * or a legacy DB restored from a pre-consolidation backup can leave an
15
+ * orphan whose parent row never made the trip.
16
+ * 2. **The FKs point at the right tables.** SQLite resolves a `REFERENCES`
17
+ * clause lazily, so an FK naming a table that no longer exists is not an
18
+ * error at DDL time — it fails only when a write touches it. A schema that
19
+ * still references the retired bare `tasks` table looks fine until
20
+ * something inserts.
21
+ *
22
+ * `PRAGMA foreign_key_check` answers both: it walks every FK in the schema and
23
+ * returns one row per violation, including one whose `parent` names a table
24
+ * that does not exist. So a structural defect and a row-level orphan surface
25
+ * through the same path.
26
+ *
27
+ * The verification runs against the LIVE consolidated files, not a fixture —
28
+ * this is the check that proves a real installation is sound.
29
+ *
30
+ * @packageDocumentation
31
+ * @task T11530 (E6-L10)
32
+ * @epic T11249 (E6)
33
+ * @saga T11242 (SG-DB-SUBSTRATE-V2)
34
+ */
35
+ import type { DatabaseSync } from 'node:sqlite';
36
+ import type { DualScope } from '../dual-scope-db.js';
37
+ /**
38
+ * One referential-integrity violation.
39
+ *
40
+ * Mirrors a `PRAGMA foreign_key_check` row, plus the scope it was found in so
41
+ * a combined project+global report stays attributable.
42
+ *
43
+ * @task T11530
44
+ */
45
+ export interface ForeignKeyViolation {
46
+ /** Which consolidated `cleo.db` the violation was found in. */
47
+ readonly scope: DualScope;
48
+ /** The child table holding the orphan row. */
49
+ readonly table: string;
50
+ /**
51
+ * `rowid` of the orphan row, or `null` when the violation is structural
52
+ * (the referenced parent TABLE does not exist, so there is no single row).
53
+ */
54
+ readonly rowid: number | null;
55
+ /** The parent table the foreign key references. */
56
+ readonly parent: string;
57
+ /**
58
+ * Index of the failing foreign key within the child table's FK list, as
59
+ * reported by `PRAGMA foreign_key_list`. `-1` for structural violations.
60
+ */
61
+ readonly fkid: number;
62
+ }
63
+ /**
64
+ * Result of verifying one or both consolidated scopes.
65
+ *
66
+ * @task T11530
67
+ */
68
+ export interface ForeignKeyIntegrityReport {
69
+ /** `true` when {@link violations} is empty. */
70
+ readonly ok: boolean;
71
+ /** The scopes actually checked. */
72
+ readonly scopes: readonly DualScope[];
73
+ /** Every violation found, in scope order. */
74
+ readonly violations: readonly ForeignKeyViolation[];
75
+ }
76
+ /**
77
+ * Run `PRAGMA foreign_key_check` against one open connection.
78
+ *
79
+ * @param native - An open consolidated `cleo.db` connection.
80
+ * @param scope - The scope label to attribute violations to.
81
+ * @returns Every violation the pragma reported.
82
+ */
83
+ export declare function checkForeignKeys(native: DatabaseSync, scope: DualScope): readonly ForeignKeyViolation[];
84
+ /**
85
+ * Verify foreign-key integrity across the consolidated dual-scope databases.
86
+ *
87
+ * Opens each requested scope through the ProjectStore/GlobalStore ports — the
88
+ * same chokepoint the runtime uses — so the check runs against exactly the
89
+ * connection (and therefore the schema and pragmas) production code sees.
90
+ *
91
+ * The `establish` callbacks are deliberately inert: this is a read-only probe
92
+ * and must not reconcile or migrate anything as a side effect of checking.
93
+ *
94
+ * @param options - Which scopes to check and which project.
95
+ * @returns A report; `ok` is `true` only when ZERO violations were found.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const report = await verifyForeignKeyIntegrity({ cwd: projectRoot });
100
+ * if (!report.ok) {
101
+ * for (const v of report.violations) {
102
+ * console.error(`${v.scope}: ${v.table} row ${v.rowid} → missing ${v.parent}`);
103
+ * }
104
+ * }
105
+ * ```
106
+ *
107
+ * @task T11530
108
+ */
109
+ export declare function verifyForeignKeyIntegrity(options?: {
110
+ /** Project working directory. Defaults to the ambient project. */
111
+ readonly cwd?: string;
112
+ /** Scopes to check. Defaults to BOTH — the AC requires project AND global. */
113
+ readonly scopes?: readonly DualScope[];
114
+ }): Promise<ForeignKeyIntegrityReport>;
115
+ /**
116
+ * Outcome of a repair pass.
117
+ *
118
+ * @task T11530
119
+ */
120
+ export interface ForeignKeyRepairReport {
121
+ /** Violations found before the repair ran. */
122
+ readonly found: readonly ForeignKeyViolation[];
123
+ /** Violations the repair deleted (parent FK declared `ON DELETE CASCADE`). */
124
+ readonly repaired: readonly ForeignKeyViolation[];
125
+ /**
126
+ * Violations left in place because the schema does NOT authorise deleting
127
+ * the child — the FK's `ON DELETE` is not `CASCADE`, so removing the row is
128
+ * a judgement call an operator must make.
129
+ */
130
+ readonly skipped: readonly ForeignKeyViolation[];
131
+ /** `true` when a re-check after the repair reported zero violations. */
132
+ readonly ok: boolean;
133
+ }
134
+ /**
135
+ * Delete orphan rows whose foreign key declares `ON DELETE CASCADE`.
136
+ *
137
+ * These rows are the residue of a parent deletion that happened while FK
138
+ * enforcement was off — most commonly a pre-consolidation legacy database that
139
+ * exodus then copied verbatim. The schema already says they should not exist;
140
+ * this restores that invariant.
141
+ *
142
+ * Rows whose FK is NOT `CASCADE` are reported in `skipped` and left untouched.
143
+ *
144
+ * @param options - Which scopes to repair and which project.
145
+ * @returns What was found, repaired, and deliberately skipped.
146
+ *
147
+ * @task T11530
148
+ */
149
+ export declare function repairForeignKeyViolations(options?: {
150
+ /** Project working directory. Defaults to the ambient project. */
151
+ readonly cwd?: string;
152
+ /** Scopes to repair. Defaults to BOTH. */
153
+ readonly scopes?: readonly DualScope[];
154
+ }): Promise<ForeignKeyRepairReport>;
155
+ //# sourceMappingURL=fk-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fk-integrity.d.ts","sourceRoot":"","sources":["../../../src/store/ports/fk-integrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,mCAAmC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACrD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,SAAS,GACf,SAAS,mBAAmB,EAAE,CA6BhC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,CAAC,EAAE;IACxD,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAcrC;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,wEAAwE;IACxE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AA6BD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,CAAC,EAAE;IACzD,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAuClC"}