@cleocode/core 2026.9.7 → 2026.9.9

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 (565) hide show
  1. package/dist/audit/reconstruct.d.ts +13 -43
  2. package/dist/audit/reconstruct.d.ts.map +1 -1
  3. package/dist/audit/reconstruct.js +295 -290
  4. package/dist/audit/reconstruct.js.map +1 -1
  5. package/dist/bootstrap.d.ts.map +1 -1
  6. package/dist/bootstrap.js +8 -2
  7. package/dist/bootstrap.js.map +1 -1
  8. package/dist/dispatch/mutate-projection.d.ts.map +1 -1
  9. package/dist/dispatch/mutate-projection.js +12 -0
  10. package/dist/dispatch/mutate-projection.js.map +1 -1
  11. package/dist/dispatch/mvi-projection.d.ts +29 -6
  12. package/dist/dispatch/mvi-projection.d.ts.map +1 -1
  13. package/dist/dispatch/mvi-projection.js +109 -139
  14. package/dist/dispatch/mvi-projection.js.map +1 -1
  15. package/dist/docs/docs-read-model.d.ts +3 -2
  16. package/dist/docs/docs-read-model.d.ts.map +1 -1
  17. package/dist/docs/docs-read-model.js +4 -3
  18. package/dist/docs/docs-read-model.js.map +1 -1
  19. package/dist/docs/export-document.js +5100 -2297
  20. package/dist/docs/export-document.js.map +3 -3
  21. package/dist/docs/projection.d.ts +67 -0
  22. package/dist/docs/projection.d.ts.map +1 -0
  23. package/dist/docs/projection.js +370 -0
  24. package/dist/docs/projection.js.map +1 -0
  25. package/dist/doctor/acceptance-drift.d.ts +6 -2
  26. package/dist/doctor/acceptance-drift.d.ts.map +1 -1
  27. package/dist/doctor/acceptance-drift.js +43 -5
  28. package/dist/doctor/acceptance-drift.js.map +1 -1
  29. package/dist/doctor/knowledge-summary.d.ts +41 -0
  30. package/dist/doctor/knowledge-summary.d.ts.map +1 -0
  31. package/dist/doctor/knowledge-summary.js +81 -0
  32. package/dist/doctor/knowledge-summary.js.map +1 -0
  33. package/dist/doctor/knowledge.d.ts +192 -0
  34. package/dist/doctor/knowledge.d.ts.map +1 -0
  35. package/dist/doctor/knowledge.js +1864 -0
  36. package/dist/doctor/knowledge.js.map +1 -0
  37. package/dist/git/work-tree.d.ts +64 -0
  38. package/dist/git/work-tree.d.ts.map +1 -0
  39. package/dist/git/work-tree.js +111 -0
  40. package/dist/git/work-tree.js.map +1 -0
  41. package/dist/hooks/handlers/task-hooks.d.ts +8 -5
  42. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  43. package/dist/hooks/handlers/task-hooks.js +15 -60
  44. package/dist/hooks/handlers/task-hooks.js.map +1 -1
  45. package/dist/identity/cleo-identity.d.ts +13 -0
  46. package/dist/identity/cleo-identity.d.ts.map +1 -1
  47. package/dist/identity/cleo-identity.js +20 -6
  48. package/dist/identity/cleo-identity.js.map +1 -1
  49. package/dist/injection.d.ts.map +1 -1
  50. package/dist/injection.js +29 -13
  51. package/dist/injection.js.map +1 -1
  52. package/dist/internal.d.ts +2 -0
  53. package/dist/internal.d.ts.map +1 -1
  54. package/dist/internal.js +2 -0
  55. package/dist/internal.js.map +1 -1
  56. package/dist/lifecycle/verification/satisfies-validator.d.ts.map +1 -1
  57. package/dist/lifecycle/verification/satisfies-validator.js +124 -120
  58. package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
  59. package/dist/llm/plugin-facade.js +4018 -1726
  60. package/dist/llm/plugin-facade.js.map +3 -3
  61. package/dist/memory/brain-backfill.d.ts +27 -12
  62. package/dist/memory/brain-backfill.d.ts.map +1 -1
  63. package/dist/memory/brain-backfill.js +325 -134
  64. package/dist/memory/brain-backfill.js.map +1 -1
  65. package/dist/memory/brain-doctor.d.ts +21 -0
  66. package/dist/memory/brain-doctor.d.ts.map +1 -1
  67. package/dist/memory/brain-doctor.js +111 -53
  68. package/dist/memory/brain-doctor.js.map +1 -1
  69. package/dist/memory/brain-links.d.ts.map +1 -1
  70. package/dist/memory/brain-links.js +31 -4
  71. package/dist/memory/brain-links.js.map +1 -1
  72. package/dist/memory/brain-reasoning.d.ts.map +1 -1
  73. package/dist/memory/brain-reasoning.js +28 -2
  74. package/dist/memory/brain-reasoning.js.map +1 -1
  75. package/dist/memory/brain-reconciler.d.ts +14 -19
  76. package/dist/memory/brain-reconciler.d.ts.map +1 -1
  77. package/dist/memory/brain-reconciler.js +70 -97
  78. package/dist/memory/brain-reconciler.js.map +1 -1
  79. package/dist/memory/brain-search.d.ts +2 -0
  80. package/dist/memory/brain-search.d.ts.map +1 -1
  81. package/dist/memory/brain-search.js +59 -29
  82. package/dist/memory/brain-search.js.map +1 -1
  83. package/dist/memory/brain-similarity.d.ts.map +1 -1
  84. package/dist/memory/brain-similarity.js +5 -4
  85. package/dist/memory/brain-similarity.js.map +1 -1
  86. package/dist/memory/brain-stub-prune.d.ts +31 -9
  87. package/dist/memory/brain-stub-prune.d.ts.map +1 -1
  88. package/dist/memory/brain-stub-prune.js +65 -18
  89. package/dist/memory/brain-stub-prune.js.map +1 -1
  90. package/dist/memory/brain-writer-handlers.d.ts +10 -1
  91. package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
  92. package/dist/memory/brain-writer-handlers.js +23 -3
  93. package/dist/memory/brain-writer-handlers.js.map +1 -1
  94. package/dist/memory/brain-writer-thread.d.ts +31 -1
  95. package/dist/memory/brain-writer-thread.d.ts.map +1 -1
  96. package/dist/memory/brain-writer-thread.js +166 -24
  97. package/dist/memory/brain-writer-thread.js.map +1 -1
  98. package/dist/memory/brain-writer-worker.js +8 -1
  99. package/dist/memory/brain-writer-worker.js.map +1 -1
  100. package/dist/memory/decision-cross-link.d.ts +39 -0
  101. package/dist/memory/decision-cross-link.d.ts.map +1 -1
  102. package/dist/memory/decision-cross-link.js +316 -0
  103. package/dist/memory/decision-cross-link.js.map +1 -1
  104. package/dist/memory/decisions.d.ts +4 -1
  105. package/dist/memory/decisions.d.ts.map +1 -1
  106. package/dist/memory/decisions.js +7 -24
  107. package/dist/memory/decisions.js.map +1 -1
  108. package/dist/memory/dispatch-trace.d.ts +12 -5
  109. package/dist/memory/dispatch-trace.d.ts.map +1 -1
  110. package/dist/memory/dispatch-trace.js +17 -7
  111. package/dist/memory/dispatch-trace.js.map +1 -1
  112. package/dist/memory/eligibility.d.ts +51 -0
  113. package/dist/memory/eligibility.d.ts.map +1 -0
  114. package/dist/memory/eligibility.js +77 -0
  115. package/dist/memory/eligibility.js.map +1 -0
  116. package/dist/memory/engine-compat.d.ts +2 -0
  117. package/dist/memory/engine-compat.d.ts.map +1 -1
  118. package/dist/memory/engine-compat.js +10 -0
  119. package/dist/memory/engine-compat.js.map +1 -1
  120. package/dist/memory/extraction-gate.d.ts +21 -7
  121. package/dist/memory/extraction-gate.d.ts.map +1 -1
  122. package/dist/memory/extraction-gate.js +178 -216
  123. package/dist/memory/extraction-gate.js.map +1 -1
  124. package/dist/memory/graph-auto-populate.d.ts +38 -5
  125. package/dist/memory/graph-auto-populate.d.ts.map +1 -1
  126. package/dist/memory/graph-auto-populate.js +182 -60
  127. package/dist/memory/graph-auto-populate.js.map +1 -1
  128. package/dist/memory/graph-memory-bridge.d.ts +14 -1
  129. package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
  130. package/dist/memory/graph-memory-bridge.js +66 -28
  131. package/dist/memory/graph-memory-bridge.js.map +1 -1
  132. package/dist/memory/index.d.ts +1 -19
  133. package/dist/memory/index.d.ts.map +1 -1
  134. package/dist/memory/index.js +1 -50
  135. package/dist/memory/index.js.map +1 -1
  136. package/dist/memory/learnings.d.ts.map +1 -1
  137. package/dist/memory/learnings.js +2 -23
  138. package/dist/memory/learnings.js.map +1 -1
  139. package/dist/memory/manifest-filter.d.ts +21 -0
  140. package/dist/memory/manifest-filter.d.ts.map +1 -0
  141. package/dist/memory/manifest-filter.js +51 -0
  142. package/dist/memory/manifest-filter.js.map +1 -0
  143. package/dist/memory/manifest-ingestion.d.ts +28 -30
  144. package/dist/memory/manifest-ingestion.d.ts.map +1 -1
  145. package/dist/memory/manifest-ingestion.js +104 -235
  146. package/dist/memory/manifest-ingestion.js.map +1 -1
  147. package/dist/memory/observer-reflector.d.ts +2 -0
  148. package/dist/memory/observer-reflector.d.ts.map +1 -1
  149. package/dist/memory/observer-reflector.js +6 -49
  150. package/dist/memory/observer-reflector.js.map +1 -1
  151. package/dist/memory/patterns.d.ts +10 -0
  152. package/dist/memory/patterns.d.ts.map +1 -1
  153. package/dist/memory/patterns.js +25 -33
  154. package/dist/memory/patterns.js.map +1 -1
  155. package/dist/memory/pipeline-manifest-sqlite.d.ts +72 -9
  156. package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
  157. package/dist/memory/pipeline-manifest-sqlite.js +508 -412
  158. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
  159. package/dist/memory/retrieval/build-retrieval-bundle.d.ts.map +1 -1
  160. package/dist/memory/retrieval/build-retrieval-bundle.js +16 -15
  161. package/dist/memory/retrieval/build-retrieval-bundle.js.map +1 -1
  162. package/dist/memory/retrieval/fetch.d.ts +7 -0
  163. package/dist/memory/retrieval/fetch.d.ts.map +1 -1
  164. package/dist/memory/retrieval/fetch.js +14 -11
  165. package/dist/memory/retrieval/fetch.js.map +1 -1
  166. package/dist/memory/retrieval/observe.d.ts +7 -1
  167. package/dist/memory/retrieval/observe.d.ts.map +1 -1
  168. package/dist/memory/retrieval/observe.js +58 -4
  169. package/dist/memory/retrieval/observe.js.map +1 -1
  170. package/dist/memory/retrieval/search.d.ts +14 -1
  171. package/dist/memory/retrieval/search.d.ts.map +1 -1
  172. package/dist/memory/retrieval/search.js +133 -69
  173. package/dist/memory/retrieval/search.js.map +1 -1
  174. package/dist/memory/retrieval/telemetry.d.ts +31 -0
  175. package/dist/memory/retrieval/telemetry.d.ts.map +1 -0
  176. package/dist/memory/retrieval/telemetry.js +53 -0
  177. package/dist/memory/retrieval/telemetry.js.map +1 -0
  178. package/dist/nexus/analyze-orchestrator.d.ts +41 -9
  179. package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
  180. package/dist/nexus/analyze-orchestrator.js +257 -58
  181. package/dist/nexus/analyze-orchestrator.js.map +1 -1
  182. package/dist/nexus/api-contracts.d.ts +2 -6
  183. package/dist/nexus/api-contracts.d.ts.map +1 -1
  184. package/dist/nexus/api-contracts.js +11 -2
  185. package/dist/nexus/api-contracts.js.map +1 -1
  186. package/dist/nexus/context.d.ts.map +1 -1
  187. package/dist/nexus/context.js +14 -37
  188. package/dist/nexus/context.js.map +1 -1
  189. package/dist/nexus/diff.d.ts.map +1 -1
  190. package/dist/nexus/diff.js +2 -1
  191. package/dist/nexus/diff.js.map +1 -1
  192. package/dist/nexus/identity.d.ts +29 -3
  193. package/dist/nexus/identity.d.ts.map +1 -1
  194. package/dist/nexus/identity.js +63 -12
  195. package/dist/nexus/identity.js.map +1 -1
  196. package/dist/nexus/impact.d.ts +21 -19
  197. package/dist/nexus/impact.d.ts.map +1 -1
  198. package/dist/nexus/impact.js +96 -192
  199. package/dist/nexus/impact.js.map +1 -1
  200. package/dist/nexus/knowledge.d.ts +80 -0
  201. package/dist/nexus/knowledge.d.ts.map +1 -0
  202. package/dist/nexus/knowledge.js +786 -0
  203. package/dist/nexus/knowledge.js.map +1 -0
  204. package/dist/nexus/living-brain.d.ts +8 -5
  205. package/dist/nexus/living-brain.d.ts.map +1 -1
  206. package/dist/nexus/living-brain.js +252 -188
  207. package/dist/nexus/living-brain.js.map +1 -1
  208. package/dist/nexus/registry.d.ts +11 -2
  209. package/dist/nexus/registry.d.ts.map +1 -1
  210. package/dist/nexus/registry.js +144 -128
  211. package/dist/nexus/registry.js.map +1 -1
  212. package/dist/nexus/source-roots.d.ts +16 -0
  213. package/dist/nexus/source-roots.d.ts.map +1 -0
  214. package/dist/nexus/source-roots.js +174 -0
  215. package/dist/nexus/source-roots.js.map +1 -0
  216. package/dist/nexus/task-evidence.d.ts +22 -0
  217. package/dist/nexus/task-evidence.d.ts.map +1 -0
  218. package/dist/nexus/task-evidence.js +210 -0
  219. package/dist/nexus/task-evidence.js.map +1 -0
  220. package/dist/nexus/tasks-bridge.d.ts +14 -7
  221. package/dist/nexus/tasks-bridge.d.ts.map +1 -1
  222. package/dist/nexus/tasks-bridge.js +81 -40
  223. package/dist/nexus/tasks-bridge.js.map +1 -1
  224. package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
  225. package/dist/orchestrate/lifecycle-ops.js +170 -120
  226. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  227. package/dist/orchestrate/query-ops.d.ts +6 -7
  228. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  229. package/dist/orchestrate/query-ops.js +509 -532
  230. package/dist/orchestrate/query-ops.js.map +1 -1
  231. package/dist/orchestration/bootstrap.d.ts +15 -1
  232. package/dist/orchestration/bootstrap.d.ts.map +1 -1
  233. package/dist/orchestration/bootstrap.js +21 -10
  234. package/dist/orchestration/bootstrap.js.map +1 -1
  235. package/dist/orchestration/index.d.ts +35 -3
  236. package/dist/orchestration/index.d.ts.map +1 -1
  237. package/dist/orchestration/index.js +64 -35
  238. package/dist/orchestration/index.js.map +1 -1
  239. package/dist/orchestration/lead-rollup.d.ts +24 -1
  240. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  241. package/dist/orchestration/lead-rollup.js +31 -11
  242. package/dist/orchestration/lead-rollup.js.map +1 -1
  243. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  244. package/dist/orchestration/spawn-prompt.js +16 -41
  245. package/dist/orchestration/spawn-prompt.js.map +1 -1
  246. package/dist/orchestration/unblock.d.ts +15 -1
  247. package/dist/orchestration/unblock.d.ts.map +1 -1
  248. package/dist/orchestration/unblock.js +23 -12
  249. package/dist/orchestration/unblock.js.map +1 -1
  250. package/dist/orchestration/validate-spawn.d.ts.map +1 -1
  251. package/dist/orchestration/validate-spawn.js +3 -3
  252. package/dist/orchestration/validate-spawn.js.map +1 -1
  253. package/dist/orchestration/waves.d.ts +29 -13
  254. package/dist/orchestration/waves.d.ts.map +1 -1
  255. package/dist/orchestration/waves.js +43 -55
  256. package/dist/orchestration/waves.js.map +1 -1
  257. package/dist/paths.d.ts +17 -163
  258. package/dist/paths.d.ts.map +1 -1
  259. package/dist/paths.js +145 -496
  260. package/dist/paths.js.map +1 -1
  261. package/dist/project-info.d.ts +2 -11
  262. package/dist/project-info.d.ts.map +1 -1
  263. package/dist/project-info.js +3 -31
  264. package/dist/project-info.js.map +1 -1
  265. package/dist/project-scope.d.ts +239 -0
  266. package/dist/project-scope.d.ts.map +1 -0
  267. package/dist/project-scope.js +473 -0
  268. package/dist/project-scope.js.map +1 -0
  269. package/dist/release/pr-evidence.d.ts +16 -3
  270. package/dist/release/pr-evidence.d.ts.map +1 -1
  271. package/dist/release/pr-evidence.js +53 -7
  272. package/dist/release/pr-evidence.js.map +1 -1
  273. package/dist/render/nexus/graph/index.d.ts.map +1 -1
  274. package/dist/render/nexus/graph/index.js +4 -2
  275. package/dist/render/nexus/graph/index.js.map +1 -1
  276. package/dist/render/orchestration/audit-reconstruct.d.ts +11 -0
  277. package/dist/render/orchestration/audit-reconstruct.d.ts.map +1 -1
  278. package/dist/render/orchestration/audit-reconstruct.js +21 -6
  279. package/dist/render/orchestration/audit-reconstruct.js.map +1 -1
  280. package/dist/render/tasks/show.js +2 -1
  281. package/dist/render/tasks/show.js.map +1 -1
  282. package/dist/resources/spawn-wrapper.d.ts +82 -14
  283. package/dist/resources/spawn-wrapper.d.ts.map +1 -1
  284. package/dist/resources/spawn-wrapper.js +585 -27
  285. package/dist/resources/spawn-wrapper.js.map +1 -1
  286. package/dist/selfimprove/fix-gen.js +4026 -1734
  287. package/dist/selfimprove/fix-gen.js.map +3 -3
  288. package/dist/sequence/index.d.ts.map +1 -1
  289. package/dist/sequence/index.js +76 -68
  290. package/dist/sequence/index.js.map +1 -1
  291. package/dist/session/canon-lint.d.ts +24 -3
  292. package/dist/session/canon-lint.d.ts.map +1 -1
  293. package/dist/session/canon-lint.js +72 -31
  294. package/dist/session/canon-lint.js.map +1 -1
  295. package/dist/session/engine-ops.d.ts +1 -4
  296. package/dist/session/engine-ops.d.ts.map +1 -1
  297. package/dist/session/engine-ops.js +6 -2
  298. package/dist/session/engine-ops.js.map +1 -1
  299. package/dist/sessions/briefing.d.ts +11 -1
  300. package/dist/sessions/briefing.d.ts.map +1 -1
  301. package/dist/sessions/briefing.js +76 -8
  302. package/dist/sessions/briefing.js.map +1 -1
  303. package/dist/sessions/handoff.d.ts +3 -1
  304. package/dist/sessions/handoff.d.ts.map +1 -1
  305. package/dist/sessions/handoff.js +2 -0
  306. package/dist/sessions/handoff.js.map +1 -1
  307. package/dist/shutdown-deadline.d.ts +24 -33
  308. package/dist/shutdown-deadline.d.ts.map +1 -1
  309. package/dist/shutdown-deadline.js +71 -14
  310. package/dist/shutdown-deadline.js.map +1 -1
  311. package/dist/shutdown.d.ts +21 -10
  312. package/dist/shutdown.d.ts.map +1 -1
  313. package/dist/shutdown.js +91 -35
  314. package/dist/shutdown.js.map +1 -1
  315. package/dist/skills/index.d.ts +1 -1
  316. package/dist/skills/index.d.ts.map +1 -1
  317. package/dist/skills/index.js +1 -1
  318. package/dist/skills/index.js.map +1 -1
  319. package/dist/skills/orchestrator/validator.d.ts +87 -20
  320. package/dist/skills/orchestrator/validator.d.ts.map +1 -1
  321. package/dist/skills/orchestrator/validator.js +330 -241
  322. package/dist/skills/orchestrator/validator.js.map +1 -1
  323. package/dist/store/agent-resolver.d.ts.map +1 -1
  324. package/dist/store/agent-resolver.js +16 -9
  325. package/dist/store/agent-resolver.js.map +1 -1
  326. package/dist/store/attachment-store.d.ts +4 -1
  327. package/dist/store/attachment-store.d.ts.map +1 -1
  328. package/dist/store/attachment-store.js +20 -3
  329. package/dist/store/attachment-store.js.map +1 -1
  330. package/dist/store/background-jobs.d.ts +426 -105
  331. package/dist/store/background-jobs.d.ts.map +1 -1
  332. package/dist/store/background-jobs.js +1208 -252
  333. package/dist/store/background-jobs.js.map +1 -1
  334. package/dist/store/background-ops.d.ts +145 -8
  335. package/dist/store/background-ops.d.ts.map +1 -1
  336. package/dist/store/background-ops.js +459 -15
  337. package/dist/store/background-ops.js.map +1 -1
  338. package/dist/store/backup-inspect.d.ts +48 -4
  339. package/dist/store/backup-inspect.d.ts.map +1 -1
  340. package/dist/store/backup-inspect.js +384 -4
  341. package/dist/store/backup-inspect.js.map +1 -1
  342. package/dist/store/converters.d.ts.map +1 -1
  343. package/dist/store/converters.js +19 -2
  344. package/dist/store/converters.js.map +1 -1
  345. package/dist/store/data-accessor.d.ts +9 -1
  346. package/dist/store/data-accessor.d.ts.map +1 -1
  347. package/dist/store/data-accessor.js +22 -5
  348. package/dist/store/data-accessor.js.map +1 -1
  349. package/dist/store/data-safety-central.d.ts.map +1 -1
  350. package/dist/store/data-safety-central.js +272 -232
  351. package/dist/store/data-safety-central.js.map +1 -1
  352. package/dist/store/db-helpers.d.ts +19 -3
  353. package/dist/store/db-helpers.d.ts.map +1 -1
  354. package/dist/store/db-helpers.js +42 -58
  355. package/dist/store/db-helpers.js.map +1 -1
  356. package/dist/store/dual-scope-db.d.ts +63 -4
  357. package/dist/store/dual-scope-db.d.ts.map +1 -1
  358. package/dist/store/dual-scope-db.js +166 -19
  359. package/dist/store/dual-scope-db.js.map +1 -1
  360. package/dist/store/exodus/archive.d.ts +21 -7
  361. package/dist/store/exodus/archive.d.ts.map +1 -1
  362. package/dist/store/exodus/archive.js +60 -18
  363. package/dist/store/exodus/archive.js.map +1 -1
  364. package/dist/store/exodus/migrate.d.ts.map +1 -1
  365. package/dist/store/exodus/migrate.js +27 -23
  366. package/dist/store/exodus/migrate.js.map +1 -1
  367. package/dist/store/exodus/on-open.d.ts +16 -27
  368. package/dist/store/exodus/on-open.d.ts.map +1 -1
  369. package/dist/store/exodus/on-open.js +121 -169
  370. package/dist/store/exodus/on-open.js.map +1 -1
  371. package/dist/store/exodus/recovery.d.ts +90 -0
  372. package/dist/store/exodus/recovery.d.ts.map +1 -0
  373. package/dist/store/exodus/recovery.js +434 -0
  374. package/dist/store/exodus/recovery.js.map +1 -0
  375. package/dist/store/exodus/types.d.ts +15 -0
  376. package/dist/store/exodus/types.d.ts.map +1 -1
  377. package/dist/store/memory-accessor.d.ts +52 -3
  378. package/dist/store/memory-accessor.d.ts.map +1 -1
  379. package/dist/store/memory-accessor.js +122 -7
  380. package/dist/store/memory-accessor.js.map +1 -1
  381. package/dist/store/nexus-sqlite.d.ts +24 -6
  382. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  383. package/dist/store/nexus-sqlite.js +39 -20
  384. package/dist/store/nexus-sqlite.js.map +1 -1
  385. package/dist/store/ports/domain-binding.d.ts.map +1 -1
  386. package/dist/store/ports/domain-binding.js +27 -5
  387. package/dist/store/ports/domain-binding.js.map +1 -1
  388. package/dist/store/schema/background-jobs.d.ts +181 -2
  389. package/dist/store/schema/background-jobs.d.ts.map +1 -1
  390. package/dist/store/schema/background-jobs.js +27 -3
  391. package/dist/store/schema/background-jobs.js.map +1 -1
  392. package/dist/store/schema/cleo-global/skills.d.ts +2 -2
  393. package/dist/store/schema/cleo-project/lifecycle.d.ts +2 -2
  394. package/dist/store/schema/cleo-project/runtime.d.ts +1 -1
  395. package/dist/store/schema/cleo-project/tasks-core.d.ts +1 -1
  396. package/dist/store/schema/lifecycle.d.ts +2 -2
  397. package/dist/store/schema/skills-schema.d.ts +2 -2
  398. package/dist/store/schema/tasks.d.ts +1 -1
  399. package/dist/store/session-store.d.ts +3 -1
  400. package/dist/store/session-store.d.ts.map +1 -1
  401. package/dist/store/session-store.js +252 -207
  402. package/dist/store/session-store.js.map +1 -1
  403. package/dist/store/sqlite-data-accessor.d.ts +39 -4
  404. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  405. package/dist/store/sqlite-data-accessor.js +689 -527
  406. package/dist/store/sqlite-data-accessor.js.map +1 -1
  407. package/dist/store/tasks-sqlite.d.ts +3 -1
  408. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  409. package/dist/store/tasks-sqlite.js +309 -279
  410. package/dist/store/tasks-sqlite.js.map +1 -1
  411. package/dist/store/validation-schemas.d.ts +8 -8
  412. package/dist/store/writer-lease.d.ts +80 -8
  413. package/dist/store/writer-lease.d.ts.map +1 -1
  414. package/dist/store/writer-lease.js +503 -134
  415. package/dist/store/writer-lease.js.map +1 -1
  416. package/dist/system/audit.d.ts +2 -1
  417. package/dist/system/audit.d.ts.map +1 -1
  418. package/dist/system/audit.js +118 -71
  419. package/dist/system/audit.js.map +1 -1
  420. package/dist/system/backup.d.ts.map +1 -1
  421. package/dist/system/backup.js +5 -5
  422. package/dist/system/backup.js.map +1 -1
  423. package/dist/system/inject-generate.js +1 -1
  424. package/dist/task-work/index.d.ts +3 -0
  425. package/dist/task-work/index.d.ts.map +1 -1
  426. package/dist/task-work/index.js +34 -37
  427. package/dist/task-work/index.js.map +1 -1
  428. package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
  429. package/dist/tasks/ac-coverage-gate.js +4 -0
  430. package/dist/tasks/ac-coverage-gate.js.map +1 -1
  431. package/dist/tasks/ac-immutability.d.ts +7 -4
  432. package/dist/tasks/ac-immutability.d.ts.map +1 -1
  433. package/dist/tasks/ac-immutability.js +19 -17
  434. package/dist/tasks/ac-immutability.js.map +1 -1
  435. package/dist/tasks/ac-table.d.ts +5 -5
  436. package/dist/tasks/ac-table.d.ts.map +1 -1
  437. package/dist/tasks/ac-table.js +26 -12
  438. package/dist/tasks/ac-table.js.map +1 -1
  439. package/dist/tasks/acceptance-input.d.ts +52 -0
  440. package/dist/tasks/acceptance-input.d.ts.map +1 -0
  441. package/dist/tasks/acceptance-input.js +259 -0
  442. package/dist/tasks/acceptance-input.js.map +1 -0
  443. package/dist/tasks/add.d.ts +67 -29
  444. package/dist/tasks/add.d.ts.map +1 -1
  445. package/dist/tasks/add.js +255 -97
  446. package/dist/tasks/add.js.map +1 -1
  447. package/dist/tasks/complete.d.ts +16 -1
  448. package/dist/tasks/complete.d.ts.map +1 -1
  449. package/dist/tasks/complete.js +152 -55
  450. package/dist/tasks/complete.js.map +1 -1
  451. package/dist/tasks/decompose.d.ts +103 -0
  452. package/dist/tasks/decompose.d.ts.map +1 -0
  453. package/dist/tasks/decompose.js +224 -0
  454. package/dist/tasks/decompose.js.map +1 -0
  455. package/dist/tasks/delete.d.ts +5 -8
  456. package/dist/tasks/delete.d.ts.map +1 -1
  457. package/dist/tasks/delete.js +4 -3
  458. package/dist/tasks/delete.js.map +1 -1
  459. package/dist/tasks/dependency-check.d.ts +59 -0
  460. package/dist/tasks/dependency-check.d.ts.map +1 -1
  461. package/dist/tasks/dependency-check.js +73 -0
  462. package/dist/tasks/dependency-check.js.map +1 -1
  463. package/dist/tasks/deps-ready.d.ts +13 -1
  464. package/dist/tasks/deps-ready.d.ts.map +1 -1
  465. package/dist/tasks/deps-ready.js +16 -5
  466. package/dist/tasks/deps-ready.js.map +1 -1
  467. package/dist/tasks/engine-converters.d.ts +7 -0
  468. package/dist/tasks/engine-converters.d.ts.map +1 -1
  469. package/dist/tasks/engine-converters.js +8 -1
  470. package/dist/tasks/engine-converters.js.map +1 -1
  471. package/dist/tasks/evidence.d.ts +54 -26
  472. package/dist/tasks/evidence.d.ts.map +1 -1
  473. package/dist/tasks/evidence.js +292 -40
  474. package/dist/tasks/evidence.js.map +1 -1
  475. package/dist/tasks/find.d.ts +16 -12
  476. package/dist/tasks/find.d.ts.map +1 -1
  477. package/dist/tasks/find.js +107 -93
  478. package/dist/tasks/find.js.map +1 -1
  479. package/dist/tasks/gate-runner.d.ts +88 -14
  480. package/dist/tasks/gate-runner.d.ts.map +1 -1
  481. package/dist/tasks/gate-runner.js +596 -253
  482. package/dist/tasks/gate-runner.js.map +1 -1
  483. package/dist/tasks/hierarchy-policy.d.ts +8 -0
  484. package/dist/tasks/hierarchy-policy.d.ts.map +1 -1
  485. package/dist/tasks/hierarchy-policy.js +9 -2
  486. package/dist/tasks/hierarchy-policy.js.map +1 -1
  487. package/dist/tasks/hierarchy.d.ts +67 -1
  488. package/dist/tasks/hierarchy.d.ts.map +1 -1
  489. package/dist/tasks/hierarchy.js +81 -1
  490. package/dist/tasks/hierarchy.js.map +1 -1
  491. package/dist/tasks/index.d.ts +3 -0
  492. package/dist/tasks/index.d.ts.map +1 -1
  493. package/dist/tasks/index.js +3 -0
  494. package/dist/tasks/index.js.map +1 -1
  495. package/dist/tasks/infer-add-params.d.ts +1 -20
  496. package/dist/tasks/infer-add-params.d.ts.map +1 -1
  497. package/dist/tasks/infer-add-params.js +2 -203
  498. package/dist/tasks/infer-add-params.js.map +1 -1
  499. package/dist/tasks/list.d.ts +7 -2
  500. package/dist/tasks/list.d.ts.map +1 -1
  501. package/dist/tasks/list.js +24 -36
  502. package/dist/tasks/list.js.map +1 -1
  503. package/dist/tasks/ops.d.ts +6 -61
  504. package/dist/tasks/ops.d.ts.map +1 -1
  505. package/dist/tasks/ops.js +5 -53
  506. package/dist/tasks/ops.js.map +1 -1
  507. package/dist/tasks/plan.d.ts +15 -1
  508. package/dist/tasks/plan.d.ts.map +1 -1
  509. package/dist/tasks/plan.js +23 -25
  510. package/dist/tasks/plan.js.map +1 -1
  511. package/dist/tasks/population.d.ts +46 -0
  512. package/dist/tasks/population.d.ts.map +1 -0
  513. package/dist/tasks/population.js +88 -0
  514. package/dist/tasks/population.js.map +1 -0
  515. package/dist/tasks/reconcile-scope.d.ts +157 -0
  516. package/dist/tasks/reconcile-scope.d.ts.map +1 -0
  517. package/dist/tasks/reconcile-scope.js +242 -0
  518. package/dist/tasks/reconcile-scope.js.map +1 -0
  519. package/dist/tasks/req.d.ts +51 -12
  520. package/dist/tasks/req.d.ts.map +1 -1
  521. package/dist/tasks/req.js +125 -69
  522. package/dist/tasks/req.js.map +1 -1
  523. package/dist/tasks/session-scope.d.ts +6 -31
  524. package/dist/tasks/session-scope.d.ts.map +1 -1
  525. package/dist/tasks/session-scope.js +3 -17
  526. package/dist/tasks/session-scope.js.map +1 -1
  527. package/dist/tasks/severity-attestation.d.ts +39 -10
  528. package/dist/tasks/severity-attestation.d.ts.map +1 -1
  529. package/dist/tasks/severity-attestation.js +75 -36
  530. package/dist/tasks/severity-attestation.js.map +1 -1
  531. package/dist/tasks/task-next.d.ts +5 -0
  532. package/dist/tasks/task-next.d.ts.map +1 -1
  533. package/dist/tasks/task-next.js +10 -9
  534. package/dist/tasks/task-next.js.map +1 -1
  535. package/dist/tasks/task-reparent.d.ts.map +1 -1
  536. package/dist/tasks/task-reparent.js +3 -13
  537. package/dist/tasks/task-reparent.js.map +1 -1
  538. package/dist/tasks/update.d.ts +20 -43
  539. package/dist/tasks/update.d.ts.map +1 -1
  540. package/dist/tasks/update.js +64 -49
  541. package/dist/tasks/update.js.map +1 -1
  542. package/dist/teardown-signal.d.ts +72 -0
  543. package/dist/teardown-signal.d.ts.map +1 -0
  544. package/dist/teardown-signal.js +108 -0
  545. package/dist/teardown-signal.js.map +1 -0
  546. package/dist/validation/engine-ops.d.ts +17 -1
  547. package/dist/validation/engine-ops.d.ts.map +1 -1
  548. package/dist/validation/engine-ops.js +149 -11
  549. package/dist/validation/engine-ops.js.map +1 -1
  550. package/dist/workgraph/reparent-preflight.d.ts +1 -1
  551. package/dist/workgraph/reparent-preflight.d.ts.map +1 -1
  552. package/dist/workgraph/reparent-preflight.js +6 -1
  553. package/dist/workgraph/reparent-preflight.js.map +1 -1
  554. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  555. package/dist/workgraph/scaffold-apply.js +81 -76
  556. package/dist/workgraph/scaffold-apply.js.map +1 -1
  557. package/dist/workgraph/structure-validation.d.ts +1 -1
  558. package/dist/workgraph/structure-validation.d.ts.map +1 -1
  559. package/dist/workgraph/structure-validation.js +6 -1
  560. package/dist/workgraph/structure-validation.js.map +1 -1
  561. package/migrations/drizzle-tasks/20260919180000_t12263-job-ownership-fences/migration.sql +34 -0
  562. package/migrations/drizzle-tasks/20260919184500_t12265-durable-pending-proposals/migration.sql +4 -0
  563. package/package.json +18 -13
  564. package/schemas/config.schema.json +2 -2
  565. package/templates/CLEO-INJECTION.md +103 -101
@@ -18,12 +18,14 @@
18
18
  *
19
19
  * T1003: Staged backfill functions (stagedBackfillRun, approveBackfillRun,
20
20
  * rollbackBackfillRun, listBackfillRuns) are appended below the graph
21
- * back-fill core. Staged runs write row IDs to brain_backfill_runs first;
22
- * actual mutations happen only on approve.
21
+ * back-fill core. Staged runs capture exact source/version and incident-edge
22
+ * preconditions in brain_backfill_runs; approval reconstructs only those missing
23
+ * nodes, without synthesizing edges or invoking the broad graph back-fill.
23
24
  *
24
25
  * @task T530
25
26
  * @epic T523
26
27
  */
28
+ import type { KnowledgeBackfillOptions } from '@cleocode/contracts';
27
29
  import type { BrainBackfillRunRow } from '../store/schema/memory-schema.js';
28
30
  /** Result returned by backfillBrainGraph. */
29
31
  export interface BrainBackfillResult {
@@ -84,8 +86,9 @@ export interface StagedBackfillRunResult {
84
86
  *
85
87
  * Discovers all candidate node IDs from typed tables (decisions, patterns,
86
88
  * learnings, observations, sticky notes) that are NOT yet in brain_page_nodes.
87
- * Writes the list to `rollback_snapshot_json` and creates a `brain_backfill_runs`
88
- * row with status='staged'. No rows are inserted into brain tables.
89
+ * Captures exact nodes, source hashes, incident-edge fingerprints and project binding in
90
+ * `rollback_snapshot_json`, and creates a `brain_backfill_runs`
91
+ * row with status='staged'. The ledger is written, but derived graph rows are unchanged.
89
92
  *
90
93
  * Pass `source` as a human-readable descriptor (e.g. a file path or session ID).
91
94
  * Pass `kind` as the backfill kind (e.g. 'graph-backfill', 'observation-promotion').
@@ -95,18 +98,20 @@ export interface StagedBackfillRunResult {
95
98
  * @returns StagedBackfillRunResult with the staged run record.
96
99
  *
97
100
  * @task T1003
101
+ * @remarks The snapshot captures exact source and relationship preconditions without mutating derived nodes.
102
+ * @example
103
+ * ```ts
104
+ * const staged = await stagedBackfillRun(projectRoot, undefined);
105
+ * ```
98
106
  */
99
- export declare function stagedBackfillRun(projectRoot: string, opts?: {
100
- source?: string;
101
- kind?: string;
102
- targetTable?: string;
103
- }): Promise<StagedBackfillRunResult>;
107
+ export declare function stagedBackfillRun(projectRoot: string, opts?: KnowledgeBackfillOptions): Promise<StagedBackfillRunResult>;
104
108
  /**
105
109
  * Approve a staged backfill run, committing its rows to live brain tables.
106
110
  *
107
- * Reads the run record, validates that it is in 'staged' status, then triggers
108
- * `backfillBrainGraph` to perform the actual INSERT OR IGNORE work. Finally,
109
- * updates the run row to status='approved' with the current timestamp.
111
+ * Reads the staged run under a writer transaction, validates its exact source
112
+ * and graph preconditions, then inserts only its reviewed derived nodes. The
113
+ * same transaction records created-row hashes and approval metadata for recovery.
114
+ * Historical edges remain unchanged.
110
115
  *
111
116
  * Double-approve is idempotent: returns `{ alreadySettled: true }` if the run
112
117
  * is already approved or rolled-back.
@@ -117,6 +122,11 @@ export declare function stagedBackfillRun(projectRoot: string, opts?: {
117
122
  * @returns Result with the updated run record and graph backfill stats.
118
123
  *
119
124
  * @task T1003
125
+ * @remarks Approval inserts only the reviewed nodes and records recovery hashes atomically; stale source or graph state is rejected.
126
+ * @example
127
+ * ```ts
128
+ * const applied = await approveBackfillRun(projectRoot, runId, 'calling-agent');
129
+ * ```
120
130
  */
121
131
  export declare function approveBackfillRun(projectRoot: string, runId: string, approvedBy?: string): Promise<{
122
132
  run: BrainBackfillRunRow;
@@ -140,6 +150,11 @@ export declare function approveBackfillRun(projectRoot: string, runId: string, a
140
150
  * @returns Result with the updated run record and optional delete count.
141
151
  *
142
152
  * @task T1003
153
+ * @remarks Recovery removes only unchanged nodes created by this run and refuses concurrent node or edge edits.
154
+ * @example
155
+ * ```ts
156
+ * const restored = await rollbackBackfillRun(projectRoot, runId);
157
+ * ```
143
158
  */
144
159
  export declare function rollbackBackfillRun(projectRoot: string, runId: string): Promise<{
145
160
  run: BrainBackfillRunRow;
@@ -1 +1 @@
1
- {"version":3,"file":"brain-backfill.d.ts","sourceRoot":"","sources":["../../src/memory/brain-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,OAAO,KAAK,EACV,mBAAmB,EAOpB,MAAM,kCAAkC,CAAC;AAO1C,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,sCAAsC;IACtC,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AA8ED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA8T1F;AAgBD;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,GAAG,EAAE,mBAAmB,CAAC;IACzB;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IACL,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACA,OAAO,CAAC,uBAAuB,CAAC,CAuDlC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IACT,GAAG,EAAE,mBAAmB,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC,CAAC,CA0DD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IACT,GAAG,EAAE,mBAAmB,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CA8ED;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAqChC"}
1
+ {"version":3,"file":"brain-backfill.d.ts","sourceRoot":"","sources":["../../src/memory/brain-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAIpE,OAAO,KAAK,EACV,mBAAmB,EAOpB,MAAM,kCAAkC,CAAC;AAO1C,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,sCAAsC;IACtC,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AA8ED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA8T1F;AAgBD;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,GAAG,EAAE,mBAAmB,CAAC;IACzB;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AA+HD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,wBAAwB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAsGlC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IACT,GAAG,EAAE,mBAAmB,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC,CAAC,CAiGD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IACT,GAAG,EAAE,mBAAmB,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CA6CD;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAqChC"}
@@ -18,13 +18,16 @@
18
18
  *
19
19
  * T1003: Staged backfill functions (stagedBackfillRun, approveBackfillRun,
20
20
  * rollbackBackfillRun, listBackfillRuns) are appended below the graph
21
- * back-fill core. Staged runs write row IDs to brain_backfill_runs first;
22
- * actual mutations happen only on approve.
21
+ * back-fill core. Staged runs capture exact source/version and incident-edge
22
+ * preconditions in brain_backfill_runs; approval reconstructs only those missing
23
+ * nodes, without synthesizing edges or invoking the broad graph back-fill.
23
24
  *
24
25
  * @task T530
25
26
  * @epic T523
26
27
  */
27
28
  import { createHash, randomBytes } from 'node:crypto';
29
+ import { realpathSync } from 'node:fs';
30
+ import { z } from 'zod';
28
31
  import { getBrainAccessor } from '../store/memory-accessor.js';
29
32
  import { getBrainDb, getBrainNativeDb } from '../store/memory-sqlite.js';
30
33
  import * as brainSchema from '../store/schema/memory-schema.js';
@@ -403,13 +406,116 @@ function generateRunId() {
403
406
  const rand = randomBytes(2).toString('hex');
404
407
  return `bfr-${ts}-${rand}`;
405
408
  }
409
+ const stagedSourceTableSchema = z.enum([
410
+ 'brain_decisions',
411
+ 'brain_patterns',
412
+ 'brain_learnings',
413
+ 'brain_observations',
414
+ 'brain_sticky_notes',
415
+ ]);
416
+ const stagedNodeSchema = z.object({
417
+ id: z.string(),
418
+ node_type: z.string(),
419
+ label: z.string(),
420
+ quality_score: z.number(),
421
+ content_hash: z.string(),
422
+ metadata_json: z.string(),
423
+ created_at: z.string(),
424
+ last_activity_at: z.string(),
425
+ updated_at: z.null(),
426
+ });
427
+ const stagedSnapshotSchema = z.object({
428
+ version: z.literal(2),
429
+ projectRoot: z.string(),
430
+ candidates: z
431
+ .array(z.object({
432
+ id: z.string(),
433
+ sourceTable: stagedSourceTableSchema,
434
+ sourceId: z.string(),
435
+ sourceHash: z.string(),
436
+ edgeHash: z.string(),
437
+ node: stagedNodeSchema,
438
+ }))
439
+ .max(500),
440
+ created: z
441
+ .array(z.object({ id: z.string(), rowHash: z.string(), edgeHash: z.string() }))
442
+ .max(500),
443
+ });
444
+ const stagedRunSchema = z.object({
445
+ id: z.string(),
446
+ kind: z.string(),
447
+ status: z.string(),
448
+ created_at: z.string(),
449
+ approved_at: z.string().nullable(),
450
+ rows_affected: z.number(),
451
+ rollback_snapshot_json: z.string().nullable(),
452
+ source: z.string(),
453
+ target_table: z.string(),
454
+ approved_by: z.string().nullable(),
455
+ });
456
+ const typedSourceSchema = z.record(z.string(), z.union([z.string(), z.number(), z.null()]));
457
+ const sourceKinds = {
458
+ brain_decisions: 'decision',
459
+ brain_patterns: 'pattern',
460
+ brain_learnings: 'learning',
461
+ brain_observations: 'observation',
462
+ brain_sticky_notes: 'sticky',
463
+ };
464
+ const volatileSourceFields = new Set([
465
+ 'access_count',
466
+ 'last_accessed_at',
467
+ 'citation_count',
468
+ 'last_cited_at',
469
+ 'updated_at',
470
+ 'retrieval_count',
471
+ 'last_retrieved_at',
472
+ 'last_activity_at',
473
+ 'reinforcement_count',
474
+ ]);
475
+ /** Hash source content and provenance without treating retrieval counters as an edit. */
476
+ function stagedSourceHash(row) {
477
+ return createHash('sha256')
478
+ .update(JSON.stringify(Object.entries(row)
479
+ .filter(([key]) => !volatileSourceFields.has(key))
480
+ .sort(([a], [b]) => a.localeCompare(b))))
481
+ .digest('hex');
482
+ }
483
+ /** Fingerprint exact graph rows, including concurrent relationship changes. */
484
+ function stagedGraphHash(value) {
485
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex');
486
+ }
487
+ /** Read all incident edges without modifying historical relationships. */
488
+ function stagedEdgesHash(db, id) {
489
+ return stagedGraphHash(db
490
+ .prepare('SELECT * FROM main.brain_page_edges WHERE from_id = ? OR to_id = ? ORDER BY from_id, to_id, edge_type')
491
+ .all(id, id));
492
+ }
493
+ /** Reject legacy and cross-project snapshots rather than guessing which rows may be removed. */
494
+ function readStagedSnapshot(raw, projectRoot) {
495
+ if (raw.target_table !== 'brain_page_nodes')
496
+ throw new Error('Staged backfill supports derived brain_page_nodes only.');
497
+ const parsed = stagedSnapshotSchema.safeParse(JSON.parse(raw.rollback_snapshot_json ?? 'null'));
498
+ if (!parsed.success)
499
+ throw new Error('Backfill snapshot lacks exact source and recovery preconditions; discard this staged run and stage a new run. Legacy approved runs require their canonical backup.');
500
+ if (parsed.data.projectRoot !== realpathSync(projectRoot))
501
+ throw new Error('Backfill project binding changed.');
502
+ return parsed.data;
503
+ }
504
+ /** Read a validated ledger row while the caller holds the writer transaction. */
505
+ function readStagedRun(db, runId) {
506
+ const row = db.prepare('SELECT * FROM main.brain_backfill_runs WHERE id = ?').get(runId);
507
+ if (!row)
508
+ throw new Error(`Backfill run '${runId}' not found`);
509
+ return stagedRunSchema.parse(row);
510
+ }
406
511
  /**
407
512
  * Stage a graph backfill run against brain_page_nodes / brain_page_edges.
408
513
  *
409
514
  * Discovers all candidate node IDs from typed tables (decisions, patterns,
410
515
  * learnings, observations, sticky notes) that are NOT yet in brain_page_nodes.
411
- * Writes the list to `rollback_snapshot_json` and creates a `brain_backfill_runs`
412
- * row with status='staged'. No rows are inserted into brain tables.
516
+ * Captures exact nodes, source hashes, incident-edge fingerprints and project binding in
517
+ * `rollback_snapshot_json`, and creates a `brain_backfill_runs`
518
+ * row with status='staged'. The ledger is written, but derived graph rows are unchanged.
413
519
  *
414
520
  * Pass `source` as a human-readable descriptor (e.g. a file path or session ID).
415
521
  * Pass `kind` as the backfill kind (e.g. 'graph-backfill', 'observation-promotion').
@@ -419,61 +525,108 @@ function generateRunId() {
419
525
  * @returns StagedBackfillRunResult with the staged run record.
420
526
  *
421
527
  * @task T1003
528
+ * @remarks The snapshot captures exact source and relationship preconditions without mutating derived nodes.
529
+ * @example
530
+ * ```ts
531
+ * const staged = await stagedBackfillRun(projectRoot, undefined);
532
+ * ```
422
533
  */
423
534
  export async function stagedBackfillRun(projectRoot, opts) {
424
- const db = await getBrainDb(projectRoot);
425
- const accessor = await getBrainAccessor(projectRoot);
426
- const source = opts?.source ?? 'staged-run';
427
- const kind = opts?.kind ?? 'graph-backfill';
428
- const targetTable = opts?.targetTable ?? 'brain_page_nodes';
429
- // Gather candidate IDs not yet in brain_page_nodes
430
- const [decisions, patterns, learnings, observations, stickyNotes] = await Promise.all([
431
- accessor.findDecisions(),
432
- accessor.findPatterns(),
433
- accessor.findLearnings(),
434
- accessor.findObservations(),
435
- accessor.findStickyNotes(),
436
- ]);
437
- // Collect candidate node IDs
438
- const candidates = [
439
- ...decisions.map((d) => `decision:${d.id}`),
440
- ...patterns.map((p) => `pattern:${p.id}`),
441
- ...learnings.map((l) => `learning:${l.id}`),
442
- ...observations.map((o) => `observation:${o.id}`),
443
- ...stickyNotes.map((s) => `sticky:${s.id}`),
444
- ];
445
- // Filter out IDs already present in brain_page_nodes
446
- const existingNodes = await db
447
- .select({ id: brainSchema.brainPageNodes.id })
448
- .from(brainSchema.brainPageNodes);
449
- const existingSet = new Set(existingNodes.map((n) => n.id));
450
- const pendingIds = candidates.filter((id) => !existingSet.has(id));
451
- const runId = generateRunId();
452
- const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
453
- const run = {
454
- id: runId,
455
- kind,
456
- status: 'staged',
457
- createdAt: now,
458
- approvedAt: null,
459
- rowsAffected: pendingIds.length,
460
- rollbackSnapshotJson: JSON.stringify(pendingIds),
461
- source,
462
- targetTable,
463
- approvedBy: null,
464
- };
465
- await db.insert(brainSchema.brainBackfillRuns).values(run);
466
- return {
467
- run,
468
- empty: pendingIds.length === 0,
469
- };
535
+ await getBrainDb(projectRoot);
536
+ const db = getBrainNativeDb(projectRoot);
537
+ if (!db)
538
+ throw new Error('brain.db native handle unavailable');
539
+ if (opts?.targetTable && opts.targetTable !== 'brain_page_nodes')
540
+ throw new Error('Staged backfill supports derived brain_page_nodes only.');
541
+ const selected = opts?.nodeIds === undefined
542
+ ? undefined
543
+ : z.array(z.string().min(1)).min(1).max(500).parse(opts.nodeIds);
544
+ if (selected && new Set(selected).size !== selected.length)
545
+ throw new Error('Backfill nodeIds must not contain duplicates.');
546
+ db.exec('BEGIN IMMEDIATE');
547
+ try {
548
+ const now = new Date().toISOString();
549
+ const snapshot = {
550
+ version: 2,
551
+ projectRoot: realpathSync(projectRoot),
552
+ candidates: [],
553
+ created: [],
554
+ };
555
+ for (const sourceTable of stagedSourceTableSchema.options) {
556
+ const kind = sourceKinds[sourceTable];
557
+ const selection = selected
558
+ ? ` AND (? || ':' || source.id) IN (${selected.map(() => '?').join(',')})`
559
+ : '';
560
+ const rows = db
561
+ .prepare(`SELECT source.* FROM main.${sourceTable} source
562
+ LEFT JOIN main.brain_page_nodes node ON node.id = ? || ':' || source.id
563
+ WHERE node.id IS NULL${selection} ORDER BY source.id LIMIT 501`)
564
+ .all(kind, ...(selected ? [kind, ...selected] : []));
565
+ for (const value of rows) {
566
+ const source = typedSourceSchema.parse(value);
567
+ if (typeof source.id !== 'string')
568
+ throw new Error('Backfill source identifier is invalid');
569
+ const id = `${kind}:${source.id}`;
570
+ if (selected &&
571
+ (source.invalid_at != null ||
572
+ (kind === 'decision' &&
573
+ (source.superseded_by != null || source.confirmation_state === 'superseded'))))
574
+ throw new Error(`Selected backfill source ${id} is not currently eligible.`);
575
+ const sourceHash = stagedSourceHash(source);
576
+ const label = source.decision ?? source.pattern ?? source.insight ?? source.title ?? source.content;
577
+ if (typeof label !== 'string' || !label.trim())
578
+ throw new Error(`Backfill source ${id} has no substantive label; resolve it before staging.`);
579
+ snapshot.candidates.push({
580
+ id,
581
+ sourceId: source.id,
582
+ sourceTable,
583
+ sourceHash,
584
+ edgeHash: stagedEdgesHash(db, id),
585
+ node: {
586
+ id,
587
+ node_type: kind,
588
+ label: label.slice(0, 200),
589
+ quality_score: typeof source.quality_score === 'number' ? source.quality_score : 0.5,
590
+ content_hash: sourceHash,
591
+ metadata_json: JSON.stringify({
592
+ sourceTable,
593
+ sourceId: source.id,
594
+ sourceHash,
595
+ derived: true,
596
+ }),
597
+ created_at: now,
598
+ last_activity_at: now,
599
+ updated_at: null,
600
+ },
601
+ });
602
+ if (snapshot.candidates.length > 500)
603
+ throw new Error('More than 500 missing nodes require a bounded source selection before staging.');
604
+ }
605
+ }
606
+ if (selected && snapshot.candidates.length !== selected.length) {
607
+ const found = new Set(snapshot.candidates.map((candidate) => candidate.id));
608
+ throw new Error(`Selected backfill IDs are missing, already indexed, or unsupported: ${selected.filter((id) => !found.has(id)).join(', ')}`);
609
+ }
610
+ const id = generateRunId();
611
+ db.prepare(`INSERT INTO main.brain_backfill_runs
612
+ (id, kind, status, created_at, rows_affected, rollback_snapshot_json, source, target_table)
613
+ VALUES (?, ?, 'staged', ?, ?, ?, ?, 'brain_page_nodes')`).run(id, opts?.kind ?? 'graph-backfill', now, snapshot.candidates.length, JSON.stringify(snapshot), opts?.source ?? 'staged-run');
614
+ const run = mapRunRow(readStagedRun(db, id));
615
+ db.exec('COMMIT');
616
+ return { run, empty: snapshot.candidates.length === 0 };
617
+ }
618
+ catch (error) {
619
+ db.exec('ROLLBACK');
620
+ throw error;
621
+ }
470
622
  }
471
623
  /**
472
624
  * Approve a staged backfill run, committing its rows to live brain tables.
473
625
  *
474
- * Reads the run record, validates that it is in 'staged' status, then triggers
475
- * `backfillBrainGraph` to perform the actual INSERT OR IGNORE work. Finally,
476
- * updates the run row to status='approved' with the current timestamp.
626
+ * Reads the staged run under a writer transaction, validates its exact source
627
+ * and graph preconditions, then inserts only its reviewed derived nodes. The
628
+ * same transaction records created-row hashes and approval metadata for recovery.
629
+ * Historical edges remain unchanged.
477
630
  *
478
631
  * Double-approve is idempotent: returns `{ alreadySettled: true }` if the run
479
632
  * is already approved or rolled-back.
@@ -484,43 +637,91 @@ export async function stagedBackfillRun(projectRoot, opts) {
484
637
  * @returns Result with the updated run record and graph backfill stats.
485
638
  *
486
639
  * @task T1003
640
+ * @remarks Approval inserts only the reviewed nodes and records recovery hashes atomically; stale source or graph state is rejected.
641
+ * @example
642
+ * ```ts
643
+ * const applied = await approveBackfillRun(projectRoot, runId, 'calling-agent');
644
+ * ```
487
645
  */
488
646
  export async function approveBackfillRun(projectRoot, runId, approvedBy) {
489
647
  await getBrainDb(projectRoot);
490
- const nativeDb = getBrainNativeDb(projectRoot);
491
- if (!nativeDb) {
648
+ const db = getBrainNativeDb(projectRoot);
649
+ if (!db)
492
650
  throw new Error('brain.db native handle unavailable');
651
+ db.exec('BEGIN IMMEDIATE');
652
+ try {
653
+ const raw = readStagedRun(db, runId);
654
+ if (raw.status === 'approved' || raw.status === 'rolled-back') {
655
+ db.exec('COMMIT');
656
+ return { run: mapRunRow(raw), alreadySettled: true };
657
+ }
658
+ if (raw.status !== 'staged')
659
+ throw new Error('Backfill run is not staged.');
660
+ const snapshot = readStagedSnapshot(raw, projectRoot);
661
+ if (snapshot.created.length)
662
+ throw new Error('Staged backfill unexpectedly contains committed nodes.');
663
+ const beforeNodes = Number(db.prepare('SELECT COUNT(*) AS count FROM main.brain_page_nodes').get()?.count);
664
+ const beforeEdges = Number(db.prepare('SELECT COUNT(*) AS count FROM main.brain_page_edges').get()?.count);
665
+ const typedCounts = Object.fromEntries(stagedSourceTableSchema.options.map((table) => [
666
+ table,
667
+ Number(db.prepare(`SELECT COUNT(*) AS count FROM main.${table}`).get()?.count),
668
+ ]));
669
+ for (const candidate of snapshot.candidates) {
670
+ const current = typedSourceSchema.parse(db
671
+ .prepare(`SELECT * FROM main.${candidate.sourceTable} WHERE id = ?`)
672
+ .get(candidate.sourceId));
673
+ if (candidate.id !== `${sourceKinds[candidate.sourceTable]}:${candidate.sourceId}` ||
674
+ candidate.node.id !== candidate.id ||
675
+ stagedSourceHash(current) !== candidate.sourceHash ||
676
+ db.prepare('SELECT id FROM main.brain_page_nodes WHERE id = ?').get(candidate.id) ||
677
+ stagedEdgesHash(db, candidate.id) !== candidate.edgeHash)
678
+ throw new Error(`Backfill proposal is stale for ${candidate.id}; stage a new run.`);
679
+ }
680
+ const insert = db.prepare(`INSERT INTO main.brain_page_nodes
681
+ (id, node_type, label, quality_score, content_hash, metadata_json, created_at, last_activity_at, updated_at)
682
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
683
+ const byType = {};
684
+ for (const candidate of snapshot.candidates) {
685
+ const node = candidate.node;
686
+ insert.run(node.id, node.node_type, node.label, node.quality_score, node.content_hash, node.metadata_json, node.created_at, node.last_activity_at, node.updated_at);
687
+ const created = db.prepare('SELECT * FROM main.brain_page_nodes WHERE id = ?').get(node.id);
688
+ if (!created)
689
+ throw new Error('Created backfill node is unreadable');
690
+ snapshot.created.push({
691
+ id: node.id,
692
+ rowHash: stagedGraphHash(created),
693
+ edgeHash: stagedEdgesHash(db, node.id),
694
+ });
695
+ byType[node.node_type] = (byType[node.node_type] ?? 0) + 1;
696
+ }
697
+ db.prepare(`UPDATE main.brain_backfill_runs SET status = 'approved', approved_at = ?, approved_by = ?, rollback_snapshot_json = ? WHERE id = ?`).run(new Date().toISOString(), approvedBy ?? 'owner', JSON.stringify(snapshot), runId);
698
+ const run = mapRunRow(readStagedRun(db, runId));
699
+ db.exec('COMMIT');
700
+ return {
701
+ run,
702
+ alreadySettled: false,
703
+ backfillResult: {
704
+ before: {
705
+ nodes: beforeNodes,
706
+ edges: beforeEdges,
707
+ decisions: typedCounts['brain_decisions'] ?? 0,
708
+ patterns: typedCounts['brain_patterns'] ?? 0,
709
+ learnings: typedCounts['brain_learnings'] ?? 0,
710
+ observations: typedCounts['brain_observations'] ?? 0,
711
+ stickyNotes: typedCounts['brain_sticky_notes'] ?? 0,
712
+ },
713
+ after: { nodes: beforeNodes + snapshot.created.length, edges: beforeEdges },
714
+ nodesInserted: snapshot.created.length,
715
+ edgesInserted: 0,
716
+ stubsCreated: 0,
717
+ byType,
718
+ },
719
+ };
493
720
  }
494
- const rawRun = nativeDb
495
- .prepare('SELECT * FROM brain_backfill_runs WHERE id = ? LIMIT 1')
496
- .get(runId);
497
- if (!rawRun) {
498
- throw new Error(`Backfill run '${runId}' not found`);
499
- }
500
- // If already settled, return as-is
501
- if (rawRun.status === 'approved' || rawRun.status === 'rolled-back') {
502
- const run = mapRunRow(rawRun);
503
- return { run, alreadySettled: true };
721
+ catch (error) {
722
+ db.exec('ROLLBACK');
723
+ throw error;
504
724
  }
505
- // Execute the actual backfill
506
- const backfillResult = await backfillBrainGraph(projectRoot);
507
- // Mark run as approved
508
- const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
509
- const approver = approvedBy ?? 'owner';
510
- nativeDb
511
- .prepare(`UPDATE brain_backfill_runs
512
- SET status = 'approved', approved_at = ?, approved_by = ?
513
- WHERE id = ?`)
514
- .run(now, approver, runId);
515
- // Re-fetch updated run
516
- const updatedRaw = nativeDb
517
- .prepare('SELECT * FROM brain_backfill_runs WHERE id = ? LIMIT 1')
518
- .get(runId);
519
- return {
520
- run: mapRunRow(updatedRaw),
521
- alreadySettled: false,
522
- backfillResult,
523
- };
524
725
  }
525
726
  /**
526
727
  * Rollback a staged backfill run, discarding staged rows.
@@ -539,64 +740,54 @@ export async function approveBackfillRun(projectRoot, runId, approvedBy) {
539
740
  * @returns Result with the updated run record and optional delete count.
540
741
  *
541
742
  * @task T1003
743
+ * @remarks Recovery removes only unchanged nodes created by this run and refuses concurrent node or edge edits.
744
+ * @example
745
+ * ```ts
746
+ * const restored = await rollbackBackfillRun(projectRoot, runId);
747
+ * ```
542
748
  */
543
749
  export async function rollbackBackfillRun(projectRoot, runId) {
544
750
  await getBrainDb(projectRoot);
545
- const nativeDb = getBrainNativeDb(projectRoot);
546
- if (!nativeDb) {
751
+ const db = getBrainNativeDb(projectRoot);
752
+ if (!db)
547
753
  throw new Error('brain.db native handle unavailable');
548
- }
549
- const rawRun = nativeDb
550
- .prepare('SELECT * FROM brain_backfill_runs WHERE id = ? LIMIT 1')
551
- .get(runId);
552
- if (!rawRun) {
553
- throw new Error(`Backfill run '${runId}' not found`);
554
- }
555
- // Already rolled back — idempotent no-op
556
- if (rawRun.status === 'rolled-back') {
557
- return { run: mapRunRow(rawRun), alreadySettled: true, deletedRows: 0 };
558
- }
559
- let deletedRows = 0;
560
- // If already approved, we need to DELETE committed rows from the target table
561
- if (rawRun.status === 'approved' && rawRun.rollback_snapshot_json) {
562
- let ids = [];
563
- try {
564
- ids = JSON.parse(rawRun.rollback_snapshot_json);
565
- }
566
- catch {
567
- // Malformed snapshot — proceed without deleting
754
+ db.exec('BEGIN IMMEDIATE');
755
+ try {
756
+ const raw = readStagedRun(db, runId);
757
+ if (raw.status === 'rolled-back') {
758
+ db.exec('COMMIT');
759
+ return { run: mapRunRow(raw), alreadySettled: true, deletedRows: 0 };
568
760
  }
569
- if (ids.length > 0) {
570
- const targetTable = rawRun.target_table;
571
- // Validate table name against known brain tables (prevent SQL injection)
572
- const allowedTables = [
573
- 'brain_page_nodes',
574
- 'brain_observations',
575
- 'brain_decisions',
576
- 'brain_patterns',
577
- 'brain_learnings',
578
- 'brain_transcript_events',
579
- ];
580
- if (allowedTables.includes(targetTable)) {
581
- // SQLite has a limit of ~999 bound params — chunk if needed
582
- const CHUNK = 200;
583
- for (let i = 0; i < ids.length; i += CHUNK) {
584
- const chunk = ids.slice(i, i + CHUNK);
585
- const placeholders = chunk.map(() => '?').join(',');
586
- const result = nativeDb
587
- .prepare(`DELETE FROM ${targetTable} WHERE id IN (${placeholders})`)
588
- .run(...chunk);
589
- deletedRows += result.changes ?? 0;
590
- }
761
+ let deletedRows = 0;
762
+ if (raw.status === 'approved') {
763
+ const snapshot = readStagedSnapshot(raw, projectRoot);
764
+ if (snapshot.created.length !== snapshot.candidates.length)
765
+ throw new Error('Backfill receipt does not cover every created node.');
766
+ for (const created of snapshot.created) {
767
+ const current = db
768
+ .prepare('SELECT * FROM main.brain_page_nodes WHERE id = ?')
769
+ .get(created.id);
770
+ if (!current ||
771
+ stagedGraphHash(current) !== created.rowHash ||
772
+ stagedEdgesHash(db, created.id) !== created.edgeHash)
773
+ throw new Error(`Backfill rollback is stale for ${created.id}; preserve concurrent changes and inspect the canonical backup.`);
774
+ }
775
+ for (const created of snapshot.created) {
776
+ const result = db.prepare('DELETE FROM main.brain_page_nodes WHERE id = ?').run(created.id);
777
+ deletedRows += Number(result.changes);
591
778
  }
592
779
  }
780
+ else if (raw.status !== 'staged')
781
+ throw new Error('Backfill run cannot be rolled back from its current state.');
782
+ db.prepare("UPDATE main.brain_backfill_runs SET status = 'rolled-back' WHERE id = ?").run(runId);
783
+ const run = mapRunRow(readStagedRun(db, runId));
784
+ db.exec('COMMIT');
785
+ return { run, alreadySettled: false, deletedRows };
786
+ }
787
+ catch (error) {
788
+ db.exec('ROLLBACK');
789
+ throw error;
593
790
  }
594
- // Mark run as rolled-back
595
- nativeDb.prepare(`UPDATE brain_backfill_runs SET status = 'rolled-back' WHERE id = ?`).run(runId);
596
- const updatedRaw = nativeDb
597
- .prepare('SELECT * FROM brain_backfill_runs WHERE id = ? LIMIT 1')
598
- .get(runId);
599
- return { run: mapRunRow(updatedRaw), alreadySettled: false, deletedRows };
600
791
  }
601
792
  /**
602
793
  * List backfill runs, optionally filtered by status.