hungry-ghost-hive 0.47.4 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/dist/agents/base-agent.d.ts +11 -11
  2. package/dist/agents/base-agent.d.ts.map +1 -1
  3. package/dist/agents/base-agent.js +25 -25
  4. package/dist/agents/base-agent.js.map +1 -1
  5. package/dist/agents/base-agent.test.js +2 -1
  6. package/dist/agents/base-agent.test.js.map +1 -1
  7. package/dist/agents/intermediate.d.ts +2 -0
  8. package/dist/agents/intermediate.d.ts.map +1 -1
  9. package/dist/agents/intermediate.js +25 -18
  10. package/dist/agents/intermediate.js.map +1 -1
  11. package/dist/agents/junior.d.ts +2 -0
  12. package/dist/agents/junior.d.ts.map +1 -1
  13. package/dist/agents/junior.js +25 -18
  14. package/dist/agents/junior.js.map +1 -1
  15. package/dist/agents/qa.d.ts +2 -0
  16. package/dist/agents/qa.d.ts.map +1 -1
  17. package/dist/agents/qa.js +47 -38
  18. package/dist/agents/qa.js.map +1 -1
  19. package/dist/agents/senior.d.ts +2 -0
  20. package/dist/agents/senior.d.ts.map +1 -1
  21. package/dist/agents/senior.js +40 -27
  22. package/dist/agents/senior.js.map +1 -1
  23. package/dist/agents/tech-lead.d.ts +2 -0
  24. package/dist/agents/tech-lead.d.ts.map +1 -1
  25. package/dist/agents/tech-lead.js +37 -31
  26. package/dist/agents/tech-lead.js.map +1 -1
  27. package/dist/cli/commands/add-repo.js +2 -2
  28. package/dist/cli/commands/add-repo.js.map +1 -1
  29. package/dist/cli/commands/add-repo.test.js +1 -1
  30. package/dist/cli/commands/add-repo.test.js.map +1 -1
  31. package/dist/cli/commands/agents.d.ts.map +1 -1
  32. package/dist/cli/commands/agents.js +12 -10
  33. package/dist/cli/commands/agents.js.map +1 -1
  34. package/dist/cli/commands/agents.test.js +7 -7
  35. package/dist/cli/commands/agents.test.js.map +1 -1
  36. package/dist/cli/commands/approach.js +2 -2
  37. package/dist/cli/commands/approach.js.map +1 -1
  38. package/dist/cli/commands/approvals.js +7 -7
  39. package/dist/cli/commands/approvals.js.map +1 -1
  40. package/dist/cli/commands/approvals.test.js +8 -8
  41. package/dist/cli/commands/approvals.test.js.map +1 -1
  42. package/dist/cli/commands/assign.js +4 -4
  43. package/dist/cli/commands/assign.js.map +1 -1
  44. package/dist/cli/commands/assign.test.js +18 -16
  45. package/dist/cli/commands/assign.test.js.map +1 -1
  46. package/dist/cli/commands/cleanup.d.ts.map +1 -1
  47. package/dist/cli/commands/cleanup.js +8 -8
  48. package/dist/cli/commands/cleanup.js.map +1 -1
  49. package/dist/cli/commands/cleanup.test.js +5 -1
  50. package/dist/cli/commands/cleanup.test.js.map +1 -1
  51. package/dist/cli/commands/escalations.js +9 -7
  52. package/dist/cli/commands/escalations.js.map +1 -1
  53. package/dist/cli/commands/escalations.test.js +2 -2
  54. package/dist/cli/commands/escalations.test.js.map +1 -1
  55. package/dist/cli/commands/init.d.ts.map +1 -1
  56. package/dist/cli/commands/init.js +48 -5
  57. package/dist/cli/commands/init.js.map +1 -1
  58. package/dist/cli/commands/init.test.js +4 -0
  59. package/dist/cli/commands/init.test.js.map +1 -1
  60. package/dist/cli/commands/manager/agent-monitoring.d.ts +2 -2
  61. package/dist/cli/commands/manager/agent-monitoring.d.ts.map +1 -1
  62. package/dist/cli/commands/manager/agent-monitoring.js +1 -1
  63. package/dist/cli/commands/manager/agent-monitoring.js.map +1 -1
  64. package/dist/cli/commands/manager/auditor-lifecycle.js +3 -3
  65. package/dist/cli/commands/manager/auditor-lifecycle.js.map +1 -1
  66. package/dist/cli/commands/manager/auditor-lifecycle.test.js +21 -14
  67. package/dist/cli/commands/manager/auditor-lifecycle.test.js.map +1 -1
  68. package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js +28 -23
  69. package/dist/cli/commands/manager/auto-reject-comment-only-reviews.test.js.map +1 -1
  70. package/dist/cli/commands/manager/escalation-handler.d.ts +2 -2
  71. package/dist/cli/commands/manager/escalation-handler.d.ts.map +1 -1
  72. package/dist/cli/commands/manager/escalation-handler.js +11 -10
  73. package/dist/cli/commands/manager/escalation-handler.js.map +1 -1
  74. package/dist/cli/commands/manager/escalation-handler.test.js +8 -8
  75. package/dist/cli/commands/manager/escalation-handler.test.js.map +1 -1
  76. package/dist/cli/commands/manager/feature-sign-off.js +7 -7
  77. package/dist/cli/commands/manager/feature-sign-off.js.map +1 -1
  78. package/dist/cli/commands/manager/feature-sign-off.test.js +40 -31
  79. package/dist/cli/commands/manager/feature-sign-off.test.js.map +1 -1
  80. package/dist/cli/commands/manager/feature-test-result.d.ts.map +1 -1
  81. package/dist/cli/commands/manager/feature-test-result.js +12 -13
  82. package/dist/cli/commands/manager/feature-test-result.js.map +1 -1
  83. package/dist/cli/commands/manager/handoff-recovery.d.ts.map +1 -1
  84. package/dist/cli/commands/manager/handoff-recovery.js +14 -15
  85. package/dist/cli/commands/manager/handoff-recovery.js.map +1 -1
  86. package/dist/cli/commands/manager/index.d.ts.map +1 -1
  87. package/dist/cli/commands/manager/index.js +26 -26
  88. package/dist/cli/commands/manager/index.js.map +1 -1
  89. package/dist/cli/commands/manager/index.test.js +3 -3
  90. package/dist/cli/commands/manager/index.test.js.map +1 -1
  91. package/dist/cli/commands/manager/merged-story-cleanup.d.ts +2 -2
  92. package/dist/cli/commands/manager/merged-story-cleanup.d.ts.map +1 -1
  93. package/dist/cli/commands/manager/merged-story-cleanup.js +6 -7
  94. package/dist/cli/commands/manager/merged-story-cleanup.js.map +1 -1
  95. package/dist/cli/commands/manager/merged-story-cleanup.test.js +27 -18
  96. package/dist/cli/commands/manager/merged-story-cleanup.test.js.map +1 -1
  97. package/dist/cli/commands/manager/pr-sync-orchestrator.d.ts.map +1 -1
  98. package/dist/cli/commands/manager/pr-sync-orchestrator.js +46 -38
  99. package/dist/cli/commands/manager/pr-sync-orchestrator.js.map +1 -1
  100. package/dist/cli/commands/manager/qa-review-handler.d.ts.map +1 -1
  101. package/dist/cli/commands/manager/qa-review-handler.js +25 -22
  102. package/dist/cli/commands/manager/qa-review-handler.js.map +1 -1
  103. package/dist/cli/commands/manager/spin-down.d.ts.map +1 -1
  104. package/dist/cli/commands/manager/spin-down.js +23 -19
  105. package/dist/cli/commands/manager/spin-down.js.map +1 -1
  106. package/dist/cli/commands/manager/stale-escalations.d.ts +2 -3
  107. package/dist/cli/commands/manager/stale-escalations.d.ts.map +1 -1
  108. package/dist/cli/commands/manager/stale-escalations.js.map +1 -1
  109. package/dist/cli/commands/manager/stuck-story-helpers.js +8 -8
  110. package/dist/cli/commands/manager/stuck-story-helpers.js.map +1 -1
  111. package/dist/cli/commands/manager/stuck-story-processor.d.ts +2 -2
  112. package/dist/cli/commands/manager/stuck-story-processor.d.ts.map +1 -1
  113. package/dist/cli/commands/manager/stuck-story-processor.js +23 -22
  114. package/dist/cli/commands/manager/stuck-story-processor.js.map +1 -1
  115. package/dist/cli/commands/manager/tech-lead-lifecycle.js +6 -6
  116. package/dist/cli/commands/manager/tech-lead-lifecycle.js.map +1 -1
  117. package/dist/cli/commands/manager/types.d.ts +2 -3
  118. package/dist/cli/commands/manager/types.d.ts.map +1 -1
  119. package/dist/cli/commands/manager/types.js.map +1 -1
  120. package/dist/cli/commands/msg.test.js +2 -2
  121. package/dist/cli/commands/msg.test.js.map +1 -1
  122. package/dist/cli/commands/my-stories.d.ts.map +1 -1
  123. package/dist/cli/commands/my-stories.js +17 -18
  124. package/dist/cli/commands/my-stories.js.map +1 -1
  125. package/dist/cli/commands/my-stories.test.js +2 -2
  126. package/dist/cli/commands/my-stories.test.js.map +1 -1
  127. package/dist/cli/commands/nuke.test.js +1 -1
  128. package/dist/cli/commands/nuke.test.js.map +1 -1
  129. package/dist/cli/commands/pr.js +32 -32
  130. package/dist/cli/commands/pr.js.map +1 -1
  131. package/dist/cli/commands/pr.test.js +10 -6
  132. package/dist/cli/commands/pr.test.js.map +1 -1
  133. package/dist/cli/commands/progress.d.ts.map +1 -1
  134. package/dist/cli/commands/progress.js +4 -5
  135. package/dist/cli/commands/progress.js.map +1 -1
  136. package/dist/cli/commands/progress.test.js +1 -1
  137. package/dist/cli/commands/progress.test.js.map +1 -1
  138. package/dist/cli/commands/req-headless.test.d.ts +2 -0
  139. package/dist/cli/commands/req-headless.test.d.ts.map +1 -0
  140. package/dist/cli/commands/req-headless.test.js +128 -0
  141. package/dist/cli/commands/req-headless.test.js.map +1 -0
  142. package/dist/cli/commands/req-spawn.test.js +5 -1
  143. package/dist/cli/commands/req-spawn.test.js.map +1 -1
  144. package/dist/cli/commands/req.d.ts.map +1 -1
  145. package/dist/cli/commands/req.js +24 -21
  146. package/dist/cli/commands/req.js.map +1 -1
  147. package/dist/cli/commands/req.test.js +31 -0
  148. package/dist/cli/commands/req.test.js.map +1 -1
  149. package/dist/cli/commands/resume.d.ts.map +1 -1
  150. package/dist/cli/commands/resume.js +7 -8
  151. package/dist/cli/commands/resume.js.map +1 -1
  152. package/dist/cli/commands/resume.test.js +1 -1
  153. package/dist/cli/commands/resume.test.js.map +1 -1
  154. package/dist/cli/commands/status.d.ts.map +1 -1
  155. package/dist/cli/commands/status.js +42 -40
  156. package/dist/cli/commands/status.js.map +1 -1
  157. package/dist/cli/commands/status.test.js +1 -1
  158. package/dist/cli/commands/status.test.js.map +1 -1
  159. package/dist/cli/commands/stories.js +9 -9
  160. package/dist/cli/commands/stories.js.map +1 -1
  161. package/dist/cli/commands/stories.test.js +2 -2
  162. package/dist/cli/commands/stories.test.js.map +1 -1
  163. package/dist/cli/commands/teams.js +11 -11
  164. package/dist/cli/commands/teams.js.map +1 -1
  165. package/dist/cli/commands/teams.test.js +2 -2
  166. package/dist/cli/commands/teams.test.js.map +1 -1
  167. package/dist/cli/dashboard/index.d.ts +2 -2
  168. package/dist/cli/dashboard/index.d.ts.map +1 -1
  169. package/dist/cli/dashboard/index.js +29 -20
  170. package/dist/cli/dashboard/index.js.map +1 -1
  171. package/dist/cli/dashboard/index.test.js +34 -32
  172. package/dist/cli/dashboard/index.test.js.map +1 -1
  173. package/dist/cli/dashboard/panels/activity.d.ts +3 -3
  174. package/dist/cli/dashboard/panels/activity.d.ts.map +1 -1
  175. package/dist/cli/dashboard/panels/activity.js +1 -1
  176. package/dist/cli/dashboard/panels/activity.js.map +1 -1
  177. package/dist/cli/dashboard/panels/agents.d.ts +3 -3
  178. package/dist/cli/dashboard/panels/agents.d.ts.map +1 -1
  179. package/dist/cli/dashboard/panels/agents.js +2 -2
  180. package/dist/cli/dashboard/panels/agents.js.map +1 -1
  181. package/dist/cli/dashboard/panels/escalations.d.ts +3 -3
  182. package/dist/cli/dashboard/panels/escalations.d.ts.map +1 -1
  183. package/dist/cli/dashboard/panels/escalations.js +1 -1
  184. package/dist/cli/dashboard/panels/escalations.js.map +1 -1
  185. package/dist/cli/dashboard/panels/merge-queue.d.ts +3 -3
  186. package/dist/cli/dashboard/panels/merge-queue.d.ts.map +1 -1
  187. package/dist/cli/dashboard/panels/merge-queue.js +1 -1
  188. package/dist/cli/dashboard/panels/merge-queue.js.map +1 -1
  189. package/dist/cli/dashboard/panels/pipeline.d.ts +3 -3
  190. package/dist/cli/dashboard/panels/pipeline.d.ts.map +1 -1
  191. package/dist/cli/dashboard/panels/pipeline.js +1 -1
  192. package/dist/cli/dashboard/panels/pipeline.js.map +1 -1
  193. package/dist/config/schema.d.ts +85 -82
  194. package/dist/config/schema.d.ts.map +1 -1
  195. package/dist/config/schema.js +1 -0
  196. package/dist/config/schema.js.map +1 -1
  197. package/dist/connectors/project-management/operations.d.ts +7 -7
  198. package/dist/connectors/project-management/operations.d.ts.map +1 -1
  199. package/dist/connectors/project-management/operations.js +2 -3
  200. package/dist/connectors/project-management/operations.js.map +1 -1
  201. package/dist/context-files/index.test.js +1 -0
  202. package/dist/context-files/index.test.js.map +1 -1
  203. package/dist/db/client.d.ts +6 -0
  204. package/dist/db/client.d.ts.map +1 -1
  205. package/dist/db/client.js +7 -0
  206. package/dist/db/client.js.map +1 -1
  207. package/dist/db/postgres-provider.d.ts +43 -0
  208. package/dist/db/postgres-provider.d.ts.map +1 -0
  209. package/dist/db/postgres-provider.integration.test.d.ts +2 -0
  210. package/dist/db/postgres-provider.integration.test.d.ts.map +1 -0
  211. package/dist/db/postgres-provider.integration.test.js +399 -0
  212. package/dist/db/postgres-provider.integration.test.js.map +1 -0
  213. package/dist/db/postgres-provider.js +315 -0
  214. package/dist/db/postgres-provider.js.map +1 -0
  215. package/dist/db/postgres-provider.test.d.ts +2 -0
  216. package/dist/db/postgres-provider.test.d.ts.map +1 -0
  217. package/dist/db/postgres-provider.test.js +72 -0
  218. package/dist/db/postgres-provider.test.js.map +1 -0
  219. package/dist/db/provider.d.ts +59 -0
  220. package/dist/db/provider.d.ts.map +1 -0
  221. package/dist/db/provider.js +121 -0
  222. package/dist/db/provider.js.map +1 -0
  223. package/dist/db/provider.test.d.ts +2 -0
  224. package/dist/db/provider.test.d.ts.map +1 -0
  225. package/dist/db/provider.test.js +226 -0
  226. package/dist/db/provider.test.js.map +1 -0
  227. package/dist/db/queries/agents.d.ts +13 -13
  228. package/dist/db/queries/agents.d.ts.map +1 -1
  229. package/dist/db/queries/agents.js +27 -28
  230. package/dist/db/queries/agents.js.map +1 -1
  231. package/dist/db/queries/agents.test.js +113 -111
  232. package/dist/db/queries/agents.test.js.map +1 -1
  233. package/dist/db/queries/escalations.d.ts +16 -16
  234. package/dist/db/queries/escalations.d.ts.map +1 -1
  235. package/dist/db/queries/escalations.js +34 -35
  236. package/dist/db/queries/escalations.js.map +1 -1
  237. package/dist/db/queries/escalations.test.js +133 -131
  238. package/dist/db/queries/escalations.test.js.map +1 -1
  239. package/dist/db/queries/heartbeat.d.ts +5 -5
  240. package/dist/db/queries/heartbeat.d.ts.map +1 -1
  241. package/dist/db/queries/heartbeat.js +7 -23
  242. package/dist/db/queries/heartbeat.js.map +1 -1
  243. package/dist/db/queries/heartbeat.test.js +76 -76
  244. package/dist/db/queries/heartbeat.test.js.map +1 -1
  245. package/dist/db/queries/integration-sync.d.ts +7 -7
  246. package/dist/db/queries/integration-sync.d.ts.map +1 -1
  247. package/dist/db/queries/integration-sync.js +13 -14
  248. package/dist/db/queries/integration-sync.js.map +1 -1
  249. package/dist/db/queries/logs.d.ts +10 -10
  250. package/dist/db/queries/logs.d.ts.map +1 -1
  251. package/dist/db/queries/logs.js +44 -42
  252. package/dist/db/queries/logs.js.map +1 -1
  253. package/dist/db/queries/logs.test.js +149 -146
  254. package/dist/db/queries/logs.test.js.map +1 -1
  255. package/dist/db/queries/messages.d.ts +6 -6
  256. package/dist/db/queries/messages.d.ts.map +1 -1
  257. package/dist/db/queries/messages.js +12 -11
  258. package/dist/db/queries/messages.js.map +1 -1
  259. package/dist/db/queries/messages.test.js +47 -46
  260. package/dist/db/queries/messages.test.js.map +1 -1
  261. package/dist/db/queries/pull-requests.d.ts +18 -18
  262. package/dist/db/queries/pull-requests.d.ts.map +1 -1
  263. package/dist/db/queries/pull-requests.js +50 -48
  264. package/dist/db/queries/pull-requests.js.map +1 -1
  265. package/dist/db/queries/pull-requests.test.js +195 -198
  266. package/dist/db/queries/pull-requests.test.js.map +1 -1
  267. package/dist/db/queries/requirements.d.ts +8 -8
  268. package/dist/db/queries/requirements.d.ts.map +1 -1
  269. package/dist/db/queries/requirements.js +17 -18
  270. package/dist/db/queries/requirements.js.map +1 -1
  271. package/dist/db/queries/requirements.test.js +83 -81
  272. package/dist/db/queries/requirements.test.js.map +1 -1
  273. package/dist/db/queries/stories.d.ts +29 -29
  274. package/dist/db/queries/stories.d.ts.map +1 -1
  275. package/dist/db/queries/stories.js +58 -64
  276. package/dist/db/queries/stories.js.map +1 -1
  277. package/dist/db/queries/stories.test.js +172 -170
  278. package/dist/db/queries/stories.test.js.map +1 -1
  279. package/dist/db/queries/teams.d.ts +6 -6
  280. package/dist/db/queries/teams.d.ts.map +1 -1
  281. package/dist/db/queries/teams.js +11 -12
  282. package/dist/db/queries/teams.js.map +1 -1
  283. package/dist/db/queries/teams.test.js +36 -34
  284. package/dist/db/queries/teams.test.js.map +1 -1
  285. package/dist/index.js +8 -1
  286. package/dist/index.js.map +1 -1
  287. package/dist/integrations/jira/repair.test.js +26 -24
  288. package/dist/integrations/jira/repair.test.js.map +1 -1
  289. package/dist/integrations/jira/stories.d.ts +3 -3
  290. package/dist/integrations/jira/stories.d.ts.map +1 -1
  291. package/dist/integrations/jira/stories.js +12 -12
  292. package/dist/integrations/jira/stories.js.map +1 -1
  293. package/dist/integrations/jira/stories.test.js +10 -8
  294. package/dist/integrations/jira/stories.test.js.map +1 -1
  295. package/dist/integrations/jira/sync.d.ts +7 -7
  296. package/dist/integrations/jira/sync.d.ts.map +1 -1
  297. package/dist/integrations/jira/sync.js +17 -20
  298. package/dist/integrations/jira/sync.js.map +1 -1
  299. package/dist/integrations/jira/sync.test.js +63 -62
  300. package/dist/integrations/jira/sync.test.js.map +1 -1
  301. package/dist/integrations/jira/transitions.d.ts +3 -3
  302. package/dist/integrations/jira/transitions.d.ts.map +1 -1
  303. package/dist/integrations/jira/transitions.js +3 -3
  304. package/dist/integrations/jira/transitions.js.map +1 -1
  305. package/dist/orchestrator/agent-selector.d.ts +3 -3
  306. package/dist/orchestrator/agent-selector.d.ts.map +1 -1
  307. package/dist/orchestrator/agent-selector.js +5 -6
  308. package/dist/orchestrator/agent-selector.js.map +1 -1
  309. package/dist/orchestrator/dependency-resolver.d.ts +4 -4
  310. package/dist/orchestrator/dependency-resolver.d.ts.map +1 -1
  311. package/dist/orchestrator/dependency-resolver.js +6 -6
  312. package/dist/orchestrator/dependency-resolver.js.map +1 -1
  313. package/dist/orchestrator/feature-branch.d.ts +3 -3
  314. package/dist/orchestrator/feature-branch.d.ts.map +1 -1
  315. package/dist/orchestrator/feature-branch.js +9 -10
  316. package/dist/orchestrator/feature-branch.js.map +1 -1
  317. package/dist/orchestrator/feature-branch.test.js +80 -78
  318. package/dist/orchestrator/feature-branch.test.js.map +1 -1
  319. package/dist/orchestrator/orphan-recovery.d.ts +2 -2
  320. package/dist/orchestrator/orphan-recovery.d.ts.map +1 -1
  321. package/dist/orchestrator/orphan-recovery.js +10 -10
  322. package/dist/orchestrator/orphan-recovery.js.map +1 -1
  323. package/dist/orchestrator/scheduler.d.ts +4 -4
  324. package/dist/orchestrator/scheduler.d.ts.map +1 -1
  325. package/dist/orchestrator/scheduler.js +90 -76
  326. package/dist/orchestrator/scheduler.js.map +1 -1
  327. package/dist/orchestrator/scheduler.test.js +496 -374
  328. package/dist/orchestrator/scheduler.test.js.map +1 -1
  329. package/dist/utils/auto-merge.d.ts.map +1 -1
  330. package/dist/utils/auto-merge.js +74 -56
  331. package/dist/utils/auto-merge.js.map +1 -1
  332. package/dist/utils/auto-merge.test.js +101 -66
  333. package/dist/utils/auto-merge.test.js.map +1 -1
  334. package/dist/utils/cli-helpers.d.ts +5 -5
  335. package/dist/utils/cli-helpers.d.ts.map +1 -1
  336. package/dist/utils/cli-helpers.js +8 -9
  337. package/dist/utils/cli-helpers.js.map +1 -1
  338. package/dist/utils/cli-helpers.test.js +28 -30
  339. package/dist/utils/cli-helpers.test.js.map +1 -1
  340. package/dist/utils/paths.d.ts +6 -0
  341. package/dist/utils/paths.d.ts.map +1 -1
  342. package/dist/utils/paths.js +12 -1
  343. package/dist/utils/paths.js.map +1 -1
  344. package/dist/utils/paths.test.js +1 -0
  345. package/dist/utils/paths.test.js.map +1 -1
  346. package/dist/utils/pr-sync.d.ts +10 -10
  347. package/dist/utils/pr-sync.d.ts.map +1 -1
  348. package/dist/utils/pr-sync.js +20 -21
  349. package/dist/utils/pr-sync.js.map +1 -1
  350. package/dist/utils/pr-sync.test.js +52 -50
  351. package/dist/utils/pr-sync.test.js.map +1 -1
  352. package/dist/utils/with-hive-context.d.ts.map +1 -1
  353. package/dist/utils/with-hive-context.js +70 -1
  354. package/dist/utils/with-hive-context.js.map +1 -1
  355. package/package.json +3 -1
  356. package/src/agents/base-agent.test.ts +2 -1
  357. package/src/agents/base-agent.ts +32 -28
  358. package/src/agents/intermediate.ts +27 -18
  359. package/src/agents/junior.ts +27 -18
  360. package/src/agents/qa.ts +54 -40
  361. package/src/agents/senior.ts +42 -27
  362. package/src/agents/tech-lead.ts +42 -32
  363. package/src/cli/commands/add-repo.test.ts +1 -1
  364. package/src/cli/commands/add-repo.ts +2 -2
  365. package/src/cli/commands/agents.test.ts +7 -7
  366. package/src/cli/commands/agents.ts +12 -10
  367. package/src/cli/commands/approach.ts +2 -2
  368. package/src/cli/commands/approvals.test.ts +8 -8
  369. package/src/cli/commands/approvals.ts +9 -7
  370. package/src/cli/commands/assign.test.ts +19 -18
  371. package/src/cli/commands/assign.ts +4 -4
  372. package/src/cli/commands/cleanup.test.ts +5 -1
  373. package/src/cli/commands/cleanup.ts +11 -9
  374. package/src/cli/commands/escalations.test.ts +2 -2
  375. package/src/cli/commands/escalations.ts +9 -7
  376. package/src/cli/commands/init.test.ts +5 -0
  377. package/src/cli/commands/init.ts +53 -5
  378. package/src/cli/commands/manager/agent-monitoring.ts +3 -3
  379. package/src/cli/commands/manager/auditor-lifecycle.test.ts +21 -14
  380. package/src/cli/commands/manager/auditor-lifecycle.ts +3 -3
  381. package/src/cli/commands/manager/auto-reject-comment-only-reviews.test.ts +28 -23
  382. package/src/cli/commands/manager/escalation-handler.test.ts +13 -13
  383. package/src/cli/commands/manager/escalation-handler.ts +19 -12
  384. package/src/cli/commands/manager/feature-sign-off.test.ts +40 -31
  385. package/src/cli/commands/manager/feature-sign-off.ts +7 -7
  386. package/src/cli/commands/manager/feature-test-result.ts +13 -16
  387. package/src/cli/commands/manager/handoff-recovery.ts +20 -20
  388. package/src/cli/commands/manager/index.test.ts +4 -4
  389. package/src/cli/commands/manager/index.ts +58 -59
  390. package/src/cli/commands/manager/merged-story-cleanup.test.ts +28 -19
  391. package/src/cli/commands/manager/merged-story-cleanup.ts +11 -14
  392. package/src/cli/commands/manager/pr-sync-orchestrator.ts +115 -110
  393. package/src/cli/commands/manager/qa-review-handler.ts +50 -63
  394. package/src/cli/commands/manager/spin-down.ts +27 -25
  395. package/src/cli/commands/manager/stale-escalations.ts +2 -3
  396. package/src/cli/commands/manager/stuck-story-helpers.ts +10 -10
  397. package/src/cli/commands/manager/stuck-story-processor.ts +56 -62
  398. package/src/cli/commands/manager/tech-lead-lifecycle.ts +6 -6
  399. package/src/cli/commands/manager/types.ts +2 -3
  400. package/src/cli/commands/msg.test.ts +2 -2
  401. package/src/cli/commands/my-stories.test.ts +4 -2
  402. package/src/cli/commands/my-stories.ts +22 -27
  403. package/src/cli/commands/nuke.test.ts +1 -1
  404. package/src/cli/commands/pr.test.ts +10 -6
  405. package/src/cli/commands/pr.ts +41 -32
  406. package/src/cli/commands/progress.test.ts +1 -1
  407. package/src/cli/commands/progress.ts +11 -6
  408. package/src/cli/commands/req-headless.test.ts +170 -0
  409. package/src/cli/commands/req-spawn.test.ts +12 -2
  410. package/src/cli/commands/req.test.ts +36 -0
  411. package/src/cli/commands/req.ts +24 -20
  412. package/src/cli/commands/resume.test.ts +1 -1
  413. package/src/cli/commands/resume.ts +7 -8
  414. package/src/cli/commands/status.test.ts +1 -1
  415. package/src/cli/commands/status.ts +52 -40
  416. package/src/cli/commands/stories.test.ts +4 -2
  417. package/src/cli/commands/stories.ts +11 -11
  418. package/src/cli/commands/teams.test.ts +2 -2
  419. package/src/cli/commands/teams.ts +11 -11
  420. package/src/cli/dashboard/index.test.ts +35 -34
  421. package/src/cli/dashboard/index.ts +34 -23
  422. package/src/cli/dashboard/panels/activity.ts +10 -4
  423. package/src/cli/dashboard/panels/agents.ts +8 -5
  424. package/src/cli/dashboard/panels/escalations.ts +4 -4
  425. package/src/cli/dashboard/panels/merge-queue.ts +4 -4
  426. package/src/cli/dashboard/panels/pipeline.ts +10 -4
  427. package/src/config/schema.ts +1 -0
  428. package/src/connectors/project-management/operations.ts +9 -10
  429. package/src/context-files/index.test.ts +1 -0
  430. package/src/db/client.ts +17 -0
  431. package/src/db/pg-migrations/001-full-schema.sql +209 -0
  432. package/src/db/postgres-provider.integration.test.ts +574 -0
  433. package/src/db/postgres-provider.test.ts +97 -0
  434. package/src/db/postgres-provider.ts +364 -0
  435. package/src/db/provider.test.ts +283 -0
  436. package/src/db/provider.ts +161 -0
  437. package/src/db/queries/agents.test.ts +114 -113
  438. package/src/db/queries/agents.ts +50 -36
  439. package/src/db/queries/escalations.test.ts +134 -133
  440. package/src/db/queries/escalations.ts +72 -57
  441. package/src/db/queries/heartbeat.test.ts +77 -78
  442. package/src/db/queries/heartbeat.ts +24 -46
  443. package/src/db/queries/integration-sync.ts +26 -26
  444. package/src/db/queries/logs.test.ts +151 -148
  445. package/src/db/queries/logs.ts +78 -53
  446. package/src/db/queries/messages.test.ts +48 -50
  447. package/src/db/queries/messages.ts +26 -18
  448. package/src/db/queries/pull-requests.test.ts +194 -199
  449. package/src/db/queries/pull-requests.ts +117 -88
  450. package/src/db/queries/requirements.test.ts +84 -83
  451. package/src/db/queries/requirements.ts +33 -28
  452. package/src/db/queries/stories.test.ts +173 -172
  453. package/src/db/queries/stories.ts +141 -110
  454. package/src/db/queries/teams.test.ts +37 -36
  455. package/src/db/queries/teams.ts +22 -14
  456. package/src/index.ts +8 -1
  457. package/src/integrations/jira/repair.test.ts +27 -26
  458. package/src/integrations/jira/stories.test.ts +15 -16
  459. package/src/integrations/jira/stories.ts +15 -15
  460. package/src/integrations/jira/sync.test.ts +68 -68
  461. package/src/integrations/jira/sync.ts +29 -39
  462. package/src/integrations/jira/transitions.ts +6 -6
  463. package/src/orchestrator/agent-selector.ts +9 -8
  464. package/src/orchestrator/dependency-resolver.ts +16 -7
  465. package/src/orchestrator/feature-branch.test.ts +85 -80
  466. package/src/orchestrator/feature-branch.ts +13 -14
  467. package/src/orchestrator/orphan-recovery.ts +14 -13
  468. package/src/orchestrator/scheduler.test.ts +536 -394
  469. package/src/orchestrator/scheduler.ts +129 -115
  470. package/src/utils/auto-merge.test.ts +102 -68
  471. package/src/utils/auto-merge.ts +161 -168
  472. package/src/utils/cli-helpers.test.ts +30 -32
  473. package/src/utils/cli-helpers.ts +15 -11
  474. package/src/utils/paths.test.ts +1 -0
  475. package/src/utils/paths.ts +14 -1
  476. package/src/utils/pr-sync.test.ts +55 -52
  477. package/src/utils/pr-sync.ts +27 -32
  478. package/src/utils/with-hive-context.ts +89 -1
@@ -4,6 +4,7 @@ import { tmpdir } from 'os';
4
4
  import { join } from 'path';
5
5
  import initSqlJs from 'sql.js';
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { SqliteProvider } from '../db/provider.js';
7
8
  import { getLogsByEventType } from '../db/queries/logs.js';
8
9
  import { createPullRequest } from '../db/queries/pull-requests.js';
9
10
  import { createRequirement } from '../db/queries/requirements.js';
@@ -162,39 +163,60 @@ CREATE TABLE IF NOT EXISTS requirements (
162
163
  `;
163
164
  beforeEach(async () => {
164
165
  const SQL = await initSqlJs();
165
- db = new SQL.Database();
166
- db.run('PRAGMA foreign_keys = ON');
167
- db.run(INITIAL_MIGRATION);
168
- db.run("INSERT INTO migrations (name) VALUES ('001-initial.sql')");
166
+ const rawDb = new SQL.Database();
167
+ rawDb.run('PRAGMA foreign_keys = ON');
168
+ rawDb.run(INITIAL_MIGRATION);
169
+ rawDb.run("INSERT INTO migrations (name) VALUES ('001-initial.sql')");
170
+ db = new SqliteProvider(rawDb);
169
171
  scheduler = new Scheduler(db, mockConfig);
170
172
  });
171
173
  describe('Scheduler Topological Sort', () => {
172
- it('should handle stories with no dependencies', () => {
173
- const team = createTeam(db, {
174
+ it('should handle stories with no dependencies', async () => {
175
+ const team = await createTeam(db, {
174
176
  name: 'Test Team',
175
177
  repoUrl: 'https://github.com/test/repo',
176
178
  repoPath: 'test',
177
179
  });
178
- const story1 = createStory(db, { teamId: team.id, title: 'Story 1', description: 'Test' });
179
- const story2 = createStory(db, { teamId: team.id, title: 'Story 2', description: 'Test' });
180
+ const story1 = await createStory(db, {
181
+ teamId: team.id,
182
+ title: 'Story 1',
183
+ description: 'Test',
184
+ });
185
+ const story2 = await createStory(db, {
186
+ teamId: team.id,
187
+ title: 'Story 2',
188
+ description: 'Test',
189
+ });
180
190
  // Mock the private method by accessing it through reflection
181
- const sorted = topologicalSort(db, [story1, story2]);
191
+ const sorted = await topologicalSort(db, [story1, story2]);
182
192
  expect(sorted).not.toBeNull();
183
193
  expect(sorted).toHaveLength(2);
184
194
  });
185
- it('should respect linear dependencies (A -> B -> C)', () => {
186
- const team = createTeam(db, {
195
+ it('should respect linear dependencies (A -> B -> C)', async () => {
196
+ const team = await createTeam(db, {
187
197
  name: 'Test Team',
188
198
  repoUrl: 'https://github.com/test/repo',
189
199
  repoPath: 'test',
190
200
  });
191
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
192
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
193
- const storyC = createStory(db, { teamId: team.id, title: 'Story C', description: 'Test' });
201
+ const storyA = await createStory(db, {
202
+ teamId: team.id,
203
+ title: 'Story A',
204
+ description: 'Test',
205
+ });
206
+ const storyB = await createStory(db, {
207
+ teamId: team.id,
208
+ title: 'Story B',
209
+ description: 'Test',
210
+ });
211
+ const storyC = await createStory(db, {
212
+ teamId: team.id,
213
+ title: 'Story C',
214
+ description: 'Test',
215
+ });
194
216
  // B depends on A, C depends on B
195
- addStoryDependency(db, storyB.id, storyA.id);
196
- addStoryDependency(db, storyC.id, storyB.id);
197
- const sorted = topologicalSort(db, [storyC, storyA, storyB]);
217
+ await addStoryDependency(db, storyB.id, storyA.id);
218
+ await addStoryDependency(db, storyC.id, storyB.id);
219
+ const sorted = await topologicalSort(db, [storyC, storyA, storyB]);
198
220
  expect(sorted).not.toBeNull();
199
221
  expect(sorted).toHaveLength(3);
200
222
  // A should come first, then B, then C
@@ -202,22 +224,38 @@ describe('Scheduler Topological Sort', () => {
202
224
  expect(ids.indexOf(storyA.id)).toBeLessThan(ids.indexOf(storyB.id));
203
225
  expect(ids.indexOf(storyB.id)).toBeLessThan(ids.indexOf(storyC.id));
204
226
  });
205
- it('should respect diamond dependencies (A -> B, A -> C, B -> D, C -> D)', () => {
206
- const team = createTeam(db, {
227
+ it('should respect diamond dependencies (A -> B, A -> C, B -> D, C -> D)', async () => {
228
+ const team = await createTeam(db, {
207
229
  name: 'Test Team',
208
230
  repoUrl: 'https://github.com/test/repo',
209
231
  repoPath: 'test',
210
232
  });
211
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
212
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
213
- const storyC = createStory(db, { teamId: team.id, title: 'Story C', description: 'Test' });
214
- const storyD = createStory(db, { teamId: team.id, title: 'Story D', description: 'Test' });
233
+ const storyA = await createStory(db, {
234
+ teamId: team.id,
235
+ title: 'Story A',
236
+ description: 'Test',
237
+ });
238
+ const storyB = await createStory(db, {
239
+ teamId: team.id,
240
+ title: 'Story B',
241
+ description: 'Test',
242
+ });
243
+ const storyC = await createStory(db, {
244
+ teamId: team.id,
245
+ title: 'Story C',
246
+ description: 'Test',
247
+ });
248
+ const storyD = await createStory(db, {
249
+ teamId: team.id,
250
+ title: 'Story D',
251
+ description: 'Test',
252
+ });
215
253
  // B and C depend on A, D depends on both B and C
216
- addStoryDependency(db, storyB.id, storyA.id);
217
- addStoryDependency(db, storyC.id, storyA.id);
218
- addStoryDependency(db, storyD.id, storyB.id);
219
- addStoryDependency(db, storyD.id, storyC.id);
220
- const sorted = topologicalSort(db, [storyD, storyB, storyA, storyC]);
254
+ await addStoryDependency(db, storyB.id, storyA.id);
255
+ await addStoryDependency(db, storyC.id, storyA.id);
256
+ await addStoryDependency(db, storyD.id, storyB.id);
257
+ await addStoryDependency(db, storyD.id, storyC.id);
258
+ const sorted = await topologicalSort(db, [storyD, storyB, storyA, storyC]);
221
259
  expect(sorted).not.toBeNull();
222
260
  expect(sorted).toHaveLength(4);
223
261
  const ids = sorted.map((s) => s.id);
@@ -231,117 +269,149 @@ describe('Scheduler Topological Sort', () => {
231
269
  expect(ids.indexOf(storyB.id)).toBeLessThan(ids.indexOf(storyD.id));
232
270
  expect(ids.indexOf(storyC.id)).toBeLessThan(ids.indexOf(storyD.id));
233
271
  });
234
- it('should detect circular dependencies', () => {
235
- const team = createTeam(db, {
272
+ it('should detect circular dependencies', async () => {
273
+ const team = await createTeam(db, {
236
274
  name: 'Test Team',
237
275
  repoUrl: 'https://github.com/test/repo',
238
276
  repoPath: 'test',
239
277
  });
240
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
241
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
278
+ const storyA = await createStory(db, {
279
+ teamId: team.id,
280
+ title: 'Story A',
281
+ description: 'Test',
282
+ });
283
+ const storyB = await createStory(db, {
284
+ teamId: team.id,
285
+ title: 'Story B',
286
+ description: 'Test',
287
+ });
242
288
  // Create circular dependency: A -> B -> A
243
- addStoryDependency(db, storyB.id, storyA.id);
244
- addStoryDependency(db, storyA.id, storyB.id);
245
- const sorted = topologicalSort(db, [storyA, storyB]);
289
+ await addStoryDependency(db, storyB.id, storyA.id);
290
+ await addStoryDependency(db, storyA.id, storyB.id);
291
+ const sorted = await topologicalSort(db, [storyA, storyB]);
246
292
  expect(sorted).toBeNull();
247
293
  });
248
294
  });
249
295
  describe('Scheduler Dependency Satisfaction', () => {
250
- it('should consider merged stories as satisfying dependencies', () => {
251
- const team = createTeam(db, {
296
+ it('should consider merged stories as satisfying dependencies', async () => {
297
+ const team = await createTeam(db, {
252
298
  name: 'Test Team',
253
299
  repoUrl: 'https://github.com/test/repo',
254
300
  repoPath: 'test',
255
301
  });
256
- const depStory = createStory(db, { teamId: team.id, title: 'Dependency', description: 'Test' });
257
- const mainStory = createStory(db, {
302
+ const depStory = await createStory(db, {
303
+ teamId: team.id,
304
+ title: 'Dependency',
305
+ description: 'Test',
306
+ });
307
+ const mainStory = await createStory(db, {
258
308
  teamId: team.id,
259
309
  title: 'Main Story',
260
310
  description: 'Test',
261
311
  });
262
- addStoryDependency(db, mainStory.id, depStory.id);
312
+ await addStoryDependency(db, mainStory.id, depStory.id);
263
313
  // Initially, dependencies are not satisfied
264
- let isSatisfied = areDependenciesSatisfied(db, mainStory.id);
314
+ let isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
265
315
  expect(isSatisfied).toBe(false);
266
316
  // Mark dependency as merged
267
- updateStory(db, depStory.id, { status: 'merged' });
268
- isSatisfied = areDependenciesSatisfied(db, mainStory.id);
317
+ await updateStory(db, depStory.id, { status: 'merged' });
318
+ isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
269
319
  expect(isSatisfied).toBe(true);
270
320
  });
271
- it('should not consider in-progress stories as satisfying dependencies', () => {
272
- const team = createTeam(db, {
321
+ it('should not consider in-progress stories as satisfying dependencies', async () => {
322
+ const team = await createTeam(db, {
273
323
  name: 'Test Team',
274
324
  repoUrl: 'https://github.com/test/repo',
275
325
  repoPath: 'test',
276
326
  });
277
- const depStory = createStory(db, { teamId: team.id, title: 'Dependency', description: 'Test' });
278
- const mainStory = createStory(db, {
327
+ const depStory = await createStory(db, {
328
+ teamId: team.id,
329
+ title: 'Dependency',
330
+ description: 'Test',
331
+ });
332
+ const mainStory = await createStory(db, {
279
333
  teamId: team.id,
280
334
  title: 'Main Story',
281
335
  description: 'Test',
282
336
  });
283
- addStoryDependency(db, mainStory.id, depStory.id);
337
+ await addStoryDependency(db, mainStory.id, depStory.id);
284
338
  // Mark dependency as in_progress - this should NOT satisfy the dependency
285
- updateStory(db, depStory.id, { status: 'in_progress' });
286
- const isSatisfied = areDependenciesSatisfied(db, mainStory.id);
339
+ await updateStory(db, depStory.id, { status: 'in_progress' });
340
+ const isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
287
341
  expect(isSatisfied).toBe(false);
288
342
  });
289
- it('should not consider planned stories as satisfying dependencies', () => {
290
- const team = createTeam(db, {
343
+ it('should not consider planned stories as satisfying dependencies', async () => {
344
+ const team = await createTeam(db, {
291
345
  name: 'Test Team',
292
346
  repoUrl: 'https://github.com/test/repo',
293
347
  repoPath: 'test',
294
348
  });
295
- const depStory = createStory(db, { teamId: team.id, title: 'Dependency', description: 'Test' });
296
- const mainStory = createStory(db, {
349
+ const depStory = await createStory(db, {
350
+ teamId: team.id,
351
+ title: 'Dependency',
352
+ description: 'Test',
353
+ });
354
+ const mainStory = await createStory(db, {
297
355
  teamId: team.id,
298
356
  title: 'Main Story',
299
357
  description: 'Test',
300
358
  });
301
- addStoryDependency(db, mainStory.id, depStory.id);
359
+ await addStoryDependency(db, mainStory.id, depStory.id);
302
360
  // Update main story status to planned (default)
303
- updateStory(db, mainStory.id, { status: 'planned' });
304
- const isSatisfied = areDependenciesSatisfied(db, mainStory.id);
361
+ await updateStory(db, mainStory.id, { status: 'planned' });
362
+ const isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
305
363
  expect(isSatisfied).toBe(false);
306
364
  });
307
- it('should handle multiple dependencies', () => {
308
- const team = createTeam(db, {
365
+ it('should handle multiple dependencies', async () => {
366
+ const team = await createTeam(db, {
309
367
  name: 'Test Team',
310
368
  repoUrl: 'https://github.com/test/repo',
311
369
  repoPath: 'test',
312
370
  });
313
- const dep1 = createStory(db, { teamId: team.id, title: 'Dep 1', description: 'Test' });
314
- const dep2 = createStory(db, { teamId: team.id, title: 'Dep 2', description: 'Test' });
315
- const mainStory = createStory(db, {
371
+ const dep1 = await createStory(db, { teamId: team.id, title: 'Dep 1', description: 'Test' });
372
+ const dep2 = await createStory(db, { teamId: team.id, title: 'Dep 2', description: 'Test' });
373
+ const mainStory = await createStory(db, {
316
374
  teamId: team.id,
317
375
  title: 'Main Story',
318
376
  description: 'Test',
319
377
  });
320
- addStoryDependency(db, mainStory.id, dep1.id);
321
- addStoryDependency(db, mainStory.id, dep2.id);
378
+ await addStoryDependency(db, mainStory.id, dep1.id);
379
+ await addStoryDependency(db, mainStory.id, dep2.id);
322
380
  // Mark only first dependency as merged
323
- updateStory(db, dep1.id, { status: 'merged' });
324
- let isSatisfied = areDependenciesSatisfied(db, mainStory.id);
381
+ await updateStory(db, dep1.id, { status: 'merged' });
382
+ let isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
325
383
  expect(isSatisfied).toBe(false);
326
384
  // Mark second dependency as merged too
327
- updateStory(db, dep2.id, { status: 'merged' });
328
- isSatisfied = areDependenciesSatisfied(db, mainStory.id);
385
+ await updateStory(db, dep2.id, { status: 'merged' });
386
+ isSatisfied = await areDependenciesSatisfied(db, mainStory.id);
329
387
  expect(isSatisfied).toBe(true);
330
388
  });
331
389
  });
332
390
  describe('Scheduler Build Dependency Graph', () => {
333
- it('should correctly build a dependency graph', () => {
334
- const team = createTeam(db, {
391
+ it('should correctly build a dependency graph', async () => {
392
+ const team = await createTeam(db, {
335
393
  name: 'Test Team',
336
394
  repoUrl: 'https://github.com/test/repo',
337
395
  repoPath: 'test',
338
396
  });
339
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
340
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
341
- const storyC = createStory(db, { teamId: team.id, title: 'Story C', description: 'Test' });
342
- addStoryDependency(db, storyB.id, storyA.id);
343
- addStoryDependency(db, storyC.id, storyA.id);
344
- const graph = buildDependencyGraph(db, [storyA, storyB, storyC]);
397
+ const storyA = await createStory(db, {
398
+ teamId: team.id,
399
+ title: 'Story A',
400
+ description: 'Test',
401
+ });
402
+ const storyB = await createStory(db, {
403
+ teamId: team.id,
404
+ title: 'Story B',
405
+ description: 'Test',
406
+ });
407
+ const storyC = await createStory(db, {
408
+ teamId: team.id,
409
+ title: 'Story C',
410
+ description: 'Test',
411
+ });
412
+ await addStoryDependency(db, storyB.id, storyA.id);
413
+ await addStoryDependency(db, storyC.id, storyA.id);
414
+ const graph = await buildDependencyGraph(db, [storyA, storyB, storyC]);
345
415
  expect(graph.has(storyA.id)).toBe(true);
346
416
  expect(graph.has(storyB.id)).toBe(true);
347
417
  expect(graph.has(storyC.id)).toBe(true);
@@ -349,26 +419,38 @@ describe('Scheduler Build Dependency Graph', () => {
349
419
  expect(graph.get(storyB.id)).toEqual(new Set([storyA.id]));
350
420
  expect(graph.get(storyC.id)).toEqual(new Set([storyA.id]));
351
421
  });
352
- it('should include only stories in the input list', () => {
353
- const team = createTeam(db, {
422
+ it('should include only stories in the input list', async () => {
423
+ const team = await createTeam(db, {
354
424
  name: 'Test Team',
355
425
  repoUrl: 'https://github.com/test/repo',
356
426
  repoPath: 'test',
357
427
  });
358
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
359
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
360
- const storyC = createStory(db, { teamId: team.id, title: 'Story C', description: 'Test' });
428
+ const storyA = await createStory(db, {
429
+ teamId: team.id,
430
+ title: 'Story A',
431
+ description: 'Test',
432
+ });
433
+ const storyB = await createStory(db, {
434
+ teamId: team.id,
435
+ title: 'Story B',
436
+ description: 'Test',
437
+ });
438
+ const storyC = await createStory(db, {
439
+ teamId: team.id,
440
+ title: 'Story C',
441
+ description: 'Test',
442
+ });
361
443
  // B depends on A (A is not in the filter list)
362
- addStoryDependency(db, storyB.id, storyA.id);
444
+ await addStoryDependency(db, storyB.id, storyA.id);
363
445
  // Only include B and C in the graph
364
- const graph = buildDependencyGraph(db, [storyB, storyC]);
446
+ const graph = await buildDependencyGraph(db, [storyB, storyC]);
365
447
  expect(graph.has(storyB.id)).toBe(true);
366
448
  expect(graph.has(storyC.id)).toBe(true);
367
449
  expect(graph.has(storyA.id)).toBe(false);
368
450
  });
369
451
  });
370
452
  describe('Scheduler Worktree Removal', () => {
371
- it('should remove worktrees with a short cleanup timeout', () => {
453
+ it('should remove worktrees with a short cleanup timeout', async () => {
372
454
  const removeSpy = vi.spyOn(worktreeModule, 'removeWorktree').mockReturnValue({
373
455
  success: true,
374
456
  fullWorktreePath: '/tmp/repos/test-agent-1',
@@ -378,7 +460,7 @@ describe('Scheduler Worktree Removal', () => {
378
460
  expect(removeSpy).toHaveBeenCalledWith('/tmp', 'repos/test-agent-1', { timeout: 5000 });
379
461
  vi.restoreAllMocks();
380
462
  });
381
- it('should log worktree removal failures to the database', () => {
463
+ it('should log worktree removal failures to the database', async () => {
382
464
  // Mock the shared removeWorktree to simulate failure
383
465
  vi.spyOn(worktreeModule, 'removeWorktree').mockReturnValue({
384
466
  success: false,
@@ -386,9 +468,9 @@ describe('Scheduler Worktree Removal', () => {
386
468
  fullWorktreePath: '/tmp/repos/test-agent-1',
387
469
  });
388
470
  const removeMethod = scheduler.removeAgentWorktree;
389
- removeMethod.call(scheduler, 'repos/test-agent-1', 'agent-test-1');
471
+ await removeMethod.call(scheduler, 'repos/test-agent-1', 'agent-test-1');
390
472
  // Check that the failure was logged
391
- const logs = getLogsByEventType(db, 'WORKTREE_REMOVAL_FAILED');
473
+ const logs = await getLogsByEventType(db, 'WORKTREE_REMOVAL_FAILED');
392
474
  expect(logs).toHaveLength(1);
393
475
  expect(logs[0].agent_id).toBe('agent-test-1');
394
476
  expect(logs[0].event_type).toBe('WORKTREE_REMOVAL_FAILED');
@@ -396,249 +478,253 @@ describe('Scheduler Worktree Removal', () => {
396
478
  expect(logs[0].message).toContain('Permission denied');
397
479
  vi.restoreAllMocks();
398
480
  });
399
- it('should handle empty worktree paths gracefully', () => {
481
+ it('should handle empty worktree paths gracefully', async () => {
400
482
  const removeMethod = scheduler.removeAgentWorktree;
401
483
  // Should return without error for empty path
402
484
  removeMethod.call(scheduler, '', 'agent-test-1');
403
485
  // Should not log anything
404
- const logs = getLogsByEventType(db, 'WORKTREE_REMOVAL_FAILED');
486
+ const logs = await getLogsByEventType(db, 'WORKTREE_REMOVAL_FAILED');
405
487
  expect(logs).toHaveLength(0);
406
488
  });
407
489
  });
408
490
  describe('Scheduler Orphaned Story Recovery', () => {
409
491
  it('should recover orphaned stories assigned to terminated agents', async () => {
410
492
  // Setup: Create team, agents, and a story
411
- const team = createTeam(db, {
493
+ const team = await createTeam(db, {
412
494
  name: 'Test Team',
413
495
  repoUrl: 'https://github.com/test/repo',
414
496
  repoPath: 'test',
415
497
  });
416
498
  // Create a terminated agent in the database
417
499
  const terminatedAgentId = 'agent-terminated-1';
418
- db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
500
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
419
501
  VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))`, [terminatedAgentId, 'intermediate', team.id, 'terminated']);
420
502
  // Create a story assigned to the terminated agent
421
- const story = createStory(db, {
503
+ const story = await createStory(db, {
422
504
  teamId: team.id,
423
505
  title: 'Orphaned Story',
424
506
  description: 'Test',
425
507
  });
426
- updateStory(db, story.id, {
508
+ await updateStory(db, story.id, {
427
509
  assignedAgentId: terminatedAgentId,
428
510
  status: 'in_progress',
429
511
  });
430
512
  // Get the recovery method
431
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
513
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
432
514
  // Verify the story was recovered
433
515
  expect(recovered).toContain(story.id);
434
516
  expect(recovered.length).toBe(1);
435
517
  // Verify the story's assignment was cleared and status changed
436
- const recoveredStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
518
+ const recoveredStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
437
519
  expect(recoveredStory?.[0]).toBeNull(); // assigned_agent_id should be null
438
520
  expect(recoveredStory?.[1]).toBe('planned'); // status should be 'planned'
439
521
  });
440
522
  it('should not affect stories assigned to active agents', async () => {
441
- const team = createTeam(db, {
523
+ const team = await createTeam(db, {
442
524
  name: 'Test Team',
443
525
  repoUrl: 'https://github.com/test/repo',
444
526
  repoPath: 'test',
445
527
  });
446
528
  // Create an active (non-terminated) agent
447
529
  const activeAgentId = 'agent-active-1';
448
- db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
530
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
449
531
  VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))`, [activeAgentId, 'intermediate', team.id, 'working']);
450
532
  // Create a story assigned to the active agent
451
- const story = createStory(db, { teamId: team.id, title: 'Active Story', description: 'Test' });
452
- updateStory(db, story.id, {
533
+ const story = await createStory(db, {
534
+ teamId: team.id,
535
+ title: 'Active Story',
536
+ description: 'Test',
537
+ });
538
+ await updateStory(db, story.id, {
453
539
  assignedAgentId: activeAgentId,
454
540
  status: 'in_progress',
455
541
  });
456
- db.run(`UPDATE agents SET current_story_id = ? WHERE id = ?`, [story.id, activeAgentId]);
542
+ db.db.run(`UPDATE agents SET current_story_id = ? WHERE id = ?`, [story.id, activeAgentId]);
457
543
  // Get the recovery method
458
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
544
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
459
545
  // Verify no stories were recovered
460
546
  expect(recovered.length).toBe(0);
461
547
  // Verify the story's assignment was NOT changed
462
- const unchangedStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
548
+ const unchangedStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
463
549
  expect(unchangedStory?.[0]).toBe(activeAgentId);
464
550
  expect(unchangedStory?.[1]).toBe('in_progress');
465
551
  });
466
552
  it('should recover in_progress stories assigned to idle agents with no current story', async () => {
467
- const team = createTeam(db, {
553
+ const team = await createTeam(db, {
468
554
  name: 'Inconsistent Team',
469
555
  repoUrl: 'https://github.com/test/repo',
470
556
  repoPath: 'test',
471
557
  });
472
558
  const idleAgentId = 'agent-idle-1';
473
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
559
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
474
560
  VALUES (?, ?, ?, ?, NULL, datetime('now'), datetime('now'))`, [idleAgentId, 'intermediate', team.id, 'idle']);
475
- const story = createStory(db, {
561
+ const story = await createStory(db, {
476
562
  teamId: team.id,
477
563
  title: 'Inconsistent Assignment Story',
478
564
  description: 'Assigned to idle agent',
479
565
  });
480
- updateStory(db, story.id, {
566
+ await updateStory(db, story.id, {
481
567
  assignedAgentId: idleAgentId,
482
568
  status: 'in_progress',
483
569
  });
484
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
570
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
485
571
  expect(recovered).toContain(story.id);
486
- const recoveredStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
572
+ const recoveredStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
487
573
  expect(recoveredStory?.[0]).toBeNull();
488
574
  expect(recoveredStory?.[1]).toBe('planned');
489
575
  });
490
576
  it('should recover in_progress stories when agent current_story_id points to a different story', async () => {
491
- const team = createTeam(db, {
577
+ const team = await createTeam(db, {
492
578
  name: 'Mismatched Team',
493
579
  repoUrl: 'https://github.com/test/repo',
494
580
  repoPath: 'test',
495
581
  });
496
582
  const workingAgentId = 'agent-working-mismatch-1';
497
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
583
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
498
584
  VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, [workingAgentId, 'intermediate', team.id, 'working', 'STORY-OTHER']);
499
- const story = createStory(db, {
585
+ const story = await createStory(db, {
500
586
  teamId: team.id,
501
587
  title: 'Mismatched Assignment Story',
502
588
  description: 'Assigned story does not match agent current story',
503
589
  });
504
- updateStory(db, story.id, {
590
+ await updateStory(db, story.id, {
505
591
  assignedAgentId: workingAgentId,
506
592
  status: 'in_progress',
507
593
  });
508
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
594
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
509
595
  expect(recovered).toContain(story.id);
510
- const recoveredStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
596
+ const recoveredStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
511
597
  expect(recoveredStory?.[0]).toBeNull();
512
598
  expect(recoveredStory?.[1]).toBe('planned');
513
599
  });
514
600
  it('should recover in_progress stories assigned to blocked agents even with matching current story', async () => {
515
- const team = createTeam(db, {
601
+ const team = await createTeam(db, {
516
602
  name: 'Blocked Team',
517
603
  repoUrl: 'https://github.com/test/repo',
518
604
  repoPath: 'test',
519
605
  });
520
606
  const blockedAgentId = 'agent-blocked-1';
521
- const story = createStory(db, {
607
+ const story = await createStory(db, {
522
608
  teamId: team.id,
523
609
  title: 'Blocked Assignment Story',
524
610
  description: 'Assigned to blocked agent',
525
611
  });
526
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
612
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
527
613
  VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, [blockedAgentId, 'intermediate', team.id, 'blocked', story.id]);
528
- updateStory(db, story.id, {
614
+ await updateStory(db, story.id, {
529
615
  assignedAgentId: blockedAgentId,
530
616
  status: 'in_progress',
531
617
  });
532
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
618
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
533
619
  expect(recovered).toContain(story.id);
534
- const recoveredStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
620
+ const recoveredStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${story.id}'`)[0]?.values[0];
535
621
  expect(recoveredStory?.[0]).toBeNull();
536
622
  expect(recoveredStory?.[1]).toBe('planned');
537
623
  });
538
624
  it('should not recover non-in_progress stories with inconsistent assignment', async () => {
539
- const team = createTeam(db, {
625
+ const team = await createTeam(db, {
540
626
  name: 'Review Team',
541
627
  repoUrl: 'https://github.com/test/repo',
542
628
  repoPath: 'test',
543
629
  });
544
630
  const idleAgentId = 'agent-idle-review-1';
545
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
631
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
546
632
  VALUES (?, ?, ?, ?, NULL, datetime('now'), datetime('now'))`, [idleAgentId, 'intermediate', team.id, 'idle']);
547
- const reviewStory = createStory(db, {
633
+ const reviewStory = await createStory(db, {
548
634
  teamId: team.id,
549
635
  title: 'Review Story',
550
636
  description: 'Should not be recovered by in_progress consistency check',
551
637
  });
552
- updateStory(db, reviewStory.id, {
638
+ await updateStory(db, reviewStory.id, {
553
639
  assignedAgentId: idleAgentId,
554
640
  status: 'review',
555
641
  });
556
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
642
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
557
643
  expect(recovered).not.toContain(reviewStory.id);
558
- const unchangedStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${reviewStory.id}'`)[0]?.values[0];
644
+ const unchangedStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${reviewStory.id}'`)[0]?.values[0];
559
645
  expect(unchangedStory?.[0]).toBe(idleAgentId);
560
646
  expect(unchangedStory?.[1]).toBe('review');
561
647
  });
562
648
  it('should recover stale in_progress stories without assigned agents', async () => {
563
- const team = createTeam(db, {
649
+ const team = await createTeam(db, {
564
650
  name: 'Stale Team',
565
651
  repoUrl: 'https://github.com/test/repo',
566
652
  repoPath: 'test',
567
653
  });
568
- const staleStory = createStory(db, {
654
+ const staleStory = await createStory(db, {
569
655
  teamId: team.id,
570
656
  title: 'Stale In Progress Story',
571
657
  description: 'Lost assignment',
572
658
  });
573
- updateStory(db, staleStory.id, {
659
+ await updateStory(db, staleStory.id, {
574
660
  status: 'in_progress',
575
661
  assignedAgentId: null,
576
662
  });
577
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
663
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
578
664
  expect(recovered).toContain(staleStory.id);
579
- const recoveredStory = db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${staleStory.id}'`)[0]?.values[0];
665
+ const recoveredStory = db.db.exec(`SELECT assigned_agent_id, status FROM stories WHERE id = '${staleStory.id}'`)[0]?.values[0];
580
666
  expect(recoveredStory?.[0]).toBeNull();
581
667
  expect(recoveredStory?.[1]).toBe('planned');
582
668
  });
583
669
  it('should not recover planned stories that are unassigned', async () => {
584
- const team = createTeam(db, {
670
+ const team = await createTeam(db, {
585
671
  name: 'Planned Team',
586
672
  repoUrl: 'https://github.com/test/repo',
587
673
  repoPath: 'test',
588
674
  });
589
- const plannedStory = createStory(db, {
675
+ const plannedStory = await createStory(db, {
590
676
  teamId: team.id,
591
677
  title: 'Already Planned',
592
678
  description: 'Should stay planned',
593
679
  });
594
- updateStory(db, plannedStory.id, {
680
+ await updateStory(db, plannedStory.id, {
595
681
  status: 'planned',
596
682
  assignedAgentId: null,
597
683
  });
598
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
684
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
599
685
  expect(recovered).not.toContain(plannedStory.id);
600
686
  });
601
687
  it('should recover multiple orphaned stories', async () => {
602
- const team = createTeam(db, {
688
+ const team = await createTeam(db, {
603
689
  name: 'Test Team',
604
690
  repoUrl: 'https://github.com/test/repo',
605
691
  repoPath: 'test',
606
692
  });
607
693
  // Create a terminated agent
608
694
  const terminatedAgentId = 'agent-terminated-2';
609
- db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
695
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
610
696
  VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))`, [terminatedAgentId, 'intermediate', team.id, 'terminated']);
611
697
  // Create multiple stories assigned to the terminated agent
612
- const story1 = createStory(db, {
698
+ const story1 = await createStory(db, {
613
699
  teamId: team.id,
614
700
  title: 'Orphaned Story 1',
615
701
  description: 'Test',
616
702
  });
617
- const story2 = createStory(db, {
703
+ const story2 = await createStory(db, {
618
704
  teamId: team.id,
619
705
  title: 'Orphaned Story 2',
620
706
  description: 'Test',
621
707
  });
622
- updateStory(db, story1.id, {
708
+ await updateStory(db, story1.id, {
623
709
  assignedAgentId: terminatedAgentId,
624
710
  status: 'in_progress',
625
711
  });
626
- updateStory(db, story2.id, {
712
+ await updateStory(db, story2.id, {
627
713
  assignedAgentId: terminatedAgentId,
628
714
  status: 'review',
629
715
  });
630
716
  // Get the recovery method
631
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp');
717
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp');
632
718
  // Verify both stories were recovered
633
719
  expect(recovered.length).toBe(2);
634
720
  expect(recovered).toContain(story1.id);
635
721
  expect(recovered).toContain(story2.id);
636
722
  });
637
- it('should write markdown files when storiesDir is provided during orphan recovery', () => {
723
+ it('should write markdown files when storiesDir is provided during orphan recovery', async () => {
638
724
  const storiesDir = join(tmpdir(), `hive-test-stories-${Date.now()}`);
639
725
  mkdirSync(storiesDir, { recursive: true });
640
726
  try {
641
- const team = createTeam(db, {
727
+ const team = await createTeam(db, {
642
728
  name: 'MD Test Team',
643
729
  repoUrl: 'https://github.com/test/repo',
644
730
  repoPath: 'test',
@@ -646,22 +732,22 @@ describe('Scheduler Orphaned Story Recovery', () => {
646
732
  const terminatedAgentId = 'agent-md-terminated';
647
733
  db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
648
734
  VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))`, [terminatedAgentId, 'intermediate', team.id, 'terminated']);
649
- const story = createStory(db, {
735
+ const story = await createStory(db, {
650
736
  teamId: team.id,
651
737
  title: 'Story with Markdown',
652
738
  description: 'Should get a markdown file on recovery',
653
739
  });
654
- updateStory(db, story.id, {
740
+ await updateStory(db, story.id, {
655
741
  assignedAgentId: terminatedAgentId,
656
742
  status: 'in_progress',
657
743
  });
658
- const recovered = detectAndRecoverOrphanedStories(db, '/tmp', storiesDir);
744
+ const recovered = await detectAndRecoverOrphanedStories(db, '/tmp', storiesDir);
659
745
  expect(recovered).toContain(story.id);
660
746
  // Verify markdown file was written
661
747
  const mdPath = join(storiesDir, `${story.id}.md`);
662
748
  expect(existsSync(mdPath)).toBe(true);
663
749
  // Verify markdown_path was set in DB
664
- const updatedStory = getStoryById(db, story.id);
750
+ const updatedStory = await getStoryById(db, story.id);
665
751
  expect(updatedStory?.markdown_path).toBe(mdPath);
666
752
  }
667
753
  finally {
@@ -676,83 +762,83 @@ describe('Scheduler Refactor Capacity Policy', () => {
676
762
  refactor: config,
677
763
  };
678
764
  }
679
- it('should enforce refactor budget based on feature workload', () => {
680
- const team = createTeam(db, {
765
+ it('should enforce refactor budget based on feature workload', async () => {
766
+ const team = await createTeam(db, {
681
767
  name: 'Refactor Team',
682
768
  repoUrl: 'https://github.com/test/repo',
683
769
  repoPath: 'test',
684
770
  });
685
- const feature = createStory(db, {
771
+ const feature = await createStory(db, {
686
772
  teamId: team.id,
687
773
  title: 'Add endpoint',
688
774
  description: 'Feature story',
689
775
  });
690
- updateStory(db, feature.id, { status: 'planned', storyPoints: 10, complexityScore: 10 });
691
- const refactorA = createStory(db, {
776
+ await updateStory(db, feature.id, { status: 'planned', storyPoints: 10, complexityScore: 10 });
777
+ const refactorA = await createStory(db, {
692
778
  teamId: team.id,
693
779
  title: 'Refactor: clean parser',
694
780
  description: 'Refactor A',
695
781
  });
696
- updateStory(db, refactorA.id, { status: 'planned', storyPoints: 1, complexityScore: 1 });
697
- const refactorB = createStory(db, {
782
+ await updateStory(db, refactorA.id, { status: 'planned', storyPoints: 1, complexityScore: 1 });
783
+ const refactorB = await createStory(db, {
698
784
  teamId: team.id,
699
785
  title: 'Refactor: simplify auth flow',
700
786
  description: 'Refactor B',
701
787
  });
702
- updateStory(db, refactorB.id, { status: 'planned', storyPoints: 2, complexityScore: 2 });
788
+ await updateStory(db, refactorB.id, { status: 'planned', storyPoints: 2, complexityScore: 2 });
703
789
  const scalingConfig = createRefactorScalingConfig({
704
790
  enabled: true,
705
791
  capacity_percent: 10,
706
792
  allow_without_feature_work: true,
707
793
  });
708
- const selected = selectStoriesForCapacity([
709
- getStoryById(db, feature.id),
710
- getStoryById(db, refactorA.id),
711
- getStoryById(db, refactorB.id),
712
- ], scalingConfig);
794
+ const selected = (await selectStoriesForCapacity([
795
+ (await getStoryById(db, feature.id)),
796
+ (await getStoryById(db, refactorA.id)),
797
+ (await getStoryById(db, refactorB.id)),
798
+ ], scalingConfig));
713
799
  expect(selected.map(s => s.id)).toContain(feature.id);
714
800
  expect(selected.map(s => s.id)).toContain(refactorA.id);
715
801
  expect(selected.map(s => s.id)).not.toContain(refactorB.id);
716
802
  });
717
- it('should allow refactor-only queues when policy permits', () => {
718
- const team = createTeam(db, {
803
+ it('should allow refactor-only queues when policy permits', async () => {
804
+ const team = await createTeam(db, {
719
805
  name: 'Maintenance Team',
720
806
  repoUrl: 'https://github.com/test/repo',
721
807
  repoPath: 'test',
722
808
  });
723
- const refactor = createStory(db, {
809
+ const refactor = await createStory(db, {
724
810
  teamId: team.id,
725
811
  title: 'Refactor: remove dead code',
726
812
  description: 'Maintenance',
727
813
  });
728
- updateStory(db, refactor.id, { status: 'planned', storyPoints: 3, complexityScore: 3 });
814
+ await updateStory(db, refactor.id, { status: 'planned', storyPoints: 3, complexityScore: 3 });
729
815
  const scalingConfig = createRefactorScalingConfig({
730
816
  enabled: true,
731
817
  capacity_percent: 10,
732
818
  allow_without_feature_work: true,
733
819
  });
734
- const selected = selectStoriesForCapacity([getStoryById(db, refactor.id)], scalingConfig);
820
+ const selected = (await selectStoriesForCapacity([(await getStoryById(db, refactor.id))], scalingConfig));
735
821
  expect(selected).toHaveLength(1);
736
822
  expect(selected[0].id).toBe(refactor.id);
737
823
  });
738
- it('should block refactor-only queues when policy disallows it', () => {
739
- const team = createTeam(db, {
824
+ it('should block refactor-only queues when policy disallows it', async () => {
825
+ const team = await createTeam(db, {
740
826
  name: 'Strict Team',
741
827
  repoUrl: 'https://github.com/test/repo',
742
828
  repoPath: 'test',
743
829
  });
744
- const refactor = createStory(db, {
830
+ const refactor = await createStory(db, {
745
831
  teamId: team.id,
746
832
  title: 'Refactor: rename internals',
747
833
  description: 'Maintenance',
748
834
  });
749
- updateStory(db, refactor.id, { status: 'planned', storyPoints: 2, complexityScore: 2 });
835
+ await updateStory(db, refactor.id, { status: 'planned', storyPoints: 2, complexityScore: 2 });
750
836
  const scalingConfig = createRefactorScalingConfig({
751
837
  enabled: true,
752
838
  capacity_percent: 10,
753
839
  allow_without_feature_work: false,
754
840
  });
755
- const selected = selectStoriesForCapacity([getStoryById(db, refactor.id)], scalingConfig);
841
+ const selected = (await selectStoriesForCapacity([(await getStoryById(db, refactor.id))], scalingConfig));
756
842
  expect(selected).toHaveLength(0);
757
843
  });
758
844
  });
@@ -823,29 +909,29 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
823
909
  expect(isRefactorStory(mkStory(title))).toBe(false);
824
910
  });
825
911
  // 5 tests: capacity point calculation
826
- it('should use story_points when both story_points and complexity_score exist', () => {
912
+ it('should use story_points when both story_points and complexity_score exist', async () => {
827
913
  expect(getCapacityPoints(mkStory('Feature', 8, 3))).toBe(8);
828
914
  });
829
- it('should use complexity_score when story_points is null', () => {
915
+ it('should use complexity_score when story_points is null', async () => {
830
916
  expect(getCapacityPoints(mkStory('Feature', null, 5))).toBe(5);
831
917
  });
832
- it('should default to 1 when both story_points and complexity_score are null', () => {
918
+ it('should default to 1 when both story_points and complexity_score are null', async () => {
833
919
  expect(getCapacityPoints(mkStory('Feature', null, null))).toBe(1);
834
920
  });
835
- it('should treat story_points 0 as missing and fall back to complexity_score', () => {
921
+ it('should treat story_points 0 as missing and fall back to complexity_score', async () => {
836
922
  expect(getCapacityPoints(mkStory('Feature', 0, 4))).toBe(4);
837
923
  });
838
- it('should treat 0/0 points as minimum 1 capacity unit', () => {
924
+ it('should treat 0/0 points as minimum 1 capacity unit', async () => {
839
925
  expect(getCapacityPoints(mkStory('Feature', 0, 0))).toBe(1);
840
926
  });
841
- it('should use story_points when complexity_score is null', () => {
927
+ it('should use story_points when complexity_score is null', async () => {
842
928
  expect(getCapacityPoints(mkStory('Feature', 6, null))).toBe(6);
843
929
  });
844
- it('should pass through non-integer capacity points as provided', () => {
930
+ it('should pass through non-integer capacity points as provided', async () => {
845
931
  expect(getCapacityPoints(mkStory('Feature', 2.5, null))).toBe(2.5);
846
932
  });
847
933
  // 12 tests: capacity selection behavior
848
- it('should filter out refactor stories when refactor policy is disabled', () => {
934
+ it('should filter out refactor stories when refactor policy is disabled', async () => {
849
935
  const scalingConfig = mkScalingConfig({
850
936
  enabled: false,
851
937
  capacity_percent: 100,
@@ -853,10 +939,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
853
939
  });
854
940
  const feature = mkStory('Feature: add endpoint', 8, 8);
855
941
  const refactor = mkStory('Refactor: split parser', 2, 2);
856
- const selected = selectStoriesForCapacity([feature, refactor], scalingConfig);
942
+ const selected = (await selectStoriesForCapacity([feature, refactor], scalingConfig));
857
943
  expect(selected.map(s => s.id)).toEqual([feature.id]);
858
944
  });
859
- it('should include all refactor stories when capacity percent is 100', () => {
945
+ it('should include all refactor stories when capacity percent is 100', async () => {
860
946
  const scalingConfig = mkScalingConfig({
861
947
  enabled: true,
862
948
  capacity_percent: 100,
@@ -865,10 +951,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
865
951
  const feature = mkStory('Feature: add endpoint', 10, 10);
866
952
  const refactorA = mkStory('Refactor: split parser', 3, 3);
867
953
  const refactorB = mkStory('Refactor: normalize naming', 4, 4);
868
- const selected = selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig);
954
+ const selected = (await selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig));
869
955
  expect(selected.map(s => s.id)).toEqual([feature.id, refactorA.id, refactorB.id]);
870
956
  });
871
- it('should include no refactor stories when capacity percent is 0 and feature work exists', () => {
957
+ it('should include no refactor stories when capacity percent is 0 and feature work exists', async () => {
872
958
  const scalingConfig = mkScalingConfig({
873
959
  enabled: true,
874
960
  capacity_percent: 0,
@@ -876,10 +962,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
876
962
  });
877
963
  const feature = mkStory('Feature: add endpoint', 10, 10);
878
964
  const refactor = mkStory('Refactor: split parser', 1, 1);
879
- const selected = selectStoriesForCapacity([feature, refactor], scalingConfig);
965
+ const selected = (await selectStoriesForCapacity([feature, refactor], scalingConfig));
880
966
  expect(selected.map(s => s.id)).toEqual([feature.id]);
881
967
  });
882
- it('should allow at least one refactor point when percent is positive but rounded budget is zero', () => {
968
+ it('should allow at least one refactor point when percent is positive but rounded budget is zero', async () => {
883
969
  const scalingConfig = mkScalingConfig({
884
970
  enabled: true,
885
971
  capacity_percent: 10,
@@ -887,10 +973,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
887
973
  });
888
974
  const feature = mkStory('Feature: tiny patch', 5, 5); // floor(5 * 0.1) = 0 -> min 1
889
975
  const refactor = mkStory('Refactor: tighten types', 1, 1);
890
- const selected = selectStoriesForCapacity([feature, refactor], scalingConfig);
976
+ const selected = (await selectStoriesForCapacity([feature, refactor], scalingConfig));
891
977
  expect(selected.map(s => s.id)).toEqual([feature.id, refactor.id]);
892
978
  });
893
- it('should compute budget from total feature story points across multiple stories', () => {
979
+ it('should compute budget from total feature story points across multiple stories', async () => {
894
980
  const scalingConfig = mkScalingConfig({
895
981
  enabled: true,
896
982
  capacity_percent: 20,
@@ -901,10 +987,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
901
987
  const refactorA = mkStory('Refactor: A', 1, 1);
902
988
  const refactorB = mkStory('Refactor: B', 1, 1);
903
989
  const refactorC = mkStory('Refactor: C', 1, 1);
904
- const selected = selectStoriesForCapacity([featureA, featureB, refactorA, refactorB, refactorC], scalingConfig);
990
+ const selected = (await selectStoriesForCapacity([featureA, featureB, refactorA, refactorB, refactorC], scalingConfig));
905
991
  expect(selected.map(s => s.id)).toEqual([featureA.id, featureB.id, refactorA.id, refactorB.id]);
906
992
  });
907
- it('should skip a refactor story that exceeds remaining budget', () => {
993
+ it('should skip a refactor story that exceeds remaining budget', async () => {
908
994
  const scalingConfig = mkScalingConfig({
909
995
  enabled: true,
910
996
  capacity_percent: 20,
@@ -912,10 +998,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
912
998
  });
913
999
  const feature = mkStory('Feature: A', 10, 10); // budget = 2
914
1000
  const refactorLarge = mkStory('Refactor: big cleanup', 3, 3);
915
- const selected = selectStoriesForCapacity([feature, refactorLarge], scalingConfig);
1001
+ const selected = (await selectStoriesForCapacity([feature, refactorLarge], scalingConfig));
916
1002
  expect(selected.map(s => s.id)).toEqual([feature.id]);
917
1003
  });
918
- it('should select a later smaller refactor story if an earlier one exceeds budget', () => {
1004
+ it('should select a later smaller refactor story if an earlier one exceeds budget', async () => {
919
1005
  const scalingConfig = mkScalingConfig({
920
1006
  enabled: true,
921
1007
  capacity_percent: 20,
@@ -924,10 +1010,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
924
1010
  const feature = mkStory('Feature: A', 10, 10); // budget = 2
925
1011
  const refactorLarge = mkStory('Refactor: big cleanup', 3, 3); // skipped
926
1012
  const refactorSmall = mkStory('Refactor: tiny cleanup', 2, 2); // fits
927
- const selected = selectStoriesForCapacity([feature, refactorLarge, refactorSmall], scalingConfig);
1013
+ const selected = (await selectStoriesForCapacity([feature, refactorLarge, refactorSmall], scalingConfig));
928
1014
  expect(selected.map(s => s.id)).toEqual([feature.id, refactorSmall.id]);
929
1015
  });
930
- it('should allow refactor-only queues when configured to allow without feature work', () => {
1016
+ it('should allow refactor-only queues when configured to allow without feature work', async () => {
931
1017
  const scalingConfig = mkScalingConfig({
932
1018
  enabled: true,
933
1019
  capacity_percent: 10,
@@ -935,10 +1021,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
935
1021
  });
936
1022
  const refactorA = mkStory('Refactor: A', 3, 3);
937
1023
  const refactorB = mkStory('Refactor: B', 5, 5);
938
- const selected = selectStoriesForCapacity([refactorA, refactorB], scalingConfig);
1024
+ const selected = (await selectStoriesForCapacity([refactorA, refactorB], scalingConfig));
939
1025
  expect(selected.map(s => s.id)).toEqual([refactorA.id, refactorB.id]);
940
1026
  });
941
- it('should block refactor-only queues when allow_without_feature_work is false', () => {
1027
+ it('should block refactor-only queues when allow_without_feature_work is false', async () => {
942
1028
  const scalingConfig = mkScalingConfig({
943
1029
  enabled: true,
944
1030
  capacity_percent: 10,
@@ -946,10 +1032,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
946
1032
  });
947
1033
  const refactorA = mkStory('Refactor: A', 3, 3);
948
1034
  const refactorB = mkStory('Refactor: B', 5, 5);
949
- const selected = selectStoriesForCapacity([refactorA, refactorB], scalingConfig);
1035
+ const selected = (await selectStoriesForCapacity([refactorA, refactorB], scalingConfig));
950
1036
  expect(selected).toHaveLength(0);
951
1037
  });
952
- it('should preserve order of selected stories', () => {
1038
+ it('should preserve order of selected stories', async () => {
953
1039
  const scalingConfig = mkScalingConfig({
954
1040
  enabled: true,
955
1041
  capacity_percent: 20,
@@ -959,26 +1045,26 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
959
1045
  const refactorA = mkStory('Refactor: A', 1, 1);
960
1046
  const featureB = mkStory('Feature: B', 5, 5);
961
1047
  const refactorB = mkStory('Refactor: B', 1, 1);
962
- const selected = selectStoriesForCapacity([featureA, refactorA, featureB, refactorB], scalingConfig);
1048
+ const selected = (await selectStoriesForCapacity([featureA, refactorA, featureB, refactorB], scalingConfig));
963
1049
  expect(selected.map(s => s.id)).toEqual([featureA.id, refactorA.id, featureB.id, refactorB.id]);
964
1050
  });
965
- it('should default to disabled behavior when refactor config is missing', () => {
1051
+ it('should default to disabled behavior when refactor config is missing', async () => {
966
1052
  const scalingConfig = mkScalingConfig();
967
1053
  const feature = mkStory('Feature: A', 5, 5);
968
1054
  const refactor = mkStory('Refactor: A', 1, 1);
969
- const selected = selectStoriesForCapacity([feature, refactor], scalingConfig);
1055
+ const selected = (await selectStoriesForCapacity([feature, refactor], scalingConfig));
970
1056
  expect(selected.map(s => s.id)).toEqual([feature.id]);
971
1057
  });
972
- it('should return an empty array when no stories are provided', () => {
1058
+ it('should return an empty array when no stories are provided', async () => {
973
1059
  const scalingConfig = mkScalingConfig({
974
1060
  enabled: true,
975
1061
  capacity_percent: 50,
976
1062
  allow_without_feature_work: true,
977
1063
  });
978
- const selected = selectStoriesForCapacity([], scalingConfig);
1064
+ const selected = (await selectStoriesForCapacity([], scalingConfig));
979
1065
  expect(selected).toEqual([]);
980
1066
  });
981
- it('should include refactor stories when cumulative points exactly match budget', () => {
1067
+ it('should include refactor stories when cumulative points exactly match budget', async () => {
982
1068
  const scalingConfig = mkScalingConfig({
983
1069
  enabled: true,
984
1070
  capacity_percent: 30,
@@ -987,10 +1073,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
987
1073
  const feature = mkStory('Feature: A', 10, 10); // budget = 3
988
1074
  const refactorA = mkStory('Refactor: A', 1, 1);
989
1075
  const refactorB = mkStory('Refactor: B', 2, 2);
990
- const selected = selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig);
1076
+ const selected = (await selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig));
991
1077
  expect(selected.map(s => s.id)).toEqual([feature.id, refactorA.id, refactorB.id]);
992
1078
  });
993
- it('should continue selecting later refactors after partially consuming budget and skipping a too-large one', () => {
1079
+ it('should continue selecting later refactors after partially consuming budget and skipping a too-large one', async () => {
994
1080
  const scalingConfig = mkScalingConfig({
995
1081
  enabled: true,
996
1082
  capacity_percent: 50,
@@ -1000,10 +1086,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
1000
1086
  const refactorA = mkStory('Refactor: A', 2, 2); // used = 2
1001
1087
  const refactorLarge = mkStory('Refactor: Large', 4, 4); // skipped (2 + 4 > 5)
1002
1088
  const refactorB = mkStory('Refactor: B', 3, 3); // used = 5
1003
- const selected = selectStoriesForCapacity([feature, refactorA, refactorLarge, refactorB], scalingConfig);
1089
+ const selected = (await selectStoriesForCapacity([feature, refactorA, refactorLarge, refactorB], scalingConfig));
1004
1090
  expect(selected.map(s => s.id)).toEqual([feature.id, refactorA.id, refactorB.id]);
1005
1091
  });
1006
- it('should derive feature budget from complexity when story_points are not set', () => {
1092
+ it('should derive feature budget from complexity when story_points are not set', async () => {
1007
1093
  const scalingConfig = mkScalingConfig({
1008
1094
  enabled: true,
1009
1095
  capacity_percent: 20,
@@ -1013,10 +1099,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
1013
1099
  const featureB = mkStory('Feature: B', null, 4); // feature total = 10, budget = 2
1014
1100
  const refactorA = mkStory('Refactor: A', 1, 1);
1015
1101
  const refactorB = mkStory('Refactor: B', 2, 2);
1016
- const selected = selectStoriesForCapacity([featureA, featureB, refactorA, refactorB], scalingConfig);
1102
+ const selected = (await selectStoriesForCapacity([featureA, featureB, refactorA, refactorB], scalingConfig));
1017
1103
  expect(selected.map(s => s.id)).toEqual([featureA.id, featureB.id, refactorA.id]);
1018
1104
  });
1019
- it('should allow one point of refactor work when feature stories have no explicit points', () => {
1105
+ it('should allow one point of refactor work when feature stories have no explicit points', async () => {
1020
1106
  const scalingConfig = mkScalingConfig({
1021
1107
  enabled: true,
1022
1108
  capacity_percent: 10,
@@ -1025,10 +1111,10 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
1025
1111
  const feature = mkStory('Feature: A', null, null); // defaults to 1, floor(1 * 0.1)=0 -> min 1
1026
1112
  const refactorA = mkStory('Refactor: A', 1, 1);
1027
1113
  const refactorB = mkStory('Refactor: B', 1, 1);
1028
- const selected = selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig);
1114
+ const selected = (await selectStoriesForCapacity([feature, refactorA, refactorB], scalingConfig));
1029
1115
  expect(selected.map(s => s.id)).toEqual([feature.id, refactorA.id]);
1030
1116
  });
1031
- it('should ignore capacity_percent for refactor-only queues when allow_without_feature_work is true', () => {
1117
+ it('should ignore capacity_percent for refactor-only queues when allow_without_feature_work is true', async () => {
1032
1118
  const scalingConfig = mkScalingConfig({
1033
1119
  enabled: true,
1034
1120
  capacity_percent: 0,
@@ -1036,28 +1122,40 @@ describe('Scheduler Refactor Policy Test Matrix', () => {
1036
1122
  });
1037
1123
  const refactorA = mkStory('Refactor: A', 2, 2);
1038
1124
  const refactorB = mkStory('Refactor: B', 4, 4);
1039
- const selected = selectStoriesForCapacity([refactorA, refactorB], scalingConfig);
1125
+ const selected = (await selectStoriesForCapacity([refactorA, refactorB], scalingConfig));
1040
1126
  expect(selected.map(s => s.id)).toEqual([refactorA.id, refactorB.id]);
1041
1127
  });
1042
1128
  });
1043
1129
  describe('Scheduler Agent Selection', () => {
1044
- it('should select agent with least workload from multiple agents', () => {
1045
- const team = createTeam(db, {
1130
+ it('should select agent with least workload from multiple agents', async () => {
1131
+ const team = await createTeam(db, {
1046
1132
  name: 'Test Team',
1047
1133
  repoUrl: 'https://github.com/test/repo',
1048
1134
  repoPath: 'test',
1049
1135
  });
1050
1136
  // Create three junior agents with different workloads
1051
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-1', 'junior', '${team.id}', 'idle')`);
1052
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-2', 'junior', '${team.id}', 'idle')`);
1053
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-3', 'junior', '${team.id}', 'idle')`);
1137
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-1', 'junior', '${team.id}', 'idle')`);
1138
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-2', 'junior', '${team.id}', 'idle')`);
1139
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('junior-3', 'junior', '${team.id}', 'idle')`);
1054
1140
  // Give junior-1 two stories, junior-2 one story, junior-3 zero stories
1055
- const story1 = createStory(db, { teamId: team.id, title: 'Story 1', description: 'Test' });
1056
- const story2 = createStory(db, { teamId: team.id, title: 'Story 2', description: 'Test' });
1057
- const story3 = createStory(db, { teamId: team.id, title: 'Story 3', description: 'Test' });
1058
- updateStory(db, story1.id, { assignedAgentId: 'junior-1', status: 'in_progress' });
1059
- updateStory(db, story2.id, { assignedAgentId: 'junior-1', status: 'in_progress' });
1060
- updateStory(db, story3.id, { assignedAgentId: 'junior-2', status: 'in_progress' });
1141
+ const story1 = await createStory(db, {
1142
+ teamId: team.id,
1143
+ title: 'Story 1',
1144
+ description: 'Test',
1145
+ });
1146
+ const story2 = await createStory(db, {
1147
+ teamId: team.id,
1148
+ title: 'Story 2',
1149
+ description: 'Test',
1150
+ });
1151
+ const story3 = await createStory(db, {
1152
+ teamId: team.id,
1153
+ title: 'Story 3',
1154
+ description: 'Test',
1155
+ });
1156
+ await updateStory(db, story1.id, { assignedAgentId: 'junior-1', status: 'in_progress' });
1157
+ await updateStory(db, story2.id, { assignedAgentId: 'junior-1', status: 'in_progress' });
1158
+ await updateStory(db, story3.id, { assignedAgentId: 'junior-2', status: 'in_progress' });
1061
1159
  const agents = [
1062
1160
  {
1063
1161
  id: 'junior-1',
@@ -1105,12 +1203,12 @@ describe('Scheduler Agent Selection', () => {
1105
1203
  updated_at: '',
1106
1204
  },
1107
1205
  ];
1108
- const selected = selectAgentWithLeastWorkload(db, agents);
1206
+ const selected = await selectAgentWithLeastWorkload(db, agents);
1109
1207
  // Should select junior-3 who has zero stories
1110
1208
  expect(selected.id).toBe('junior-3');
1111
1209
  });
1112
- it('should select first agent when all have equal workload', () => {
1113
- const team = createTeam(db, {
1210
+ it('should select first agent when all have equal workload', async () => {
1211
+ const team = await createTeam(db, {
1114
1212
  name: 'Test Team',
1115
1213
  repoUrl: 'https://github.com/test/repo',
1116
1214
  repoPath: 'test',
@@ -1147,67 +1245,75 @@ describe('Scheduler Agent Selection', () => {
1147
1245
  updated_at: '',
1148
1246
  },
1149
1247
  ];
1150
- const selected = selectAgentWithLeastWorkload(db, agents);
1248
+ const selected = await selectAgentWithLeastWorkload(db, agents);
1151
1249
  expect(selected.id).toBe('agent-1');
1152
1250
  });
1153
- it('should calculate agent workload correctly', () => {
1154
- const team = createTeam(db, {
1251
+ it('should calculate agent workload correctly', async () => {
1252
+ const team = await createTeam(db, {
1155
1253
  name: 'Test Team',
1156
1254
  repoUrl: 'https://github.com/test/repo',
1157
1255
  repoPath: 'test',
1158
1256
  });
1159
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1160
- const story1 = createStory(db, { teamId: team.id, title: 'Story 1', description: 'Test' });
1161
- const story2 = createStory(db, { teamId: team.id, title: 'Story 2', description: 'Test' });
1162
- updateStory(db, story1.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1163
- updateStory(db, story2.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1164
- const workload = getAgentWorkload(db, 'agent-1');
1257
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1258
+ const story1 = await createStory(db, {
1259
+ teamId: team.id,
1260
+ title: 'Story 1',
1261
+ description: 'Test',
1262
+ });
1263
+ const story2 = await createStory(db, {
1264
+ teamId: team.id,
1265
+ title: 'Story 2',
1266
+ description: 'Test',
1267
+ });
1268
+ await updateStory(db, story1.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1269
+ await updateStory(db, story2.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1270
+ const workload = await getAgentWorkload(db, 'agent-1');
1165
1271
  expect(workload).toBe(2);
1166
1272
  });
1167
- it('should return zero workload for agent with no stories', () => {
1168
- const team = createTeam(db, {
1273
+ it('should return zero workload for agent with no stories', async () => {
1274
+ const team = await createTeam(db, {
1169
1275
  name: 'Test Team',
1170
1276
  repoUrl: 'https://github.com/test/repo',
1171
1277
  repoPath: 'test',
1172
1278
  });
1173
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1174
- const workload = getAgentWorkload(db, 'agent-1');
1279
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1280
+ const workload = await getAgentWorkload(db, 'agent-1');
1175
1281
  expect(workload).toBe(0);
1176
1282
  });
1177
1283
  });
1178
1284
  describe('Scheduler Complexity Routing', () => {
1179
- it('should route low complexity stories to junior agents', () => {
1285
+ it('should route low complexity stories to junior agents', async () => {
1180
1286
  // Test the routing logic: complexity <= junior_max_complexity goes to junior
1181
1287
  const complexity = 2;
1182
1288
  expect(complexity).toBeLessThanOrEqual(mockConfig.scaling.junior_max_complexity);
1183
1289
  });
1184
- it('should route medium complexity stories to intermediate agents', () => {
1290
+ it('should route medium complexity stories to intermediate agents', async () => {
1185
1291
  // Test the routing logic: complexity between junior and intermediate thresholds
1186
1292
  const complexity = 4;
1187
1293
  expect(complexity).toBeGreaterThan(mockConfig.scaling.junior_max_complexity);
1188
1294
  expect(complexity).toBeLessThanOrEqual(mockConfig.scaling.intermediate_max_complexity);
1189
1295
  });
1190
- it('should route high complexity stories to senior agents', () => {
1296
+ it('should route high complexity stories to senior agents', async () => {
1191
1297
  // Test the routing logic: complexity > intermediate_max_complexity goes to senior
1192
1298
  const complexity = 8;
1193
1299
  expect(complexity).toBeGreaterThan(mockConfig.scaling.intermediate_max_complexity);
1194
1300
  });
1195
- it('should handle edge case at junior boundary', () => {
1301
+ it('should handle edge case at junior boundary', async () => {
1196
1302
  // Complexity exactly at junior_max_complexity should still go to junior
1197
1303
  const complexity = 3;
1198
1304
  expect(complexity).toBeLessThanOrEqual(mockConfig.scaling.junior_max_complexity);
1199
1305
  });
1200
- it('should handle edge case at intermediate boundary', () => {
1306
+ it('should handle edge case at intermediate boundary', async () => {
1201
1307
  // Complexity exactly at intermediate_max_complexity should still go to intermediate
1202
1308
  const complexity = 5;
1203
1309
  expect(complexity).toBeLessThanOrEqual(mockConfig.scaling.intermediate_max_complexity);
1204
1310
  });
1205
- it('should use config values for routing thresholds', () => {
1311
+ it('should use config values for routing thresholds', async () => {
1206
1312
  // Verify config values are set correctly for routing logic
1207
1313
  expect(mockConfig.scaling.junior_max_complexity).toBe(3);
1208
1314
  expect(mockConfig.scaling.intermediate_max_complexity).toBe(5);
1209
1315
  });
1210
- it('should default to complexity 5 when not specified', () => {
1316
+ it('should default to complexity 5 when not specified', async () => {
1211
1317
  // Test default complexity value used in assignStories
1212
1318
  const complexity = null;
1213
1319
  const defaultComplexity = complexity || 5;
@@ -1215,187 +1321,203 @@ describe('Scheduler Complexity Routing', () => {
1215
1321
  });
1216
1322
  });
1217
1323
  describe('Scheduler Story Assignment Prevention', () => {
1218
- it('should prevent duplicate story assignments', () => {
1219
- const team = createTeam(db, {
1324
+ it('should prevent duplicate story assignments', async () => {
1325
+ const team = await createTeam(db, {
1220
1326
  name: 'Test Team',
1221
1327
  repoUrl: 'https://github.com/test/repo',
1222
1328
  repoPath: 'test',
1223
1329
  });
1224
- db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1330
+ db.db.run(`INSERT INTO agents (id, type, team_id, status) VALUES ('agent-1', 'junior', '${team.id}', 'idle')`);
1225
1331
  // Create a story and assign it
1226
- const story = createStory(db, {
1332
+ const story = await createStory(db, {
1227
1333
  teamId: team.id,
1228
1334
  title: 'Story',
1229
1335
  description: 'Test',
1230
1336
  });
1231
- updateStory(db, story.id, { complexityScore: 2, status: 'planned' });
1337
+ await updateStory(db, story.id, { complexityScore: 2, status: 'planned' });
1232
1338
  // First assignment
1233
- updateStory(db, story.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1339
+ await updateStory(db, story.id, { assignedAgentId: 'agent-1', status: 'in_progress' });
1234
1340
  // Verify the story is now assigned
1235
- const result = db.exec(`SELECT assigned_agent_id FROM stories WHERE id = '${story.id}'`);
1341
+ const result = db.db.exec(`SELECT assigned_agent_id FROM stories WHERE id = '${story.id}'`);
1236
1342
  expect(result[0].values[0][0]).toBe('agent-1');
1237
1343
  });
1238
- it('should verify story assignment changes status', () => {
1239
- const team = createTeam(db, {
1344
+ it('should verify story assignment changes status', async () => {
1345
+ const team = await createTeam(db, {
1240
1346
  name: 'Test Team',
1241
1347
  repoUrl: 'https://github.com/test/repo',
1242
1348
  repoPath: 'test',
1243
1349
  });
1244
- const story = createStory(db, {
1350
+ const story = await createStory(db, {
1245
1351
  teamId: team.id,
1246
1352
  title: 'Story',
1247
1353
  description: 'Test',
1248
1354
  });
1249
- updateStory(db, story.id, { status: 'planned' });
1355
+ await updateStory(db, story.id, { status: 'planned' });
1250
1356
  // Change status to in_progress
1251
- updateStory(db, story.id, { status: 'in_progress' });
1357
+ await updateStory(db, story.id, { status: 'in_progress' });
1252
1358
  // Verify status changed
1253
- const result = db.exec(`SELECT status FROM stories WHERE id = '${story.id}'`);
1359
+ const result = db.db.exec(`SELECT status FROM stories WHERE id = '${story.id}'`);
1254
1360
  expect(result[0].values[0][0]).toBe('in_progress');
1255
1361
  });
1256
- it('should skip stories with unsatisfied dependencies', () => {
1257
- const team = createTeam(db, {
1362
+ it('should skip stories with unsatisfied dependencies', async () => {
1363
+ const team = await createTeam(db, {
1258
1364
  name: 'Test Team',
1259
1365
  repoUrl: 'https://github.com/test/repo',
1260
1366
  repoPath: 'test',
1261
1367
  });
1262
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
1263
- updateStory(db, storyA.id, { status: 'planned' });
1264
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
1265
- updateStory(db, storyB.id, { status: 'planned' });
1368
+ const storyA = await createStory(db, {
1369
+ teamId: team.id,
1370
+ title: 'Story A',
1371
+ description: 'Test',
1372
+ });
1373
+ await updateStory(db, storyA.id, { status: 'planned' });
1374
+ const storyB = await createStory(db, {
1375
+ teamId: team.id,
1376
+ title: 'Story B',
1377
+ description: 'Test',
1378
+ });
1379
+ await updateStory(db, storyB.id, { status: 'planned' });
1266
1380
  // B depends on A, but A is still planned
1267
- addStoryDependency(db, storyB.id, storyA.id);
1381
+ await addStoryDependency(db, storyB.id, storyA.id);
1268
1382
  // B should not be ready for assignment because A is not merged yet
1269
- const satisfied = areDependenciesSatisfied(db, storyB.id);
1383
+ const satisfied = await areDependenciesSatisfied(db, storyB.id);
1270
1384
  expect(satisfied).toBe(false);
1271
1385
  });
1272
- it('should allow stories when dependencies are in terminal states', () => {
1273
- const team = createTeam(db, {
1386
+ it('should allow stories when dependencies are in terminal states', async () => {
1387
+ const team = await createTeam(db, {
1274
1388
  name: 'Test Team',
1275
1389
  repoUrl: 'https://github.com/test/repo',
1276
1390
  repoPath: 'test',
1277
1391
  });
1278
1392
  // Test with merged status (terminal state)
1279
- const storyA = createStory(db, { teamId: team.id, title: 'Story A', description: 'Test' });
1280
- const storyB = createStory(db, { teamId: team.id, title: 'Story B', description: 'Test' });
1393
+ const storyA = await createStory(db, {
1394
+ teamId: team.id,
1395
+ title: 'Story A',
1396
+ description: 'Test',
1397
+ });
1398
+ const storyB = await createStory(db, {
1399
+ teamId: team.id,
1400
+ title: 'Story B',
1401
+ description: 'Test',
1402
+ });
1281
1403
  // Update A to merged status
1282
- updateStory(db, storyA.id, { status: 'merged' });
1404
+ await updateStory(db, storyA.id, { status: 'merged' });
1283
1405
  // B depends on A, and A is merged
1284
- addStoryDependency(db, storyB.id, storyA.id);
1406
+ await addStoryDependency(db, storyB.id, storyA.id);
1285
1407
  // B should be ready for assignment
1286
- const satisfied = areDependenciesSatisfied(db, storyB.id);
1408
+ const satisfied = await areDependenciesSatisfied(db, storyB.id);
1287
1409
  expect(satisfied).toBe(true);
1288
1410
  });
1289
- it('should map claude model IDs to claude runtime shorthands', () => {
1411
+ it('should map claude model IDs to claude runtime shorthands', async () => {
1290
1412
  const runtimeModel = scheduler.getRuntimeModel('claude-sonnet-4-5-20250929', 'claude');
1291
1413
  expect(runtimeModel).toBe('sonnet');
1292
1414
  });
1293
- it('should remap unsupported codex mini model and preserve gemini runtime model', () => {
1415
+ it('should remap unsupported codex mini model and preserve gemini runtime model', async () => {
1294
1416
  const codexModel = scheduler.getRuntimeModel('gpt-4o-mini', 'codex');
1295
1417
  const geminiModel = scheduler.getRuntimeModel('gemini-2.5-pro', 'gemini');
1296
1418
  expect(codexModel).toBe('gpt-5.2-codex');
1297
1419
  expect(geminiModel).toBe('gemini-2.5-pro');
1298
1420
  });
1299
- it('should not fallback unknown claude models to haiku', () => {
1421
+ it('should not fallback unknown claude models to haiku', async () => {
1300
1422
  const runtimeModel = scheduler.getRuntimeModel('claude-custom-model', 'claude');
1301
1423
  expect(runtimeModel).toBe('claude-custom-model');
1302
1424
  });
1303
- it('should detect godmode is active when an active requirement has godmode enabled', () => {
1425
+ it('should detect godmode is active when an active requirement has godmode enabled', async () => {
1304
1426
  // Create a requirement with godmode and set it to planning status
1305
- const req = createRequirement(db, {
1427
+ const req = await createRequirement(db, {
1306
1428
  title: 'Godmode Requirement',
1307
1429
  description: 'Test requirement with godmode',
1308
1430
  godmode: true,
1309
1431
  });
1310
1432
  db.run(`UPDATE requirements SET status = 'planning' WHERE id = ?`, [req.id]);
1311
1433
  // Godmode should be detected as active
1312
- const isGodmodeActive = scheduler.isGodmodeActive();
1434
+ const isGodmodeActive = await scheduler.isGodmodeActive();
1313
1435
  expect(isGodmodeActive).toBe(true);
1314
1436
  });
1315
- it('should detect godmode even when all stories have moved to in_progress', () => {
1316
- const team = createTeam(db, {
1437
+ it('should detect godmode even when all stories have moved to in_progress', async () => {
1438
+ const team = await createTeam(db, {
1317
1439
  name: 'Test Team',
1318
1440
  repoUrl: 'https://github.com/test/repo',
1319
1441
  repoPath: 'test',
1320
1442
  });
1321
1443
  // Create a godmode requirement in in_progress status
1322
- const req = createRequirement(db, {
1444
+ const req = await createRequirement(db, {
1323
1445
  title: 'Godmode Requirement',
1324
1446
  description: 'Test requirement with godmode',
1325
1447
  godmode: true,
1326
1448
  });
1327
1449
  db.run(`UPDATE requirements SET status = 'in_progress' WHERE id = ?`, [req.id]);
1328
1450
  // Create a story that has moved to in_progress (no longer planned)
1329
- const story = createStory(db, {
1451
+ const story = await createStory(db, {
1330
1452
  requirementId: req.id,
1331
1453
  teamId: team.id,
1332
1454
  title: 'Godmode Story',
1333
1455
  description: 'Test',
1334
1456
  });
1335
- updateStory(db, story.id, { status: 'in_progress' });
1457
+ await updateStory(db, story.id, { status: 'in_progress' });
1336
1458
  // Godmode should still be active even though no stories are planned
1337
- const isGodmodeActive = scheduler.isGodmodeActive();
1459
+ const isGodmodeActive = await scheduler.isGodmodeActive();
1338
1460
  expect(isGodmodeActive).toBe(true);
1339
1461
  });
1340
- it('should not detect godmode when no requirements have godmode enabled', () => {
1462
+ it('should not detect godmode when no requirements have godmode enabled', async () => {
1341
1463
  // Create a normal requirement (without godmode) in planning status
1342
- const req = createRequirement(db, {
1464
+ const req = await createRequirement(db, {
1343
1465
  title: 'Normal Requirement',
1344
1466
  description: 'Test requirement without godmode',
1345
1467
  godmode: false,
1346
1468
  });
1347
1469
  db.run(`UPDATE requirements SET status = 'planning' WHERE id = ?`, [req.id]);
1348
1470
  // Godmode should not be detected as active
1349
- const isGodmodeActive = scheduler.isGodmodeActive();
1471
+ const isGodmodeActive = await scheduler.isGodmodeActive();
1350
1472
  expect(isGodmodeActive).toBe(false);
1351
1473
  });
1352
- it('should not detect godmode when godmode requirement is completed', () => {
1474
+ it('should not detect godmode when godmode requirement is completed', async () => {
1353
1475
  // Create a godmode requirement that is already completed
1354
- const req = createRequirement(db, {
1476
+ const req = await createRequirement(db, {
1355
1477
  title: 'Godmode Requirement',
1356
1478
  description: 'Test requirement with godmode',
1357
1479
  godmode: true,
1358
1480
  });
1359
1481
  db.run(`UPDATE requirements SET status = 'completed' WHERE id = ?`, [req.id]);
1360
1482
  // Godmode should not be active for completed requirements
1361
- const isGodmodeActive = scheduler.isGodmodeActive();
1483
+ const isGodmodeActive = await scheduler.isGodmodeActive();
1362
1484
  expect(isGodmodeActive).toBe(false);
1363
1485
  });
1364
- it('should not detect godmode when no requirements exist', () => {
1486
+ it('should not detect godmode when no requirements exist', async () => {
1365
1487
  // No requirements created, so godmode cannot be active
1366
- const isGodmodeActive = scheduler.isGodmodeActive();
1488
+ const isGodmodeActive = await scheduler.isGodmodeActive();
1367
1489
  expect(isGodmodeActive).toBe(false);
1368
1490
  });
1369
1491
  });
1370
1492
  describe('Scheduler Agent Reassignment for Working Agents with NULL currentStoryId', () => {
1371
- it('should consider working agents with null current_story_id as available for assignment', () => {
1372
- const team = createTeam(db, {
1493
+ it('should consider working agents with null current_story_id as available for assignment', async () => {
1494
+ const team = await createTeam(db, {
1373
1495
  name: 'Test Team',
1374
1496
  repoUrl: 'https://github.com/test/repo',
1375
1497
  repoPath: 'test',
1376
1498
  });
1377
1499
  // Create a working agent with no current story (effectively idle)
1378
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1500
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1379
1501
  VALUES (?, ?, ?, ?, NULL, datetime('now'), datetime('now'))`, ['senior-orphan-1', 'senior', team.id, 'working']);
1380
1502
  // Query agents using the same filter logic from assignStories
1381
- const result = db.exec(`SELECT id, type, status, current_story_id FROM agents
1503
+ const result = db.db.exec(`SELECT id, type, status, current_story_id FROM agents
1382
1504
  WHERE team_id = '${team.id}' AND type != 'qa'
1383
1505
  AND (status = 'idle' OR (status = 'working' AND current_story_id IS NULL))`);
1384
1506
  expect(result[0].values).toHaveLength(1);
1385
1507
  expect(result[0].values[0][0]).toBe('senior-orphan-1');
1386
1508
  });
1387
- it('should not consider working agents with a current story as available', () => {
1388
- const team = createTeam(db, {
1509
+ it('should not consider working agents with a current story as available', async () => {
1510
+ const team = await createTeam(db, {
1389
1511
  name: 'Test Team',
1390
1512
  repoUrl: 'https://github.com/test/repo',
1391
1513
  repoPath: 'test',
1392
1514
  });
1393
- const story = createStory(db, { teamId: team.id, title: 'Active', description: 'Test' });
1515
+ const story = await createStory(db, { teamId: team.id, title: 'Active', description: 'Test' });
1394
1516
  // Create a working agent with a current story
1395
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1517
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1396
1518
  VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, ['senior-busy-1', 'senior', team.id, 'working', story.id]);
1397
1519
  // Query agents using the same filter logic from assignStories
1398
- const result = db.exec(`SELECT id, type, status, current_story_id FROM agents
1520
+ const result = db.db.exec(`SELECT id, type, status, current_story_id FROM agents
1399
1521
  WHERE team_id = '${team.id}' AND type != 'qa'
1400
1522
  AND (status = 'idle' OR (status = 'working' AND current_story_id IS NULL))`);
1401
1523
  // Should not include the busy agent
@@ -1426,18 +1548,18 @@ describe('Scheduler checkMergeQueue', () => {
1426
1548
  };
1427
1549
  it('should spawn QA when a queued PR exists for an in_progress story', async () => {
1428
1550
  ensurePullRequestsTable();
1429
- const team = createTeam(db, {
1551
+ const team = await createTeam(db, {
1430
1552
  name: 'Test Team',
1431
1553
  repoUrl: 'https://github.com/test/repo',
1432
1554
  repoPath: 'test',
1433
1555
  });
1434
- const story = createStory(db, {
1556
+ const story = await createStory(db, {
1435
1557
  teamId: team.id,
1436
1558
  title: 'Queued PR Story',
1437
1559
  description: 'Story has queued PR but stale in_progress status',
1438
1560
  });
1439
- updateStory(db, story.id, { status: 'in_progress' });
1440
- createPullRequest(db, {
1561
+ await updateStory(db, story.id, { status: 'in_progress' });
1562
+ await createPullRequest(db, {
1441
1563
  storyId: story.id,
1442
1564
  teamId: team.id,
1443
1565
  branchName: 'feature/queued-pr-story',
@@ -1457,18 +1579,18 @@ describe('Scheduler checkMergeQueue', () => {
1457
1579
  });
1458
1580
  it('should not spawn QA for merged stories even if PR row is still queued', async () => {
1459
1581
  ensurePullRequestsTable();
1460
- const team = createTeam(db, {
1582
+ const team = await createTeam(db, {
1461
1583
  name: 'Test Team',
1462
1584
  repoUrl: 'https://github.com/test/repo',
1463
1585
  repoPath: 'test',
1464
1586
  });
1465
- const story = createStory(db, {
1587
+ const story = await createStory(db, {
1466
1588
  teamId: team.id,
1467
1589
  title: 'Merged Story',
1468
1590
  description: 'Merged stories should not drive QA scaling',
1469
1591
  });
1470
- updateStory(db, story.id, { status: 'merged' });
1471
- createPullRequest(db, {
1592
+ await updateStory(db, story.id, { status: 'merged' });
1593
+ await createPullRequest(db, {
1472
1594
  storyId: story.id,
1473
1595
  teamId: team.id,
1474
1596
  branchName: 'feature/merged-story',
@@ -1488,19 +1610,19 @@ describe('Scheduler checkMergeQueue', () => {
1488
1610
  });
1489
1611
  describe('Scheduler checkScaling', () => {
1490
1612
  it('should spawn a new indexed senior when the base senior is busy', async () => {
1491
- const team = createTeam(db, {
1613
+ const team = await createTeam(db, {
1492
1614
  name: 'Busy Senior Team',
1493
1615
  repoUrl: 'https://github.com/test/repo',
1494
1616
  repoPath: 'test',
1495
1617
  });
1496
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1618
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1497
1619
  VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, ['senior-busy-1', 'senior', team.id, 'working', 'STORY-OLD']);
1498
- const story = createStory(db, {
1620
+ const story = await createStory(db, {
1499
1621
  teamId: team.id,
1500
1622
  title: 'Needs Senior',
1501
1623
  description: 'High complexity story',
1502
1624
  });
1503
- updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1625
+ await updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1504
1626
  const spawnSeniorSpy = vi
1505
1627
  .spyOn(scheduler, 'spawnSenior')
1506
1628
  .mockImplementation(async (...args) => {
@@ -1519,16 +1641,16 @@ describe('Scheduler checkScaling', () => {
1519
1641
  const result = await scheduler.assignStories();
1520
1642
  expect(result.assigned).toBe(1);
1521
1643
  expect(spawnSeniorSpy).toHaveBeenCalledTimes(1);
1522
- const updatedStory = getStoryById(db, story.id);
1644
+ const updatedStory = (await getStoryById(db, story.id));
1523
1645
  expect(updatedStory.status).toBe('in_progress');
1524
1646
  expect(updatedStory.assigned_agent_id).toBe('senior-spawned-2');
1525
- const busySeniorRow = db.exec(`SELECT status, current_story_id FROM agents WHERE id = 'senior-busy-1'`)[0]?.values[0];
1647
+ const busySeniorRow = db.db.exec(`SELECT status, current_story_id FROM agents WHERE id = 'senior-busy-1'`)[0]?.values[0];
1526
1648
  expect(busySeniorRow?.[0]).toBe('working');
1527
1649
  expect(busySeniorRow?.[1]).toBe('STORY-OLD');
1528
1650
  spawnSeniorSpy.mockRestore();
1529
1651
  });
1530
1652
  it('should choose next senior index from max active index when index 1 is absent', async () => {
1531
- const team = createTeam(db, {
1653
+ const team = await createTeam(db, {
1532
1654
  name: 'Gap Index Team',
1533
1655
  repoUrl: 'https://github.com/test/repo',
1534
1656
  repoPath: 'test',
@@ -1546,12 +1668,12 @@ describe('Scheduler checkScaling', () => {
1546
1668
  `STORY-EXISTING-${index}`,
1547
1669
  ]);
1548
1670
  }
1549
- const story = createStory(db, {
1671
+ const story = await createStory(db, {
1550
1672
  teamId: team.id,
1551
1673
  title: 'Gap Index Story',
1552
1674
  description: 'Requires spawning the next indexed senior',
1553
1675
  });
1554
- updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1676
+ await updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1555
1677
  const spawnSeniorSpy = vi
1556
1678
  .spyOn(scheduler, 'spawnSenior')
1557
1679
  .mockImplementation(async (...args) => {
@@ -1571,29 +1693,29 @@ describe('Scheduler checkScaling', () => {
1571
1693
  const result = await scheduler.assignStories();
1572
1694
  expect(result.assigned).toBe(1);
1573
1695
  expect(spawnSeniorSpy).toHaveBeenCalledTimes(1);
1574
- expect(getStoryById(db, story.id)?.assigned_agent_id).toBe('senior-gap-6');
1696
+ expect((await getStoryById(db, story.id))?.assigned_agent_id).toBe('senior-gap-6');
1575
1697
  spawnSeniorSpy.mockRestore();
1576
1698
  });
1577
1699
  it('should not assign multiple stories to the same senior in one cycle', async () => {
1578
- const team = createTeam(db, {
1700
+ const team = await createTeam(db, {
1579
1701
  name: 'Single Senior Team',
1580
1702
  repoUrl: 'https://github.com/test/repo',
1581
1703
  repoPath: 'test',
1582
1704
  });
1583
- db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1705
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, current_story_id, created_at, updated_at)
1584
1706
  VALUES (?, ?, ?, ?, NULL, datetime('now'), datetime('now'))`, ['senior-single-1', 'senior', team.id, 'idle']);
1585
- const story1 = createStory(db, {
1707
+ const story1 = await createStory(db, {
1586
1708
  teamId: team.id,
1587
1709
  title: 'High Complexity 1',
1588
1710
  description: 'Needs senior',
1589
1711
  });
1590
- updateStory(db, story1.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1591
- const story2 = createStory(db, {
1712
+ await updateStory(db, story1.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1713
+ const story2 = await createStory(db, {
1592
1714
  teamId: team.id,
1593
1715
  title: 'High Complexity 2',
1594
1716
  description: 'Needs senior too',
1595
1717
  });
1596
- updateStory(db, story2.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1718
+ await updateStory(db, story2.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1597
1719
  const spawnSeniorSpy = vi
1598
1720
  .spyOn(scheduler, 'spawnSenior')
1599
1721
  .mockRejectedValue(new Error('senior capacity exhausted'));
@@ -1601,34 +1723,34 @@ describe('Scheduler checkScaling', () => {
1601
1723
  expect(result.assigned).toBe(1);
1602
1724
  expect(result.errors.some(e => e.includes('Failed to spawn Senior'))).toBe(true);
1603
1725
  expect(spawnSeniorSpy).toHaveBeenCalledTimes(1);
1604
- const updatedStory1 = getStoryById(db, story1.id);
1605
- const updatedStory2 = getStoryById(db, story2.id);
1726
+ const updatedStory1 = (await getStoryById(db, story1.id));
1727
+ const updatedStory2 = (await getStoryById(db, story2.id));
1606
1728
  const inProgress = [updatedStory1, updatedStory2].filter(s => s.status === 'in_progress');
1607
1729
  const planned = [updatedStory1, updatedStory2].filter(s => s.status === 'planned');
1608
1730
  expect(inProgress).toHaveLength(1);
1609
1731
  expect(planned).toHaveLength(1);
1610
1732
  expect(inProgress[0].assigned_agent_id).toBe('senior-single-1');
1611
1733
  expect(planned[0].assigned_agent_id).toBeNull();
1612
- const seniorRow = db.exec(`SELECT status, current_story_id FROM agents WHERE id = 'senior-single-1'`)[0]?.values[0];
1734
+ const seniorRow = db.db.exec(`SELECT status, current_story_id FROM agents WHERE id = 'senior-single-1'`)[0]?.values[0];
1613
1735
  expect(seniorRow?.[0]).toBe('working');
1614
1736
  expect(seniorRow?.[1]).toBe(inProgress[0].id);
1615
1737
  spawnSeniorSpy.mockRestore();
1616
1738
  });
1617
1739
  it('should send an explicit assignment handoff to the assigned tmux session', async () => {
1618
- const team = createTeam(db, {
1740
+ const team = await createTeam(db, {
1619
1741
  name: 'Handoff Team',
1620
1742
  repoUrl: 'https://github.com/test/repo',
1621
1743
  repoPath: 'test',
1622
1744
  });
1623
1745
  const sessionName = 'hive-senior-handoff-team';
1624
- db.run(`INSERT INTO agents (id, type, team_id, tmux_session, status, current_story_id, created_at, updated_at)
1746
+ db.db.run(`INSERT INTO agents (id, type, team_id, tmux_session, status, current_story_id, created_at, updated_at)
1625
1747
  VALUES (?, ?, ?, ?, ?, NULL, datetime('now'), datetime('now'))`, ['senior-handoff-1', 'senior', team.id, sessionName, 'idle']);
1626
- const story = createStory(db, {
1748
+ const story = await createStory(db, {
1627
1749
  teamId: team.id,
1628
1750
  title: 'Needs Context Reset',
1629
1751
  description: 'Verify assignment handoff message is sent',
1630
1752
  });
1631
- updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1753
+ await updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1632
1754
  const isRunningSpy = vi.spyOn(tmuxModule, 'isTmuxSessionRunning').mockResolvedValue(true);
1633
1755
  const sendSpy = vi.spyOn(tmuxModule, 'sendToTmuxSession').mockResolvedValue();
1634
1756
  const result = await scheduler.assignStories();
@@ -1642,49 +1764,49 @@ describe('Scheduler checkScaling', () => {
1642
1764
  sendSpy.mockRestore();
1643
1765
  });
1644
1766
  it('should reject spawning a senior on a busy existing session', async () => {
1645
- const team = createTeam(db, {
1767
+ const team = await createTeam(db, {
1646
1768
  name: 'Spawn Guard Team',
1647
1769
  repoUrl: 'https://github.com/test/repo',
1648
1770
  repoPath: 'test',
1649
1771
  });
1650
1772
  const hiveDir = join(mockConfig.rootDir, '.hive');
1651
1773
  const expectedSession = generateSessionName('senior', team.name, undefined, hiveDir);
1652
- db.run(`INSERT INTO agents (id, type, team_id, tmux_session, status, current_story_id, created_at, updated_at)
1774
+ db.db.run(`INSERT INTO agents (id, type, team_id, tmux_session, status, current_story_id, created_at, updated_at)
1653
1775
  VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`, ['senior-guard-1', 'senior', team.id, expectedSession, 'working', 'STORY-ACTIVE']);
1654
1776
  const isRunningSpy = vi.spyOn(tmuxModule, 'isTmuxSessionRunning').mockResolvedValue(true);
1655
1777
  await expect(scheduler.spawnAgent('senior', team.id, team.name, team.repo_path)).rejects.toThrow(/busy session/i);
1656
1778
  isRunningSpy.mockRestore();
1657
1779
  });
1658
1780
  it('should only spawn agents for assignable stories (unblocked dependencies)', async () => {
1659
- const team = createTeam(db, {
1781
+ const team = await createTeam(db, {
1660
1782
  name: 'Test Team',
1661
1783
  repoUrl: 'https://github.com/test/repo',
1662
1784
  repoPath: 'test',
1663
1785
  });
1664
1786
  // Create a blocker story that is not yet merged
1665
- const blockerStory = createStory(db, {
1787
+ const blockerStory = await createStory(db, {
1666
1788
  teamId: team.id,
1667
1789
  title: 'Blocker Story',
1668
1790
  description: 'Must be completed first',
1669
1791
  });
1670
- updateStory(db, blockerStory.id, { status: 'planned', storyPoints: 10 });
1792
+ await updateStory(db, blockerStory.id, { status: 'planned', storyPoints: 10 });
1671
1793
  // Create 4 stories that depend on the blocker (cannot be assigned yet)
1672
1794
  for (let i = 1; i <= 4; i++) {
1673
- const story = createStory(db, {
1795
+ const story = await createStory(db, {
1674
1796
  teamId: team.id,
1675
1797
  title: `Blocked Story ${i}`,
1676
1798
  description: 'Depends on blocker',
1677
1799
  });
1678
- updateStory(db, story.id, { status: 'planned', storyPoints: 10 });
1679
- addStoryDependency(db, story.id, blockerStory.id);
1800
+ await updateStory(db, story.id, { status: 'planned', storyPoints: 10 });
1801
+ await addStoryDependency(db, story.id, blockerStory.id);
1680
1802
  }
1681
1803
  // Create 1 story with no dependencies (can be assigned)
1682
- const unblockedStory = createStory(db, {
1804
+ const unblockedStory = await createStory(db, {
1683
1805
  teamId: team.id,
1684
1806
  title: 'Unblocked Story',
1685
1807
  description: 'No dependencies',
1686
1808
  });
1687
- updateStory(db, unblockedStory.id, { status: 'planned', storyPoints: 10 });
1809
+ await updateStory(db, unblockedStory.id, { status: 'planned', storyPoints: 10 });
1688
1810
  // Total: 50 story points, but only 10 are assignable
1689
1811
  // With senior_capacity: 50, this should spawn 1 senior (10/50 = 0.2, ceil = 1)
1690
1812
  // NOT 1 senior (50/50 = 1)
@@ -1702,27 +1824,27 @@ describe('Scheduler checkScaling', () => {
1702
1824
  spawnSeniorSpy.mockRestore();
1703
1825
  });
1704
1826
  it('should not spawn agents when all stories are blocked', async () => {
1705
- const team = createTeam(db, {
1827
+ const team = await createTeam(db, {
1706
1828
  name: 'Test Team',
1707
1829
  repoUrl: 'https://github.com/test/repo',
1708
1830
  repoPath: 'test',
1709
1831
  });
1710
1832
  // Create a blocker story that is not yet merged
1711
- const blockerStory = createStory(db, {
1833
+ const blockerStory = await createStory(db, {
1712
1834
  teamId: team.id,
1713
1835
  title: 'Blocker Story',
1714
1836
  description: 'Must be completed first',
1715
1837
  });
1716
- updateStory(db, blockerStory.id, { status: 'planned', storyPoints: 10 });
1838
+ await updateStory(db, blockerStory.id, { status: 'planned', storyPoints: 10 });
1717
1839
  // Create stories that all depend on the blocker
1718
1840
  for (let i = 1; i <= 5; i++) {
1719
- const story = createStory(db, {
1841
+ const story = await createStory(db, {
1720
1842
  teamId: team.id,
1721
1843
  title: `Blocked Story ${i}`,
1722
1844
  description: 'Depends on blocker',
1723
1845
  });
1724
- updateStory(db, story.id, { status: 'planned', storyPoints: 10 });
1725
- addStoryDependency(db, story.id, blockerStory.id);
1846
+ await updateStory(db, story.id, { status: 'planned', storyPoints: 10 });
1847
+ await addStoryDependency(db, story.id, blockerStory.id);
1726
1848
  }
1727
1849
  // Mock spawnSenior to track calls
1728
1850
  const spawnSeniorSpy = vi.spyOn(scheduler, 'spawnSenior').mockResolvedValue({
@@ -1749,20 +1871,20 @@ describe('Scheduler Markdown File Writing', () => {
1749
1871
  rmSync(storiesDir, { recursive: true, force: true });
1750
1872
  });
1751
1873
  it('should write markdown files when assigning stories via scheduler', async () => {
1752
- const team = createTeam(db, {
1874
+ const team = await createTeam(db, {
1753
1875
  name: 'MD Write Team',
1754
1876
  repoUrl: 'https://github.com/test/repo',
1755
1877
  repoPath: 'test',
1756
1878
  });
1757
1879
  // Create an idle senior agent
1758
- db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
1880
+ db.db.run(`INSERT INTO agents (id, type, team_id, status, created_at, updated_at)
1759
1881
  VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))`, ['senior-md-1', 'senior', team.id, 'idle']);
1760
- const story = createStory(db, {
1882
+ const story = await createStory(db, {
1761
1883
  teamId: team.id,
1762
1884
  title: 'Markdown Test Story',
1763
1885
  description: 'Should get a markdown file on assignment',
1764
1886
  });
1765
- updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1887
+ await updateStory(db, story.id, { status: 'planned', complexityScore: 10, storyPoints: 8 });
1766
1888
  // Create scheduler with rootDir pointing to a temp dir that has .hive/stories/
1767
1889
  const hiveRoot = join(tmpdir(), `hive-md-root-${Date.now()}`);
1768
1890
  const hiveStoriesDir = join(hiveRoot, '.hive', 'stories');
@@ -1779,27 +1901,27 @@ describe('Scheduler Markdown File Writing', () => {
1779
1901
  const mdPath = join(hiveStoriesDir, `${story.id}.md`);
1780
1902
  expect(existsSync(mdPath)).toBe(true);
1781
1903
  // Verify markdown_path was set in DB
1782
- const updatedStory = getStoryById(db, story.id);
1904
+ const updatedStory = await getStoryById(db, story.id);
1783
1905
  expect(updatedStory?.markdown_path).toBe(mdPath);
1784
1906
  expect(updatedStory?.status).toBe('in_progress');
1785
1907
  rmSync(hiveRoot, { recursive: true, force: true });
1786
1908
  });
1787
1909
  });
1788
1910
  describe('Scheduler Target Branch Propagation', () => {
1789
- it('should retrieve target_branch from requirement when creating story', () => {
1790
- const team = createTeam(db, {
1911
+ it('should retrieve target_branch from requirement when creating story', async () => {
1912
+ const team = await createTeam(db, {
1791
1913
  name: 'Test Team',
1792
1914
  repoUrl: 'https://github.com/test/repo',
1793
1915
  repoPath: 'test',
1794
1916
  });
1795
1917
  // Create a requirement with custom target_branch
1796
- const requirement = createRequirement(db, {
1918
+ const requirement = await createRequirement(db, {
1797
1919
  title: 'Feature for Release Branch',
1798
1920
  description: 'Test feature',
1799
1921
  targetBranch: 'release/v2.0',
1800
1922
  });
1801
1923
  // Create a story linked to this requirement
1802
- const story = createStory(db, {
1924
+ const story = await createStory(db, {
1803
1925
  teamId: team.id,
1804
1926
  requirementId: requirement.id,
1805
1927
  title: 'Story for Release',
@@ -1809,51 +1931,51 @@ describe('Scheduler Target Branch Propagation', () => {
1809
1931
  expect(story.requirement_id).toBe(requirement.id);
1810
1932
  expect(story.team_id).toBe(team.id);
1811
1933
  // Verify we can retrieve the requirement and its target_branch
1812
- const retrievedReq = db.exec(`SELECT target_branch FROM requirements WHERE id = '${requirement.id}'`)[0]?.values[0];
1934
+ const retrievedReq = db.db.exec(`SELECT target_branch FROM requirements WHERE id = '${requirement.id}'`)[0]?.values[0];
1813
1935
  expect(retrievedReq?.[0]).toBe('release/v2.0');
1814
1936
  });
1815
- it('should use default target_branch (main) when requirement has no custom branch', () => {
1937
+ it('should use default target_branch (main) when requirement has no custom branch', async () => {
1816
1938
  // Create a requirement without specifying target_branch
1817
- const requirement = createRequirement(db, {
1939
+ const requirement = await createRequirement(db, {
1818
1940
  title: 'Feature for Main Branch',
1819
1941
  description: 'Test feature',
1820
1942
  });
1821
1943
  // Verify the requirement defaults to main branch
1822
- const retrievedReq = db.exec(`SELECT target_branch FROM requirements WHERE id = '${requirement.id}'`)[0]?.values[0];
1944
+ const retrievedReq = db.db.exec(`SELECT target_branch FROM requirements WHERE id = '${requirement.id}'`)[0]?.values[0];
1823
1945
  expect(retrievedReq?.[0]).toBe('main');
1824
1946
  });
1825
- it('should handle stories with different target branches from different requirements', () => {
1826
- const team = createTeam(db, {
1947
+ it('should handle stories with different target branches from different requirements', async () => {
1948
+ const team = await createTeam(db, {
1827
1949
  name: 'Test Team',
1828
1950
  repoUrl: 'https://github.com/test/repo',
1829
1951
  repoPath: 'test',
1830
1952
  });
1831
1953
  // Create two requirements with different target branches
1832
- const req1 = createRequirement(db, {
1954
+ const req1 = await createRequirement(db, {
1833
1955
  title: 'Main Feature',
1834
1956
  description: 'Goes to main',
1835
1957
  targetBranch: 'main',
1836
1958
  });
1837
- const req2 = createRequirement(db, {
1959
+ const req2 = await createRequirement(db, {
1838
1960
  title: 'Staging Feature',
1839
1961
  description: 'Goes to staging',
1840
1962
  targetBranch: 'staging',
1841
1963
  });
1842
1964
  // Create stories for each requirement
1843
- const story1 = createStory(db, {
1965
+ const story1 = await createStory(db, {
1844
1966
  teamId: team.id,
1845
1967
  requirementId: req1.id,
1846
1968
  title: 'Story 1',
1847
1969
  description: 'Test',
1848
1970
  });
1849
- const story2 = createStory(db, {
1971
+ const story2 = await createStory(db, {
1850
1972
  teamId: team.id,
1851
1973
  requirementId: req2.id,
1852
1974
  title: 'Story 2',
1853
1975
  description: 'Test',
1854
1976
  });
1855
1977
  // Verify each story can access its requirement's target_branch via JOIN
1856
- const result = db.exec(`SELECT s.id, r.target_branch
1978
+ const result = db.db.exec(`SELECT s.id, r.target_branch
1857
1979
  FROM stories s
1858
1980
  LEFT JOIN requirements r ON s.requirement_id = r.id
1859
1981
  WHERE s.id IN ('${story1.id}', '${story2.id}')
@@ -1863,21 +1985,21 @@ describe('Scheduler Target Branch Propagation', () => {
1863
1985
  expect(branches).toContain('main');
1864
1986
  expect(branches).toContain('staging');
1865
1987
  });
1866
- it('should handle stories without a linked requirement (null requirement_id)', () => {
1867
- const team = createTeam(db, {
1988
+ it('should handle stories without a linked requirement (null requirement_id)', async () => {
1989
+ const team = await createTeam(db, {
1868
1990
  name: 'Test Team',
1869
1991
  repoUrl: 'https://github.com/test/repo',
1870
1992
  repoPath: 'test',
1871
1993
  });
1872
1994
  // Create a story without a requirement
1873
- const story = createStory(db, {
1995
+ const story = await createStory(db, {
1874
1996
  teamId: team.id,
1875
1997
  title: 'Standalone Story',
1876
1998
  description: 'No requirement',
1877
1999
  });
1878
2000
  expect(story.requirement_id).toBeNull();
1879
2001
  // When joining with requirements, should get null for target_branch
1880
- const result = db.exec(`SELECT s.id, r.target_branch
2002
+ const result = db.db.exec(`SELECT s.id, r.target_branch
1881
2003
  FROM stories s
1882
2004
  LEFT JOIN requirements r ON s.requirement_id = r.id
1883
2005
  WHERE s.id = '${story.id}'`);