@soleri/core 9.0.4 → 9.3.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 (744) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/brain/intelligence.d.ts +27 -0
  10. package/dist/brain/intelligence.d.ts.map +1 -1
  11. package/dist/brain/intelligence.js +160 -14
  12. package/dist/brain/intelligence.js.map +1 -1
  13. package/dist/brain/learning-radar.d.ts +4 -0
  14. package/dist/brain/learning-radar.d.ts.map +1 -1
  15. package/dist/brain/learning-radar.js +20 -1
  16. package/dist/brain/learning-radar.js.map +1 -1
  17. package/dist/brain/strength-scorer.d.ts +31 -0
  18. package/dist/brain/strength-scorer.d.ts.map +1 -0
  19. package/dist/brain/strength-scorer.js +264 -0
  20. package/dist/brain/strength-scorer.js.map +1 -0
  21. package/dist/chat/agent-loop.d.ts.map +1 -1
  22. package/dist/chat/agent-loop.js +2 -0
  23. package/dist/chat/agent-loop.js.map +1 -1
  24. package/dist/chat/notifications.d.ts.map +1 -1
  25. package/dist/chat/notifications.js +2 -0
  26. package/dist/chat/notifications.js.map +1 -1
  27. package/dist/claudemd/compose.js +1 -1
  28. package/dist/claudemd/compose.js.map +1 -1
  29. package/dist/control/intent-router.d.ts.map +1 -1
  30. package/dist/control/intent-router.js +12 -4
  31. package/dist/control/intent-router.js.map +1 -1
  32. package/dist/curator/contradiction-detector.d.ts +27 -0
  33. package/dist/curator/contradiction-detector.d.ts.map +1 -0
  34. package/dist/curator/contradiction-detector.js +62 -0
  35. package/dist/curator/contradiction-detector.js.map +1 -0
  36. package/dist/curator/curator.d.ts +3 -4
  37. package/dist/curator/curator.d.ts.map +1 -1
  38. package/dist/curator/curator.js +94 -453
  39. package/dist/curator/curator.js.map +1 -1
  40. package/dist/curator/duplicate-detector.d.ts +14 -0
  41. package/dist/curator/duplicate-detector.d.ts.map +1 -0
  42. package/dist/curator/duplicate-detector.js +77 -0
  43. package/dist/curator/duplicate-detector.js.map +1 -0
  44. package/dist/curator/health-audit.d.ts +15 -0
  45. package/dist/curator/health-audit.d.ts.map +1 -0
  46. package/dist/curator/health-audit.js +97 -0
  47. package/dist/curator/health-audit.js.map +1 -0
  48. package/dist/curator/metadata-enricher.d.ts +17 -0
  49. package/dist/curator/metadata-enricher.d.ts.map +1 -0
  50. package/dist/curator/metadata-enricher.js +60 -0
  51. package/dist/curator/metadata-enricher.js.map +1 -0
  52. package/dist/curator/schema.d.ts +7 -0
  53. package/dist/curator/schema.d.ts.map +1 -0
  54. package/dist/curator/schema.js +62 -0
  55. package/dist/curator/schema.js.map +1 -0
  56. package/dist/curator/tag-manager.d.ts +36 -0
  57. package/dist/curator/tag-manager.d.ts.map +1 -0
  58. package/dist/curator/tag-manager.js +78 -0
  59. package/dist/curator/tag-manager.js.map +1 -0
  60. package/dist/engine/bin/soleri-engine.js +24 -3
  61. package/dist/engine/bin/soleri-engine.js.map +1 -1
  62. package/dist/engine/core-ops.d.ts.map +1 -1
  63. package/dist/engine/core-ops.js +23 -8
  64. package/dist/engine/core-ops.js.map +1 -1
  65. package/dist/engine/module-manifest.d.ts.map +1 -1
  66. package/dist/engine/module-manifest.js +42 -2
  67. package/dist/engine/module-manifest.js.map +1 -1
  68. package/dist/engine/register-engine.d.ts.map +1 -1
  69. package/dist/engine/register-engine.js +50 -2
  70. package/dist/engine/register-engine.js.map +1 -1
  71. package/dist/errors/retry.d.ts.map +1 -1
  72. package/dist/errors/retry.js +2 -0
  73. package/dist/errors/retry.js.map +1 -1
  74. package/dist/facades/types.d.ts +1 -1
  75. package/dist/flows/chain-types.d.ts +18 -18
  76. package/dist/flows/gate-evaluator.d.ts.map +1 -1
  77. package/dist/flows/gate-evaluator.js +22 -0
  78. package/dist/flows/gate-evaluator.js.map +1 -1
  79. package/dist/flows/types.d.ts +157 -28
  80. package/dist/flows/types.d.ts.map +1 -1
  81. package/dist/flows/types.js +4 -0
  82. package/dist/flows/types.js.map +1 -1
  83. package/dist/index.d.ts +10 -2
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +9 -1
  86. package/dist/index.js.map +1 -1
  87. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  88. package/dist/intake/intake-pipeline.js +1 -0
  89. package/dist/intake/intake-pipeline.js.map +1 -1
  90. package/dist/intake/text-ingester.d.ts.map +1 -1
  91. package/dist/intake/text-ingester.js +2 -0
  92. package/dist/intake/text-ingester.js.map +1 -1
  93. package/dist/llm/key-pool.d.ts +1 -1
  94. package/dist/llm/key-pool.d.ts.map +1 -1
  95. package/dist/llm/key-pool.js +3 -4
  96. package/dist/llm/key-pool.js.map +1 -1
  97. package/dist/llm/utils.d.ts.map +1 -1
  98. package/dist/llm/utils.js +2 -0
  99. package/dist/llm/utils.js.map +1 -1
  100. package/dist/migrations/migration-runner.test-helpers.d.ts +13 -0
  101. package/dist/migrations/migration-runner.test-helpers.d.ts.map +1 -0
  102. package/dist/migrations/migration-runner.test-helpers.js +47 -0
  103. package/dist/migrations/migration-runner.test-helpers.js.map +1 -0
  104. package/dist/operator/operator-profile.d.ts +44 -0
  105. package/dist/operator/operator-profile.d.ts.map +1 -0
  106. package/dist/operator/operator-profile.js +383 -0
  107. package/dist/operator/operator-profile.js.map +1 -0
  108. package/dist/operator/operator-signals.d.ts +45 -0
  109. package/dist/operator/operator-signals.d.ts.map +1 -0
  110. package/dist/operator/operator-signals.js +228 -0
  111. package/dist/operator/operator-signals.js.map +1 -0
  112. package/dist/operator/operator-types.d.ts +360 -0
  113. package/dist/operator/operator-types.d.ts.map +1 -0
  114. package/dist/operator/operator-types.js +24 -0
  115. package/dist/operator/operator-types.js.map +1 -0
  116. package/dist/packs/types.d.ts +27 -27
  117. package/dist/paths.d.ts +40 -0
  118. package/dist/paths.d.ts.map +1 -0
  119. package/dist/paths.js +98 -0
  120. package/dist/paths.js.map +1 -0
  121. package/dist/persistence/index.d.ts +1 -1
  122. package/dist/persistence/index.d.ts.map +1 -1
  123. package/dist/persistence/index.js +1 -1
  124. package/dist/persistence/index.js.map +1 -1
  125. package/dist/persistence/sqlite-provider.d.ts +2 -0
  126. package/dist/persistence/sqlite-provider.d.ts.map +1 -1
  127. package/dist/persistence/sqlite-provider.js +8 -5
  128. package/dist/persistence/sqlite-provider.js.map +1 -1
  129. package/dist/planning/evidence-collector.d.ts +13 -1
  130. package/dist/planning/evidence-collector.d.ts.map +1 -1
  131. package/dist/planning/evidence-collector.js +33 -0
  132. package/dist/planning/evidence-collector.js.map +1 -1
  133. package/dist/planning/gap-analysis.d.ts +5 -4
  134. package/dist/planning/gap-analysis.d.ts.map +1 -1
  135. package/dist/planning/gap-analysis.js +7 -341
  136. package/dist/planning/gap-analysis.js.map +1 -1
  137. package/dist/planning/gap-passes.d.ts +19 -0
  138. package/dist/planning/gap-passes.d.ts.map +1 -0
  139. package/dist/planning/gap-passes.js +174 -0
  140. package/dist/planning/gap-passes.js.map +1 -0
  141. package/dist/planning/gap-patterns.d.ts +29 -0
  142. package/dist/planning/gap-patterns.d.ts.map +1 -0
  143. package/dist/planning/gap-patterns.js +175 -0
  144. package/dist/planning/gap-patterns.js.map +1 -0
  145. package/dist/planning/gap-types.d.ts +1 -1
  146. package/dist/planning/gap-types.d.ts.map +1 -1
  147. package/dist/planning/gap-types.js +1 -0
  148. package/dist/planning/gap-types.js.map +1 -1
  149. package/dist/planning/github-projection.d.ts +122 -0
  150. package/dist/planning/github-projection.d.ts.map +1 -0
  151. package/dist/planning/github-projection.js +313 -0
  152. package/dist/planning/github-projection.js.map +1 -0
  153. package/dist/planning/impact-analyzer.d.ts +26 -0
  154. package/dist/planning/impact-analyzer.d.ts.map +1 -0
  155. package/dist/planning/impact-analyzer.js +201 -0
  156. package/dist/planning/impact-analyzer.js.map +1 -0
  157. package/dist/planning/plan-lifecycle.d.ts +136 -0
  158. package/dist/planning/plan-lifecycle.d.ts.map +1 -0
  159. package/dist/planning/plan-lifecycle.js +309 -0
  160. package/dist/planning/plan-lifecycle.js.map +1 -0
  161. package/dist/planning/planner-types.d.ts +202 -0
  162. package/dist/planning/planner-types.d.ts.map +1 -0
  163. package/dist/planning/planner-types.js +6 -0
  164. package/dist/planning/planner-types.js.map +1 -0
  165. package/dist/planning/planner.d.ts +31 -383
  166. package/dist/planning/planner.d.ts.map +1 -1
  167. package/dist/planning/planner.js +151 -832
  168. package/dist/planning/planner.js.map +1 -1
  169. package/dist/planning/rationalization-detector.d.ts +32 -0
  170. package/dist/planning/rationalization-detector.d.ts.map +1 -0
  171. package/dist/planning/rationalization-detector.js +89 -0
  172. package/dist/planning/rationalization-detector.js.map +1 -0
  173. package/dist/planning/reconciliation-engine.d.ts +47 -0
  174. package/dist/planning/reconciliation-engine.d.ts.map +1 -0
  175. package/dist/planning/reconciliation-engine.js +128 -0
  176. package/dist/planning/reconciliation-engine.js.map +1 -0
  177. package/dist/planning/task-verifier.d.ts +85 -0
  178. package/dist/planning/task-verifier.d.ts.map +1 -0
  179. package/dist/planning/task-verifier.js +235 -0
  180. package/dist/planning/task-verifier.js.map +1 -0
  181. package/dist/plugins/types.d.ts +4 -4
  182. package/dist/runtime/admin-ops.d.ts +2 -2
  183. package/dist/runtime/admin-ops.d.ts.map +1 -1
  184. package/dist/runtime/admin-ops.js +44 -17
  185. package/dist/runtime/admin-ops.js.map +1 -1
  186. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  187. package/dist/runtime/admin-setup-ops.js +22 -46
  188. package/dist/runtime/admin-setup-ops.js.map +1 -1
  189. package/dist/runtime/archive-ops.d.ts +10 -0
  190. package/dist/runtime/archive-ops.d.ts.map +1 -0
  191. package/dist/runtime/archive-ops.js +310 -0
  192. package/dist/runtime/archive-ops.js.map +1 -0
  193. package/dist/runtime/branching-ops.d.ts +12 -0
  194. package/dist/runtime/branching-ops.d.ts.map +1 -0
  195. package/dist/runtime/branching-ops.js +100 -0
  196. package/dist/runtime/branching-ops.js.map +1 -0
  197. package/dist/runtime/capture-ops.d.ts.map +1 -1
  198. package/dist/runtime/capture-ops.js +42 -7
  199. package/dist/runtime/capture-ops.js.map +1 -1
  200. package/dist/runtime/claude-md-helpers.js +1 -1
  201. package/dist/runtime/claude-md-helpers.js.map +1 -1
  202. package/dist/runtime/context-health.d.ts +31 -0
  203. package/dist/runtime/context-health.d.ts.map +1 -0
  204. package/dist/runtime/context-health.js +57 -0
  205. package/dist/runtime/context-health.js.map +1 -0
  206. package/dist/runtime/facades/archive-facade.d.ts +10 -0
  207. package/dist/runtime/facades/archive-facade.d.ts.map +1 -0
  208. package/dist/runtime/facades/archive-facade.js +11 -0
  209. package/dist/runtime/facades/archive-facade.js.map +1 -0
  210. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  211. package/dist/runtime/facades/brain-facade.js +2 -0
  212. package/dist/runtime/facades/brain-facade.js.map +1 -1
  213. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  214. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  215. package/dist/runtime/facades/branching-facade.js +8 -0
  216. package/dist/runtime/facades/branching-facade.js.map +1 -0
  217. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  218. package/dist/runtime/facades/chat-facade.d.ts.map +1 -1
  219. package/dist/runtime/facades/chat-facade.js +15 -800
  220. package/dist/runtime/facades/chat-facade.js.map +1 -1
  221. package/dist/runtime/facades/chat-service-ops.d.ts +9 -0
  222. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -0
  223. package/dist/runtime/facades/chat-service-ops.js +332 -0
  224. package/dist/runtime/facades/chat-service-ops.js.map +1 -0
  225. package/dist/runtime/facades/chat-session-ops.d.ts +8 -0
  226. package/dist/runtime/facades/chat-session-ops.d.ts.map +1 -0
  227. package/dist/runtime/facades/chat-session-ops.js +136 -0
  228. package/dist/runtime/facades/chat-session-ops.js.map +1 -0
  229. package/dist/runtime/facades/chat-state.d.ts +31 -0
  230. package/dist/runtime/facades/chat-state.d.ts.map +1 -0
  231. package/dist/runtime/facades/chat-state.js +32 -0
  232. package/dist/runtime/facades/chat-state.js.map +1 -0
  233. package/dist/runtime/facades/chat-transport-ops.d.ts +9 -0
  234. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -0
  235. package/dist/runtime/facades/chat-transport-ops.js +337 -0
  236. package/dist/runtime/facades/chat-transport-ops.js.map +1 -0
  237. package/dist/runtime/facades/control-facade.d.ts.map +1 -1
  238. package/dist/runtime/facades/control-facade.js +4 -1
  239. package/dist/runtime/facades/control-facade.js.map +1 -1
  240. package/dist/runtime/facades/index.d.ts.map +1 -1
  241. package/dist/runtime/facades/index.js +48 -0
  242. package/dist/runtime/facades/index.js.map +1 -1
  243. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  244. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  245. package/dist/runtime/facades/intake-facade.js +11 -0
  246. package/dist/runtime/facades/intake-facade.js.map +1 -0
  247. package/dist/runtime/facades/links-facade.d.ts +9 -0
  248. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  249. package/dist/runtime/facades/links-facade.js +10 -0
  250. package/dist/runtime/facades/links-facade.js.map +1 -0
  251. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  252. package/dist/runtime/facades/memory-facade.js +75 -6
  253. package/dist/runtime/facades/memory-facade.js.map +1 -1
  254. package/dist/runtime/facades/operator-facade.d.ts +8 -0
  255. package/dist/runtime/facades/operator-facade.d.ts.map +1 -0
  256. package/dist/runtime/facades/operator-facade.js +220 -0
  257. package/dist/runtime/facades/operator-facade.js.map +1 -0
  258. package/dist/runtime/facades/orchestrate-facade.js +3 -3
  259. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  260. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  261. package/dist/runtime/facades/plan-facade.js +42 -6
  262. package/dist/runtime/facades/plan-facade.js.map +1 -1
  263. package/dist/runtime/facades/review-facade.d.ts +7 -0
  264. package/dist/runtime/facades/review-facade.d.ts.map +1 -0
  265. package/dist/runtime/facades/review-facade.js +8 -0
  266. package/dist/runtime/facades/review-facade.js.map +1 -0
  267. package/dist/runtime/facades/sync-facade.d.ts +7 -0
  268. package/dist/runtime/facades/sync-facade.d.ts.map +1 -0
  269. package/dist/runtime/facades/sync-facade.js +8 -0
  270. package/dist/runtime/facades/sync-facade.js.map +1 -0
  271. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  272. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  273. package/dist/runtime/facades/tier-facade.js +8 -0
  274. package/dist/runtime/facades/tier-facade.js.map +1 -0
  275. package/dist/runtime/facades/vault-facade.d.ts +12 -1
  276. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  277. package/dist/runtime/facades/vault-facade.js +55 -251
  278. package/dist/runtime/facades/vault-facade.js.map +1 -1
  279. package/dist/runtime/github-integration.d.ts +49 -0
  280. package/dist/runtime/github-integration.d.ts.map +1 -0
  281. package/dist/runtime/github-integration.js +120 -0
  282. package/dist/runtime/github-integration.js.map +1 -0
  283. package/dist/runtime/grading-ops.js +1 -1
  284. package/dist/runtime/grading-ops.js.map +1 -1
  285. package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
  286. package/dist/runtime/memory-extra-ops.js +6 -2
  287. package/dist/runtime/memory-extra-ops.js.map +1 -1
  288. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  289. package/dist/runtime/orchestrate-ops.js +386 -37
  290. package/dist/runtime/orchestrate-ops.js.map +1 -1
  291. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  292. package/dist/runtime/planning-extra-ops.js +69 -4
  293. package/dist/runtime/planning-extra-ops.js.map +1 -1
  294. package/dist/runtime/review-ops.d.ts +10 -0
  295. package/dist/runtime/review-ops.d.ts.map +1 -0
  296. package/dist/runtime/review-ops.js +97 -0
  297. package/dist/runtime/review-ops.js.map +1 -0
  298. package/dist/runtime/runtime.d.ts.map +1 -1
  299. package/dist/runtime/runtime.js +29 -12
  300. package/dist/runtime/runtime.js.map +1 -1
  301. package/dist/runtime/session-briefing.d.ts +3 -0
  302. package/dist/runtime/session-briefing.d.ts.map +1 -1
  303. package/dist/runtime/session-briefing.js +72 -1
  304. package/dist/runtime/session-briefing.js.map +1 -1
  305. package/dist/runtime/sync-ops.d.ts +12 -0
  306. package/dist/runtime/sync-ops.d.ts.map +1 -0
  307. package/dist/runtime/sync-ops.js +288 -0
  308. package/dist/runtime/sync-ops.js.map +1 -0
  309. package/dist/runtime/tier-ops.d.ts +13 -0
  310. package/dist/runtime/tier-ops.d.ts.map +1 -0
  311. package/dist/runtime/tier-ops.js +110 -0
  312. package/dist/runtime/tier-ops.js.map +1 -0
  313. package/dist/runtime/types.d.ts +10 -4
  314. package/dist/runtime/types.d.ts.map +1 -1
  315. package/dist/runtime/vault-extra-ops.d.ts +5 -4
  316. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  317. package/dist/runtime/vault-extra-ops.js +5 -300
  318. package/dist/runtime/vault-extra-ops.js.map +1 -1
  319. package/dist/runtime/vault-sharing-ops.d.ts +4 -4
  320. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
  321. package/dist/runtime/vault-sharing-ops.js +5 -300
  322. package/dist/runtime/vault-sharing-ops.js.map +1 -1
  323. package/dist/skills/sync-skills.d.ts +27 -0
  324. package/dist/skills/sync-skills.d.ts.map +1 -0
  325. package/dist/skills/sync-skills.js +81 -0
  326. package/dist/skills/sync-skills.js.map +1 -0
  327. package/dist/update-check.d.ts +14 -0
  328. package/dist/update-check.d.ts.map +1 -0
  329. package/dist/update-check.js +96 -0
  330. package/dist/update-check.js.map +1 -0
  331. package/dist/vault/linking.d.ts +10 -12
  332. package/dist/vault/linking.d.ts.map +1 -1
  333. package/dist/vault/linking.js +140 -161
  334. package/dist/vault/linking.js.map +1 -1
  335. package/dist/vault/vault-entries.d.ts +69 -0
  336. package/dist/vault/vault-entries.d.ts.map +1 -0
  337. package/dist/vault/vault-entries.js +299 -0
  338. package/dist/vault/vault-entries.js.map +1 -0
  339. package/dist/vault/vault-interfaces.d.ts +153 -0
  340. package/dist/vault/vault-interfaces.d.ts.map +1 -0
  341. package/dist/vault/vault-interfaces.js +2 -0
  342. package/dist/vault/vault-interfaces.js.map +1 -0
  343. package/dist/vault/vault-maintenance.d.ts +40 -0
  344. package/dist/vault/vault-maintenance.d.ts.map +1 -0
  345. package/dist/vault/vault-maintenance.js +146 -0
  346. package/dist/vault/vault-maintenance.js.map +1 -0
  347. package/dist/vault/vault-markdown-sync.d.ts +22 -0
  348. package/dist/vault/vault-markdown-sync.d.ts.map +1 -0
  349. package/dist/vault/vault-markdown-sync.js +143 -0
  350. package/dist/vault/vault-markdown-sync.js.map +1 -0
  351. package/dist/vault/vault-memories.d.ts +61 -0
  352. package/dist/vault/vault-memories.d.ts.map +1 -0
  353. package/dist/vault/vault-memories.js +242 -0
  354. package/dist/vault/vault-memories.js.map +1 -0
  355. package/dist/vault/vault-schema.d.ts +9 -0
  356. package/dist/vault/vault-schema.d.ts.map +1 -0
  357. package/dist/vault/vault-schema.js +205 -0
  358. package/dist/vault/vault-schema.js.map +1 -0
  359. package/dist/vault/vault.d.ts +29 -81
  360. package/dist/vault/vault.d.ts.map +1 -1
  361. package/dist/vault/vault.js +82 -931
  362. package/dist/vault/vault.js.map +1 -1
  363. package/package.json +7 -7
  364. package/src/agency/agency-manager.test.ts +620 -0
  365. package/src/agency/default-rules.test.ts +236 -0
  366. package/src/{__tests__ → brain}/brain-intelligence.test.ts +37 -14
  367. package/src/{__tests__ → brain}/brain.test.ts +1 -1
  368. package/src/brain/intelligence.ts +196 -15
  369. package/src/brain/learning-radar.ts +22 -1
  370. package/src/{__tests__ → brain}/second-brain-features.test.ts +4 -4
  371. package/src/{__tests__ → brain}/session-lifecycle.test.ts +2 -2
  372. package/src/capabilities/chain-mapping.test.ts +66 -0
  373. package/src/capabilities/registry.test.ts +359 -0
  374. package/src/chat/agent-loop.test.ts +384 -0
  375. package/src/chat/agent-loop.ts +2 -0
  376. package/src/{__tests__ → chat}/chat-differentiators.test.ts +3 -3
  377. package/src/{__tests__ → chat}/chat-enhanced.test.ts +4 -4
  378. package/src/{__tests__ → chat}/chat-transport.test.ts +6 -6
  379. package/src/chat/mcp-bridge.test.ts +178 -0
  380. package/src/chat/notifications.ts +2 -0
  381. package/src/chat/output-compressor.test.ts +164 -0
  382. package/src/claudemd/compose.test.ts +178 -0
  383. package/src/claudemd/compose.ts +1 -1
  384. package/src/claudemd/inject.test.ts +201 -0
  385. package/src/context/context-engine.test.ts +506 -0
  386. package/src/control/identity-manager.test.ts +305 -0
  387. package/src/control/intent-router.test.ts +360 -0
  388. package/src/control/intent-router.ts +13 -4
  389. package/src/curator/classifier.test.ts +110 -0
  390. package/src/curator/contradiction-detector.test.ts +205 -0
  391. package/src/curator/contradiction-detector.ts +87 -0
  392. package/src/{__tests__ → curator}/curator-pipeline-e2e.test.ts +10 -10
  393. package/src/{__tests__ → curator}/curator.test.ts +77 -1
  394. package/src/curator/curator.ts +160 -600
  395. package/src/curator/duplicate-detector.test.ts +245 -0
  396. package/src/curator/duplicate-detector.ts +103 -0
  397. package/src/curator/health-audit.ts +126 -0
  398. package/src/curator/metadata-enricher.ts +84 -0
  399. package/src/curator/quality-gate.test.ts +175 -0
  400. package/src/curator/schema.ts +65 -0
  401. package/src/curator/tag-manager.test.ts +173 -0
  402. package/src/curator/tag-manager.ts +109 -0
  403. package/src/domain-packs/inject-rules.test.ts +117 -0
  404. package/src/domain-packs/knowledge-installer.test.ts +163 -0
  405. package/src/domain-packs/loader.test.ts +86 -0
  406. package/src/domain-packs/pack-runtime.test.ts +140 -0
  407. package/src/domain-packs/skills-installer.test.ts +135 -0
  408. package/src/domain-packs/token-resolver.test.ts +148 -0
  409. package/src/domain-packs/types.test.ts +144 -0
  410. package/src/enforcement/adapters/claude-code.test.ts +216 -0
  411. package/src/enforcement/registry.test.ts +258 -0
  412. package/src/engine/bin/soleri-engine.ts +30 -4
  413. package/src/engine/core-ops.test.ts +254 -0
  414. package/src/engine/core-ops.ts +25 -8
  415. package/src/engine/module-manifest.test.ts +125 -0
  416. package/src/engine/module-manifest.ts +42 -2
  417. package/src/engine/register-engine.test.ts +235 -0
  418. package/src/engine/register-engine.ts +50 -3
  419. package/src/errors/classify.test.ts +203 -0
  420. package/src/errors/retry.test.ts +153 -0
  421. package/src/errors/retry.ts +2 -0
  422. package/src/errors/types.test.ts +108 -0
  423. package/src/events/event-bus.test.ts +149 -0
  424. package/src/extensions/middleware.test.ts +234 -0
  425. package/src/facades/facade-factory.test.ts +470 -0
  426. package/src/flows/chain-runner.test.ts +273 -0
  427. package/src/flows/context-router.test.ts +52 -0
  428. package/src/flows/dispatch-registry.test.ts +128 -0
  429. package/src/flows/epilogue.test.ts +113 -0
  430. package/src/flows/executor.test.ts +263 -0
  431. package/src/flows/gate-evaluator.test.ts +200 -0
  432. package/src/flows/gate-evaluator.ts +23 -0
  433. package/src/flows/types.ts +4 -0
  434. package/src/governance/governance.test.ts +842 -0
  435. package/src/{__tests__ → health}/health-registry.test.ts +75 -55
  436. package/src/health/vault-integrity.test.ts +110 -0
  437. package/src/index.ts +92 -0
  438. package/src/intake/content-classifier.test.ts +279 -0
  439. package/src/intake/dedup-gate.test.ts +147 -0
  440. package/src/intake/intake-pipeline.test.ts +508 -0
  441. package/src/intake/intake-pipeline.ts +1 -0
  442. package/src/intake/text-ingester.test.ts +200 -0
  443. package/src/intake/text-ingester.ts +2 -0
  444. package/src/llm/key-pool.test.ts +234 -0
  445. package/src/llm/key-pool.ts +3 -4
  446. package/src/llm/llm-client.test.ts +342 -0
  447. package/src/llm/oauth-discovery.test.ts +180 -0
  448. package/src/llm/utils.test.ts +371 -0
  449. package/src/llm/utils.ts +2 -0
  450. package/src/{__tests__ → logging}/logger.test.ts +44 -62
  451. package/src/loop/loop-manager.test.ts +515 -0
  452. package/src/migrations/migration-runner.edge-cases.test.ts +314 -0
  453. package/src/migrations/migration-runner.test-helpers.ts +64 -0
  454. package/src/migrations/migration-runner.test.ts +385 -0
  455. package/src/operator/auto-signal-pipeline.test.ts +207 -0
  456. package/src/operator/operator-profile-extended.test.ts +330 -0
  457. package/src/operator/operator-profile.test.ts +332 -0
  458. package/src/operator/operator-profile.ts +485 -0
  459. package/src/operator/operator-signals-extended.test.ts +257 -0
  460. package/src/operator/operator-signals.test.ts +277 -0
  461. package/src/operator/operator-signals.ts +262 -0
  462. package/src/operator/operator-types.ts +444 -0
  463. package/src/operator/prompts/hook-precompact-operator-dispatch.md +98 -0
  464. package/src/operator/prompts/subagent-soft-signal-extractor.md +130 -0
  465. package/src/operator/prompts/subagent-synthesis-cognition.md +190 -0
  466. package/src/operator/prompts/subagent-synthesis-communication.md +146 -0
  467. package/src/operator/prompts/subagent-synthesis-technical.md +170 -0
  468. package/src/operator/prompts/subagent-synthesis-trust.md +149 -0
  469. package/src/{__tests__ → packs}/pack-lockfile.test.ts +3 -3
  470. package/src/{__tests__ → packs}/pack-system.test.ts +2 -2
  471. package/src/paths.ts +115 -0
  472. package/src/persistence/index.ts +1 -1
  473. package/src/persistence/sqlite-provider.test.ts +540 -0
  474. package/src/persistence/sqlite-provider.ts +8 -5
  475. package/src/persona/defaults.test.ts +55 -0
  476. package/src/persona/loader.test.ts +67 -0
  477. package/src/persona/prompt-generator.test.ts +127 -0
  478. package/src/planning/evidence-collector.test.ts +515 -0
  479. package/src/planning/evidence-collector.ts +47 -0
  480. package/src/planning/gap-analysis-alternatives.test.ts +199 -0
  481. package/src/planning/gap-analysis.ts +21 -636
  482. package/src/planning/gap-passes.test.ts +554 -0
  483. package/src/planning/gap-passes.ts +367 -0
  484. package/src/planning/gap-patterns.test.ts +394 -0
  485. package/src/planning/gap-patterns.ts +317 -0
  486. package/src/planning/gap-types.ts +4 -1
  487. package/src/planning/github-projection.test.ts +182 -0
  488. package/src/planning/github-projection.ts +446 -0
  489. package/src/planning/impact-analyzer.test.ts +167 -0
  490. package/src/planning/impact-analyzer.ts +251 -0
  491. package/src/planning/plan-lifecycle.test.ts +379 -0
  492. package/src/planning/plan-lifecycle.ts +377 -0
  493. package/src/planning/planner-types.ts +215 -0
  494. package/src/{__tests__ → planning}/planner.test.ts +179 -15
  495. package/src/planning/planner.ts +221 -1112
  496. package/src/planning/rationalization-detector.test.ts +156 -0
  497. package/src/planning/rationalization-detector.ts +136 -0
  498. package/src/planning/reconciliation-engine.test.ts +158 -0
  499. package/src/planning/reconciliation-engine.ts +161 -0
  500. package/src/planning/task-verifier.test.ts +267 -0
  501. package/src/planning/task-verifier.ts +309 -0
  502. package/src/planning/verification-protocol.test.ts +201 -0
  503. package/src/playbooks/generic/generic-playbooks.test.ts +438 -0
  504. package/src/playbooks/index.test.ts +77 -0
  505. package/src/playbooks/playbook-executor.test.ts +253 -0
  506. package/src/playbooks/playbook-registry.test.ts +232 -0
  507. package/src/playbooks/playbook-seeder.test.ts +153 -0
  508. package/src/plugins/plugin-loader.test.ts +217 -0
  509. package/src/plugins/plugin-registry.test.ts +284 -0
  510. package/src/project/project-registry.test.ts +439 -0
  511. package/src/prompts/parser.test.ts +100 -0
  512. package/src/prompts/template-manager.test.ts +112 -0
  513. package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
  514. package/src/queue/job-queue.test.ts +327 -0
  515. package/src/queue/pipeline-runner.test.ts +209 -0
  516. package/src/runtime/admin-extra-ops.test.ts +513 -0
  517. package/src/runtime/admin-ops.test.ts +255 -0
  518. package/src/runtime/admin-ops.ts +45 -17
  519. package/src/runtime/admin-setup-ops.test.ts +327 -0
  520. package/src/runtime/admin-setup-ops.ts +26 -42
  521. package/src/runtime/archive-ops.test.ts +272 -0
  522. package/src/runtime/archive-ops.ts +347 -0
  523. package/src/runtime/branching-ops.test.ts +144 -0
  524. package/src/runtime/branching-ops.ts +107 -0
  525. package/src/runtime/capture-ops.test.ts +419 -0
  526. package/src/runtime/capture-ops.ts +50 -8
  527. package/src/runtime/chain-ops.test.ts +159 -0
  528. package/src/runtime/claude-md-helpers.test.ts +189 -0
  529. package/src/runtime/claude-md-helpers.ts +1 -1
  530. package/src/runtime/context-health.test.ts +76 -0
  531. package/src/runtime/context-health.ts +83 -0
  532. package/src/runtime/curator-extra-ops.test.ts +204 -0
  533. package/src/runtime/deprecation.test.ts +98 -0
  534. package/src/runtime/domain-ops.test.ts +278 -0
  535. package/src/runtime/facades/admin-facade.test.ts +330 -0
  536. package/src/runtime/facades/agency-facade.test.ts +278 -0
  537. package/src/runtime/facades/archive-facade.test.ts +308 -0
  538. package/src/runtime/facades/archive-facade.ts +14 -0
  539. package/src/runtime/facades/brain-facade.test.ts +818 -0
  540. package/src/runtime/facades/brain-facade.ts +2 -0
  541. package/src/runtime/facades/branching-facade.test.ts +43 -0
  542. package/src/runtime/facades/branching-facade.ts +11 -0
  543. package/src/runtime/facades/chat-facade.test.ts +219 -0
  544. package/src/runtime/facades/chat-facade.ts +15 -906
  545. package/src/runtime/facades/chat-service-ops.test.ts +381 -0
  546. package/src/runtime/facades/chat-service-ops.ts +376 -0
  547. package/src/runtime/facades/chat-session-ops.test.ts +212 -0
  548. package/src/runtime/facades/chat-session-ops.ts +146 -0
  549. package/src/runtime/facades/chat-state.ts +60 -0
  550. package/src/runtime/facades/chat-transport-ops.test.ts +336 -0
  551. package/src/runtime/facades/chat-transport-ops.ts +379 -0
  552. package/src/runtime/facades/context-facade.test.ts +123 -0
  553. package/src/runtime/facades/control-facade.test.ts +436 -0
  554. package/src/runtime/facades/control-facade.ts +6 -1
  555. package/src/runtime/facades/curator-facade.test.ts +303 -0
  556. package/src/runtime/facades/index.ts +48 -0
  557. package/src/runtime/facades/intake-facade.test.ts +215 -0
  558. package/src/runtime/facades/intake-facade.ts +14 -0
  559. package/src/runtime/facades/links-facade.test.ts +203 -0
  560. package/src/runtime/facades/links-facade.ts +13 -0
  561. package/src/runtime/facades/loop-facade.test.ts +262 -0
  562. package/src/runtime/facades/memory-facade.test.ts +283 -0
  563. package/src/runtime/facades/memory-facade.ts +78 -6
  564. package/src/runtime/facades/operator-facade.test.ts +221 -0
  565. package/src/runtime/facades/operator-facade.ts +244 -0
  566. package/src/runtime/facades/orchestrate-facade.test.ts +191 -0
  567. package/src/runtime/facades/orchestrate-facade.ts +3 -3
  568. package/src/runtime/facades/plan-facade.test.ts +283 -0
  569. package/src/runtime/facades/plan-facade.ts +47 -6
  570. package/src/runtime/facades/review-facade.test.ts +82 -0
  571. package/src/runtime/facades/review-facade.ts +11 -0
  572. package/src/runtime/facades/sync-facade.test.ts +113 -0
  573. package/src/runtime/facades/sync-facade.ts +11 -0
  574. package/src/runtime/facades/tier-facade.test.ts +47 -0
  575. package/src/runtime/facades/tier-facade.ts +11 -0
  576. package/src/runtime/facades/vault-facade.test.ts +563 -0
  577. package/src/runtime/facades/vault-facade.ts +66 -265
  578. package/src/runtime/feature-flags.test.ts +140 -0
  579. package/src/runtime/github-integration.test.ts +89 -0
  580. package/src/runtime/github-integration.ts +162 -0
  581. package/src/runtime/grading-ops.test.ts +172 -0
  582. package/src/runtime/grading-ops.ts +1 -1
  583. package/src/runtime/intake-ops.test.ts +261 -0
  584. package/src/runtime/loop-ops.test.ts +248 -0
  585. package/src/runtime/memory-cross-project-ops.test.ts +188 -0
  586. package/src/runtime/memory-extra-ops.test.ts +453 -0
  587. package/src/runtime/memory-extra-ops.ts +6 -2
  588. package/src/runtime/orchestrate-ops.test.ts +302 -0
  589. package/src/runtime/orchestrate-ops.ts +461 -45
  590. package/src/runtime/pack-ops.test.ts +175 -0
  591. package/src/runtime/planning-extra-ops.test.ts +593 -0
  592. package/src/runtime/planning-extra-ops.ts +74 -4
  593. package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
  594. package/src/runtime/playbook-ops.test.ts +285 -0
  595. package/src/runtime/plugin-ops.test.ts +259 -0
  596. package/src/runtime/project-ops.test.ts +255 -0
  597. package/src/runtime/review-ops.test.ts +142 -0
  598. package/src/runtime/review-ops.ts +99 -0
  599. package/src/runtime/runtime.test.ts +363 -0
  600. package/src/runtime/runtime.ts +41 -12
  601. package/src/runtime/session-briefing.test.ts +431 -0
  602. package/src/runtime/session-briefing.ts +86 -1
  603. package/src/runtime/sync-ops.test.ts +212 -0
  604. package/src/runtime/sync-ops.ts +325 -0
  605. package/src/runtime/telemetry-ops.test.ts +157 -0
  606. package/src/runtime/tier-ops.test.ts +159 -0
  607. package/src/runtime/tier-ops.ts +119 -0
  608. package/src/runtime/types.ts +10 -4
  609. package/src/runtime/vault-extra-ops.test.ts +270 -0
  610. package/src/runtime/vault-extra-ops.ts +5 -332
  611. package/src/runtime/vault-linking-ops.test.ts +237 -0
  612. package/src/runtime/vault-sharing-ops.test.ts +127 -0
  613. package/src/runtime/vault-sharing-ops.ts +5 -329
  614. package/src/skills/sync-skills.ts +98 -0
  615. package/src/streams/normalize.test.ts +95 -0
  616. package/src/streams/replayable-stream.test.ts +166 -0
  617. package/src/telemetry/telemetry.test.ts +143 -0
  618. package/src/transport/http-server.test.ts +394 -0
  619. package/src/transport/lsp-server.test.ts +458 -0
  620. package/src/transport/rate-limiter.test.ts +126 -0
  621. package/src/transport/session-manager.test.ts +133 -0
  622. package/src/transport/token-auth.test.ts +136 -0
  623. package/src/transport/ws-server.test.ts +297 -0
  624. package/src/update-check.ts +111 -0
  625. package/src/vault/__tests__/vault-characterization.test.ts +579 -0
  626. package/src/vault/content-hash.test.ts +78 -0
  627. package/src/vault/git-vault-sync.test.ts +234 -0
  628. package/src/vault/knowledge-review.test.ts +269 -0
  629. package/src/vault/linking.test.ts +391 -0
  630. package/src/vault/linking.ts +188 -181
  631. package/src/vault/obsidian-sync.test.ts +345 -0
  632. package/src/vault/playbook.test.ts +152 -0
  633. package/src/vault/scope-detector.test.ts +185 -0
  634. package/src/vault/vault-branching.test.ts +252 -0
  635. package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
  636. package/src/vault/vault-entries.ts +426 -0
  637. package/src/vault/vault-maintenance.ts +200 -0
  638. package/src/vault/vault-manager.test.ts +206 -0
  639. package/src/vault/vault-markdown-sync.test.ts +203 -0
  640. package/src/vault/vault-markdown-sync.ts +163 -0
  641. package/src/vault/vault-memories.ts +339 -0
  642. package/src/{__tests__ → vault}/vault-scaling.test.ts +1 -1
  643. package/src/vault/vault-schema.ts +238 -0
  644. package/src/{__tests__ → vault}/vault-sharing.test.ts +4 -4
  645. package/src/{__tests__ → vault}/vault.test.ts +2 -2
  646. package/src/vault/vault.ts +87 -1123
  647. package/dist/cognee/client.d.ts +0 -43
  648. package/dist/cognee/client.d.ts.map +0 -1
  649. package/dist/cognee/client.js +0 -375
  650. package/dist/cognee/client.js.map +0 -1
  651. package/dist/cognee/sync-manager.d.ts +0 -153
  652. package/dist/cognee/sync-manager.d.ts.map +0 -1
  653. package/dist/cognee/sync-manager.js +0 -390
  654. package/dist/cognee/sync-manager.js.map +0 -1
  655. package/dist/cognee/types.d.ts +0 -62
  656. package/dist/cognee/types.d.ts.map +0 -1
  657. package/dist/cognee/types.js +0 -3
  658. package/dist/cognee/types.js.map +0 -1
  659. package/dist/governance/index.d.ts +0 -3
  660. package/dist/governance/index.d.ts.map +0 -1
  661. package/dist/governance/index.js +0 -2
  662. package/dist/governance/index.js.map +0 -1
  663. package/dist/health/doctor-checks.d.ts +0 -15
  664. package/dist/health/doctor-checks.d.ts.map +0 -1
  665. package/dist/health/doctor-checks.js +0 -98
  666. package/dist/health/doctor-checks.js.map +0 -1
  667. package/dist/persistence/postgres-provider.d.ts +0 -81
  668. package/dist/persistence/postgres-provider.d.ts.map +0 -1
  669. package/dist/persistence/postgres-provider.js +0 -256
  670. package/dist/persistence/postgres-provider.js.map +0 -1
  671. package/dist/runtime/cognee-sync-ops.d.ts +0 -12
  672. package/dist/runtime/cognee-sync-ops.d.ts.map +0 -1
  673. package/dist/runtime/cognee-sync-ops.js +0 -93
  674. package/dist/runtime/cognee-sync-ops.js.map +0 -1
  675. package/dist/runtime/core-ops.d.ts +0 -23
  676. package/dist/runtime/core-ops.d.ts.map +0 -1
  677. package/dist/runtime/core-ops.js +0 -1296
  678. package/dist/runtime/core-ops.js.map +0 -1
  679. package/dist/runtime/facades/cognee-facade.d.ts +0 -8
  680. package/dist/runtime/facades/cognee-facade.d.ts.map +0 -1
  681. package/dist/runtime/facades/cognee-facade.js +0 -156
  682. package/dist/runtime/facades/cognee-facade.js.map +0 -1
  683. package/src/__tests__/admin-extra-ops.test.ts +0 -484
  684. package/src/__tests__/admin-ops.test.ts +0 -268
  685. package/src/__tests__/admin-setup-ops.test.ts +0 -355
  686. package/src/__tests__/agency-manager.test.ts +0 -374
  687. package/src/__tests__/agent-loop.test.ts +0 -256
  688. package/src/__tests__/capture-ops.test.ts +0 -784
  689. package/src/__tests__/claudemd.test.ts +0 -282
  690. package/src/__tests__/content-hash.test.ts +0 -60
  691. package/src/__tests__/context-engine.test.ts +0 -251
  692. package/src/__tests__/core-ops.test.ts +0 -550
  693. package/src/__tests__/curator-extra-ops.test.ts +0 -383
  694. package/src/__tests__/deprecation.test.ts +0 -78
  695. package/src/__tests__/domain-ops.test.ts +0 -226
  696. package/src/__tests__/domain-packs.test.ts +0 -421
  697. package/src/__tests__/enforcement.test.ts +0 -153
  698. package/src/__tests__/errors.test.ts +0 -388
  699. package/src/__tests__/extensions.test.ts +0 -233
  700. package/src/__tests__/facade-factory.test.ts +0 -271
  701. package/src/__tests__/feature-flags.test.ts +0 -137
  702. package/src/__tests__/flows.test.ts +0 -604
  703. package/src/__tests__/git-vault-sync.test.ts +0 -230
  704. package/src/__tests__/governance.test.ts +0 -522
  705. package/src/__tests__/grading-ops.test.ts +0 -361
  706. package/src/__tests__/identity-manager.test.ts +0 -243
  707. package/src/__tests__/intake-pipeline.test.ts +0 -162
  708. package/src/__tests__/intent-router.test.ts +0 -222
  709. package/src/__tests__/knowledge-review.test.ts +0 -104
  710. package/src/__tests__/llm-client.test.ts +0 -69
  711. package/src/__tests__/llm.test.ts +0 -556
  712. package/src/__tests__/loader.test.ts +0 -176
  713. package/src/__tests__/loop-ops.test.ts +0 -469
  714. package/src/__tests__/lsp-transport.test.ts +0 -442
  715. package/src/__tests__/memory-cross-project-ops.test.ts +0 -248
  716. package/src/__tests__/memory-extra-ops.test.ts +0 -352
  717. package/src/__tests__/migration-runner.test.ts +0 -170
  718. package/src/__tests__/module-manifest-drift.test.ts +0 -59
  719. package/src/__tests__/normalize.test.ts +0 -85
  720. package/src/__tests__/obsidian-sync.test.ts +0 -354
  721. package/src/__tests__/orchestrate-ops.test.ts +0 -289
  722. package/src/__tests__/pack-ops.test.ts +0 -146
  723. package/src/__tests__/persistence.test.ts +0 -291
  724. package/src/__tests__/planning-extra-ops.test.ts +0 -706
  725. package/src/__tests__/playbook-executor.test.ts +0 -249
  726. package/src/__tests__/playbook-registry.test.ts +0 -326
  727. package/src/__tests__/playbook-seeder.test.ts +0 -163
  728. package/src/__tests__/playbook.test.ts +0 -389
  729. package/src/__tests__/plugin-ops.test.ts +0 -411
  730. package/src/__tests__/plugin-system.test.ts +0 -509
  731. package/src/__tests__/project-ops.test.ts +0 -381
  732. package/src/__tests__/replayable-stream.test.ts +0 -177
  733. package/src/__tests__/runtime.test.ts +0 -95
  734. package/src/__tests__/scope-detector.test.ts +0 -121
  735. package/src/__tests__/template-manager.test.ts +0 -222
  736. package/src/__tests__/token-resolver.test.ts +0 -79
  737. package/src/__tests__/transport.test.ts +0 -758
  738. package/src/__tests__/vault-branching.test.ts +0 -274
  739. package/src/__tests__/vault-extra-ops.test.ts +0 -482
  740. package/src/__tests__/vault-integrity.test.ts +0 -71
  741. package/src/__tests__/vault-manager.test.ts +0 -238
  742. package/src/__tests__/ws-transport.test.ts +0 -479
  743. package/src/engine/index.ts +0 -21
  744. package/src/persona/index.ts +0 -9
@@ -1,328 +1,76 @@
1
+ /**
2
+ * Planner facade — delegates to extracted modules, owns persistence.
3
+ * Re-exports all public types for backward compatibility.
4
+ */
1
5
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
2
- import { createHash } from 'node:crypto';
3
6
  import { dirname } from 'node:path';
4
- import type { PlanGap } from './gap-types.js';
5
- import { SEVERITY_WEIGHTS, CATEGORY_PENALTY_CAPS, CATEGORY_BONUS_CAPS } from './gap-types.js';
6
7
  import { runGapAnalysis } from './gap-analysis.js';
7
8
  import type { GapAnalysisOptions } from './gap-analysis.js';
8
-
9
- /**
10
- * Plan lifecycle status.
11
- * Ported from Salvador's PlanLifecycleStatus with full 8-state lifecycle.
12
- *
13
- * Lifecycle: brainstorming → draft → approved → executing → [validating] → reconciling → completed → archived
14
- */
15
- export type PlanStatus =
16
- | 'brainstorming'
17
- | 'draft'
18
- | 'approved'
19
- | 'executing'
20
- | 'validating'
21
- | 'reconciling'
22
- | 'completed'
23
- | 'archived';
24
-
25
- /**
26
- * Valid status transitions.
27
- * Each key maps to the set of statuses it can transition to.
28
- * Ported from Salvador's LIFECYCLE_TRANSITIONS.
29
- */
30
- export const LIFECYCLE_TRANSITIONS: Record<PlanStatus, PlanStatus[]> = {
31
- brainstorming: ['draft'],
32
- draft: ['approved'],
33
- approved: ['executing'],
34
- executing: ['validating', 'reconciling'],
35
- validating: ['reconciling', 'executing'],
36
- reconciling: ['completed'],
37
- completed: ['archived'],
38
- archived: [],
39
- };
40
-
41
- /**
42
- * Statuses where the 30-minute TTL should NOT apply.
43
- * Plans in these states may span multiple sessions.
44
- */
45
- export const NON_EXPIRING_STATUSES: PlanStatus[] = [
46
- 'brainstorming',
47
- 'executing',
48
- 'validating',
49
- 'reconciling',
50
- ];
51
-
52
- /**
53
- * Validate a lifecycle status transition.
54
- * Returns true if the transition is valid, false otherwise.
55
- */
56
- export function isValidTransition(from: PlanStatus, to: PlanStatus): boolean {
57
- return LIFECYCLE_TRANSITIONS[from].includes(to);
58
- }
59
-
60
- /**
61
- * Get the valid next statuses for a given status.
62
- */
63
- export function getValidNextStatuses(status: PlanStatus): PlanStatus[] {
64
- return LIFECYCLE_TRANSITIONS[status];
65
- }
66
-
67
- /**
68
- * Check if a status should have TTL expiration.
69
- * Plans in executing/reconciling states persist indefinitely.
70
- */
71
- export function shouldExpire(status: PlanStatus): boolean {
72
- return !NON_EXPIRING_STATUSES.includes(status);
73
- }
74
-
75
- export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'skipped' | 'failed';
76
-
77
- export interface TaskEvidence {
78
- /** What the evidence proves (maps to an acceptance criterion). */
79
- criterion: string;
80
- /** Evidence content — command output, URL, file path, description. */
81
- content: string;
82
- /** Evidence type. */
83
- type: 'command_output' | 'url' | 'file' | 'description';
84
- submittedAt: number;
85
- }
86
-
87
- export interface TaskMetrics {
88
- durationMs?: number;
89
- iterations?: number;
90
- toolCalls?: number;
91
- modelTier?: string;
92
- estimatedCostUsd?: number;
93
- }
94
-
95
- export interface TaskDeliverable {
96
- type: 'file' | 'vault_entry' | 'url';
97
- path: string;
98
- hash?: string;
99
- verifiedAt?: number;
100
- stale?: boolean;
101
- }
102
-
103
- export interface ExecutionSummary {
104
- totalDurationMs: number;
105
- tasksCompleted: number;
106
- tasksSkipped: number;
107
- tasksFailed: number;
108
- avgTaskDurationMs: number;
109
- }
110
-
111
- export interface PlanTask {
112
- id: string;
113
- title: string;
114
- description: string;
115
- status: TaskStatus;
116
- /** Optional dependency IDs — tasks that must complete before this one. */
117
- dependsOn?: string[];
118
- /** Evidence submitted for task acceptance criteria. */
119
- evidence?: TaskEvidence[];
120
- /** Whether this task has been verified (all evidence checked + reviews passed). */
121
- verified?: boolean;
122
- /** Task-level acceptance criteria (for verification checking). */
123
- acceptanceCriteria?: string[];
124
- /** Timestamp when task was first moved to in_progress. */
125
- startedAt?: number;
126
- /** Timestamp when task reached a terminal state (completed/skipped/failed). */
127
- completedAt?: number;
128
- /** Per-task execution metrics. */
129
- metrics?: TaskMetrics;
130
- /** Deliverables produced by this task. */
131
- deliverables?: TaskDeliverable[];
132
- updatedAt: number;
133
- }
134
-
135
- export interface DriftItem {
136
- /** Type of drift */
137
- type: 'skipped' | 'added' | 'modified' | 'reordered';
138
- /** What drifted */
139
- description: string;
140
- /** How much this affected the plan */
141
- impact: 'low' | 'medium' | 'high';
142
- /** Why the drift occurred */
143
- rationale: string;
144
- }
145
-
146
- /**
147
- * Severity weights for drift accuracy score calculation.
148
- * Score = 100 - sum(drift_items * weight_per_impact)
149
- * Ported from Salvador's plan-lifecycle-types.ts.
150
- */
151
- export const DRIFT_WEIGHTS: Record<DriftItem['impact'], number> = {
152
- high: 20,
153
- medium: 10,
154
- low: 5,
155
- };
156
-
157
- /**
158
- * Calculate drift accuracy score from drift items.
159
- * Score = max(0, 100 - sum(weight_per_impact))
160
- * Ported from Salvador's calculateDriftScore.
161
- */
162
- export function calculateDriftScore(items: DriftItem[]): number {
163
- let deductions = 0;
164
- for (const item of items) {
165
- deductions += DRIFT_WEIGHTS[item.impact];
166
- }
167
- return Math.max(0, 100 - deductions);
168
- }
169
-
170
- export interface ReconciliationReport {
171
- planId: string;
172
- /** Accuracy score: 100 = perfect execution, 0 = total drift. Impact-weighted. */
173
- accuracy: number;
174
- driftItems: DriftItem[];
175
- /** Human-readable summary of the drift */
176
- summary: string;
177
- reconciledAt: number;
178
- }
179
-
180
- export interface ReviewEvidence {
181
- planId: string;
182
- taskId?: string;
183
- reviewer: string;
184
- outcome: 'approved' | 'rejected' | 'needs_changes';
185
- comments: string;
186
- reviewedAt: number;
187
- }
188
-
189
- export type PlanGrade = 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
190
-
191
- export interface PlanCheck {
192
- checkId: string;
193
- planId: string;
194
- grade: PlanGrade;
195
- score: number; // 0-100
196
- gaps: PlanGap[];
197
- iteration: number;
198
- checkedAt: number;
199
- }
200
-
201
- /**
202
- * Calculate score from gaps with severity-weighted deductions and iteration leniency.
203
- * Ported from Salvador MCP's plan-grading.ts.
204
- *
205
- * - Minor gaps: weight=0 on iteration 1 (free sketching), weight=1 on iteration 2, full weight on 3+
206
- * - Per-category deductions are capped before summing (prevents one category from tanking the score)
207
- * - Score = max(0, 100 - totalDeductions)
208
- */
209
- export function calculateScore(gaps: PlanGap[], iteration: number = 1): number {
210
- const categoryDeductions = new Map<string, number>();
211
- const categoryBonuses = new Map<string, number>();
212
-
213
- for (const gap of gaps) {
214
- let weight: number = SEVERITY_WEIGHTS[gap.severity];
215
-
216
- // Iteration leniency for minor gaps
217
- if (gap.severity === 'minor') {
218
- if (iteration === 1) weight = 0;
219
- else if (iteration === 2) weight = 1;
220
- // iteration 3+: full weight (2)
221
- }
222
-
223
- const category = gap.category;
224
- if (weight < 0) {
225
- // Bonus — accumulate as positive value for capping, apply as negative deduction
226
- categoryBonuses.set(category, (categoryBonuses.get(category) ?? 0) + Math.abs(weight));
227
- } else {
228
- categoryDeductions.set(category, (categoryDeductions.get(category) ?? 0) + weight);
229
- }
230
- }
231
-
232
- let deductions = 0;
233
- for (const [category, total] of categoryDeductions) {
234
- const cap = CATEGORY_PENALTY_CAPS[category];
235
- deductions += cap !== undefined ? Math.min(total, cap) : total;
236
- }
237
-
238
- let bonuses = 0;
239
- for (const [category, total] of categoryBonuses) {
240
- const cap = CATEGORY_BONUS_CAPS[category];
241
- bonuses += cap !== undefined ? Math.min(total, cap) : total;
242
- }
243
-
244
- return Math.max(0, Math.min(100, 100 - deductions + bonuses));
245
- }
246
-
247
- /**
248
- * A structured decision with rationale.
249
- * Ported from Salvador's PlanContent.decisions.
250
- */
251
- export interface PlanDecision {
252
- decision: string;
253
- rationale: string;
254
- }
255
-
256
- export interface Plan {
257
- id: string;
258
- objective: string;
259
- scope: string;
260
- status: PlanStatus;
261
- /**
262
- * Decisions can be flat strings (backward compat) or structured {decision, rationale}.
263
- * New plans should prefer PlanDecision[].
264
- */
265
- decisions: (string | PlanDecision)[];
266
- tasks: PlanTask[];
267
- /** High-level approach description. Ported from Salvador's PlanContent. */
268
- approach?: string;
269
- /** Additional context for the plan. */
270
- context?: string;
271
- /** Measurable success criteria. */
272
- success_criteria?: string[];
273
- /** Tools to use in execution order. */
274
- tool_chain?: string[];
275
- /** Flow definition to follow (e.g., 'developer', 'reviewer', 'designer'). */
276
- flow?: string;
277
- /** Target operational mode (e.g., 'build', 'review', 'fix'). */
278
- target_mode?: string;
279
- /** Reconciliation report — populated by reconcile(). */
280
- reconciliation?: ReconciliationReport;
281
- /** Review evidence — populated by addReview(). */
282
- reviews?: ReviewEvidence[];
283
- /** Latest grading check. */
284
- latestCheck?: PlanCheck;
285
- /** All check history. */
286
- checks: PlanCheck[];
287
- /** Matched playbook info (set by orchestration layer via playbook_match). */
288
- playbookMatch?: {
289
- label: string;
290
- genericId?: string;
291
- domainId?: string;
292
- };
293
- /** Aggregate execution metrics — populated by reconcile() and complete(). */
294
- executionSummary?: ExecutionSummary;
295
- createdAt: number;
296
- updatedAt: number;
297
- }
298
-
299
- export interface PlanStore {
300
- version: string;
301
- plans: Plan[];
302
- }
9
+ export * from './plan-lifecycle.js';
10
+ export * from './reconciliation-engine.js';
11
+ export * from './task-verifier.js';
12
+ export * from './planner-types.js';
13
+ import type {
14
+ TaskStatus,
15
+ PlanTask,
16
+ Plan,
17
+ PlanStore,
18
+ PlanCheck,
19
+ PlannerOptions,
20
+ } from './planner-types.js';
21
+ import {
22
+ applyTransition,
23
+ scoreToGrade,
24
+ gradeToMinScore,
25
+ PlanGradeRejectionError,
26
+ hasCircularDependencies,
27
+ applyIteration,
28
+ applySplitTasks,
29
+ calculateScore,
30
+ applyTaskStatusUpdate,
31
+ createPlanObject,
32
+ } from './plan-lifecycle.js';
33
+ import type { PlanStatus, PlanGrade, IterateChanges } from './plan-lifecycle.js';
34
+ import {
35
+ buildReconciliationReport,
36
+ buildAutoReconcileInput,
37
+ computeExecutionSummary,
38
+ } from './reconciliation-engine.js';
39
+ import type { ReconcileInput } from './reconciliation-engine.js';
40
+ import {
41
+ createEvidence,
42
+ verifyTaskLogic,
43
+ verifyPlanLogic,
44
+ verifyDeliverablesLogic,
45
+ createDeliverable,
46
+ buildSpecReviewPrompt,
47
+ buildQualityReviewPrompt,
48
+ } from './task-verifier.js';
303
49
 
304
50
  export class Planner {
305
51
  private filePath: string;
306
52
  private store: PlanStore;
307
53
  private gapOptions?: GapAnalysisOptions;
54
+ private minGradeForApproval: PlanGrade;
308
55
 
309
- constructor(filePath: string, gapOptions?: GapAnalysisOptions) {
56
+ constructor(filePath: string, options?: GapAnalysisOptions | PlannerOptions) {
310
57
  this.filePath = filePath;
311
- this.gapOptions = gapOptions;
58
+ if (options && 'minGradeForApproval' in options) {
59
+ this.gapOptions = options.gapOptions;
60
+ this.minGradeForApproval = options.minGradeForApproval ?? 'A';
61
+ } else {
62
+ this.gapOptions = options as GapAnalysisOptions | undefined;
63
+ this.minGradeForApproval = 'A';
64
+ }
312
65
  this.store = this.load();
313
66
  }
314
67
 
315
68
  private load(): PlanStore {
316
- if (!existsSync(this.filePath)) {
317
- return { version: '1.0', plans: [] };
318
- }
69
+ if (!existsSync(this.filePath)) return { version: '1.0', plans: [] };
319
70
  try {
320
71
  const data = readFileSync(this.filePath, 'utf-8');
321
72
  const store = JSON.parse(data) as PlanStore;
322
- // Backward compatibility: ensure every plan has a checks array
323
- for (const plan of store.plans) {
324
- plan.checks = plan.checks ?? [];
325
- }
73
+ for (const plan of store.plans) plan.checks = plan.checks ?? [];
326
74
  return store;
327
75
  } catch {
328
76
  return { version: '1.0', plans: [] };
@@ -334,44 +82,26 @@ export class Planner {
334
82
  writeFileSync(this.filePath, JSON.stringify(this.store, null, 2), 'utf-8');
335
83
  }
336
84
 
337
- create(params: {
338
- objective: string;
339
- scope: string;
340
- decisions?: (string | PlanDecision)[];
341
- tasks?: Array<{ title: string; description: string }>;
342
- approach?: string;
343
- context?: string;
344
- success_criteria?: string[];
345
- tool_chain?: string[];
346
- flow?: string;
347
- target_mode?: string;
348
- /** Start in 'brainstorming' instead of 'draft'. Default: 'draft'. */
349
- initialStatus?: 'brainstorming' | 'draft';
350
- }): Plan {
351
- const now = Date.now();
352
- const plan: Plan = {
353
- id: `plan-${now}-${Math.random().toString(36).slice(2, 8)}`,
354
- objective: params.objective,
355
- scope: params.scope,
356
- status: params.initialStatus ?? 'draft',
357
- decisions: params.decisions ?? [],
358
- tasks: (params.tasks ?? []).map((t, i) => ({
359
- id: `task-${i + 1}`,
360
- title: t.title,
361
- description: t.description,
362
- status: 'pending' as TaskStatus,
363
- updatedAt: now,
364
- })),
365
- ...(params.approach !== undefined && { approach: params.approach }),
366
- ...(params.context !== undefined && { context: params.context }),
367
- ...(params.success_criteria !== undefined && { success_criteria: params.success_criteria }),
368
- ...(params.tool_chain !== undefined && { tool_chain: params.tool_chain }),
369
- ...(params.flow !== undefined && { flow: params.flow }),
370
- ...(params.target_mode !== undefined && { target_mode: params.target_mode }),
371
- checks: [],
372
- createdAt: now,
373
- updatedAt: now,
374
- };
85
+ private transition(plan: Plan, to: PlanStatus): void {
86
+ const r = applyTransition(plan.status, to);
87
+ plan.status = r.status;
88
+ plan.updatedAt = r.updatedAt;
89
+ }
90
+
91
+ private requirePlan(planId: string): Plan {
92
+ const plan = this.get(planId);
93
+ if (!plan) throw new Error(`Plan not found: ${planId}`);
94
+ return plan;
95
+ }
96
+
97
+ private requireTask(plan: Plan, taskId: string): PlanTask {
98
+ const task = plan.tasks.find((t) => t.id === taskId);
99
+ if (!task) throw new Error(`Task not found: ${taskId}`);
100
+ return task;
101
+ }
102
+
103
+ create(params: Parameters<typeof createPlanObject>[0]): Plan {
104
+ const plan = createPlanObject(params);
375
105
  this.store.plans.push(plan);
376
106
  this.save();
377
107
  return plan;
@@ -385,9 +115,6 @@ export class Planner {
385
115
  return [...this.store.plans];
386
116
  }
387
117
 
388
- /**
389
- * Permanently remove a plan by ID. Returns true if found and removed.
390
- */
391
118
  remove(planId: string): boolean {
392
119
  const idx = this.store.plans.findIndex((p) => p.id === planId);
393
120
  if (idx < 0) return false;
@@ -396,121 +123,102 @@ export class Planner {
396
123
  return true;
397
124
  }
398
125
 
399
- /**
400
- * Transition a plan to a new status using the typed FSM.
401
- * Validates that the transition is allowed before applying it.
402
- */
403
- private transition(plan: Plan, to: PlanStatus): void {
404
- if (!isValidTransition(plan.status, to)) {
405
- const valid = getValidNextStatuses(plan.status);
406
- throw new Error(
407
- `Invalid transition: '${plan.status}' → '${to}'. ` +
408
- `Valid transitions from '${plan.status}': ${valid.length > 0 ? valid.join(', ') : 'none'}`,
409
- );
410
- }
411
- plan.status = to;
412
- plan.updatedAt = Date.now();
413
- }
414
-
415
- /**
416
- * Promote a brainstorming plan to draft status.
417
- * Only allowed from 'brainstorming'.
418
- */
419
126
  promoteToDraft(planId: string): Plan {
420
- const plan = this.get(planId);
421
- if (!plan) throw new Error(`Plan not found: ${planId}`);
127
+ const plan = this.requirePlan(planId);
422
128
  this.transition(plan, 'draft');
423
129
  this.save();
424
130
  return plan;
425
131
  }
426
132
 
427
133
  approve(planId: string): Plan {
428
- const plan = this.get(planId);
429
- if (!plan) throw new Error(`Plan not found: ${planId}`);
134
+ const plan = this.requirePlan(planId);
135
+ const check = plan.latestCheck;
136
+ if (check && check.score < gradeToMinScore(this.minGradeForApproval)) {
137
+ throw new PlanGradeRejectionError(
138
+ check.grade,
139
+ check.score,
140
+ this.minGradeForApproval,
141
+ check.gaps,
142
+ );
143
+ }
430
144
  this.transition(plan, 'approved');
431
145
  this.save();
432
146
  return plan;
433
147
  }
434
148
 
435
149
  startExecution(planId: string): Plan {
436
- const plan = this.get(planId);
437
- if (!plan) throw new Error(`Plan not found: ${planId}`);
150
+ const plan = this.requirePlan(planId);
438
151
  this.transition(plan, 'executing');
439
152
  this.save();
440
153
  return plan;
441
154
  }
442
155
 
443
- updateTask(planId: string, taskId: string, status: TaskStatus): Plan {
444
- const plan = this.get(planId);
445
- if (!plan) throw new Error(`Plan not found: ${planId}`);
446
- if (plan.status !== 'executing' && plan.status !== 'validating')
447
- throw new Error(
448
- `Cannot update tasks on plan in '${plan.status}' status — must be 'executing' or 'validating'`,
449
- );
450
- const task = plan.tasks.find((t) => t.id === taskId);
451
- if (!task) throw new Error(`Task not found: ${taskId}`);
452
-
453
- const now = Date.now();
454
-
455
- // Auto-set startedAt on first in_progress transition
456
- if (status === 'in_progress' && !task.startedAt) {
457
- task.startedAt = now;
458
- }
459
-
460
- // Auto-set completedAt and compute durationMs on terminal transitions
461
- if (status === 'completed' || status === 'skipped' || status === 'failed') {
462
- task.completedAt = now;
463
- if (task.startedAt) {
464
- if (!task.metrics) task.metrics = {};
465
- task.metrics.durationMs = now - task.startedAt;
466
- }
467
- }
156
+ startValidation(planId: string): Plan {
157
+ const plan = this.requirePlan(planId);
158
+ this.transition(plan, 'validating');
159
+ this.save();
160
+ return plan;
161
+ }
468
162
 
469
- task.status = status;
470
- task.updatedAt = now;
471
- plan.updatedAt = now;
163
+ startReconciliation(planId: string): Plan {
164
+ const plan = this.requirePlan(planId);
165
+ this.transition(plan, 'reconciling');
472
166
  this.save();
473
167
  return plan;
474
168
  }
475
169
 
476
- /**
477
- * Transition plan to 'validating' state (post-execution verification).
478
- * Only allowed from 'executing'.
479
- */
480
- startValidation(planId: string): Plan {
481
- const plan = this.get(planId);
482
- if (!plan) throw new Error(`Plan not found: ${planId}`);
483
- this.transition(plan, 'validating');
170
+ updateTask(planId: string, taskId: string, status: TaskStatus): Plan {
171
+ const plan = this.requirePlan(planId);
172
+ if (plan.status !== 'executing' && plan.status !== 'validating')
173
+ throw new Error(
174
+ `Cannot update tasks on plan in '${plan.status}' status — must be 'executing' or 'validating'`,
175
+ );
176
+ applyTaskStatusUpdate(this.requireTask(plan, taskId), status);
177
+ plan.updatedAt = Date.now();
484
178
  this.save();
485
179
  return plan;
486
180
  }
487
181
 
488
- /**
489
- * Transition plan to 'reconciling' state.
490
- * Allowed from 'executing' or 'validating'.
491
- */
492
- startReconciliation(planId: string): Plan {
493
- const plan = this.get(planId);
494
- if (!plan) throw new Error(`Plan not found: ${planId}`);
495
- this.transition(plan, 'reconciling');
182
+ reconcile(planId: string, report: ReconcileInput): Plan {
183
+ const plan = this.requirePlan(planId);
184
+ if (
185
+ plan.status !== 'executing' &&
186
+ plan.status !== 'validating' &&
187
+ plan.status !== 'reconciling'
188
+ )
189
+ throw new Error(
190
+ `Cannot reconcile plan in '${plan.status}' status — must be 'executing', 'validating', or 'reconciling'`,
191
+ );
192
+ plan.reconciliation = buildReconciliationReport(planId, report);
193
+ plan.executionSummary = computeExecutionSummary(plan.tasks);
194
+ if (plan.status === 'executing' || plan.status === 'validating') plan.status = 'reconciling';
195
+ plan.status = 'completed';
196
+ plan.updatedAt = Date.now();
496
197
  this.save();
497
198
  return plan;
498
199
  }
499
200
 
500
- /**
501
- * Complete a plan. Only allowed from 'reconciling'.
502
- * Use startReconciliation() + reconcile() + complete() for the full lifecycle,
503
- * or reconcile() which auto-transitions through reconciling → completed.
504
- */
505
201
  complete(planId: string): Plan {
506
- const plan = this.get(planId);
507
- if (!plan) throw new Error(`Plan not found: ${planId}`);
508
- plan.executionSummary = this.computeExecutionSummary(plan);
202
+ const plan = this.requirePlan(planId);
203
+ if (plan.status === 'executing' || plan.status === 'validating')
204
+ return this.reconcile(planId, { actualOutcome: 'All tasks completed', reconciledBy: 'auto' });
205
+ plan.executionSummary = computeExecutionSummary(plan.tasks);
509
206
  this.transition(plan, 'completed');
510
207
  this.save();
511
208
  return plan;
512
209
  }
513
210
 
211
+ autoReconcile(planId: string): Plan | null {
212
+ const plan = this.requirePlan(planId);
213
+ if (plan.status !== 'executing' && plan.status !== 'validating')
214
+ throw new Error(
215
+ `Cannot auto-reconcile plan in '${plan.status}' status — must be 'executing' or 'validating'`,
216
+ );
217
+ const result = buildAutoReconcileInput(plan.tasks);
218
+ if (!result.canAutoReconcile || !result.input) return null;
219
+ return this.reconcile(planId, result.input);
220
+ }
221
+
514
222
  getExecuting(): Plan[] {
515
223
  return this.store.plans.filter((p) => p.status === 'executing' || p.status === 'validating');
516
224
  }
@@ -527,77 +235,17 @@ export class Planner {
527
235
  );
528
236
  }
529
237
 
530
- /**
531
- * Iterate on a draft plan modify objective, scope, decisions, or tasks.
532
- * Only allowed on plans in 'draft' status.
533
- */
534
- iterate(
535
- planId: string,
536
- changes: {
537
- objective?: string;
538
- scope?: string;
539
- decisions?: (string | PlanDecision)[];
540
- addTasks?: Array<{ title: string; description: string }>;
541
- removeTasks?: string[];
542
- approach?: string;
543
- context?: string;
544
- success_criteria?: string[];
545
- tool_chain?: string[];
546
- flow?: string;
547
- target_mode?: string;
548
- },
549
- ): Plan {
550
- const plan = this.get(planId);
551
- if (!plan) throw new Error(`Plan not found: ${planId}`);
238
+ iterate(planId: string, changes: IterateChanges): Plan {
239
+ const plan = this.requirePlan(planId);
552
240
  if (plan.status !== 'draft' && plan.status !== 'brainstorming')
553
241
  throw new Error(
554
242
  `Cannot iterate plan in '${plan.status}' status — must be 'draft' or 'brainstorming'`,
555
243
  );
556
-
557
- const now = Date.now();
558
- if (changes.objective !== undefined) plan.objective = changes.objective;
559
- if (changes.scope !== undefined) plan.scope = changes.scope;
560
- if (changes.decisions !== undefined) plan.decisions = changes.decisions;
561
- if (changes.approach !== undefined) plan.approach = changes.approach;
562
- if (changes.context !== undefined) plan.context = changes.context;
563
- if (changes.success_criteria !== undefined) plan.success_criteria = changes.success_criteria;
564
- if (changes.tool_chain !== undefined) plan.tool_chain = changes.tool_chain;
565
- if (changes.flow !== undefined) plan.flow = changes.flow;
566
- if (changes.target_mode !== undefined) plan.target_mode = changes.target_mode;
567
-
568
- // Remove tasks by ID
569
- if (changes.removeTasks && changes.removeTasks.length > 0) {
570
- const removeSet = new Set(changes.removeTasks);
571
- plan.tasks = plan.tasks.filter((t) => !removeSet.has(t.id));
572
- }
573
-
574
- // Add new tasks
575
- if (changes.addTasks && changes.addTasks.length > 0) {
576
- const maxIndex = plan.tasks.reduce((max, t) => {
577
- const num = parseInt(t.id.replace('task-', ''), 10);
578
- return isNaN(num) ? max : Math.max(max, num);
579
- }, 0);
580
- for (let i = 0; i < changes.addTasks.length; i++) {
581
- plan.tasks.push({
582
- id: `task-${maxIndex + i + 1}`,
583
- title: changes.addTasks[i].title,
584
- description: changes.addTasks[i].description,
585
- status: 'pending',
586
- updatedAt: now,
587
- });
588
- }
589
- }
590
-
591
- plan.updatedAt = now;
244
+ applyIteration(plan, changes);
592
245
  this.save();
593
246
  return plan;
594
247
  }
595
248
 
596
- /**
597
- * Split a plan's tasks into sub-tasks with dependency tracking.
598
- * Replaces existing tasks with a new set that includes dependency references.
599
- * Only allowed on 'draft' or 'approved' plans.
600
- */
601
249
  splitTasks(
602
250
  planId: string,
603
251
  tasks: Array<{
@@ -607,98 +255,16 @@ export class Planner {
607
255
  acceptanceCriteria?: string[];
608
256
  }>,
609
257
  ): Plan {
610
- const plan = this.get(planId);
611
- if (!plan) throw new Error(`Plan not found: ${planId}`);
258
+ const plan = this.requirePlan(planId);
612
259
  if (plan.status !== 'brainstorming' && plan.status !== 'draft' && plan.status !== 'approved')
613
260
  throw new Error(
614
261
  `Cannot split tasks on plan in '${plan.status}' status — must be 'brainstorming', 'draft', or 'approved'`,
615
262
  );
616
-
617
- const now = Date.now();
618
- plan.tasks = tasks.map((t, i) => ({
619
- id: `task-${i + 1}`,
620
- title: t.title,
621
- description: t.description,
622
- status: 'pending' as TaskStatus,
623
- dependsOn: t.dependsOn,
624
- ...(t.acceptanceCriteria && { acceptanceCriteria: t.acceptanceCriteria }),
625
- updatedAt: now,
626
- }));
627
-
628
- // Validate dependency references
629
- const taskIds = new Set(plan.tasks.map((t) => t.id));
630
- for (const task of plan.tasks) {
631
- if (task.dependsOn) {
632
- for (const dep of task.dependsOn) {
633
- if (!taskIds.has(dep)) {
634
- throw new Error(`Task '${task.id}' depends on unknown task '${dep}'`);
635
- }
636
- }
637
- }
638
- }
639
-
640
- plan.updatedAt = now;
263
+ applySplitTasks(plan, tasks);
641
264
  this.save();
642
265
  return plan;
643
266
  }
644
267
 
645
- /**
646
- * Reconcile a plan — compare what was planned vs what actually happened.
647
- * Uses impact-weighted drift scoring (ported from Salvador's calculateDriftScore).
648
- *
649
- * Transitions: executing → reconciling → completed (automatic).
650
- * Also allowed from 'validating' and 'reconciling' states.
651
- */
652
- reconcile(
653
- planId: string,
654
- report: {
655
- actualOutcome: string;
656
- driftItems?: DriftItem[];
657
- /** Who initiated the reconciliation. */
658
- reconciledBy?: 'human' | 'auto';
659
- },
660
- ): Plan {
661
- const plan = this.get(planId);
662
- if (!plan) throw new Error(`Plan not found: ${planId}`);
663
- if (
664
- plan.status !== 'executing' &&
665
- plan.status !== 'validating' &&
666
- plan.status !== 'reconciling'
667
- )
668
- throw new Error(
669
- `Cannot reconcile plan in '${plan.status}' status — must be 'executing', 'validating', or 'reconciling'`,
670
- );
671
-
672
- const driftItems = report.driftItems ?? [];
673
-
674
- // Impact-weighted drift scoring (ported from Salvador)
675
- const accuracy = calculateDriftScore(driftItems);
676
-
677
- plan.reconciliation = {
678
- planId,
679
- accuracy,
680
- driftItems,
681
- summary: report.actualOutcome,
682
- reconciledAt: Date.now(),
683
- };
684
-
685
- // Compute execution summary from per-task metrics
686
- plan.executionSummary = this.computeExecutionSummary(plan);
687
-
688
- // Transition through reconciling → completed via FSM
689
- if (plan.status === 'executing' || plan.status === 'validating') {
690
- plan.status = 'reconciling';
691
- }
692
- // Auto-complete after reconciliation
693
- plan.status = 'completed';
694
- plan.updatedAt = Date.now();
695
- this.save();
696
- return plan;
697
- }
698
-
699
- /**
700
- * Add review evidence to a plan or specific task.
701
- */
702
268
  addReview(
703
269
  planId: string,
704
270
  review: {
@@ -708,14 +274,8 @@ export class Planner {
708
274
  comments: string;
709
275
  },
710
276
  ): Plan {
711
- const plan = this.get(planId);
712
- if (!plan) throw new Error(`Plan not found: ${planId}`);
713
-
714
- if (review.taskId) {
715
- const task = plan.tasks.find((t) => t.id === review.taskId);
716
- if (!task) throw new Error(`Task not found: ${review.taskId}`);
717
- }
718
-
277
+ const plan = this.requirePlan(planId);
278
+ if (review.taskId) this.requireTask(plan, review.taskId);
719
279
  if (!plan.reviews) plan.reviews = [];
720
280
  plan.reviews.push({
721
281
  planId,
@@ -725,16 +285,27 @@ export class Planner {
725
285
  comments: review.comments,
726
286
  reviewedAt: Date.now(),
727
287
  });
288
+ plan.updatedAt = Date.now();
289
+ this.save();
290
+ return plan;
291
+ }
728
292
 
293
+ setGitHubProjection(
294
+ planId: string,
295
+ projection: {
296
+ repo: string;
297
+ milestone?: number;
298
+ issues: Array<{ taskId: string; issueNumber: number }>;
299
+ projectedAt: number;
300
+ },
301
+ ): Plan {
302
+ const plan = this.requirePlan(planId);
303
+ plan.githubProjection = projection;
729
304
  plan.updatedAt = Date.now();
730
305
  this.save();
731
306
  return plan;
732
307
  }
733
308
 
734
- /**
735
- * Get dispatch instructions for a specific task — returns the task and its
736
- * unmet dependencies so a subagent knows what to work on and what to wait for.
737
- */
738
309
  getDispatch(
739
310
  planId: string,
740
311
  taskId: string,
@@ -744,210 +315,77 @@ export class Planner {
744
315
  ready: boolean;
745
316
  deliverableStatus?: { count: number; staleCount: number };
746
317
  } {
747
- const plan = this.get(planId);
748
- if (!plan) throw new Error(`Plan not found: ${planId}`);
749
- const task = plan.tasks.find((t) => t.id === taskId);
750
- if (!task) throw new Error(`Task not found: ${taskId}`);
751
-
752
- const unmetDependencies: PlanTask[] = [];
753
- if (task.dependsOn) {
754
- for (const depId of task.dependsOn) {
755
- const dep = plan.tasks.find((t) => t.id === depId);
756
- if (dep && dep.status !== 'completed') {
757
- unmetDependencies.push(dep);
758
- }
759
- }
318
+ const plan = this.requirePlan(planId);
319
+ const task = this.requireTask(plan, taskId);
320
+ const unmetDeps: PlanTask[] = [];
321
+ for (const depId of task.dependsOn ?? []) {
322
+ const dep = plan.tasks.find((t) => t.id === depId);
323
+ if (dep && dep.status !== 'completed') unmetDeps.push(dep);
760
324
  }
761
-
762
- const result: {
763
- task: PlanTask;
764
- unmetDependencies: PlanTask[];
765
- ready: boolean;
766
- deliverableStatus?: { count: number; staleCount: number };
767
- } = {
768
- task,
769
- unmetDependencies,
770
- ready: unmetDependencies.length === 0,
771
- };
772
-
773
- // Include deliverable status if deliverables exist
774
- if (task.deliverables && task.deliverables.length > 0) {
775
- result.deliverableStatus = {
776
- count: task.deliverables.length,
777
- staleCount: task.deliverables.filter((d) => d.stale).length,
778
- };
779
- }
780
-
781
- return result;
782
- }
783
-
784
- // ─── Execution Metrics & Deliverables ──────────────────────────
785
-
786
- /**
787
- * Compute aggregate execution summary from per-task metrics.
788
- * Called from reconcile() and complete() to populate plan.executionSummary.
789
- */
790
- private computeExecutionSummary(plan: Plan): ExecutionSummary {
791
- let totalDurationMs = 0;
792
- let tasksCompleted = 0;
793
- let tasksSkipped = 0;
794
- let tasksFailed = 0;
795
- let tasksWithDuration = 0;
796
-
797
- for (const task of plan.tasks) {
798
- if (task.status === 'completed') tasksCompleted++;
799
- else if (task.status === 'skipped') tasksSkipped++;
800
- else if (task.status === 'failed') tasksFailed++;
801
-
802
- if (task.metrics?.durationMs) {
803
- totalDurationMs += task.metrics.durationMs;
804
- tasksWithDuration++;
805
- }
806
- }
807
-
808
325
  return {
809
- totalDurationMs,
810
- tasksCompleted,
811
- tasksSkipped,
812
- tasksFailed,
813
- avgTaskDurationMs:
814
- tasksWithDuration > 0 ? Math.round(totalDurationMs / tasksWithDuration) : 0,
326
+ task,
327
+ unmetDependencies: unmetDeps,
328
+ ready: unmetDeps.length === 0,
329
+ ...(task.deliverables?.length && {
330
+ deliverableStatus: {
331
+ count: task.deliverables.length,
332
+ staleCount: task.deliverables.filter((d) => d.stale).length,
333
+ },
334
+ }),
815
335
  };
816
336
  }
817
337
 
818
- /**
819
- * Submit a deliverable for a task. Auto-computes SHA-256 hash for file deliverables.
820
- */
821
338
  submitDeliverable(
822
339
  planId: string,
823
340
  taskId: string,
824
- deliverable: { type: TaskDeliverable['type']; path: string; hash?: string },
341
+ deliverable: { type: 'file' | 'vault_entry' | 'url'; path: string; hash?: string },
825
342
  ): PlanTask {
826
- const plan = this.get(planId);
827
- if (!plan) throw new Error(`Plan not found: ${planId}`);
828
- const task = plan.tasks.find((t) => t.id === taskId);
829
- if (!task) throw new Error(`Task not found: ${taskId}`);
830
-
831
- const entry: TaskDeliverable = {
832
- type: deliverable.type,
833
- path: deliverable.path,
834
- };
835
-
836
- // Auto-compute hash for file deliverables
837
- if (deliverable.type === 'file' && !deliverable.hash) {
838
- try {
839
- if (existsSync(deliverable.path)) {
840
- const content = readFileSync(deliverable.path);
841
- entry.hash = createHash('sha256').update(content).digest('hex');
842
- }
843
- } catch {
844
- // Graceful degradation — skip hash if file can't be read
845
- }
846
- } else if (deliverable.hash) {
847
- entry.hash = deliverable.hash;
848
- }
849
-
343
+ const plan = this.requirePlan(planId);
344
+ const task = this.requireTask(plan, taskId);
850
345
  if (!task.deliverables) task.deliverables = [];
851
- task.deliverables.push(entry);
346
+ task.deliverables.push(createDeliverable(deliverable));
852
347
  task.updatedAt = Date.now();
853
348
  plan.updatedAt = Date.now();
854
349
  this.save();
855
350
  return task;
856
351
  }
857
352
 
858
- /**
859
- * Verify all deliverables for a task.
860
- * - file: checks existsSync + SHA-256 hash match
861
- * - vault_entry: checks vault.get(path) non-null (requires vault instance)
862
- * - url: skips (just records, no fetch)
863
- */
864
353
  verifyDeliverables(
865
354
  planId: string,
866
355
  taskId: string,
867
356
  vault?: { get(id: string): unknown | null },
868
- ): { verified: boolean; deliverables: TaskDeliverable[]; staleCount: number } {
869
- const plan = this.get(planId);
870
- if (!plan) throw new Error(`Plan not found: ${planId}`);
871
- const task = plan.tasks.find((t) => t.id === taskId);
872
- if (!task) throw new Error(`Task not found: ${taskId}`);
873
-
874
- const deliverables = task.deliverables ?? [];
875
- let staleCount = 0;
876
- const now = Date.now();
877
-
878
- for (const d of deliverables) {
879
- d.stale = false;
880
-
881
- if (d.type === 'file') {
882
- if (!existsSync(d.path)) {
883
- d.stale = true;
884
- staleCount++;
885
- } else if (d.hash) {
886
- try {
887
- const content = readFileSync(d.path);
888
- const currentHash = createHash('sha256').update(content).digest('hex');
889
- if (currentHash !== d.hash) {
890
- d.stale = true;
891
- staleCount++;
892
- }
893
- } catch {
894
- d.stale = true;
895
- staleCount++;
896
- }
897
- }
898
- d.verifiedAt = now;
899
- } else if (d.type === 'vault_entry') {
900
- if (vault) {
901
- const entry = vault.get(d.path);
902
- if (!entry) {
903
- d.stale = true;
904
- staleCount++;
905
- }
906
- }
907
- d.verifiedAt = now;
908
- }
909
- // url: skip — just record
910
- }
911
-
357
+ ): {
358
+ verified: boolean;
359
+ deliverables: import('./planner-types.js').TaskDeliverable[];
360
+ staleCount: number;
361
+ } {
362
+ const plan = this.requirePlan(planId);
363
+ const task = this.requireTask(plan, taskId);
364
+ const result = verifyDeliverablesLogic(task.deliverables ?? [], vault);
365
+ task.deliverables = result.deliverables;
912
366
  plan.updatedAt = Date.now();
913
367
  this.save();
914
-
915
- return { verified: staleCount === 0, deliverables, staleCount };
368
+ return result;
916
369
  }
917
370
 
918
- // ─── Evidence & Verification ────────────────────────────────────
919
-
920
- /**
921
- * Submit evidence for a task acceptance criterion.
922
- * Evidence is stored on the task and used by verifyTask() to check completeness.
923
- */
924
371
  submitEvidence(
925
372
  planId: string,
926
373
  taskId: string,
927
- evidence: { criterion: string; content: string; type: TaskEvidence['type'] },
374
+ evidence: {
375
+ criterion: string;
376
+ content: string;
377
+ type: import('./planner-types.js').TaskEvidence['type'];
378
+ },
928
379
  ): PlanTask {
929
- const plan = this.get(planId);
930
- if (!plan) throw new Error(`Plan not found: ${planId}`);
931
- const task = plan.tasks.find((t) => t.id === taskId);
932
- if (!task) throw new Error(`Task not found: ${taskId}`);
933
- if (!task.evidence) task.evidence = [];
934
- task.evidence.push({
935
- criterion: evidence.criterion,
936
- content: evidence.content,
937
- type: evidence.type,
938
- submittedAt: Date.now(),
939
- });
380
+ const plan = this.requirePlan(planId);
381
+ const task = this.requireTask(plan, taskId);
382
+ task.evidence = createEvidence(task.evidence ?? [], evidence);
940
383
  task.updatedAt = Date.now();
941
384
  plan.updatedAt = Date.now();
942
385
  this.save();
943
386
  return task;
944
387
  }
945
388
 
946
- /**
947
- * Verify a task — check that evidence exists for all acceptance criteria
948
- * and any reviews have passed.
949
- * Returns verification status with details.
950
- */
951
389
  verifyTask(
952
390
  planId: string,
953
391
  taskId: string,
@@ -957,270 +395,45 @@ export class Planner {
957
395
  missingCriteria: string[];
958
396
  reviewStatus: 'approved' | 'rejected' | 'needs_changes' | 'no_reviews';
959
397
  } {
960
- const plan = this.get(planId);
961
- if (!plan) throw new Error(`Plan not found: ${planId}`);
962
- const task = plan.tasks.find((t) => t.id === taskId);
963
- if (!task) throw new Error(`Task not found: ${taskId}`);
964
-
965
- // Check evidence coverage
966
- const criteria = task.acceptanceCriteria ?? [];
967
- const evidencedCriteria = new Set((task.evidence ?? []).map((e) => e.criterion));
968
- const missingCriteria = criteria.filter((c) => !evidencedCriteria.has(c));
969
-
970
- // Check task-level reviews
971
- const taskReviews = (plan.reviews ?? []).filter((r) => r.taskId === taskId);
972
- let reviewStatus: 'approved' | 'rejected' | 'needs_changes' | 'no_reviews' = 'no_reviews';
973
- if (taskReviews.length > 0) {
974
- const latest = taskReviews[taskReviews.length - 1];
975
- reviewStatus = latest.outcome;
976
- }
977
-
978
- const verified =
979
- task.status === 'completed' &&
980
- missingCriteria.length === 0 &&
981
- (reviewStatus === 'approved' || reviewStatus === 'no_reviews');
982
-
983
- if (verified !== task.verified) {
984
- task.verified = verified;
398
+ const plan = this.requirePlan(planId);
399
+ const task = this.requireTask(plan, taskId);
400
+ const result = verifyTaskLogic(task, plan.reviews ?? []);
401
+ if (result.verified !== task.verified) {
402
+ task.verified = result.verified;
985
403
  task.updatedAt = Date.now();
986
404
  plan.updatedAt = Date.now();
987
405
  this.save();
988
406
  }
989
-
990
- return { verified, task, missingCriteria, reviewStatus };
991
- }
992
-
993
- /**
994
- * Verify an entire plan — check all tasks are in a final state,
995
- * all verification-required tasks have evidence, no tasks stuck in_progress.
996
- * Returns a validation report.
997
- */
998
- verifyPlan(planId: string): {
999
- valid: boolean;
1000
- planId: string;
1001
- issues: Array<{ taskId: string; issue: string }>;
1002
- summary: {
1003
- total: number;
1004
- completed: number;
1005
- skipped: number;
1006
- failed: number;
1007
- pending: number;
1008
- inProgress: number;
1009
- verified: number;
1010
- };
1011
- } {
1012
- const plan = this.get(planId);
1013
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1014
-
1015
- const issues: Array<{ taskId: string; issue: string }> = [];
1016
- let verified = 0;
1017
- let completed = 0;
1018
- let skipped = 0;
1019
- let failed = 0;
1020
- let pending = 0;
1021
- let inProgress = 0;
1022
-
1023
- for (const task of plan.tasks) {
1024
- switch (task.status) {
1025
- case 'completed':
1026
- completed++;
1027
- break;
1028
- case 'skipped':
1029
- skipped++;
1030
- break;
1031
- case 'failed':
1032
- failed++;
1033
- break;
1034
- case 'pending':
1035
- pending++;
1036
- break;
1037
- case 'in_progress':
1038
- inProgress++;
1039
- break;
1040
- }
1041
-
1042
- if (task.verified) verified++;
1043
-
1044
- // Check for stuck tasks
1045
- if (task.status === 'in_progress') {
1046
- issues.push({ taskId: task.id, issue: 'Task stuck in in_progress state' });
1047
- }
1048
- if (task.status === 'pending') {
1049
- issues.push({ taskId: task.id, issue: 'Task still pending — not started' });
1050
- }
1051
-
1052
- // Check evidence for completed tasks with acceptance criteria
1053
- if (
1054
- task.status === 'completed' &&
1055
- task.acceptanceCriteria &&
1056
- task.acceptanceCriteria.length > 0
1057
- ) {
1058
- const evidencedCriteria = new Set((task.evidence ?? []).map((e) => e.criterion));
1059
- const missing = task.acceptanceCriteria.filter((c) => !evidencedCriteria.has(c));
1060
- if (missing.length > 0) {
1061
- issues.push({
1062
- taskId: task.id,
1063
- issue: `Missing evidence for ${missing.length} criteria: ${missing.join(', ')}`,
1064
- });
1065
- }
1066
- }
1067
- }
1068
-
1069
- const valid = issues.length === 0 && pending === 0 && inProgress === 0;
1070
-
1071
- return {
1072
- valid,
1073
- planId,
1074
- issues,
1075
- summary: {
1076
- total: plan.tasks.length,
1077
- completed,
1078
- skipped,
1079
- failed,
1080
- pending,
1081
- inProgress,
1082
- verified,
1083
- },
1084
- };
407
+ return { ...result, task };
1085
408
  }
1086
409
 
1087
- /**
1088
- * Auto-reconcile a plan — fast path for plans with minimal drift.
1089
- * Checks all tasks are in final state, generates reconciliation report automatically.
1090
- * Returns null if drift is too significant for auto-reconciliation (>2 non-completed tasks).
1091
- */
1092
- autoReconcile(planId: string): Plan | null {
1093
- const plan = this.get(planId);
1094
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1095
- if (plan.status !== 'executing' && plan.status !== 'validating')
1096
- throw new Error(
1097
- `Cannot auto-reconcile plan in '${plan.status}' status — must be 'executing' or 'validating'`,
1098
- );
1099
-
1100
- const completed = plan.tasks.filter((t) => t.status === 'completed').length;
1101
- const skipped = plan.tasks.filter((t) => t.status === 'skipped').length;
1102
- const failed = plan.tasks.filter((t) => t.status === 'failed').length;
1103
- const pending = plan.tasks.filter((t) => t.status === 'pending').length;
1104
- const inProgress = plan.tasks.filter((t) => t.status === 'in_progress').length;
1105
-
1106
- // Can't auto-reconcile if tasks are still in progress
1107
- if (inProgress > 0) return null;
1108
- // Can't auto-reconcile if too many non-completed tasks
1109
- if (pending + failed > 2) return null;
1110
-
1111
- const driftItems: DriftItem[] = [];
1112
-
1113
- for (const task of plan.tasks) {
1114
- if (task.status === 'skipped') {
1115
- driftItems.push({
1116
- type: 'skipped',
1117
- description: `Task '${task.title}' was skipped`,
1118
- impact: 'medium',
1119
- rationale: 'Task not executed during plan implementation',
1120
- });
1121
- } else if (task.status === 'failed') {
1122
- driftItems.push({
1123
- type: 'modified',
1124
- description: `Task '${task.title}' failed`,
1125
- impact: 'high',
1126
- rationale: 'Task execution failed',
1127
- });
1128
- } else if (task.status === 'pending') {
1129
- driftItems.push({
1130
- type: 'skipped',
1131
- description: `Task '${task.title}' was never started`,
1132
- impact: 'low',
1133
- rationale: 'Task left in pending state',
1134
- });
1135
- }
1136
- }
1137
-
1138
- return this.reconcile(planId, {
1139
- actualOutcome: `Auto-reconciled: ${completed}/${plan.tasks.length} tasks completed, ${skipped} skipped, ${failed} failed`,
1140
- driftItems,
1141
- reconciledBy: 'auto',
1142
- });
410
+ verifyPlan(planId: string) {
411
+ return verifyPlanLogic(planId, this.requirePlan(planId).tasks);
1143
412
  }
1144
413
 
1145
- /**
1146
- * Generate a review prompt for spec compliance checking.
1147
- * Used by subagent dispatch — the controller generates the prompt, a subagent executes it.
1148
- */
1149
414
  generateReviewSpec(
1150
415
  planId: string,
1151
416
  taskId: string,
1152
417
  ): { prompt: string; task: PlanTask; plan: Plan } {
1153
- const plan = this.get(planId);
1154
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1155
- const task = plan.tasks.find((t) => t.id === taskId);
1156
- if (!task) throw new Error(`Task not found: ${taskId}`);
1157
-
1158
- const criteria = task.acceptanceCriteria?.length
1159
- ? `\n\nAcceptance Criteria:\n${task.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`
1160
- : '';
1161
-
1162
- const prompt = [
1163
- `# Spec Compliance Review`,
1164
- ``,
1165
- `## Task: ${task.title}`,
1166
- `**Description:** ${task.description}`,
1167
- `**Plan Objective:** ${plan.objective}${criteria}`,
1168
- ``,
1169
- `## Review Checklist`,
1170
- `1. Does the implementation match the task description?`,
1171
- `2. Are all acceptance criteria satisfied?`,
1172
- `3. Does it align with the plan's overall objective?`,
1173
- `4. Are there any spec deviations?`,
1174
- ``,
1175
- `Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
1176
- ].join('\n');
1177
-
1178
- return { prompt, task, plan };
418
+ const plan = this.requirePlan(planId);
419
+ const task = this.requireTask(plan, taskId);
420
+ return { prompt: buildSpecReviewPrompt(task, plan.objective), task, plan };
1179
421
  }
1180
422
 
1181
- /**
1182
- * Generate a review prompt for code quality checking.
1183
- */
1184
423
  generateReviewQuality(
1185
424
  planId: string,
1186
425
  taskId: string,
1187
426
  ): { prompt: string; task: PlanTask; plan: Plan } {
1188
- const plan = this.get(planId);
1189
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1190
- const task = plan.tasks.find((t) => t.id === taskId);
1191
- if (!task) throw new Error(`Task not found: ${taskId}`);
1192
-
1193
- const prompt = [
1194
- `# Code Quality Review`,
1195
- ``,
1196
- `## Task: ${task.title}`,
1197
- `**Description:** ${task.description}`,
1198
- ``,
1199
- `## Quality Checklist`,
1200
- `1. **Correctness** — Does it work as intended?`,
1201
- `2. **Security** — No injection, XSS, or OWASP top 10 vulnerabilities?`,
1202
- `3. **Performance** — No unnecessary allocations, N+1 queries, or blocking calls?`,
1203
- `4. **Maintainability** — Clear naming, appropriate abstractions, documented intent?`,
1204
- `5. **Testing** — Adequate test coverage for the changes?`,
1205
- `6. **Error Handling** — Graceful degradation, no swallowed errors?`,
1206
- `7. **Conventions** — Follows project coding standards?`,
1207
- ``,
1208
- `Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
1209
- ].join('\n');
1210
-
1211
- return { prompt, task, plan };
427
+ const plan = this.requirePlan(planId);
428
+ const task = this.requireTask(plan, taskId);
429
+ return { prompt: buildQualityReviewPrompt(task), task, plan };
1212
430
  }
1213
431
 
1214
- /**
1215
- * Archive completed plans — transitions them to 'archived' status.
1216
- * If olderThanDays is provided, only archives plans older than that.
1217
- * Returns the archived plans.
1218
- */
1219
432
  archive(olderThanDays?: number): Plan[] {
1220
433
  const cutoff =
1221
434
  olderThanDays !== undefined
1222
435
  ? Date.now() - olderThanDays * 24 * 60 * 60 * 1000
1223
- : Date.now() + 1; // +1ms so archive() with no args archives all completed plans
436
+ : Date.now() + 1;
1224
437
  const toArchive = this.store.plans.filter(
1225
438
  (p) => p.status === 'completed' && p.updatedAt < cutoff,
1226
439
  );
@@ -1228,15 +441,10 @@ export class Planner {
1228
441
  plan.status = 'archived';
1229
442
  plan.updatedAt = Date.now();
1230
443
  }
1231
- if (toArchive.length > 0) {
1232
- this.save();
1233
- }
444
+ if (toArchive.length > 0) this.save();
1234
445
  return toArchive;
1235
446
  }
1236
447
 
1237
- /**
1238
- * Get statistics about all plans.
1239
- */
1240
448
  stats(): {
1241
449
  total: number;
1242
450
  byStatus: Record<PlanStatus, number>;
@@ -1245,7 +453,7 @@ export class Planner {
1245
453
  tasksByStatus: Record<TaskStatus, number>;
1246
454
  } {
1247
455
  const plans = this.store.plans;
1248
- const byStatus: Record<PlanStatus, number> = {
456
+ const byStatus = {
1249
457
  brainstorming: 0,
1250
458
  draft: 0,
1251
459
  approved: 0,
@@ -1254,58 +462,33 @@ export class Planner {
1254
462
  reconciling: 0,
1255
463
  completed: 0,
1256
464
  archived: 0,
1257
- };
1258
- const tasksByStatus: Record<TaskStatus, number> = {
465
+ } as Record<PlanStatus, number>;
466
+ const tasksByStatus = {
1259
467
  pending: 0,
1260
468
  in_progress: 0,
1261
469
  completed: 0,
1262
470
  skipped: 0,
1263
471
  failed: 0,
1264
- };
472
+ } as Record<TaskStatus, number>;
1265
473
  let totalTasks = 0;
1266
-
1267
474
  for (const p of plans) {
1268
475
  byStatus[p.status]++;
1269
476
  totalTasks += p.tasks.length;
1270
- for (const t of p.tasks) {
1271
- tasksByStatus[t.status]++;
1272
- }
477
+ for (const t of p.tasks) tasksByStatus[t.status]++;
1273
478
  }
1274
-
1275
479
  return {
1276
480
  total: plans.length,
1277
481
  byStatus,
1278
- avgTasksPerPlan: plans.length > 0 ? Math.round((totalTasks / plans.length) * 100) / 100 : 0,
1279
482
  totalTasks,
1280
483
  tasksByStatus,
484
+ avgTasksPerPlan: plans.length > 0 ? Math.round((totalTasks / plans.length) * 100) / 100 : 0,
1281
485
  };
1282
486
  }
1283
487
 
1284
- // ─── Grading ──────────────────────────────────────────────────────
1285
-
1286
- /**
1287
- * Grade a plan using gap analysis with severity-weighted scoring.
1288
- * Ported from Salvador MCP's multi-pass grading engine.
1289
- *
1290
- * 6 built-in passes + optional custom passes (domain-specific checks).
1291
- *
1292
- * Scoring:
1293
- * - Each gap has a severity (critical=30, major=15, minor=2, info=0)
1294
- * - Deductions are per-category with optional caps
1295
- * - Iteration leniency: minor gaps free on iter 1, half on iter 2, full on 3+
1296
- * - Score = max(0, 100 - deductions)
1297
- *
1298
- * Grade thresholds: A+=95, A=90, B=80, C=70, D=60, F=<60
1299
- */
1300
488
  grade(planId: string): PlanCheck {
1301
- const plan = this.get(planId);
1302
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1303
-
1304
- // Run 6-pass gap analysis
489
+ const plan = this.requirePlan(planId);
1305
490
  const gaps = runGapAnalysis(plan, this.gapOptions);
1306
-
1307
- // Add circular dependency check (structural, not covered by gap-analysis passes)
1308
- if (this.hasCircularDependencies(plan)) {
491
+ if (hasCircularDependencies(plan.tasks)) {
1309
492
  gaps.push({
1310
493
  id: `gap_${Date.now()}_circ`,
1311
494
  severity: 'critical',
@@ -1316,108 +499,34 @@ export class Planner {
1316
499
  _trigger: 'circular_dependencies',
1317
500
  });
1318
501
  }
1319
-
1320
- // Iteration = number of previous checks + 1
1321
502
  const iteration = plan.checks.length + 1;
1322
503
  const score = calculateScore(gaps, iteration);
1323
- const grade = this.scoreToGrade(score);
1324
-
1325
504
  const check: PlanCheck = {
1326
505
  checkId: `chk-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1327
506
  planId,
1328
- grade,
507
+ grade: scoreToGrade(score),
1329
508
  score,
1330
509
  gaps,
1331
510
  iteration,
1332
511
  checkedAt: Date.now(),
1333
512
  };
1334
-
1335
513
  plan.checks.push(check);
1336
514
  plan.latestCheck = check;
1337
515
  plan.updatedAt = Date.now();
1338
516
  this.save();
1339
-
1340
517
  return check;
1341
518
  }
1342
519
 
1343
- /**
1344
- * Get the latest check for a plan.
1345
- */
1346
520
  getLatestCheck(planId: string): PlanCheck | null {
1347
- const plan = this.get(planId);
1348
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1349
- return plan.latestCheck ?? null;
521
+ return this.requirePlan(planId).latestCheck ?? null;
1350
522
  }
1351
523
 
1352
- /**
1353
- * Get all checks for a plan (history).
1354
- */
1355
524
  getCheckHistory(planId: string): PlanCheck[] {
1356
- const plan = this.get(planId);
1357
- if (!plan) throw new Error(`Plan not found: ${planId}`);
1358
- return [...plan.checks];
525
+ return [...this.requirePlan(planId).checks];
1359
526
  }
1360
527
 
1361
- /**
1362
- * Auto-grade: grade the plan and return whether it meets a target grade.
1363
- */
1364
528
  meetsGrade(planId: string, targetGrade: PlanGrade): { meets: boolean; check: PlanCheck } {
1365
529
  const check = this.grade(planId);
1366
- const targetScore = this.gradeToMinScore(targetGrade);
1367
- return { meets: check.score >= targetScore, check };
1368
- }
1369
-
1370
- // ─── Grading Helpers ──────────────────────────────────────────────
1371
-
1372
- private scoreToGrade(score: number): PlanGrade {
1373
- if (score >= 95) return 'A+';
1374
- if (score >= 90) return 'A';
1375
- if (score >= 80) return 'B';
1376
- if (score >= 70) return 'C';
1377
- if (score >= 60) return 'D';
1378
- return 'F';
1379
- }
1380
-
1381
- private gradeToMinScore(grade: PlanGrade): number {
1382
- switch (grade) {
1383
- case 'A+':
1384
- return 95;
1385
- case 'A':
1386
- return 90;
1387
- case 'B':
1388
- return 80;
1389
- case 'C':
1390
- return 70;
1391
- case 'D':
1392
- return 60;
1393
- case 'F':
1394
- return 0;
1395
- }
1396
- }
1397
-
1398
- private hasCircularDependencies(plan: Plan): boolean {
1399
- const visited = new Set<string>();
1400
- const inStack = new Set<string>();
1401
- const taskMap = new Map(plan.tasks.map((t) => [t.id, t]));
1402
-
1403
- const dfs = (taskId: string): boolean => {
1404
- if (inStack.has(taskId)) return true;
1405
- if (visited.has(taskId)) return false;
1406
- visited.add(taskId);
1407
- inStack.add(taskId);
1408
- const task = taskMap.get(taskId);
1409
- if (task?.dependsOn) {
1410
- for (const dep of task.dependsOn) {
1411
- if (dfs(dep)) return true;
1412
- }
1413
- }
1414
- inStack.delete(taskId);
1415
- return false;
1416
- };
1417
-
1418
- for (const task of plan.tasks) {
1419
- if (dfs(task.id)) return true;
1420
- }
1421
- return false;
530
+ return { meets: check.score >= gradeToMinScore(targetGrade), check };
1422
531
  }
1423
532
  }