@soleri/core 2.4.0 → 2.6.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 (328) hide show
  1. package/dist/brain/brain.d.ts +7 -0
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +56 -9
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/intelligence.d.ts +1 -0
  6. package/dist/brain/intelligence.d.ts.map +1 -1
  7. package/dist/brain/intelligence.js +164 -148
  8. package/dist/brain/intelligence.js.map +1 -1
  9. package/dist/brain/types.d.ts +2 -2
  10. package/dist/brain/types.d.ts.map +1 -1
  11. package/dist/cognee/client.d.ts +3 -0
  12. package/dist/cognee/client.d.ts.map +1 -1
  13. package/dist/cognee/client.js +17 -0
  14. package/dist/cognee/client.js.map +1 -1
  15. package/dist/cognee/sync-manager.d.ts +94 -0
  16. package/dist/cognee/sync-manager.d.ts.map +1 -0
  17. package/dist/cognee/sync-manager.js +293 -0
  18. package/dist/cognee/sync-manager.js.map +1 -0
  19. package/dist/control/identity-manager.d.ts +3 -1
  20. package/dist/control/identity-manager.d.ts.map +1 -1
  21. package/dist/control/identity-manager.js +49 -51
  22. package/dist/control/identity-manager.js.map +1 -1
  23. package/dist/control/intent-router.d.ts +1 -0
  24. package/dist/control/intent-router.d.ts.map +1 -1
  25. package/dist/control/intent-router.js +32 -32
  26. package/dist/control/intent-router.js.map +1 -1
  27. package/dist/curator/curator.d.ts +9 -1
  28. package/dist/curator/curator.d.ts.map +1 -1
  29. package/dist/curator/curator.js +104 -92
  30. package/dist/curator/curator.js.map +1 -1
  31. package/dist/errors/classify.d.ts +13 -0
  32. package/dist/errors/classify.d.ts.map +1 -0
  33. package/dist/errors/classify.js +97 -0
  34. package/dist/errors/classify.js.map +1 -0
  35. package/dist/errors/index.d.ts +6 -0
  36. package/dist/errors/index.d.ts.map +1 -0
  37. package/dist/errors/index.js +4 -0
  38. package/dist/errors/index.js.map +1 -0
  39. package/dist/errors/retry.d.ts +40 -0
  40. package/dist/errors/retry.d.ts.map +1 -0
  41. package/dist/errors/retry.js +97 -0
  42. package/dist/errors/retry.js.map +1 -0
  43. package/dist/errors/types.d.ts +48 -0
  44. package/dist/errors/types.d.ts.map +1 -0
  45. package/dist/errors/types.js +59 -0
  46. package/dist/errors/types.js.map +1 -0
  47. package/dist/governance/governance.d.ts +1 -0
  48. package/dist/governance/governance.d.ts.map +1 -1
  49. package/dist/governance/governance.js +51 -68
  50. package/dist/governance/governance.js.map +1 -1
  51. package/dist/index.d.ts +26 -5
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +22 -3
  54. package/dist/index.js.map +1 -1
  55. package/dist/intake/content-classifier.d.ts +14 -0
  56. package/dist/intake/content-classifier.d.ts.map +1 -0
  57. package/dist/intake/content-classifier.js +125 -0
  58. package/dist/intake/content-classifier.js.map +1 -0
  59. package/dist/intake/dedup-gate.d.ts +17 -0
  60. package/dist/intake/dedup-gate.d.ts.map +1 -0
  61. package/dist/intake/dedup-gate.js +66 -0
  62. package/dist/intake/dedup-gate.js.map +1 -0
  63. package/dist/intake/intake-pipeline.d.ts +63 -0
  64. package/dist/intake/intake-pipeline.d.ts.map +1 -0
  65. package/dist/intake/intake-pipeline.js +373 -0
  66. package/dist/intake/intake-pipeline.js.map +1 -0
  67. package/dist/intake/types.d.ts +65 -0
  68. package/dist/intake/types.d.ts.map +1 -0
  69. package/dist/intake/types.js +3 -0
  70. package/dist/intake/types.js.map +1 -0
  71. package/dist/intelligence/loader.js +1 -1
  72. package/dist/intelligence/loader.js.map +1 -1
  73. package/dist/intelligence/types.d.ts +3 -1
  74. package/dist/intelligence/types.d.ts.map +1 -1
  75. package/dist/loop/loop-manager.d.ts +58 -7
  76. package/dist/loop/loop-manager.d.ts.map +1 -1
  77. package/dist/loop/loop-manager.js +280 -6
  78. package/dist/loop/loop-manager.js.map +1 -1
  79. package/dist/loop/types.d.ts +69 -1
  80. package/dist/loop/types.d.ts.map +1 -1
  81. package/dist/loop/types.js +4 -1
  82. package/dist/loop/types.js.map +1 -1
  83. package/dist/persistence/index.d.ts +4 -0
  84. package/dist/persistence/index.d.ts.map +1 -0
  85. package/dist/persistence/index.js +3 -0
  86. package/dist/persistence/index.js.map +1 -0
  87. package/dist/persistence/postgres-provider.d.ts +46 -0
  88. package/dist/persistence/postgres-provider.d.ts.map +1 -0
  89. package/dist/persistence/postgres-provider.js +115 -0
  90. package/dist/persistence/postgres-provider.js.map +1 -0
  91. package/dist/persistence/sqlite-provider.d.ts +28 -0
  92. package/dist/persistence/sqlite-provider.d.ts.map +1 -0
  93. package/dist/persistence/sqlite-provider.js +97 -0
  94. package/dist/persistence/sqlite-provider.js.map +1 -0
  95. package/dist/persistence/types.d.ts +58 -0
  96. package/dist/persistence/types.d.ts.map +1 -0
  97. package/dist/persistence/types.js +8 -0
  98. package/dist/persistence/types.js.map +1 -0
  99. package/dist/planning/gap-analysis.d.ts +47 -4
  100. package/dist/planning/gap-analysis.d.ts.map +1 -1
  101. package/dist/planning/gap-analysis.js +190 -13
  102. package/dist/planning/gap-analysis.js.map +1 -1
  103. package/dist/planning/gap-types.d.ts +1 -1
  104. package/dist/planning/gap-types.d.ts.map +1 -1
  105. package/dist/planning/gap-types.js.map +1 -1
  106. package/dist/planning/planner.d.ts +277 -9
  107. package/dist/planning/planner.d.ts.map +1 -1
  108. package/dist/planning/planner.js +611 -46
  109. package/dist/planning/planner.js.map +1 -1
  110. package/dist/playbooks/generic/brainstorming.d.ts +9 -0
  111. package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
  112. package/dist/playbooks/generic/brainstorming.js +105 -0
  113. package/dist/playbooks/generic/brainstorming.js.map +1 -0
  114. package/dist/playbooks/generic/code-review.d.ts +11 -0
  115. package/dist/playbooks/generic/code-review.d.ts.map +1 -0
  116. package/dist/playbooks/generic/code-review.js +176 -0
  117. package/dist/playbooks/generic/code-review.js.map +1 -0
  118. package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
  119. package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
  120. package/dist/playbooks/generic/subagent-execution.js +68 -0
  121. package/dist/playbooks/generic/subagent-execution.js.map +1 -0
  122. package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
  123. package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
  124. package/dist/playbooks/generic/systematic-debugging.js +87 -0
  125. package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
  126. package/dist/playbooks/generic/tdd.d.ts +9 -0
  127. package/dist/playbooks/generic/tdd.d.ts.map +1 -0
  128. package/dist/playbooks/generic/tdd.js +70 -0
  129. package/dist/playbooks/generic/tdd.js.map +1 -0
  130. package/dist/playbooks/generic/verification.d.ts +9 -0
  131. package/dist/playbooks/generic/verification.d.ts.map +1 -0
  132. package/dist/playbooks/generic/verification.js +74 -0
  133. package/dist/playbooks/generic/verification.js.map +1 -0
  134. package/dist/playbooks/index.d.ts +4 -0
  135. package/dist/playbooks/index.d.ts.map +1 -0
  136. package/dist/playbooks/index.js +5 -0
  137. package/dist/playbooks/index.js.map +1 -0
  138. package/dist/playbooks/playbook-registry.d.ts +42 -0
  139. package/dist/playbooks/playbook-registry.d.ts.map +1 -0
  140. package/dist/playbooks/playbook-registry.js +227 -0
  141. package/dist/playbooks/playbook-registry.js.map +1 -0
  142. package/dist/playbooks/playbook-seeder.d.ts +47 -0
  143. package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
  144. package/dist/playbooks/playbook-seeder.js +104 -0
  145. package/dist/playbooks/playbook-seeder.js.map +1 -0
  146. package/dist/playbooks/playbook-types.d.ts +132 -0
  147. package/dist/playbooks/playbook-types.d.ts.map +1 -0
  148. package/dist/playbooks/playbook-types.js +12 -0
  149. package/dist/playbooks/playbook-types.js.map +1 -0
  150. package/dist/project/project-registry.d.ts +4 -4
  151. package/dist/project/project-registry.d.ts.map +1 -1
  152. package/dist/project/project-registry.js +30 -57
  153. package/dist/project/project-registry.js.map +1 -1
  154. package/dist/prompts/index.d.ts +4 -0
  155. package/dist/prompts/index.d.ts.map +1 -0
  156. package/dist/prompts/index.js +3 -0
  157. package/dist/prompts/index.js.map +1 -0
  158. package/dist/prompts/parser.d.ts +17 -0
  159. package/dist/prompts/parser.d.ts.map +1 -0
  160. package/dist/prompts/parser.js +47 -0
  161. package/dist/prompts/parser.js.map +1 -0
  162. package/dist/prompts/template-manager.d.ts +25 -0
  163. package/dist/prompts/template-manager.d.ts.map +1 -0
  164. package/dist/prompts/template-manager.js +71 -0
  165. package/dist/prompts/template-manager.js.map +1 -0
  166. package/dist/prompts/types.d.ts +26 -0
  167. package/dist/prompts/types.d.ts.map +1 -0
  168. package/dist/prompts/types.js +5 -0
  169. package/dist/prompts/types.js.map +1 -0
  170. package/dist/runtime/admin-extra-ops.d.ts +5 -3
  171. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  172. package/dist/runtime/admin-extra-ops.js +348 -11
  173. package/dist/runtime/admin-extra-ops.js.map +1 -1
  174. package/dist/runtime/admin-ops.d.ts.map +1 -1
  175. package/dist/runtime/admin-ops.js +10 -3
  176. package/dist/runtime/admin-ops.js.map +1 -1
  177. package/dist/runtime/capture-ops.d.ts.map +1 -1
  178. package/dist/runtime/capture-ops.js +20 -2
  179. package/dist/runtime/capture-ops.js.map +1 -1
  180. package/dist/runtime/cognee-sync-ops.d.ts +12 -0
  181. package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
  182. package/dist/runtime/cognee-sync-ops.js +55 -0
  183. package/dist/runtime/cognee-sync-ops.js.map +1 -0
  184. package/dist/runtime/core-ops.d.ts +8 -6
  185. package/dist/runtime/core-ops.d.ts.map +1 -1
  186. package/dist/runtime/core-ops.js +226 -9
  187. package/dist/runtime/core-ops.js.map +1 -1
  188. package/dist/runtime/curator-extra-ops.d.ts +2 -2
  189. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  190. package/dist/runtime/curator-extra-ops.js +15 -3
  191. package/dist/runtime/curator-extra-ops.js.map +1 -1
  192. package/dist/runtime/domain-ops.js +2 -2
  193. package/dist/runtime/domain-ops.js.map +1 -1
  194. package/dist/runtime/grading-ops.d.ts.map +1 -1
  195. package/dist/runtime/grading-ops.js.map +1 -1
  196. package/dist/runtime/intake-ops.d.ts +14 -0
  197. package/dist/runtime/intake-ops.d.ts.map +1 -0
  198. package/dist/runtime/intake-ops.js +110 -0
  199. package/dist/runtime/intake-ops.js.map +1 -0
  200. package/dist/runtime/loop-ops.d.ts +5 -4
  201. package/dist/runtime/loop-ops.d.ts.map +1 -1
  202. package/dist/runtime/loop-ops.js +84 -12
  203. package/dist/runtime/loop-ops.js.map +1 -1
  204. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
  205. package/dist/runtime/memory-cross-project-ops.js.map +1 -1
  206. package/dist/runtime/memory-extra-ops.js +5 -5
  207. package/dist/runtime/memory-extra-ops.js.map +1 -1
  208. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  209. package/dist/runtime/orchestrate-ops.js +8 -2
  210. package/dist/runtime/orchestrate-ops.js.map +1 -1
  211. package/dist/runtime/planning-extra-ops.d.ts +13 -5
  212. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  213. package/dist/runtime/planning-extra-ops.js +381 -18
  214. package/dist/runtime/planning-extra-ops.js.map +1 -1
  215. package/dist/runtime/playbook-ops.d.ts +14 -0
  216. package/dist/runtime/playbook-ops.d.ts.map +1 -0
  217. package/dist/runtime/playbook-ops.js +141 -0
  218. package/dist/runtime/playbook-ops.js.map +1 -0
  219. package/dist/runtime/project-ops.d.ts.map +1 -1
  220. package/dist/runtime/project-ops.js +7 -2
  221. package/dist/runtime/project-ops.js.map +1 -1
  222. package/dist/runtime/runtime.d.ts.map +1 -1
  223. package/dist/runtime/runtime.js +28 -9
  224. package/dist/runtime/runtime.js.map +1 -1
  225. package/dist/runtime/types.d.ts +8 -0
  226. package/dist/runtime/types.d.ts.map +1 -1
  227. package/dist/runtime/vault-extra-ops.d.ts +4 -2
  228. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  229. package/dist/runtime/vault-extra-ops.js +383 -4
  230. package/dist/runtime/vault-extra-ops.js.map +1 -1
  231. package/dist/vault/playbook.d.ts +34 -0
  232. package/dist/vault/playbook.d.ts.map +1 -0
  233. package/dist/vault/playbook.js +60 -0
  234. package/dist/vault/playbook.js.map +1 -0
  235. package/dist/vault/vault.d.ts +52 -32
  236. package/dist/vault/vault.d.ts.map +1 -1
  237. package/dist/vault/vault.js +300 -181
  238. package/dist/vault/vault.js.map +1 -1
  239. package/package.json +9 -3
  240. package/src/__tests__/admin-extra-ops.test.ts +62 -15
  241. package/src/__tests__/admin-ops.test.ts +2 -2
  242. package/src/__tests__/brain.test.ts +3 -3
  243. package/src/__tests__/cognee-integration.test.ts +80 -0
  244. package/src/__tests__/cognee-sync-manager.test.ts +103 -0
  245. package/src/__tests__/core-ops.test.ts +36 -4
  246. package/src/__tests__/curator-extra-ops.test.ts +24 -2
  247. package/src/__tests__/errors.test.ts +388 -0
  248. package/src/__tests__/grading-ops.test.ts +28 -7
  249. package/src/__tests__/intake-pipeline.test.ts +162 -0
  250. package/src/__tests__/loop-ops.test.ts +74 -3
  251. package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
  252. package/src/__tests__/orchestrate-ops.test.ts +8 -3
  253. package/src/__tests__/persistence.test.ts +291 -0
  254. package/src/__tests__/planner.test.ts +99 -21
  255. package/src/__tests__/planning-extra-ops.test.ts +168 -10
  256. package/src/__tests__/playbook-registry.test.ts +326 -0
  257. package/src/__tests__/playbook-seeder.test.ts +163 -0
  258. package/src/__tests__/playbook.test.ts +389 -0
  259. package/src/__tests__/postgres-provider.test.ts +58 -0
  260. package/src/__tests__/project-ops.test.ts +18 -4
  261. package/src/__tests__/template-manager.test.ts +222 -0
  262. package/src/__tests__/vault-extra-ops.test.ts +82 -7
  263. package/src/__tests__/vault.test.ts +184 -0
  264. package/src/brain/brain.ts +71 -9
  265. package/src/brain/intelligence.ts +258 -307
  266. package/src/brain/types.ts +2 -2
  267. package/src/cognee/client.ts +18 -0
  268. package/src/cognee/sync-manager.ts +389 -0
  269. package/src/control/identity-manager.ts +77 -75
  270. package/src/control/intent-router.ts +55 -57
  271. package/src/curator/curator.ts +199 -139
  272. package/src/errors/classify.ts +102 -0
  273. package/src/errors/index.ts +5 -0
  274. package/src/errors/retry.ts +132 -0
  275. package/src/errors/types.ts +81 -0
  276. package/src/governance/governance.ts +90 -107
  277. package/src/index.ts +116 -3
  278. package/src/intake/content-classifier.ts +146 -0
  279. package/src/intake/dedup-gate.ts +92 -0
  280. package/src/intake/intake-pipeline.ts +503 -0
  281. package/src/intake/types.ts +69 -0
  282. package/src/intelligence/loader.ts +1 -1
  283. package/src/intelligence/types.ts +3 -1
  284. package/src/loop/loop-manager.ts +325 -7
  285. package/src/loop/types.ts +72 -1
  286. package/src/persistence/index.ts +9 -0
  287. package/src/persistence/postgres-provider.ts +157 -0
  288. package/src/persistence/sqlite-provider.ts +115 -0
  289. package/src/persistence/types.ts +74 -0
  290. package/src/planning/gap-analysis.ts +286 -17
  291. package/src/planning/gap-types.ts +4 -1
  292. package/src/planning/planner.ts +828 -55
  293. package/src/playbooks/generic/brainstorming.ts +110 -0
  294. package/src/playbooks/generic/code-review.ts +181 -0
  295. package/src/playbooks/generic/subagent-execution.ts +74 -0
  296. package/src/playbooks/generic/systematic-debugging.ts +92 -0
  297. package/src/playbooks/generic/tdd.ts +75 -0
  298. package/src/playbooks/generic/verification.ts +79 -0
  299. package/src/playbooks/index.ts +27 -0
  300. package/src/playbooks/playbook-registry.ts +284 -0
  301. package/src/playbooks/playbook-seeder.ts +119 -0
  302. package/src/playbooks/playbook-types.ts +162 -0
  303. package/src/project/project-registry.ts +81 -74
  304. package/src/prompts/index.ts +3 -0
  305. package/src/prompts/parser.ts +59 -0
  306. package/src/prompts/template-manager.ts +77 -0
  307. package/src/prompts/types.ts +28 -0
  308. package/src/runtime/admin-extra-ops.ts +391 -13
  309. package/src/runtime/admin-ops.ts +17 -6
  310. package/src/runtime/capture-ops.ts +25 -6
  311. package/src/runtime/cognee-sync-ops.ts +63 -0
  312. package/src/runtime/core-ops.ts +258 -8
  313. package/src/runtime/curator-extra-ops.ts +17 -3
  314. package/src/runtime/domain-ops.ts +2 -2
  315. package/src/runtime/grading-ops.ts +11 -2
  316. package/src/runtime/intake-ops.ts +126 -0
  317. package/src/runtime/loop-ops.ts +96 -13
  318. package/src/runtime/memory-cross-project-ops.ts +1 -2
  319. package/src/runtime/memory-extra-ops.ts +5 -5
  320. package/src/runtime/orchestrate-ops.ts +8 -2
  321. package/src/runtime/planning-extra-ops.ts +414 -23
  322. package/src/runtime/playbook-ops.ts +169 -0
  323. package/src/runtime/project-ops.ts +9 -3
  324. package/src/runtime/runtime.ts +36 -10
  325. package/src/runtime/types.ts +8 -0
  326. package/src/runtime/vault-extra-ops.ts +425 -4
  327. package/src/vault/playbook.ts +87 -0
  328. package/src/vault/vault.ts +419 -235
@@ -34,8 +34,8 @@ describe('createPlanningExtraOps', () => {
34
34
  return op;
35
35
  }
36
36
 
37
- it('should return 9 ops', () => {
38
- expect(ops.length).toBe(9);
37
+ it('should return 22 ops', () => {
38
+ expect(ops.length).toBe(22);
39
39
  });
40
40
 
41
41
  it('should have all expected op names', () => {
@@ -49,6 +49,26 @@ describe('createPlanningExtraOps', () => {
49
49
  expect(names).toContain('plan_archive');
50
50
  expect(names).toContain('plan_list_tasks');
51
51
  expect(names).toContain('plan_stats');
52
+ // #148: Evidence
53
+ expect(names).toContain('plan_submit_evidence');
54
+ expect(names).toContain('plan_verify_task');
55
+ expect(names).toContain('plan_verify_plan');
56
+ // #149: Subagent dispatch
57
+ expect(names).toContain('plan_review_spec');
58
+ expect(names).toContain('plan_review_quality');
59
+ expect(names).toContain('plan_review_outcome');
60
+ // #150: Brainstorm
61
+ expect(names).toContain('plan_brainstorm');
62
+ // #151: Auto-reconcile
63
+ expect(names).toContain('plan_auto_reconcile');
64
+ // #152: Validate
65
+ expect(names).toContain('plan_validate');
66
+ // #80: Execution metrics
67
+ expect(names).toContain('plan_execution_metrics');
68
+ expect(names).toContain('plan_record_task_metrics');
69
+ // #83: Deliverables
70
+ expect(names).toContain('plan_submit_deliverable');
71
+ expect(names).toContain('plan_verify_deliverables');
52
72
  });
53
73
 
54
74
  it('should assign correct auth levels', () => {
@@ -115,7 +135,7 @@ describe('createPlanningExtraOps', () => {
115
135
  planId: plan.id,
116
136
  objective: 'Updated',
117
137
  })) as { error: string };
118
- expect(result.error).toContain("must be 'draft'");
138
+ expect(result.error).toContain("must be 'draft' or 'brainstorming'");
119
139
  });
120
140
 
121
141
  it('should return error for unknown plan', async () => {
@@ -138,7 +158,11 @@ describe('createPlanningExtraOps', () => {
138
158
  { title: 'Implement', description: 'Core implementation', dependsOn: ['task-1'] },
139
159
  { title: 'Test', description: 'Write tests', dependsOn: ['task-2'] },
140
160
  ],
141
- })) as { split: boolean; taskCount: number; plan: { tasks: Array<{ dependsOn?: string[] }> } };
161
+ })) as {
162
+ split: boolean;
163
+ taskCount: number;
164
+ plan: { tasks: Array<{ dependsOn?: string[] }> };
165
+ };
142
166
  expect(result.split).toBe(true);
143
167
  expect(result.taskCount).toBe(3);
144
168
  expect(result.plan.tasks[1].dependsOn).toEqual(['task-1']);
@@ -161,9 +185,7 @@ describe('createPlanningExtraOps', () => {
161
185
  runtime.planner.approve(plan.id);
162
186
  const result = (await findOp('plan_split').handler({
163
187
  planId: plan.id,
164
- tasks: [
165
- { title: 'Only task', description: 'Single task' },
166
- ],
188
+ tasks: [{ title: 'Only task', description: 'Single task' }],
167
189
  })) as { split: boolean; taskCount: number };
168
190
  expect(result.split).toBe(true);
169
191
  expect(result.taskCount).toBe(1);
@@ -177,7 +199,7 @@ describe('createPlanningExtraOps', () => {
177
199
  planId: plan.id,
178
200
  tasks: [{ title: 'T', description: 'D' }],
179
201
  })) as { error: string };
180
- expect(result.error).toContain("must be 'draft' or 'approved'");
202
+ expect(result.error).toContain("must be 'brainstorming', 'draft', or 'approved'");
181
203
  });
182
204
  });
183
205
 
@@ -201,7 +223,7 @@ describe('createPlanningExtraOps', () => {
201
223
  ],
202
224
  })) as { reconciled: boolean; accuracy: number; driftCount: number };
203
225
  expect(result.reconciled).toBe(true);
204
- expect(result.accuracy).toBe(50); // 1 drift out of 2 tasks = 50%
226
+ expect(result.accuracy).toBe(95); // 1 low-impact drift = 100 - 5 = 95
205
227
  expect(result.driftCount).toBe(1);
206
228
  });
207
229
 
@@ -236,7 +258,7 @@ describe('createPlanningExtraOps', () => {
236
258
  planId: plan.id,
237
259
  actualOutcome: 'Done',
238
260
  })) as { error: string };
239
- expect(result.error).toContain("must be 'executing' or 'completed'");
261
+ expect(result.error).toContain("must be 'executing', 'validating', or 'reconciling'");
240
262
  });
241
263
  });
242
264
 
@@ -246,6 +268,7 @@ describe('createPlanningExtraOps', () => {
246
268
  const plan = createDraftPlan();
247
269
  runtime.planner.approve(plan.id);
248
270
  runtime.planner.startExecution(plan.id);
271
+ runtime.planner.startReconciliation(plan.id);
249
272
  runtime.planner.complete(plan.id);
250
273
 
251
274
  const result = (await findOp('plan_complete_lifecycle').handler({
@@ -272,6 +295,7 @@ describe('createPlanningExtraOps', () => {
272
295
  const plan = createDraftPlan();
273
296
  runtime.planner.approve(plan.id);
274
297
  runtime.planner.startExecution(plan.id);
298
+ runtime.planner.startReconciliation(plan.id);
275
299
  runtime.planner.complete(plan.id);
276
300
 
277
301
  const result = (await findOp('plan_complete_lifecycle').handler({
@@ -419,6 +443,7 @@ describe('createPlanningExtraOps', () => {
419
443
  const plan = createDraftPlan();
420
444
  runtime.planner.approve(plan.id);
421
445
  runtime.planner.startExecution(plan.id);
446
+ runtime.planner.startReconciliation(plan.id);
422
447
  runtime.planner.complete(plan.id);
423
448
 
424
449
  // Hack: set updatedAt to 60 days ago
@@ -446,6 +471,7 @@ describe('createPlanningExtraOps', () => {
446
471
  const plan = createDraftPlan();
447
472
  runtime.planner.approve(plan.id);
448
473
  runtime.planner.startExecution(plan.id);
474
+ runtime.planner.startReconciliation(plan.id);
449
475
  runtime.planner.complete(plan.id);
450
476
 
451
477
  const result = (await findOp('plan_archive').handler({
@@ -501,6 +527,137 @@ describe('createPlanningExtraOps', () => {
501
527
  });
502
528
  });
503
529
 
530
+ // ─── plan_execution_metrics ─────────────────────────────────────
531
+ describe('plan_execution_metrics', () => {
532
+ it('should return metrics for a plan with timed tasks', async () => {
533
+ const plan = createDraftPlan();
534
+ runtime.planner.approve(plan.id);
535
+ runtime.planner.startExecution(plan.id);
536
+ runtime.planner.updateTask(plan.id, 'task-1', 'in_progress');
537
+ runtime.planner.updateTask(plan.id, 'task-1', 'completed');
538
+
539
+ const result = (await findOp('plan_execution_metrics').handler({
540
+ planId: plan.id,
541
+ })) as {
542
+ planId: string;
543
+ taskMetrics: Array<{ id: string; startedAt: number | null; completedAt: number | null }>;
544
+ executionSummary: { tasksCompleted: number } | null;
545
+ };
546
+
547
+ expect(result.planId).toBe(plan.id);
548
+ expect(result.taskMetrics).toHaveLength(2);
549
+ // task-1 was transitioned, should have timing
550
+ const task1 = result.taskMetrics.find((t) => t.id === 'task-1')!;
551
+ expect(task1.startedAt).not.toBeNull();
552
+ expect(task1.completedAt).not.toBeNull();
553
+ });
554
+
555
+ it('should return error for unknown plan', async () => {
556
+ const result = (await findOp('plan_execution_metrics').handler({
557
+ planId: 'nonexistent',
558
+ })) as { error: string };
559
+ expect(result.error).toContain('Plan not found');
560
+ });
561
+ });
562
+
563
+ // ─── plan_record_task_metrics ─────────────────────────────────
564
+ describe('plan_record_task_metrics', () => {
565
+ it('should record tool calls and model tier on a task', async () => {
566
+ const plan = createDraftPlan();
567
+ runtime.planner.approve(plan.id);
568
+ runtime.planner.startExecution(plan.id);
569
+
570
+ const result = (await findOp('plan_record_task_metrics').handler({
571
+ planId: plan.id,
572
+ taskId: 'task-1',
573
+ toolCalls: 15,
574
+ modelTier: 'opus',
575
+ })) as {
576
+ recorded: boolean;
577
+ taskId: string;
578
+ metrics: { toolCalls: number; modelTier: string };
579
+ };
580
+
581
+ expect(result.recorded).toBe(true);
582
+ expect(result.taskId).toBe('task-1');
583
+ expect(result.metrics.toolCalls).toBe(15);
584
+ expect(result.metrics.modelTier).toBe('opus');
585
+ });
586
+
587
+ it('should return error for unknown task', async () => {
588
+ const plan = createDraftPlan();
589
+ const result = (await findOp('plan_record_task_metrics').handler({
590
+ planId: plan.id,
591
+ taskId: 'task-99',
592
+ toolCalls: 5,
593
+ })) as { error: string };
594
+ expect(result.error).toContain('Task not found');
595
+ });
596
+ });
597
+
598
+ // ─── plan_submit_deliverable ──────────────────────────────────
599
+ describe('plan_submit_deliverable', () => {
600
+ it('should record a vault_entry deliverable on a task', async () => {
601
+ const plan = createDraftPlan();
602
+
603
+ const result = (await findOp('plan_submit_deliverable').handler({
604
+ planId: plan.id,
605
+ taskId: 'task-1',
606
+ type: 'vault_entry',
607
+ path: 'patterns/my-pattern',
608
+ })) as { submitted: boolean; taskId: string; deliverableCount: number };
609
+
610
+ expect(result.submitted).toBe(true);
611
+ expect(result.taskId).toBe('task-1');
612
+ expect(result.deliverableCount).toBe(1);
613
+ });
614
+
615
+ it('should return error for unknown plan', async () => {
616
+ const result = (await findOp('plan_submit_deliverable').handler({
617
+ planId: 'nonexistent',
618
+ taskId: 'task-1',
619
+ type: 'file',
620
+ path: '/tmp/test.txt',
621
+ })) as { error: string };
622
+ expect(result.error).toContain('Plan not found');
623
+ });
624
+ });
625
+
626
+ // ─── plan_verify_deliverables ─────────────────────────────────
627
+ describe('plan_verify_deliverables', () => {
628
+ it('should verify deliverables on a task', async () => {
629
+ const plan = createDraftPlan();
630
+ // Submit a vault_entry deliverable
631
+ await runtime.planner.submitDeliverable(plan.id, 'task-1', {
632
+ type: 'vault_entry',
633
+ path: 'nonexistent-entry',
634
+ });
635
+
636
+ const result = (await findOp('plan_verify_deliverables').handler({
637
+ planId: plan.id,
638
+ taskId: 'task-1',
639
+ })) as { verified: boolean; deliverables: Array<{ stale: boolean }>; staleCount: number };
640
+
641
+ expect(result.verified).toBe(false); // vault entry doesn't exist
642
+ expect(result.deliverables).toHaveLength(1);
643
+ expect(result.staleCount).toBe(1);
644
+ });
645
+
646
+ it('should return error for task with no deliverables', async () => {
647
+ const plan = createDraftPlan();
648
+
649
+ const result = (await findOp('plan_verify_deliverables').handler({
650
+ planId: plan.id,
651
+ taskId: 'task-1',
652
+ })) as { verified: boolean; deliverables: unknown[]; staleCount: number };
653
+
654
+ // No deliverables means verified = true (nothing to check)
655
+ expect(result.verified).toBe(true);
656
+ expect(result.deliverables).toHaveLength(0);
657
+ expect(result.staleCount).toBe(0);
658
+ });
659
+ });
660
+
504
661
  // ─── plan_stats ───────────────────────────────────────────────────
505
662
  describe('plan_stats', () => {
506
663
  it('should return zero stats when no plans exist', async () => {
@@ -527,6 +684,7 @@ describe('createPlanningExtraOps', () => {
527
684
  runtime.planner.approve(plan2.id);
528
685
  runtime.planner.startExecution(plan2.id);
529
686
  runtime.planner.updateTask(plan2.id, 'task-1', 'completed');
687
+ runtime.planner.startReconciliation(plan2.id);
530
688
  runtime.planner.complete(plan2.id);
531
689
 
532
690
  const result = (await findOp('plan_stats').handler({})) as {
@@ -0,0 +1,326 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ scorePlaybook,
4
+ matchPlaybooks,
5
+ mergePlaybooks,
6
+ getBuiltinPlaybook,
7
+ getAllBuiltinPlaybooks,
8
+ } from '../playbooks/playbook-registry.js';
9
+ import type { PlaybookDefinition } from '../playbooks/playbook-types.js';
10
+
11
+ describe('scorePlaybook', () => {
12
+ it('should add 10 for intent match', () => {
13
+ const pb = getBuiltinPlaybook('generic-tdd')!;
14
+ expect(scorePlaybook(pb, 'BUILD', '')).toBe(10);
15
+ });
16
+
17
+ it('should add 5 per keyword match', () => {
18
+ const pb = getBuiltinPlaybook('generic-tdd')!;
19
+ // 'implement' and 'build' are keywords
20
+ expect(scorePlaybook(pb, undefined, 'implement and build')).toBe(10);
21
+ });
22
+
23
+ it('should combine intent + keyword scores', () => {
24
+ const pb = getBuiltinPlaybook('generic-tdd')!;
25
+ // BUILD intent (10) + 'implement' (5) + 'feature' (5) = 20
26
+ expect(scorePlaybook(pb, 'BUILD', 'implement a feature')).toBe(20);
27
+ });
28
+
29
+ it('should return 0 for unrelated content', () => {
30
+ const pb = getBuiltinPlaybook('generic-tdd')!;
31
+ expect(scorePlaybook(pb, 'REVIEW', 'nothing related here')).toBe(0);
32
+ });
33
+
34
+ it('should be case-insensitive for keywords', () => {
35
+ const pb = getBuiltinPlaybook('generic-systematic-debugging')!;
36
+ expect(scorePlaybook(pb, undefined, 'FIX THE BUG')).toBeGreaterThan(0);
37
+ });
38
+ });
39
+
40
+ describe('matchPlaybooks', () => {
41
+ it('should match TDD for BUILD intent with "implement"', () => {
42
+ const result = matchPlaybooks('BUILD', 'implement the new feature');
43
+ expect(result.playbook).not.toBeNull();
44
+ expect(result.genericMatch?.id).toBe('generic-tdd');
45
+ });
46
+
47
+ it('should match systematic-debugging for FIX intent', () => {
48
+ const result = matchPlaybooks('FIX', 'fix the broken authentication bug');
49
+ expect(result.playbook).not.toBeNull();
50
+ expect(result.genericMatch?.id).toBe('generic-systematic-debugging');
51
+ });
52
+
53
+ it('should match brainstorming for PLAN intent', () => {
54
+ const result = matchPlaybooks('PLAN', 'design a new architecture from scratch');
55
+ expect(result.playbook).not.toBeNull();
56
+ expect(result.genericMatch?.id).toBe('generic-brainstorming');
57
+ });
58
+
59
+ it('should match code-review for REVIEW intent', () => {
60
+ const result = matchPlaybooks('REVIEW', 'review this pull request');
61
+ expect(result.playbook).not.toBeNull();
62
+ expect(result.genericMatch?.id).toBe('generic-code-review');
63
+ });
64
+
65
+ it('should return null playbook for unrelated text with no intent', () => {
66
+ const result = matchPlaybooks(undefined, 'random unrelated text xyz');
67
+ expect(result.playbook).toBeNull();
68
+ });
69
+
70
+ it('should prefer vault playbooks over builtins', () => {
71
+ const vaultPlaybook: PlaybookDefinition = {
72
+ id: 'custom-tdd',
73
+ tier: 'generic',
74
+ title: 'Custom TDD',
75
+ trigger: 'Custom TDD trigger',
76
+ description: 'Custom TDD description',
77
+ steps: 'Custom steps',
78
+ expectedOutcome: 'Custom outcome',
79
+ category: 'methodology',
80
+ tags: ['tdd'],
81
+ matchIntents: ['BUILD', 'FIX'],
82
+ matchKeywords: [
83
+ 'implement',
84
+ 'build',
85
+ 'create',
86
+ 'add',
87
+ 'fix',
88
+ 'refactor',
89
+ 'feature',
90
+ 'code',
91
+ 'custom-unique-keyword',
92
+ ],
93
+ gates: [],
94
+ taskTemplates: [],
95
+ toolInjections: [],
96
+ verificationCriteria: [],
97
+ };
98
+
99
+ // The vault playbook should score higher because it has more keyword matches
100
+ const result = matchPlaybooks(
101
+ 'BUILD',
102
+ 'implement and build and create with custom-unique-keyword',
103
+ [vaultPlaybook],
104
+ );
105
+ expect(result.genericMatch?.id).toBe('custom-tdd');
106
+ });
107
+
108
+ it('should resolve domain extends to correct generic', () => {
109
+ const domainPlaybook: PlaybookDefinition = {
110
+ id: 'domain-component-build',
111
+ tier: 'domain',
112
+ title: 'Component Build',
113
+ trigger: 'Build a component',
114
+ description: 'Build with design tokens',
115
+ steps: 'Domain steps',
116
+ expectedOutcome: 'Component built with tokens',
117
+ extends: 'generic-tdd',
118
+ category: 'design-system',
119
+ tags: ['component', 'build'],
120
+ matchIntents: ['BUILD'],
121
+ matchKeywords: ['component', 'build'],
122
+ gates: [{ phase: 'completion', requirement: 'Tokens validated', checkType: 'token-check' }],
123
+ taskTemplates: [],
124
+ toolInjections: ['validate_tokens'],
125
+ verificationCriteria: ['All tokens are semantic'],
126
+ };
127
+
128
+ const result = matchPlaybooks('BUILD', 'build a component', [domainPlaybook]);
129
+ expect(result.domainMatch?.id).toBe('domain-component-build');
130
+ // Should resolve the extended generic
131
+ expect(result.genericMatch?.id).toBe('generic-tdd');
132
+ expect(result.playbook?.label).toContain('extends');
133
+ });
134
+ });
135
+
136
+ describe('mergePlaybooks', () => {
137
+ it('should concatenate gates (generic first, then domain)', () => {
138
+ const generic = getBuiltinPlaybook('generic-tdd')!;
139
+ const domain: PlaybookDefinition = {
140
+ id: 'domain-test',
141
+ tier: 'domain',
142
+ title: 'Domain Test',
143
+ trigger: '',
144
+ description: '',
145
+ steps: '',
146
+ expectedOutcome: '',
147
+ category: 'test',
148
+ tags: [],
149
+ matchIntents: [],
150
+ matchKeywords: [],
151
+ gates: [{ phase: 'completion', requirement: 'Domain gate', checkType: 'domain-gate' }],
152
+ taskTemplates: [],
153
+ toolInjections: [],
154
+ verificationCriteria: [],
155
+ };
156
+
157
+ const merged = mergePlaybooks(generic, domain);
158
+ expect(merged.mergedGates.length).toBe(generic.gates.length + 1);
159
+ expect(merged.mergedGates[0]).toEqual(generic.gates[0]);
160
+ expect(merged.mergedGates[merged.mergedGates.length - 1].checkType).toBe('domain-gate');
161
+ });
162
+
163
+ it('should override tasks at same order+taskType', () => {
164
+ const generic: PlaybookDefinition = {
165
+ id: 'g',
166
+ tier: 'generic',
167
+ title: 'G',
168
+ trigger: '',
169
+ description: '',
170
+ steps: '',
171
+ expectedOutcome: '',
172
+ category: 'test',
173
+ tags: [],
174
+ matchIntents: [],
175
+ matchKeywords: [],
176
+ gates: [],
177
+ taskTemplates: [
178
+ {
179
+ taskType: 'test',
180
+ titleTemplate: 'Generic test',
181
+ acceptanceCriteria: ['generic'],
182
+ tools: [],
183
+ order: 'before-implementation',
184
+ },
185
+ ],
186
+ toolInjections: [],
187
+ verificationCriteria: [],
188
+ };
189
+
190
+ const domain: PlaybookDefinition = {
191
+ ...generic,
192
+ id: 'd',
193
+ tier: 'domain',
194
+ title: 'D',
195
+ taskTemplates: [
196
+ {
197
+ taskType: 'test',
198
+ titleTemplate: 'Domain test',
199
+ acceptanceCriteria: ['domain'],
200
+ tools: ['custom_tool'],
201
+ order: 'before-implementation',
202
+ },
203
+ ],
204
+ };
205
+
206
+ const merged = mergePlaybooks(generic, domain);
207
+ // Domain should override the same order+taskType
208
+ expect(merged.mergedTasks).toHaveLength(1);
209
+ expect(merged.mergedTasks[0].titleTemplate).toBe('Domain test');
210
+ });
211
+
212
+ it('should deduplicate tools', () => {
213
+ const generic: PlaybookDefinition = {
214
+ id: 'g',
215
+ tier: 'generic',
216
+ title: 'G',
217
+ trigger: '',
218
+ description: '',
219
+ steps: '',
220
+ expectedOutcome: '',
221
+ category: 'test',
222
+ tags: [],
223
+ matchIntents: [],
224
+ matchKeywords: [],
225
+ gates: [],
226
+ taskTemplates: [],
227
+ toolInjections: ['search_intelligent', 'shared_tool'],
228
+ verificationCriteria: [],
229
+ };
230
+
231
+ const domain: PlaybookDefinition = {
232
+ ...generic,
233
+ id: 'd',
234
+ tier: 'domain',
235
+ title: 'D',
236
+ toolInjections: ['shared_tool', 'domain_tool'],
237
+ };
238
+
239
+ const merged = mergePlaybooks(generic, domain);
240
+ expect(merged.mergedTools).toHaveLength(3);
241
+ expect(merged.mergedTools).toContain('search_intelligent');
242
+ expect(merged.mergedTools).toContain('shared_tool');
243
+ expect(merged.mergedTools).toContain('domain_tool');
244
+ });
245
+
246
+ it('should deduplicate verification criteria', () => {
247
+ const generic: PlaybookDefinition = {
248
+ id: 'g',
249
+ tier: 'generic',
250
+ title: 'G',
251
+ trigger: '',
252
+ description: '',
253
+ steps: '',
254
+ expectedOutcome: '',
255
+ category: 'test',
256
+ tags: [],
257
+ matchIntents: [],
258
+ matchKeywords: [],
259
+ gates: [],
260
+ taskTemplates: [],
261
+ toolInjections: [],
262
+ verificationCriteria: ['Tests pass', 'Build succeeds'],
263
+ };
264
+
265
+ const domain: PlaybookDefinition = {
266
+ ...generic,
267
+ id: 'd',
268
+ tier: 'domain',
269
+ title: 'D',
270
+ verificationCriteria: ['Tests pass', 'Tokens validated'],
271
+ };
272
+
273
+ const merged = mergePlaybooks(generic, domain);
274
+ expect(merged.mergedVerification).toHaveLength(3);
275
+ });
276
+
277
+ it('should build correct label for generic + domain', () => {
278
+ const generic = getBuiltinPlaybook('generic-tdd')!;
279
+ const domain: PlaybookDefinition = {
280
+ id: 'd',
281
+ tier: 'domain',
282
+ title: 'Component Build',
283
+ trigger: '',
284
+ description: '',
285
+ steps: '',
286
+ expectedOutcome: '',
287
+ category: 'test',
288
+ tags: [],
289
+ matchIntents: [],
290
+ matchKeywords: [],
291
+ gates: [],
292
+ taskTemplates: [],
293
+ toolInjections: [],
294
+ verificationCriteria: [],
295
+ };
296
+
297
+ const merged = mergePlaybooks(generic, domain);
298
+ expect(merged.label).toBe('Component Build (extends Test-Driven Development)');
299
+ });
300
+
301
+ it('should handle generic-only merge', () => {
302
+ const generic = getBuiltinPlaybook('generic-tdd')!;
303
+ const merged = mergePlaybooks(generic, undefined);
304
+ expect(merged.label).toBe(generic.title);
305
+ expect(merged.generic).toBe(generic);
306
+ expect(merged.domain).toBeUndefined();
307
+ });
308
+ });
309
+
310
+ describe('getAllBuiltinPlaybooks', () => {
311
+ it('should return 6 built-in playbooks', () => {
312
+ const all = getAllBuiltinPlaybooks();
313
+ expect(all).toHaveLength(6);
314
+ });
315
+
316
+ it('should all be generic tier', () => {
317
+ const all = getAllBuiltinPlaybooks();
318
+ expect(all.every((p) => p.tier === 'generic')).toBe(true);
319
+ });
320
+
321
+ it('should have unique IDs', () => {
322
+ const all = getAllBuiltinPlaybooks();
323
+ const ids = all.map((p) => p.id);
324
+ expect(new Set(ids).size).toBe(ids.length);
325
+ });
326
+ });