@jamesaphoenix/tx-core 0.4.2 → 0.4.4

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 (336) hide show
  1. package/README.md +480 -0
  2. package/dist/db.d.ts +28 -14
  3. package/dist/db.d.ts.map +1 -1
  4. package/dist/db.js +108 -14
  5. package/dist/db.js.map +1 -1
  6. package/dist/errors.d.ts +178 -34
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +119 -26
  9. package/dist/errors.js.map +1 -1
  10. package/dist/id.d.ts +10 -0
  11. package/dist/id.d.ts.map +1 -1
  12. package/dist/id.js +17 -1
  13. package/dist/id.js.map +1 -1
  14. package/dist/index.d.ts +15 -7
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +62 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/layer.d.ts +23 -14
  19. package/dist/layer.d.ts.map +1 -1
  20. package/dist/layer.js +75 -76
  21. package/dist/layer.js.map +1 -1
  22. package/dist/mappers/attempt.d.ts +3 -1
  23. package/dist/mappers/attempt.d.ts.map +1 -1
  24. package/dist/mappers/attempt.js +23 -9
  25. package/dist/mappers/attempt.js.map +1 -1
  26. package/dist/mappers/claim.d.ts +1 -1
  27. package/dist/mappers/claim.d.ts.map +1 -1
  28. package/dist/mappers/claim.js +11 -4
  29. package/dist/mappers/claim.js.map +1 -1
  30. package/dist/mappers/deduplication.d.ts +13 -1
  31. package/dist/mappers/deduplication.d.ts.map +1 -1
  32. package/dist/mappers/deduplication.js +22 -3
  33. package/dist/mappers/deduplication.js.map +1 -1
  34. package/dist/mappers/doc.d.ts +24 -0
  35. package/dist/mappers/doc.d.ts.map +1 -0
  36. package/dist/mappers/doc.js +161 -0
  37. package/dist/mappers/doc.js.map +1 -0
  38. package/dist/mappers/file-learning.d.ts.map +1 -1
  39. package/dist/mappers/file-learning.js +2 -1
  40. package/dist/mappers/file-learning.js.map +1 -1
  41. package/dist/mappers/index.d.ts +6 -7
  42. package/dist/mappers/index.d.ts.map +1 -1
  43. package/dist/mappers/index.js +10 -12
  44. package/dist/mappers/index.js.map +1 -1
  45. package/dist/mappers/learning.d.ts +9 -1
  46. package/dist/mappers/learning.d.ts.map +1 -1
  47. package/dist/mappers/learning.js +94 -14
  48. package/dist/mappers/learning.js.map +1 -1
  49. package/dist/mappers/orchestrator-state.d.ts +1 -1
  50. package/dist/mappers/orchestrator-state.d.ts.map +1 -1
  51. package/dist/mappers/orchestrator-state.js +31 -5
  52. package/dist/mappers/orchestrator-state.js.map +1 -1
  53. package/dist/mappers/parse-date.d.ts +11 -0
  54. package/dist/mappers/parse-date.d.ts.map +1 -0
  55. package/dist/mappers/parse-date.js +18 -0
  56. package/dist/mappers/parse-date.js.map +1 -0
  57. package/dist/mappers/run.d.ts +14 -4
  58. package/dist/mappers/run.d.ts.map +1 -1
  59. package/dist/mappers/run.js +49 -18
  60. package/dist/mappers/run.js.map +1 -1
  61. package/dist/mappers/task.d.ts +5 -1
  62. package/dist/mappers/task.d.ts.map +1 -1
  63. package/dist/mappers/task.js +66 -16
  64. package/dist/mappers/task.js.map +1 -1
  65. package/dist/mappers/tracked-project.d.ts +3 -1
  66. package/dist/mappers/tracked-project.d.ts.map +1 -1
  67. package/dist/mappers/tracked-project.js +23 -9
  68. package/dist/mappers/tracked-project.js.map +1 -1
  69. package/dist/mappers/worker.d.ts +1 -1
  70. package/dist/mappers/worker.d.ts.map +1 -1
  71. package/dist/mappers/worker.js +44 -6
  72. package/dist/mappers/worker.js.map +1 -1
  73. package/dist/repo/attempt-repo.d.ts +2 -2
  74. package/dist/repo/attempt-repo.d.ts.map +1 -1
  75. package/dist/repo/attempt-repo.js +16 -6
  76. package/dist/repo/attempt-repo.js.map +1 -1
  77. package/dist/repo/claim-repo.d.ts +46 -2
  78. package/dist/repo/claim-repo.d.ts.map +1 -1
  79. package/dist/repo/claim-repo.js +113 -6
  80. package/dist/repo/claim-repo.js.map +1 -1
  81. package/dist/repo/deduplication-repo.d.ts +9 -1
  82. package/dist/repo/deduplication-repo.d.ts.map +1 -1
  83. package/dist/repo/deduplication-repo.js +46 -9
  84. package/dist/repo/deduplication-repo.js.map +1 -1
  85. package/dist/repo/dep-repo.d.ts +27 -3
  86. package/dist/repo/dep-repo.d.ts.map +1 -1
  87. package/dist/repo/dep-repo.js +166 -39
  88. package/dist/repo/dep-repo.js.map +1 -1
  89. package/dist/repo/doc-repo.d.ts +59 -0
  90. package/dist/repo/doc-repo.d.ts.map +1 -0
  91. package/dist/repo/doc-repo.js +276 -0
  92. package/dist/repo/doc-repo.js.map +1 -0
  93. package/dist/repo/file-learning-repo.d.ts +3 -3
  94. package/dist/repo/file-learning-repo.d.ts.map +1 -1
  95. package/dist/repo/file-learning-repo.js +19 -8
  96. package/dist/repo/file-learning-repo.js.map +1 -1
  97. package/dist/repo/index.d.ts +4 -6
  98. package/dist/repo/index.d.ts.map +1 -1
  99. package/dist/repo/index.js +3 -5
  100. package/dist/repo/index.js.map +1 -1
  101. package/dist/repo/learning-repo.d.ts +10 -3
  102. package/dist/repo/learning-repo.d.ts.map +1 -1
  103. package/dist/repo/learning-repo.js +68 -11
  104. package/dist/repo/learning-repo.js.map +1 -1
  105. package/dist/repo/orchestrator-state-repo.d.ts.map +1 -1
  106. package/dist/repo/orchestrator-state-repo.js +8 -1
  107. package/dist/repo/orchestrator-state-repo.js.map +1 -1
  108. package/dist/repo/run-repo.d.ts +3 -3
  109. package/dist/repo/run-repo.d.ts.map +1 -1
  110. package/dist/repo/run-repo.js +40 -19
  111. package/dist/repo/run-repo.js.map +1 -1
  112. package/dist/repo/task-repo.d.ts +14 -3
  113. package/dist/repo/task-repo.d.ts.map +1 -1
  114. package/dist/repo/task-repo.js +194 -20
  115. package/dist/repo/task-repo.js.map +1 -1
  116. package/dist/repo/tracked-project-repo.d.ts.map +1 -1
  117. package/dist/repo/tracked-project-repo.js +15 -1
  118. package/dist/repo/tracked-project-repo.js.map +1 -1
  119. package/dist/repo/worker-repo.d.ts +3 -2
  120. package/dist/repo/worker-repo.d.ts.map +1 -1
  121. package/dist/repo/worker-repo.js +54 -8
  122. package/dist/repo/worker-repo.js.map +1 -1
  123. package/dist/schemas/sync.js +2 -2
  124. package/dist/schemas/sync.js.map +1 -1
  125. package/dist/schemas/worker.d.ts +1 -0
  126. package/dist/schemas/worker.d.ts.map +1 -1
  127. package/dist/schemas/worker.js +1 -0
  128. package/dist/schemas/worker.js.map +1 -1
  129. package/dist/services/agent-service.d.ts +57 -0
  130. package/dist/services/agent-service.d.ts.map +1 -0
  131. package/dist/services/agent-service.js +81 -0
  132. package/dist/services/agent-service.js.map +1 -0
  133. package/dist/services/attempt-service.d.ts.map +1 -1
  134. package/dist/services/attempt-service.js +1 -4
  135. package/dist/services/attempt-service.js.map +1 -1
  136. package/dist/services/auto-sync-service.d.ts.map +1 -1
  137. package/dist/services/auto-sync-service.js +18 -10
  138. package/dist/services/auto-sync-service.js.map +1 -1
  139. package/dist/services/claim-service.d.ts +8 -2
  140. package/dist/services/claim-service.d.ts.map +1 -1
  141. package/dist/services/claim-service.js +37 -23
  142. package/dist/services/claim-service.js.map +1 -1
  143. package/dist/services/cycle-scan-service.d.ts +32 -0
  144. package/dist/services/cycle-scan-service.d.ts.map +1 -0
  145. package/dist/services/cycle-scan-service.js +546 -0
  146. package/dist/services/cycle-scan-service.js.map +1 -0
  147. package/dist/services/daemon-service.d.ts +40 -2
  148. package/dist/services/daemon-service.d.ts.map +1 -1
  149. package/dist/services/daemon-service.js +199 -52
  150. package/dist/services/daemon-service.js.map +1 -1
  151. package/dist/services/deduplication-service.d.ts +8 -4
  152. package/dist/services/deduplication-service.d.ts.map +1 -1
  153. package/dist/services/deduplication-service.js +79 -25
  154. package/dist/services/deduplication-service.js.map +1 -1
  155. package/dist/services/dep-service.d.ts +2 -2
  156. package/dist/services/dep-service.d.ts.map +1 -1
  157. package/dist/services/dep-service.js +9 -5
  158. package/dist/services/dep-service.js.map +1 -1
  159. package/dist/services/diversifier-service.d.ts +2 -1
  160. package/dist/services/diversifier-service.d.ts.map +1 -1
  161. package/dist/services/diversifier-service.js +37 -43
  162. package/dist/services/diversifier-service.js.map +1 -1
  163. package/dist/services/doc-service.d.ts +49 -0
  164. package/dist/services/doc-service.d.ts.map +1 -0
  165. package/dist/services/doc-service.js +605 -0
  166. package/dist/services/doc-service.js.map +1 -0
  167. package/dist/services/embedding-service.d.ts +66 -2
  168. package/dist/services/embedding-service.d.ts.map +1 -1
  169. package/dist/services/embedding-service.js +138 -24
  170. package/dist/services/embedding-service.js.map +1 -1
  171. package/dist/services/file-learning-service.d.ts.map +1 -1
  172. package/dist/services/file-learning-service.js +8 -7
  173. package/dist/services/file-learning-service.js.map +1 -1
  174. package/dist/services/file-watcher-service.d.ts.map +1 -1
  175. package/dist/services/file-watcher-service.js +58 -11
  176. package/dist/services/file-watcher-service.js.map +1 -1
  177. package/dist/services/hierarchy-service.d.ts +1 -1
  178. package/dist/services/hierarchy-service.d.ts.map +1 -1
  179. package/dist/services/hierarchy-service.js +50 -32
  180. package/dist/services/hierarchy-service.js.map +1 -1
  181. package/dist/services/index.d.ts +13 -15
  182. package/dist/services/index.d.ts.map +1 -1
  183. package/dist/services/index.js +13 -15
  184. package/dist/services/index.js.map +1 -1
  185. package/dist/services/learning-service.d.ts +3 -3
  186. package/dist/services/learning-service.d.ts.map +1 -1
  187. package/dist/services/learning-service.js +75 -42
  188. package/dist/services/learning-service.js.map +1 -1
  189. package/dist/services/llm-service.d.ts +62 -0
  190. package/dist/services/llm-service.d.ts.map +1 -0
  191. package/dist/services/llm-service.js +172 -0
  192. package/dist/services/llm-service.js.map +1 -0
  193. package/dist/services/migration-service.d.ts +1 -1
  194. package/dist/services/migration-service.d.ts.map +1 -1
  195. package/dist/services/migration-service.js +18 -7
  196. package/dist/services/migration-service.js.map +1 -1
  197. package/dist/services/orchestrator-service.d.ts +4 -3
  198. package/dist/services/orchestrator-service.d.ts.map +1 -1
  199. package/dist/services/orchestrator-service.js +66 -27
  200. package/dist/services/orchestrator-service.js.map +1 -1
  201. package/dist/services/query-expansion-service.d.ts +30 -9
  202. package/dist/services/query-expansion-service.d.ts.map +1 -1
  203. package/dist/services/query-expansion-service.js +54 -63
  204. package/dist/services/query-expansion-service.js.map +1 -1
  205. package/dist/services/ready-service.d.ts +21 -1
  206. package/dist/services/ready-service.d.ts.map +1 -1
  207. package/dist/services/ready-service.js +44 -21
  208. package/dist/services/ready-service.js.map +1 -1
  209. package/dist/services/retriever-service.d.ts +10 -10
  210. package/dist/services/retriever-service.d.ts.map +1 -1
  211. package/dist/services/retriever-service.js +53 -161
  212. package/dist/services/retriever-service.js.map +1 -1
  213. package/dist/services/sync-service.d.ts +17 -4
  214. package/dist/services/sync-service.d.ts.map +1 -1
  215. package/dist/services/sync-service.js +381 -116
  216. package/dist/services/sync-service.js.map +1 -1
  217. package/dist/services/task-service.d.ts +6 -4
  218. package/dist/services/task-service.d.ts.map +1 -1
  219. package/dist/services/task-service.js +165 -35
  220. package/dist/services/task-service.js.map +1 -1
  221. package/dist/services/tracing-service.d.ts +55 -0
  222. package/dist/services/tracing-service.d.ts.map +1 -0
  223. package/dist/services/tracing-service.js +99 -0
  224. package/dist/services/tracing-service.js.map +1 -0
  225. package/dist/services/transcript-adapter.d.ts +99 -0
  226. package/dist/services/transcript-adapter.d.ts.map +1 -0
  227. package/dist/services/transcript-adapter.js +283 -0
  228. package/dist/services/transcript-adapter.js.map +1 -0
  229. package/dist/services/validation-service.d.ts +85 -0
  230. package/dist/services/validation-service.d.ts.map +1 -0
  231. package/dist/services/validation-service.js +289 -0
  232. package/dist/services/validation-service.js.map +1 -0
  233. package/dist/services/worker-process.d.ts +23 -4
  234. package/dist/services/worker-process.d.ts.map +1 -1
  235. package/dist/services/worker-process.js +168 -72
  236. package/dist/services/worker-process.js.map +1 -1
  237. package/dist/services/worker-service.d.ts.map +1 -1
  238. package/dist/services/worker-service.js +7 -12
  239. package/dist/services/worker-service.js.map +1 -1
  240. package/dist/sync/claude-task-writer.d.ts +49 -0
  241. package/dist/sync/claude-task-writer.d.ts.map +1 -0
  242. package/dist/sync/claude-task-writer.js +135 -0
  243. package/dist/sync/claude-task-writer.js.map +1 -0
  244. package/dist/utils/doc-hash.d.ts +10 -0
  245. package/dist/utils/doc-hash.d.ts.map +1 -0
  246. package/dist/utils/doc-hash.js +14 -0
  247. package/dist/utils/doc-hash.js.map +1 -0
  248. package/dist/utils/doc-renderer.d.ts +44 -0
  249. package/dist/utils/doc-renderer.d.ts.map +1 -0
  250. package/dist/utils/doc-renderer.js +202 -0
  251. package/dist/utils/doc-renderer.js.map +1 -0
  252. package/dist/utils/math.d.ts +5 -1
  253. package/dist/utils/math.d.ts.map +1 -1
  254. package/dist/utils/math.js +12 -4
  255. package/dist/utils/math.js.map +1 -1
  256. package/dist/utils/sql.d.ts +9 -0
  257. package/dist/utils/sql.d.ts.map +1 -0
  258. package/dist/utils/sql.js +9 -0
  259. package/dist/utils/sql.js.map +1 -0
  260. package/dist/utils/toml-config.d.ts +22 -0
  261. package/dist/utils/toml-config.d.ts.map +1 -0
  262. package/dist/utils/toml-config.js +75 -0
  263. package/dist/utils/toml-config.js.map +1 -0
  264. package/dist/worker/hooks.d.ts +102 -0
  265. package/dist/worker/hooks.d.ts.map +1 -0
  266. package/dist/worker/hooks.js +11 -0
  267. package/dist/worker/hooks.js.map +1 -0
  268. package/dist/worker/index.d.ts +9 -0
  269. package/dist/worker/index.d.ts.map +1 -0
  270. package/dist/worker/index.js +8 -0
  271. package/dist/worker/index.js.map +1 -0
  272. package/dist/worker/run-worker.d.ts +33 -0
  273. package/dist/worker/run-worker.d.ts.map +1 -0
  274. package/dist/worker/run-worker.js +265 -0
  275. package/dist/worker/run-worker.js.map +1 -0
  276. package/package.json +14 -12
  277. package/dist/mappers/anchor.d.ts +0 -14
  278. package/dist/mappers/anchor.d.ts.map +0 -1
  279. package/dist/mappers/anchor.js +0 -38
  280. package/dist/mappers/anchor.js.map +0 -1
  281. package/dist/mappers/candidate.d.ts +0 -23
  282. package/dist/mappers/candidate.d.ts.map +0 -1
  283. package/dist/mappers/candidate.js +0 -53
  284. package/dist/mappers/candidate.js.map +0 -1
  285. package/dist/mappers/edge.d.ts +0 -10
  286. package/dist/mappers/edge.d.ts.map +0 -1
  287. package/dist/mappers/edge.js +0 -19
  288. package/dist/mappers/edge.js.map +0 -1
  289. package/dist/repo/anchor-repo.d.ts +0 -52
  290. package/dist/repo/anchor-repo.d.ts.map +0 -1
  291. package/dist/repo/anchor-repo.js +0 -204
  292. package/dist/repo/anchor-repo.js.map +0 -1
  293. package/dist/repo/candidate-repo.d.ts +0 -16
  294. package/dist/repo/candidate-repo.d.ts.map +0 -1
  295. package/dist/repo/candidate-repo.js +0 -143
  296. package/dist/repo/candidate-repo.js.map +0 -1
  297. package/dist/repo/edge-repo.d.ts +0 -26
  298. package/dist/repo/edge-repo.d.ts.map +0 -1
  299. package/dist/repo/edge-repo.js +0 -227
  300. package/dist/repo/edge-repo.js.map +0 -1
  301. package/dist/services/anchor-service.d.ts +0 -147
  302. package/dist/services/anchor-service.d.ts.map +0 -1
  303. package/dist/services/anchor-service.js +0 -540
  304. package/dist/services/anchor-service.js.map +0 -1
  305. package/dist/services/anchor-verification.d.ts +0 -94
  306. package/dist/services/anchor-verification.d.ts.map +0 -1
  307. package/dist/services/anchor-verification.js +0 -617
  308. package/dist/services/anchor-verification.js.map +0 -1
  309. package/dist/services/ast-grep-service.d.ts +0 -58
  310. package/dist/services/ast-grep-service.d.ts.map +0 -1
  311. package/dist/services/ast-grep-service.js +0 -356
  312. package/dist/services/ast-grep-service.js.map +0 -1
  313. package/dist/services/candidate-extractor-service.d.ts +0 -56
  314. package/dist/services/candidate-extractor-service.d.ts.map +0 -1
  315. package/dist/services/candidate-extractor-service.js +0 -365
  316. package/dist/services/candidate-extractor-service.js.map +0 -1
  317. package/dist/services/edge-service.d.ts +0 -78
  318. package/dist/services/edge-service.d.ts.map +0 -1
  319. package/dist/services/edge-service.js +0 -158
  320. package/dist/services/edge-service.js.map +0 -1
  321. package/dist/services/feedback-tracker.d.ts +0 -64
  322. package/dist/services/feedback-tracker.d.ts.map +0 -1
  323. package/dist/services/feedback-tracker.js +0 -110
  324. package/dist/services/feedback-tracker.js.map +0 -1
  325. package/dist/services/graph-expansion.d.ts +0 -155
  326. package/dist/services/graph-expansion.d.ts.map +0 -1
  327. package/dist/services/graph-expansion.js +0 -466
  328. package/dist/services/graph-expansion.js.map +0 -1
  329. package/dist/services/promotion-service.d.ts +0 -67
  330. package/dist/services/promotion-service.d.ts.map +0 -1
  331. package/dist/services/promotion-service.js +0 -151
  332. package/dist/services/promotion-service.js.map +0 -1
  333. package/dist/services/swarm-verification.d.ts +0 -104
  334. package/dist/services/swarm-verification.d.ts.map +0 -1
  335. package/dist/services/swarm-verification.js +0 -400
  336. package/dist/services/swarm-verification.js.map +0 -1
@@ -1,7 +1,16 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { SqliteClient } from "../db.js";
3
- import { DatabaseError } from "../errors.js";
3
+ import { DatabaseError, DependencyNotFoundError, UnexpectedRowCountError } from "../errors.js";
4
4
  import { rowToDependency } from "../mappers/task.js";
5
+ import { DEFAULT_QUERY_LIMIT } from "../utils/sql.js";
6
+ // Shared frozen empty array to avoid allocating new arrays for IDs with no dependencies
7
+ const EMPTY_TASK_IDS = Object.freeze([]);
8
+ /**
9
+ * Maximum recursion depth for dependency graph traversal.
10
+ * Prevents unbounded recursive CTEs from hitting SQLite's internal
11
+ * 1000-level recursion limit on deep dependency chains.
12
+ */
13
+ const MAX_DEPENDENCY_DEPTH = 100;
5
14
  export class DependencyRepository extends Context.Tag("DependencyRepository")() {
6
15
  }
7
16
  export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effect.gen(function* () {
@@ -9,15 +18,25 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
9
18
  return {
10
19
  insert: (blockerId, blockedId) => Effect.try({
11
20
  try: () => {
12
- db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
21
+ const result = db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
22
+ if (result.changes !== 1) {
23
+ throw new UnexpectedRowCountError({
24
+ operation: "dependency insert",
25
+ expected: 1,
26
+ actual: result.changes
27
+ });
28
+ }
13
29
  },
14
30
  catch: (cause) => new DatabaseError({ cause })
15
31
  }),
16
- remove: (blockerId, blockedId) => Effect.try({
17
- try: () => {
18
- db.prepare("DELETE FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ?").run(blockerId, blockedId);
19
- },
20
- catch: (cause) => new DatabaseError({ cause })
32
+ remove: (blockerId, blockedId) => Effect.gen(function* () {
33
+ const result = yield* Effect.try({
34
+ try: () => db.prepare("DELETE FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ?").run(blockerId, blockedId),
35
+ catch: (cause) => new DatabaseError({ cause })
36
+ });
37
+ if (result.changes === 0) {
38
+ yield* Effect.fail(new DependencyNotFoundError({ blockerId, blockedId }));
39
+ }
21
40
  }),
22
41
  getBlockerIds: (blockedId) => Effect.try({
23
42
  try: () => {
@@ -40,14 +59,20 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
40
59
  return result;
41
60
  const placeholders = blockedIds.map(() => "?").join(",");
42
61
  const rows = db.prepare(`SELECT blocked_id, blocker_id FROM task_dependencies WHERE blocked_id IN (${placeholders})`).all(...blockedIds);
43
- // Initialize all requested IDs with empty arrays
44
- for (const id of blockedIds) {
45
- result.set(id, []);
46
- }
47
- // Group by blocked_id
62
+ // Group rows by blocked_id in a temporary mutable map
63
+ const grouped = new Map();
48
64
  for (const row of rows) {
49
- const existing = result.get(row.blocked_id) ?? [];
50
- result.set(row.blocked_id, [...existing, row.blocker_id]);
65
+ const existing = grouped.get(row.blocked_id);
66
+ if (existing) {
67
+ existing.push(row.blocker_id);
68
+ }
69
+ else {
70
+ grouped.set(row.blocked_id, [row.blocker_id]);
71
+ }
72
+ }
73
+ // Populate result: use grouped array or shared empty array
74
+ for (const id of blockedIds) {
75
+ result.set(id, grouped.get(id) ?? EMPTY_TASK_IDS);
51
76
  }
52
77
  return result;
53
78
  },
@@ -60,44 +85,146 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
60
85
  return result;
61
86
  const placeholders = blockerIds.map(() => "?").join(",");
62
87
  const rows = db.prepare(`SELECT blocker_id, blocked_id FROM task_dependencies WHERE blocker_id IN (${placeholders})`).all(...blockerIds);
63
- // Initialize all requested IDs with empty arrays
64
- for (const id of blockerIds) {
65
- result.set(id, []);
66
- }
67
- // Group by blocker_id
88
+ // Group rows by blocker_id in a temporary mutable map
89
+ const grouped = new Map();
68
90
  for (const row of rows) {
69
- const existing = result.get(row.blocker_id) ?? [];
70
- result.set(row.blocker_id, [...existing, row.blocked_id]);
91
+ const existing = grouped.get(row.blocker_id);
92
+ if (existing) {
93
+ existing.push(row.blocked_id);
94
+ }
95
+ else {
96
+ grouped.set(row.blocker_id, [row.blocked_id]);
97
+ }
98
+ }
99
+ // Populate result: use grouped array or shared empty array
100
+ for (const id of blockerIds) {
101
+ result.set(id, grouped.get(id) ?? EMPTY_TASK_IDS);
71
102
  }
72
103
  return result;
73
104
  },
74
105
  catch: (cause) => new DatabaseError({ cause })
75
106
  }),
76
- // BFS cycle detection: can we reach toId by following blocker chains from fromId?
77
- hasPath: (fromId, toId) => Effect.try({
107
+ // Cycle detection using recursive CTE: can we reach toId by following blocker chains from fromId?
108
+ // Uses UNION (not UNION ALL) to automatically prevent infinite recursion on existing cycles
109
+ // Depth-limited to MAX_DEPENDENCY_DEPTH to prevent hitting SQLite's internal 1000-level limit
110
+ hasPath: (fromId, toId) => Effect.gen(function* () {
111
+ // Special case: same ID
112
+ if (fromId === toId)
113
+ return true;
114
+ // Recursive CTE performs BFS in a single query
115
+ // UNION deduplicates visited nodes, preventing infinite loops
116
+ // depth tracking prevents unbounded recursion on deep chains
117
+ const result = yield* Effect.try({
118
+ try: () => db.prepare(`
119
+ WITH RECURSIVE reachable(id, depth) AS (
120
+ -- Base case: direct blockers of fromId (depth 1)
121
+ SELECT blocker_id, 1 FROM task_dependencies WHERE blocked_id = ?
122
+ UNION
123
+ -- Recursive case: follow blocker chain, bounded by depth limit
124
+ SELECT d.blocker_id, r.depth + 1
125
+ FROM task_dependencies d
126
+ JOIN reachable r ON d.blocked_id = r.id
127
+ WHERE r.depth < ?
128
+ )
129
+ SELECT
130
+ MAX(CASE WHEN id = ? THEN 1 ELSE 0 END) AS found,
131
+ MAX(depth) AS max_depth
132
+ FROM reachable
133
+ `).get(fromId, MAX_DEPENDENCY_DEPTH, toId),
134
+ catch: (cause) => new DatabaseError({ cause })
135
+ });
136
+ const pathFound = result?.found === 1;
137
+ const depthLimitHit = result?.max_depth != null && result.max_depth >= MAX_DEPENDENCY_DEPTH;
138
+ if (depthLimitHit) {
139
+ yield* Effect.logWarning(`Dependency depth limit (${MAX_DEPENDENCY_DEPTH}) reached checking path from ${fromId} to ${toId}. Graph may have unexplored deeper chains.`);
140
+ }
141
+ return pathFound;
142
+ }),
143
+ getAll: (limit) => Effect.try({
78
144
  try: () => {
79
- const visited = new Set();
80
- const queue = [fromId];
81
- while (queue.length > 0) {
82
- const current = queue.shift();
83
- if (current === toId)
84
- return true;
85
- if (visited.has(current))
86
- continue;
87
- visited.add(current);
88
- const rows = db.prepare("SELECT blocker_id FROM task_dependencies WHERE blocked_id = ?").all(current);
89
- queue.push(...rows.map(r => r.blocker_id));
90
- }
91
- return false;
145
+ const rows = db.prepare("SELECT blocker_id, blocked_id, created_at FROM task_dependencies LIMIT ?").all(limit ?? DEFAULT_QUERY_LIMIT);
146
+ return rows.map(rowToDependency);
92
147
  },
93
148
  catch: (cause) => new DatabaseError({ cause })
94
149
  }),
95
- getAll: () => Effect.try({
150
+ removeByTaskIds: (taskIds) => Effect.try({
96
151
  try: () => {
97
- const rows = db.prepare("SELECT blocker_id, blocked_id, created_at FROM task_dependencies").all();
98
- return rows.map(rowToDependency);
152
+ if (taskIds.length === 0)
153
+ return;
154
+ const placeholders = taskIds.map(() => "?").join(",");
155
+ db.prepare(`DELETE FROM task_dependencies WHERE blocker_id IN (${placeholders}) OR blocked_id IN (${placeholders})`).run(...taskIds, ...taskIds);
99
156
  },
100
157
  catch: (cause) => new DatabaseError({ cause })
158
+ }),
159
+ insertWithCycleCheck: (blockerId, blockedId) => Effect.gen(function* () {
160
+ const txResult = yield* Effect.try({
161
+ try: () => {
162
+ // BEGIN IMMEDIATE acquires write lock immediately, preventing other writers
163
+ // from modifying the dependency graph until we commit/rollback
164
+ db.exec("BEGIN IMMEDIATE");
165
+ try {
166
+ // Special case: same ID always indicates a cycle
167
+ if (blockerId === blockedId) {
168
+ db.exec("ROLLBACK");
169
+ return { _tag: "wouldCycle", depthLimitHit: false };
170
+ }
171
+ // Check if dependency already exists (idempotent)
172
+ const existing = db.prepare("SELECT 1 FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ? LIMIT 1").get(blockerId, blockedId);
173
+ if (existing != null) {
174
+ db.exec("ROLLBACK");
175
+ return { _tag: "alreadyExists", depthLimitHit: false };
176
+ }
177
+ // Check if adding this dependency would create a cycle
178
+ // Uses depth-limited recursive CTE to detect if blockedId can already reach blockerId
179
+ const cycleCheck = db.prepare(`
180
+ WITH RECURSIVE reachable(id, depth) AS (
181
+ SELECT blocker_id, 1 FROM task_dependencies WHERE blocked_id = ?
182
+ UNION
183
+ SELECT d.blocker_id, r.depth + 1
184
+ FROM task_dependencies d
185
+ JOIN reachable r ON d.blocked_id = r.id
186
+ WHERE r.depth < ?
187
+ )
188
+ SELECT
189
+ MAX(CASE WHEN id = ? THEN 1 ELSE 0 END) AS found,
190
+ MAX(depth) AS max_depth
191
+ FROM reachable
192
+ `).get(blockerId, MAX_DEPENDENCY_DEPTH, blockedId);
193
+ const depthLimitHit = cycleCheck?.max_depth != null && cycleCheck.max_depth >= MAX_DEPENDENCY_DEPTH;
194
+ if (cycleCheck?.found === 1) {
195
+ db.exec("ROLLBACK");
196
+ return { _tag: "wouldCycle", depthLimitHit };
197
+ }
198
+ // No cycle detected - safe to insert
199
+ const result = db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
200
+ if (result.changes !== 1) {
201
+ db.exec("ROLLBACK");
202
+ throw new UnexpectedRowCountError({
203
+ operation: "dependency insert (with cycle check)",
204
+ expected: 1,
205
+ actual: result.changes
206
+ });
207
+ }
208
+ db.exec("COMMIT");
209
+ return { _tag: "inserted", depthLimitHit };
210
+ }
211
+ catch (e) {
212
+ // Ensure rollback on any error
213
+ try {
214
+ db.exec("ROLLBACK");
215
+ }
216
+ catch {
217
+ // Ignore rollback errors (transaction may already be rolled back)
218
+ }
219
+ throw e;
220
+ }
221
+ },
222
+ catch: (cause) => new DatabaseError({ cause })
223
+ });
224
+ if (txResult.depthLimitHit) {
225
+ yield* Effect.logWarning(`Dependency depth limit (${MAX_DEPENDENCY_DEPTH}) reached during cycle check for ${blockerId} → ${blockedId}. Insert proceeded but deeper cycles may exist.`);
226
+ }
227
+ return { _tag: txResult._tag };
101
228
  })
102
229
  };
103
230
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"dep-repo.js","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dep-repo.js","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { SqliteClient } from "../db.js";
3
+ import { DatabaseError } from "../errors.js";
4
+ import type { Doc, DocKind, DocStatus, DocLink, DocLinkType, TaskDocLink, TaskDocLinkType, Invariant, InvariantCheck, UpsertInvariantInput } from "@jamesaphoenix/tx-types";
5
+ /** Input for inserting a new doc row. */
6
+ export type InsertDocInput = {
7
+ readonly hash: string;
8
+ readonly kind: DocKind;
9
+ readonly name: string;
10
+ readonly title: string;
11
+ readonly version: number;
12
+ readonly filePath: string;
13
+ readonly parentDocId: number | null;
14
+ readonly metadata?: string;
15
+ };
16
+ /** Input for updating an existing doc row. */
17
+ export type UpdateDocInput = {
18
+ readonly hash?: string;
19
+ readonly title?: string;
20
+ readonly status?: DocStatus;
21
+ readonly lockedAt?: string;
22
+ readonly metadata?: string;
23
+ };
24
+ declare const DocRepository_base: Context.TagClass<DocRepository, "DocRepository", {
25
+ readonly insert: (input: InsertDocInput) => Effect.Effect<Doc, DatabaseError>;
26
+ readonly findById: (id: number) => Effect.Effect<Doc | null, DatabaseError>;
27
+ readonly findByName: (name: string, version?: number) => Effect.Effect<Doc | null, DatabaseError>;
28
+ readonly findAll: (filter?: {
29
+ kind?: DocKind;
30
+ status?: DocStatus;
31
+ }) => Effect.Effect<readonly Doc[], DatabaseError>;
32
+ readonly update: (id: number, input: UpdateDocInput) => Effect.Effect<void, DatabaseError>;
33
+ readonly lock: (id: number, lockedAt: string) => Effect.Effect<void, DatabaseError>;
34
+ readonly remove: (id: number) => Effect.Effect<void, DatabaseError>;
35
+ readonly createLink: (fromDocId: number, toDocId: number, linkType: DocLinkType) => Effect.Effect<DocLink, DatabaseError>;
36
+ readonly getLinksFrom: (docId: number) => Effect.Effect<readonly DocLink[], DatabaseError>;
37
+ readonly getLinksTo: (docId: number) => Effect.Effect<readonly DocLink[], DatabaseError>;
38
+ readonly getAllLinks: () => Effect.Effect<readonly DocLink[], DatabaseError>;
39
+ readonly createTaskLink: (taskId: string, docId: number, linkType: TaskDocLinkType) => Effect.Effect<TaskDocLink, DatabaseError>;
40
+ readonly getTaskLinksForDoc: (docId: number) => Effect.Effect<readonly TaskDocLink[], DatabaseError>;
41
+ readonly getDocForTask: (taskId: string) => Effect.Effect<Doc | null, DatabaseError>;
42
+ readonly getUnlinkedTaskIds: () => Effect.Effect<readonly string[], DatabaseError>;
43
+ readonly upsertInvariant: (input: UpsertInvariantInput) => Effect.Effect<Invariant, DatabaseError>;
44
+ readonly findInvariantById: (id: string) => Effect.Effect<Invariant | null, DatabaseError>;
45
+ readonly findInvariants: (filter?: {
46
+ docId?: number;
47
+ subsystem?: string;
48
+ enforcement?: string;
49
+ }) => Effect.Effect<readonly Invariant[], DatabaseError>;
50
+ readonly deprecateInvariantsNotIn: (docId: number, activeIds: readonly string[]) => Effect.Effect<void, DatabaseError>;
51
+ readonly insertInvariantCheck: (invariantId: string, passed: boolean, details: string | null, durationMs: number | null) => Effect.Effect<InvariantCheck, DatabaseError>;
52
+ readonly getInvariantChecks: (invariantId: string, limit?: number) => Effect.Effect<readonly InvariantCheck[], DatabaseError>;
53
+ readonly countInvariantsByDoc: (docId: number) => Effect.Effect<number, DatabaseError>;
54
+ }>;
55
+ export declare class DocRepository extends DocRepository_base {
56
+ }
57
+ export declare const DocRepositoryLive: Layer.Layer<DocRepository, never, SqliteClient>;
58
+ export {};
59
+ //# sourceMappingURL=doc-repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-repo.d.ts","sourceRoot":"","sources":["../../src/repo/doc-repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,276 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { SqliteClient } from "../db.js";
3
+ import { DatabaseError, EntityFetchError } from "../errors.js";
4
+ import { rowToDoc, rowToDocLink, rowToTaskDocLink, rowToInvariant, rowToInvariantCheck, } from "../mappers/doc.js";
5
+ export class DocRepository extends Context.Tag("DocRepository")() {
6
+ }
7
+ export const DocRepositoryLive = Layer.effect(DocRepository, Effect.gen(function* () {
8
+ const db = yield* SqliteClient;
9
+ return {
10
+ insert: (input) => Effect.try({
11
+ try: () => {
12
+ const now = new Date().toISOString();
13
+ const result = db.prepare(`INSERT INTO docs (hash, kind, name, title, version, status, file_path, parent_doc_id, created_at, metadata)
14
+ VALUES (?, ?, ?, ?, ?, 'changing', ?, ?, ?, ?)`).run(input.hash, input.kind, input.name, input.title, input.version, input.filePath, input.parentDocId, now, input.metadata ?? "{}");
15
+ const row = db.prepare("SELECT * FROM docs WHERE id = ?").get(result.lastInsertRowid);
16
+ if (!row) {
17
+ throw new EntityFetchError({ entity: "doc", id: result.lastInsertRowid, operation: "insert" });
18
+ }
19
+ return rowToDoc(row);
20
+ },
21
+ catch: (cause) => new DatabaseError({ cause })
22
+ }),
23
+ findById: (id) => Effect.try({
24
+ try: () => {
25
+ const row = db.prepare("SELECT * FROM docs WHERE id = ?").get(id);
26
+ return row ? rowToDoc(row) : null;
27
+ },
28
+ catch: (cause) => new DatabaseError({ cause })
29
+ }),
30
+ findByName: (name, version) => Effect.try({
31
+ try: () => {
32
+ const row = version !== undefined
33
+ ? db.prepare("SELECT * FROM docs WHERE name = ? AND version = ?").get(name, version)
34
+ : db.prepare("SELECT * FROM docs WHERE name = ? ORDER BY version DESC LIMIT 1").get(name);
35
+ return row ? rowToDoc(row) : null;
36
+ },
37
+ catch: (cause) => new DatabaseError({ cause })
38
+ }),
39
+ findAll: (filter) => Effect.try({
40
+ try: () => {
41
+ let sql = "SELECT * FROM docs";
42
+ const params = [];
43
+ const conditions = [];
44
+ if (filter?.kind) {
45
+ conditions.push("kind = ?");
46
+ params.push(filter.kind);
47
+ }
48
+ if (filter?.status) {
49
+ conditions.push("status = ?");
50
+ params.push(filter.status);
51
+ }
52
+ if (conditions.length > 0) {
53
+ sql += " WHERE " + conditions.join(" AND ");
54
+ }
55
+ sql += " ORDER BY kind, name, version";
56
+ const rows = db.prepare(sql).all(...params);
57
+ return rows.map(rowToDoc);
58
+ },
59
+ catch: (cause) => new DatabaseError({ cause })
60
+ }),
61
+ update: (id, input) => Effect.try({
62
+ try: () => {
63
+ const sets = [];
64
+ const params = [];
65
+ if (input.hash !== undefined) {
66
+ sets.push("hash = ?");
67
+ params.push(input.hash);
68
+ }
69
+ if (input.title !== undefined) {
70
+ sets.push("title = ?");
71
+ params.push(input.title);
72
+ }
73
+ if (input.status !== undefined) {
74
+ sets.push("status = ?");
75
+ params.push(input.status);
76
+ }
77
+ if (input.lockedAt !== undefined) {
78
+ sets.push("locked_at = ?");
79
+ params.push(input.lockedAt);
80
+ }
81
+ if (input.metadata !== undefined) {
82
+ sets.push("metadata = ?");
83
+ params.push(input.metadata);
84
+ }
85
+ if (sets.length === 0)
86
+ return;
87
+ params.push(id);
88
+ db.prepare(`UPDATE docs SET ${sets.join(", ")} WHERE id = ?`).run(...params);
89
+ },
90
+ catch: (cause) => new DatabaseError({ cause })
91
+ }),
92
+ lock: (id, lockedAt) => Effect.try({
93
+ try: () => {
94
+ db.prepare("UPDATE docs SET status = 'locked', locked_at = ? WHERE id = ?").run(lockedAt, id);
95
+ },
96
+ catch: (cause) => new DatabaseError({ cause })
97
+ }),
98
+ remove: (id) => Effect.try({
99
+ try: () => {
100
+ // CASCADE on doc_links, task_doc_links, invariants handled by FK constraints
101
+ db.prepare("DELETE FROM docs WHERE id = ?").run(id);
102
+ },
103
+ catch: (cause) => new DatabaseError({ cause })
104
+ }),
105
+ // Doc links
106
+ createLink: (fromDocId, toDocId, linkType) => Effect.try({
107
+ try: () => {
108
+ const now = new Date().toISOString();
109
+ const result = db.prepare("INSERT INTO doc_links (from_doc_id, to_doc_id, link_type, created_at) VALUES (?, ?, ?, ?)").run(fromDocId, toDocId, linkType, now);
110
+ const row = db.prepare("SELECT * FROM doc_links WHERE id = ?").get(result.lastInsertRowid);
111
+ if (!row) {
112
+ throw new EntityFetchError({ entity: "doc_link", id: result.lastInsertRowid, operation: "insert" });
113
+ }
114
+ return rowToDocLink(row);
115
+ },
116
+ catch: (cause) => new DatabaseError({ cause })
117
+ }),
118
+ getLinksFrom: (docId) => Effect.try({
119
+ try: () => {
120
+ const rows = db.prepare("SELECT * FROM doc_links WHERE from_doc_id = ?").all(docId);
121
+ return rows.map(rowToDocLink);
122
+ },
123
+ catch: (cause) => new DatabaseError({ cause })
124
+ }),
125
+ getLinksTo: (docId) => Effect.try({
126
+ try: () => {
127
+ const rows = db.prepare("SELECT * FROM doc_links WHERE to_doc_id = ?").all(docId);
128
+ return rows.map(rowToDocLink);
129
+ },
130
+ catch: (cause) => new DatabaseError({ cause })
131
+ }),
132
+ getAllLinks: () => Effect.try({
133
+ try: () => {
134
+ const rows = db.prepare("SELECT * FROM doc_links ORDER BY created_at").all();
135
+ return rows.map(rowToDocLink);
136
+ },
137
+ catch: (cause) => new DatabaseError({ cause })
138
+ }),
139
+ // Task-doc links
140
+ createTaskLink: (taskId, docId, linkType) => Effect.try({
141
+ try: () => {
142
+ const now = new Date().toISOString();
143
+ const result = db.prepare("INSERT INTO task_doc_links (task_id, doc_id, link_type, created_at) VALUES (?, ?, ?, ?)").run(taskId, docId, linkType, now);
144
+ const row = db.prepare("SELECT * FROM task_doc_links WHERE id = ?").get(result.lastInsertRowid);
145
+ if (!row) {
146
+ throw new EntityFetchError({ entity: "task_doc_link", id: result.lastInsertRowid, operation: "insert" });
147
+ }
148
+ return rowToTaskDocLink(row);
149
+ },
150
+ catch: (cause) => new DatabaseError({ cause })
151
+ }),
152
+ getTaskLinksForDoc: (docId) => Effect.try({
153
+ try: () => {
154
+ const rows = db.prepare("SELECT * FROM task_doc_links WHERE doc_id = ?").all(docId);
155
+ return rows.map(rowToTaskDocLink);
156
+ },
157
+ catch: (cause) => new DatabaseError({ cause })
158
+ }),
159
+ getDocForTask: (taskId) => Effect.try({
160
+ try: () => {
161
+ const row = db.prepare(`SELECT d.* FROM docs d
162
+ JOIN task_doc_links tdl ON tdl.doc_id = d.id
163
+ WHERE tdl.task_id = ?
164
+ ORDER BY d.version DESC LIMIT 1`).get(taskId);
165
+ return row ? rowToDoc(row) : null;
166
+ },
167
+ catch: (cause) => new DatabaseError({ cause })
168
+ }),
169
+ getUnlinkedTaskIds: () => Effect.try({
170
+ try: () => {
171
+ const rows = db.prepare(`SELECT t.id FROM tasks t
172
+ LEFT JOIN task_doc_links tdl ON tdl.task_id = t.id
173
+ WHERE tdl.id IS NULL
174
+ ORDER BY t.id`).all();
175
+ return rows.map(r => r.id);
176
+ },
177
+ catch: (cause) => new DatabaseError({ cause })
178
+ }),
179
+ // Invariants
180
+ upsertInvariant: (input) => Effect.try({
181
+ try: () => {
182
+ const now = new Date().toISOString();
183
+ db.prepare(`INSERT INTO invariants (id, rule, enforcement, doc_id, subsystem, test_ref, lint_rule, prompt_ref, status, created_at, metadata)
184
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, '{}')
185
+ ON CONFLICT(id) DO UPDATE SET
186
+ rule = excluded.rule,
187
+ enforcement = excluded.enforcement,
188
+ doc_id = excluded.doc_id,
189
+ subsystem = excluded.subsystem,
190
+ test_ref = excluded.test_ref,
191
+ lint_rule = excluded.lint_rule,
192
+ prompt_ref = excluded.prompt_ref,
193
+ status = 'active'`).run(input.id, input.rule, input.enforcement, input.docId, input.subsystem ?? null, input.testRef ?? null, input.lintRule ?? null, input.promptRef ?? null, now);
194
+ const row = db.prepare("SELECT * FROM invariants WHERE id = ?").get(input.id);
195
+ if (!row) {
196
+ throw new EntityFetchError({ entity: "invariant", id: input.id, operation: "insert" });
197
+ }
198
+ return rowToInvariant(row);
199
+ },
200
+ catch: (cause) => new DatabaseError({ cause })
201
+ }),
202
+ findInvariantById: (id) => Effect.try({
203
+ try: () => {
204
+ const row = db.prepare("SELECT * FROM invariants WHERE id = ?").get(id);
205
+ return row ? rowToInvariant(row) : null;
206
+ },
207
+ catch: (cause) => new DatabaseError({ cause })
208
+ }),
209
+ findInvariants: (filter) => Effect.try({
210
+ try: () => {
211
+ let sql = "SELECT * FROM invariants";
212
+ const params = [];
213
+ const conditions = [];
214
+ if (filter?.docId !== undefined) {
215
+ conditions.push("doc_id = ?");
216
+ params.push(filter.docId);
217
+ }
218
+ if (filter?.subsystem) {
219
+ conditions.push("subsystem = ?");
220
+ params.push(filter.subsystem);
221
+ }
222
+ if (filter?.enforcement) {
223
+ conditions.push("enforcement = ?");
224
+ params.push(filter.enforcement);
225
+ }
226
+ if (conditions.length > 0) {
227
+ sql += " WHERE " + conditions.join(" AND ");
228
+ }
229
+ sql += " ORDER BY id";
230
+ const rows = db.prepare(sql).all(...params);
231
+ return rows.map(rowToInvariant);
232
+ },
233
+ catch: (cause) => new DatabaseError({ cause })
234
+ }),
235
+ deprecateInvariantsNotIn: (docId, activeIds) => Effect.try({
236
+ try: () => {
237
+ if (activeIds.length === 0) {
238
+ // Deprecate all invariants for this doc
239
+ db.prepare("UPDATE invariants SET status = 'deprecated' WHERE doc_id = ? AND status = 'active'").run(docId);
240
+ return;
241
+ }
242
+ const placeholders = activeIds.map(() => "?").join(", ");
243
+ db.prepare(`UPDATE invariants SET status = 'deprecated'
244
+ WHERE doc_id = ? AND status = 'active' AND id NOT IN (${placeholders})`).run(docId, ...activeIds);
245
+ },
246
+ catch: (cause) => new DatabaseError({ cause })
247
+ }),
248
+ insertInvariantCheck: (invariantId, passed, details, durationMs) => Effect.try({
249
+ try: () => {
250
+ const now = new Date().toISOString();
251
+ const result = db.prepare("INSERT INTO invariant_checks (invariant_id, passed, details, checked_at, duration_ms) VALUES (?, ?, ?, ?, ?)").run(invariantId, passed ? 1 : 0, details, now, durationMs);
252
+ const row = db.prepare("SELECT * FROM invariant_checks WHERE id = ?").get(result.lastInsertRowid);
253
+ if (!row) {
254
+ throw new EntityFetchError({ entity: "invariant_check", id: result.lastInsertRowid, operation: "insert" });
255
+ }
256
+ return rowToInvariantCheck(row);
257
+ },
258
+ catch: (cause) => new DatabaseError({ cause })
259
+ }),
260
+ getInvariantChecks: (invariantId, limit = 20) => Effect.try({
261
+ try: () => {
262
+ const rows = db.prepare("SELECT * FROM invariant_checks WHERE invariant_id = ? ORDER BY checked_at DESC LIMIT ?").all(invariantId, limit);
263
+ return rows.map(rowToInvariantCheck);
264
+ },
265
+ catch: (cause) => new DatabaseError({ cause })
266
+ }),
267
+ countInvariantsByDoc: (docId) => Effect.try({
268
+ try: () => {
269
+ const result = db.prepare("SELECT COUNT(*) as cnt FROM invariants WHERE doc_id = ? AND status = 'active'").get(docId);
270
+ return result.cnt;
271
+ },
272
+ catch: (cause) => new DatabaseError({ cause })
273
+ }),
274
+ };
275
+ }));
276
+ //# sourceMappingURL=doc-repo.js.map