@soleri/core 9.14.4 → 9.16.7

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 (355) hide show
  1. package/data/flows/deliver.flow.yaml +11 -0
  2. package/data/flows/design.flow.yaml +4 -14
  3. package/data/flows/enhance.flow.yaml +10 -0
  4. package/data/flows/explore.flow.yaml +16 -0
  5. package/data/flows/fix.flow.yaml +1 -1
  6. package/data/flows/review.flow.yaml +13 -4
  7. package/dist/brain/brain.d.ts +9 -0
  8. package/dist/brain/brain.d.ts.map +1 -1
  9. package/dist/brain/brain.js +11 -1
  10. package/dist/brain/brain.js.map +1 -1
  11. package/dist/brain/intelligence.d.ts.map +1 -1
  12. package/dist/brain/intelligence.js +24 -0
  13. package/dist/brain/intelligence.js.map +1 -1
  14. package/dist/brain/types.d.ts +1 -0
  15. package/dist/brain/types.d.ts.map +1 -1
  16. package/dist/capabilities/chain-mapping.d.ts.map +1 -1
  17. package/dist/capabilities/chain-mapping.js +5 -4
  18. package/dist/capabilities/chain-mapping.js.map +1 -1
  19. package/dist/capabilities/registry.d.ts +6 -0
  20. package/dist/capabilities/registry.d.ts.map +1 -1
  21. package/dist/capabilities/registry.js +3 -2
  22. package/dist/capabilities/registry.js.map +1 -1
  23. package/dist/chat/chat-session.d.ts +6 -0
  24. package/dist/chat/chat-session.d.ts.map +1 -1
  25. package/dist/chat/chat-session.js +68 -17
  26. package/dist/chat/chat-session.js.map +1 -1
  27. package/dist/context/context-engine.js +1 -1
  28. package/dist/context/context-engine.js.map +1 -1
  29. package/dist/curator/curator.d.ts +6 -0
  30. package/dist/curator/curator.d.ts.map +1 -1
  31. package/dist/curator/curator.js +138 -0
  32. package/dist/curator/curator.js.map +1 -1
  33. package/dist/curator/types.d.ts +10 -0
  34. package/dist/curator/types.d.ts.map +1 -1
  35. package/dist/engine/bin/soleri-engine.js +0 -0
  36. package/dist/engine/core-ops.d.ts.map +1 -1
  37. package/dist/engine/core-ops.js +38 -1
  38. package/dist/engine/core-ops.js.map +1 -1
  39. package/dist/flows/epilogue.d.ts +5 -1
  40. package/dist/flows/epilogue.d.ts.map +1 -1
  41. package/dist/flows/epilogue.js +11 -3
  42. package/dist/flows/epilogue.js.map +1 -1
  43. package/dist/flows/executor.d.ts.map +1 -1
  44. package/dist/flows/executor.js +13 -5
  45. package/dist/flows/executor.js.map +1 -1
  46. package/dist/flows/index.d.ts +1 -2
  47. package/dist/flows/index.d.ts.map +1 -1
  48. package/dist/flows/index.js +1 -0
  49. package/dist/flows/index.js.map +1 -1
  50. package/dist/flows/plan-builder.d.ts +17 -1
  51. package/dist/flows/plan-builder.d.ts.map +1 -1
  52. package/dist/flows/plan-builder.js +67 -6
  53. package/dist/flows/plan-builder.js.map +1 -1
  54. package/dist/flows/probes.d.ts +1 -1
  55. package/dist/flows/probes.d.ts.map +1 -1
  56. package/dist/flows/probes.js +15 -3
  57. package/dist/flows/probes.js.map +1 -1
  58. package/dist/flows/types.d.ts +47 -20
  59. package/dist/flows/types.d.ts.map +1 -1
  60. package/dist/flows/types.js +6 -1
  61. package/dist/flows/types.js.map +1 -1
  62. package/dist/index.d.ts +10 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +9 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/intake/content-classifier.d.ts +10 -4
  67. package/dist/intake/content-classifier.d.ts.map +1 -1
  68. package/dist/intake/content-classifier.js +19 -5
  69. package/dist/intake/content-classifier.js.map +1 -1
  70. package/dist/intake/text-ingester.d.ts +18 -0
  71. package/dist/intake/text-ingester.d.ts.map +1 -1
  72. package/dist/intake/text-ingester.js +37 -13
  73. package/dist/intake/text-ingester.js.map +1 -1
  74. package/dist/packs/pack-installer.d.ts.map +1 -1
  75. package/dist/packs/pack-installer.js +28 -2
  76. package/dist/packs/pack-installer.js.map +1 -1
  77. package/dist/planning/planner-types.d.ts +2 -0
  78. package/dist/planning/planner-types.d.ts.map +1 -1
  79. package/dist/planning/planner.d.ts +4 -0
  80. package/dist/planning/planner.d.ts.map +1 -1
  81. package/dist/planning/planner.js +50 -4
  82. package/dist/planning/planner.js.map +1 -1
  83. package/dist/playbooks/playbook-executor.d.ts +10 -1
  84. package/dist/playbooks/playbook-executor.d.ts.map +1 -1
  85. package/dist/playbooks/playbook-executor.js +8 -2
  86. package/dist/playbooks/playbook-executor.js.map +1 -1
  87. package/dist/playbooks/playbook-types.d.ts +8 -0
  88. package/dist/playbooks/playbook-types.d.ts.map +1 -1
  89. package/dist/plugins/types.d.ts +2 -2
  90. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  91. package/dist/runtime/admin-extra-ops.js +30 -0
  92. package/dist/runtime/admin-extra-ops.js.map +1 -1
  93. package/dist/runtime/admin-ops.d.ts.map +1 -1
  94. package/dist/runtime/admin-ops.js +60 -21
  95. package/dist/runtime/admin-ops.js.map +1 -1
  96. package/dist/runtime/admin-setup-ops.d.ts +11 -0
  97. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  98. package/dist/runtime/admin-setup-ops.js +146 -37
  99. package/dist/runtime/admin-setup-ops.js.map +1 -1
  100. package/dist/runtime/capture-ops.d.ts.map +1 -1
  101. package/dist/runtime/capture-ops.js +38 -12
  102. package/dist/runtime/capture-ops.js.map +1 -1
  103. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  104. package/dist/runtime/facades/brain-facade.js +16 -4
  105. package/dist/runtime/facades/brain-facade.js.map +1 -1
  106. package/dist/runtime/facades/context-facade.d.ts.map +1 -1
  107. package/dist/runtime/facades/context-facade.js +9 -3
  108. package/dist/runtime/facades/context-facade.js.map +1 -1
  109. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  110. package/dist/runtime/facades/memory-facade.js +20 -7
  111. package/dist/runtime/facades/memory-facade.js.map +1 -1
  112. package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
  113. package/dist/runtime/facades/orchestrate-facade.js +40 -1
  114. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  115. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  116. package/dist/runtime/facades/plan-facade.js +113 -4
  117. package/dist/runtime/facades/plan-facade.js.map +1 -1
  118. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  119. package/dist/runtime/facades/vault-facade.js +24 -3
  120. package/dist/runtime/facades/vault-facade.js.map +1 -1
  121. package/dist/runtime/orchestrate-ops.d.ts +21 -0
  122. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  123. package/dist/runtime/orchestrate-ops.js +132 -38
  124. package/dist/runtime/orchestrate-ops.js.map +1 -1
  125. package/dist/runtime/runtime.d.ts.map +1 -1
  126. package/dist/runtime/runtime.js +16 -0
  127. package/dist/runtime/runtime.js.map +1 -1
  128. package/dist/runtime/schema-helpers.d.ts.map +1 -1
  129. package/dist/runtime/schema-helpers.js +4 -0
  130. package/dist/runtime/schema-helpers.js.map +1 -1
  131. package/dist/runtime/types.d.ts +19 -0
  132. package/dist/runtime/types.d.ts.map +1 -1
  133. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  134. package/dist/runtime/vault-linking-ops.js +16 -3
  135. package/dist/runtime/vault-linking-ops.js.map +1 -1
  136. package/dist/scheduler/cron-validator.d.ts +15 -0
  137. package/dist/scheduler/cron-validator.d.ts.map +1 -0
  138. package/dist/scheduler/cron-validator.js +93 -0
  139. package/dist/scheduler/cron-validator.js.map +1 -0
  140. package/dist/scheduler/platform-linux.d.ts +14 -0
  141. package/dist/scheduler/platform-linux.d.ts.map +1 -0
  142. package/dist/scheduler/platform-linux.js +107 -0
  143. package/dist/scheduler/platform-linux.js.map +1 -0
  144. package/dist/scheduler/platform-macos.d.ts +15 -0
  145. package/dist/scheduler/platform-macos.d.ts.map +1 -0
  146. package/dist/scheduler/platform-macos.js +131 -0
  147. package/dist/scheduler/platform-macos.js.map +1 -0
  148. package/dist/scheduler/scheduler-ops.d.ts +14 -0
  149. package/dist/scheduler/scheduler-ops.d.ts.map +1 -0
  150. package/dist/scheduler/scheduler-ops.js +77 -0
  151. package/dist/scheduler/scheduler-ops.js.map +1 -0
  152. package/dist/scheduler/scheduler.d.ts +55 -0
  153. package/dist/scheduler/scheduler.d.ts.map +1 -0
  154. package/dist/scheduler/scheduler.js +144 -0
  155. package/dist/scheduler/scheduler.js.map +1 -0
  156. package/dist/scheduler/types.d.ts +48 -0
  157. package/dist/scheduler/types.d.ts.map +1 -0
  158. package/dist/scheduler/types.js +6 -0
  159. package/dist/scheduler/types.js.map +1 -0
  160. package/dist/skills/sync-skills.d.ts +11 -0
  161. package/dist/skills/sync-skills.d.ts.map +1 -1
  162. package/dist/skills/sync-skills.js +132 -38
  163. package/dist/skills/sync-skills.js.map +1 -1
  164. package/dist/skills/validate-skills.d.ts +32 -0
  165. package/dist/skills/validate-skills.d.ts.map +1 -0
  166. package/dist/skills/validate-skills.js +396 -0
  167. package/dist/skills/validate-skills.js.map +1 -0
  168. package/dist/utils/worktree-reaper.d.ts +38 -0
  169. package/dist/utils/worktree-reaper.d.ts.map +1 -0
  170. package/dist/utils/worktree-reaper.js +85 -0
  171. package/dist/utils/worktree-reaper.js.map +1 -0
  172. package/dist/vault/default-canonical-tags.d.ts +15 -0
  173. package/dist/vault/default-canonical-tags.d.ts.map +1 -0
  174. package/dist/vault/default-canonical-tags.js +65 -0
  175. package/dist/vault/default-canonical-tags.js.map +1 -0
  176. package/dist/vault/scope-detector.d.ts.map +1 -1
  177. package/dist/vault/scope-detector.js +37 -4
  178. package/dist/vault/scope-detector.js.map +1 -1
  179. package/dist/vault/tag-normalizer.d.ts +42 -0
  180. package/dist/vault/tag-normalizer.d.ts.map +1 -0
  181. package/dist/vault/tag-normalizer.js +157 -0
  182. package/dist/vault/tag-normalizer.js.map +1 -0
  183. package/dist/vault/vault-entries.d.ts.map +1 -1
  184. package/dist/vault/vault-entries.js +3 -1
  185. package/dist/vault/vault-entries.js.map +1 -1
  186. package/package.json +5 -1
  187. package/src/__tests__/embeddings.test.ts +3 -3
  188. package/src/agency/agency-manager.test.ts +4 -4
  189. package/src/agency/default-rules.test.ts +0 -13
  190. package/src/brain/brain-intelligence.test.ts +0 -5
  191. package/src/brain/brain.ts +25 -1
  192. package/src/brain/intelligence.ts +25 -0
  193. package/src/brain/second-brain-features.test.ts +2 -14
  194. package/src/brain/types.ts +1 -0
  195. package/src/capabilities/chain-mapping.test.ts +1 -6
  196. package/src/capabilities/chain-mapping.ts +6 -4
  197. package/src/capabilities/registry.test.ts +1 -1
  198. package/src/capabilities/registry.ts +9 -2
  199. package/src/chat/agent-loop.test.ts +1 -1
  200. package/src/chat/chat-enhanced.test.ts +0 -8
  201. package/src/chat/chat-session.ts +75 -17
  202. package/src/chat/chat-transport.test.ts +31 -1
  203. package/src/claudemd/compose.test.ts +0 -5
  204. package/src/context/context-engine.test.ts +0 -1
  205. package/src/context/context-engine.ts +1 -1
  206. package/src/control/intent-router.test.ts +2 -2
  207. package/src/curator/curator.ts +180 -0
  208. package/src/curator/tag-manager.test.ts +0 -4
  209. package/src/curator/types.ts +10 -0
  210. package/src/domain-packs/types.test.ts +0 -5
  211. package/src/dream/dream.test.ts +0 -7
  212. package/src/enforcement/registry.test.ts +2 -2
  213. package/src/engine/core-ops.test.ts +4 -22
  214. package/src/engine/core-ops.ts +36 -1
  215. package/src/engine/module-manifest.test.ts +1 -31
  216. package/src/engine/register-engine.test.ts +3 -33
  217. package/src/errors/retry.test.ts +3 -1
  218. package/src/flows/chain-runner.test.ts +0 -6
  219. package/src/flows/context-router.test.ts +3 -3
  220. package/src/flows/epilogue.test.ts +40 -2
  221. package/src/flows/epilogue.ts +11 -2
  222. package/src/flows/executor.test.ts +48 -2
  223. package/src/flows/executor.ts +15 -5
  224. package/src/flows/index.ts +1 -3
  225. package/src/flows/plan-builder.test.ts +201 -0
  226. package/src/flows/plan-builder.ts +81 -5
  227. package/src/flows/probes.ts +17 -3
  228. package/src/flows/types.ts +31 -2
  229. package/src/health/health-registry.test.ts +3 -1
  230. package/src/index.ts +24 -0
  231. package/src/intake/content-classifier.ts +22 -4
  232. package/src/intake/dedup-gate.test.ts +2 -6
  233. package/src/intake/text-ingester.test.ts +3 -4
  234. package/src/intake/text-ingester.ts +61 -12
  235. package/src/llm/llm-client.test.ts +1 -1
  236. package/src/llm/utils.test.ts +1 -1
  237. package/src/migrations/migration-runner.test.ts +0 -1
  238. package/src/operator/operator-context-store.test.ts +0 -13
  239. package/src/operator/operator-profile.test.ts +2 -20
  240. package/src/packs/pack-installer.ts +28 -2
  241. package/src/packs/pack-system.test.ts +2 -2
  242. package/src/persona/defaults.test.ts +19 -19
  243. package/src/planning/gap-passes.test.ts +0 -46
  244. package/src/planning/gap-patterns.test.ts +0 -42
  245. package/src/planning/goal-ancestry.test.ts +3 -1
  246. package/src/planning/plan-lifecycle.test.ts +15 -7
  247. package/src/planning/planner-types.ts +2 -0
  248. package/src/planning/planner.test.ts +86 -90
  249. package/src/planning/planner.ts +56 -4
  250. package/src/planning/reconciliation-engine.test.ts +3 -10
  251. package/src/planning/task-complexity-assessor.test.ts +0 -5
  252. package/src/planning/task-verifier.test.ts +3 -1
  253. package/src/playbooks/generic/generic-playbooks.test.ts +0 -28
  254. package/src/playbooks/index.test.ts +0 -55
  255. package/src/playbooks/playbook-executor.test.ts +76 -0
  256. package/src/playbooks/playbook-executor.ts +24 -3
  257. package/src/playbooks/playbook-types.ts +8 -0
  258. package/src/plugins/plugin-registry.test.ts +6 -2
  259. package/src/project/project-registry.test.ts +2 -0
  260. package/src/queue/async-infrastructure.test.ts +6 -4
  261. package/src/queue/job-queue.test.ts +13 -7
  262. package/src/runtime/admin-extra-ops.test.ts +35 -30
  263. package/src/runtime/admin-extra-ops.ts +30 -0
  264. package/src/runtime/admin-ops.test.ts +0 -4
  265. package/src/runtime/admin-ops.ts +63 -21
  266. package/src/runtime/admin-setup-ops.test.ts +229 -13
  267. package/src/runtime/admin-setup-ops.ts +145 -36
  268. package/src/runtime/archive-ops.test.ts +0 -28
  269. package/src/runtime/branching-ops.test.ts +0 -17
  270. package/src/runtime/capture-ops.test.ts +41 -16
  271. package/src/runtime/capture-ops.ts +78 -46
  272. package/src/runtime/chain-ops.test.ts +0 -21
  273. package/src/runtime/facades/admin-facade.test.ts +0 -34
  274. package/src/runtime/facades/agency-facade.test.ts +0 -39
  275. package/src/runtime/facades/archive-facade.test.ts +0 -43
  276. package/src/runtime/facades/brain-facade.test.ts +8 -99
  277. package/src/runtime/facades/brain-facade.ts +29 -12
  278. package/src/runtime/facades/branching-facade.test.ts +30 -17
  279. package/src/runtime/facades/chat-facade.test.ts +0 -91
  280. package/src/runtime/facades/chat-service-ops.test.ts +0 -24
  281. package/src/runtime/facades/chat-session-ops.test.ts +0 -12
  282. package/src/runtime/facades/chat-transport-ops.test.ts +0 -23
  283. package/src/runtime/facades/context-facade.test.ts +0 -17
  284. package/src/runtime/facades/context-facade.ts +11 -4
  285. package/src/runtime/facades/control-facade.test.ts +0 -30
  286. package/src/runtime/facades/curator-facade.test.ts +0 -33
  287. package/src/runtime/facades/intake-facade.test.ts +0 -33
  288. package/src/runtime/facades/links-facade.test.ts +0 -37
  289. package/src/runtime/facades/loop-facade.test.ts +0 -26
  290. package/src/runtime/facades/memory-facade.test.ts +0 -18
  291. package/src/runtime/facades/memory-facade.ts +27 -11
  292. package/src/runtime/facades/operator-facade.test.ts +0 -31
  293. package/src/runtime/facades/orchestrate-facade.test.ts +0 -21
  294. package/src/runtime/facades/orchestrate-facade.ts +39 -1
  295. package/src/runtime/facades/plan-facade.test.ts +7 -32
  296. package/src/runtime/facades/plan-facade.ts +137 -4
  297. package/src/runtime/facades/review-facade.test.ts +1 -49
  298. package/src/runtime/facades/sync-facade.test.ts +24 -41
  299. package/src/runtime/facades/tier-facade.test.ts +30 -22
  300. package/src/runtime/facades/vault-facade.test.ts +0 -41
  301. package/src/runtime/facades/vault-facade.ts +26 -3
  302. package/src/runtime/grading-ops.test.ts +0 -27
  303. package/src/runtime/intake-ops.test.ts +0 -19
  304. package/src/runtime/loop-ops.test.ts +0 -48
  305. package/src/runtime/memory-cross-project-ops.test.ts +0 -14
  306. package/src/runtime/memory-extra-ops.test.ts +4 -8
  307. package/src/runtime/orchestrate-ops.test.ts +238 -19
  308. package/src/runtime/orchestrate-ops.ts +166 -41
  309. package/src/runtime/pack-ops.test.ts +0 -26
  310. package/src/runtime/planning-extra-ops.test.ts +2 -14
  311. package/src/runtime/playbook-ops-execution.test.ts +9 -20
  312. package/src/runtime/playbook-ops.test.ts +4 -67
  313. package/src/runtime/review-ops.test.ts +0 -15
  314. package/src/runtime/runtime.ts +18 -0
  315. package/src/runtime/schema-helpers.ts +4 -0
  316. package/src/runtime/sync-ops.test.ts +0 -18
  317. package/src/runtime/tier-ops.test.ts +0 -21
  318. package/src/runtime/types.ts +19 -0
  319. package/src/runtime/vault-extra-ops.test.ts +0 -12
  320. package/src/runtime/vault-linking-ops.test.ts +0 -4
  321. package/src/runtime/vault-linking-ops.ts +26 -8
  322. package/src/runtime/vault-sharing-ops.test.ts +0 -9
  323. package/src/scheduler/cron-validator.ts +101 -0
  324. package/src/scheduler/platform-linux.ts +122 -0
  325. package/src/scheduler/platform-macos.ts +150 -0
  326. package/src/scheduler/scheduler-ops.ts +77 -0
  327. package/src/scheduler/scheduler.test.ts +247 -0
  328. package/src/scheduler/scheduler.ts +174 -0
  329. package/src/scheduler/types.ts +52 -0
  330. package/src/skills/__tests__/sync-skills.test.ts +6 -17
  331. package/src/skills/global-claude-md.test.ts +113 -0
  332. package/src/skills/sync-skills.ts +143 -35
  333. package/src/skills/validate-skills.test.ts +206 -0
  334. package/src/skills/validate-skills.ts +470 -0
  335. package/src/telemetry/telemetry.test.ts +1 -0
  336. package/src/transport/http-server.test.ts +3 -0
  337. package/src/transport/session-manager.test.ts +3 -1
  338. package/src/transport/token-auth.test.ts +6 -9
  339. package/src/transport/ws-server.test.ts +10 -2
  340. package/src/utils/worktree-reaper.ts +113 -0
  341. package/src/vault/__tests__/vault-characterization.test.ts +0 -108
  342. package/src/vault/default-canonical-tags.ts +64 -0
  343. package/src/vault/linking.test.ts +0 -2
  344. package/src/vault/playbook.test.ts +4 -1
  345. package/src/vault/scope-detector.test.ts +3 -1
  346. package/src/vault/scope-detector.ts +42 -4
  347. package/src/vault/tag-normalizer.test.ts +214 -0
  348. package/src/vault/tag-normalizer.ts +188 -0
  349. package/src/vault/vault-connect.test.ts +1 -1
  350. package/src/vault/vault-entries.ts +3 -1
  351. package/src/vault/vault.test.ts +23 -8
  352. package/dist/embeddings/index.d.ts +0 -5
  353. package/dist/embeddings/index.d.ts.map +0 -1
  354. package/dist/embeddings/index.js +0 -3
  355. package/dist/embeddings/index.js.map +0 -1
@@ -68,9 +68,11 @@ describe('plan-lifecycle', () => {
68
68
 
69
69
  describe('applyTransition', () => {
70
70
  it('returns new status and timestamp for valid transition', () => {
71
+ const before = Date.now();
71
72
  const result = applyTransition('draft', 'approved');
72
73
  expect(result.status).toBe('approved');
73
- expect(result.updatedAt).toBeGreaterThan(0);
74
+ expect(result.updatedAt).toBeGreaterThanOrEqual(before);
75
+ expect(result.updatedAt).toBeLessThanOrEqual(Date.now());
74
76
  });
75
77
  it('throws for invalid transition', () => {
76
78
  expect(() => applyTransition('draft', 'executing')).toThrow('Invalid transition');
@@ -312,9 +314,11 @@ describe('plan-lifecycle', () => {
312
314
  });
313
315
 
314
316
  it('sets startedAt on first in_progress', () => {
317
+ const before = Date.now();
315
318
  const task = makeTask();
316
319
  applyTaskStatusUpdate(task, 'in_progress');
317
- expect(task.startedAt).toBeGreaterThan(0);
320
+ expect(task.startedAt).toBeGreaterThanOrEqual(before);
321
+ expect(task.startedAt).toBeLessThanOrEqual(Date.now());
318
322
  expect(task.status).toBe('in_progress');
319
323
  });
320
324
  it('does not overwrite startedAt on repeated in_progress', () => {
@@ -324,22 +328,26 @@ describe('plan-lifecycle', () => {
324
328
  expect(task.startedAt).toBe(1000);
325
329
  });
326
330
  it('sets completedAt and durationMs on completed', () => {
331
+ const before = Date.now();
327
332
  const task = makeTask();
328
- task.startedAt = Date.now() - 500;
333
+ task.startedAt = before - 500;
329
334
  applyTaskStatusUpdate(task, 'completed');
330
- expect(task.completedAt).toBeGreaterThan(0);
331
- expect(task.metrics?.durationMs).toBeGreaterThanOrEqual(0);
335
+ expect(task.completedAt).toBeGreaterThanOrEqual(before);
336
+ expect(task.completedAt).toBeLessThanOrEqual(Date.now());
337
+ expect(task.metrics?.durationMs).toBeGreaterThanOrEqual(500);
332
338
  });
333
339
  it('sets completedAt on skipped', () => {
340
+ const before = Date.now();
334
341
  const task = makeTask();
335
342
  applyTaskStatusUpdate(task, 'skipped');
336
- expect(task.completedAt).toBeGreaterThan(0);
343
+ expect(task.completedAt).toBeGreaterThanOrEqual(before);
337
344
  expect(task.status).toBe('skipped');
338
345
  });
339
346
  it('sets completedAt on failed', () => {
347
+ const before = Date.now();
340
348
  const task = makeTask();
341
349
  applyTaskStatusUpdate(task, 'failed');
342
- expect(task.completedAt).toBeGreaterThan(0);
350
+ expect(task.completedAt).toBeGreaterThanOrEqual(before);
343
351
  expect(task.status).toBe('failed');
344
352
  });
345
353
 
@@ -193,6 +193,8 @@ export interface Plan {
193
193
  genericId?: string;
194
194
  domainId?: string;
195
195
  };
196
+ /** Active playbook executor session ID — used to enforce gates during task updates and plan completion. */
197
+ playbookSessionId?: string;
196
198
  /** Source GitHub issue this plan was created from (e.g., #NNN in prompt). */
197
199
  githubIssue?: { owner: string; repo: string; number: number };
198
200
  /** GitHub issue projection — populated by orchestrate_project_to_github. */
@@ -74,6 +74,26 @@ describe('Planner', () => {
74
74
  const planner2 = new Planner(join(tempDir, 'plans.json'));
75
75
  expect(planner2.list()).toHaveLength(1);
76
76
  });
77
+
78
+ it('should make newly created plans visible to other planner instances', () => {
79
+ const planner2 = new Planner(join(tempDir, 'plans.json'));
80
+ const plan = planner.create({ objective: 'Shared plan', scope: 'sync' });
81
+
82
+ expect(planner2.get(plan.id)?.objective).toBe('Shared plan');
83
+ });
84
+
85
+ it('should merge plans created by multiple planner instances', () => {
86
+ const planner2 = new Planner(join(tempDir, 'plans.json'));
87
+ const planA = planner.create({ objective: 'Plan A', scope: 'sync-a' });
88
+ const planB = planner2.create({ objective: 'Plan B', scope: 'sync-b' });
89
+
90
+ const reloaded = new Planner(join(tempDir, 'plans.json'));
91
+ const ids = reloaded.list().map((plan) => plan.id);
92
+
93
+ expect(ids).toContain(planA.id);
94
+ expect(ids).toContain(planB.id);
95
+ expect(ids).toHaveLength(2);
96
+ });
77
97
  });
78
98
 
79
99
  describe('get', () => {
@@ -120,26 +140,36 @@ describe('Planner', () => {
120
140
 
121
141
  it('should approve a plan with A+ grade', () => {
122
142
  const plan = planner.create({
123
- objective: 'Well-graded plan',
124
- scope: 'test',
143
+ objective: 'Implement a Redis caching layer for the API to reduce DB load by 50%',
144
+ scope: 'Backend API services only. Does not include frontend caching or CDN.',
125
145
  tasks: [
126
- { title: 'Task 1', description: 'A well-described task for implementation' },
127
- { title: 'Task 2', description: 'Another well-described task for testing' },
146
+ {
147
+ title: 'Set up Redis client',
148
+ description: 'Install and configure Redis connection pool',
149
+ },
150
+ {
151
+ title: 'Add cache middleware',
152
+ description: 'Express middleware for transparent caching',
153
+ },
154
+ {
155
+ title: 'Add invalidation logic',
156
+ description: 'Purge cache on write operations to ensure consistency',
157
+ },
158
+ {
159
+ title: 'Write integration tests',
160
+ description: 'Test cache hit/miss scenarios with Redis',
161
+ },
162
+ { title: 'Add monitoring', description: 'Track and verify cache hit rate metrics' },
128
163
  ],
129
- decisions: ['Use TypeScript for type safety'],
130
- });
131
- // Grade the plan set an A+ grade check
132
- const p = planner.get(plan.id)!;
133
- p.latestCheck = {
134
- checkId: 'chk-test-aplus',
135
- planId: plan.id,
136
- grade: 'A+',
137
- score: 98,
138
- gaps: [],
139
- iteration: 1,
140
- checkedAt: Date.now(),
141
- };
142
- p.checks = [p.latestCheck];
164
+ decisions: [
165
+ 'Use Redis because it provides sub-millisecond latency and supports TTL natively',
166
+ 'Set TTL to 5 minutes since average data freshness requirement is 10 minutes',
167
+ ],
168
+ alternatives: TWO_ALTERNATIVES,
169
+ });
170
+ const check = planner.grade(plan.id);
171
+ expect(check.score).toBeGreaterThanOrEqual(95);
172
+ expect(check.grade).toMatch(/^A/);
143
173
  const approved = planner.approve(plan.id);
144
174
  expect(approved.status).toBe('approved');
145
175
  });
@@ -149,27 +179,8 @@ describe('Planner', () => {
149
179
  objective: 'Bad plan',
150
180
  scope: 'test',
151
181
  });
152
- // Manually set a B grade check on the plan
153
- const p = planner.get(plan.id)!;
154
- p.latestCheck = {
155
- checkId: 'chk-test',
156
- planId: plan.id,
157
- grade: 'B',
158
- score: 82,
159
- gaps: [
160
- {
161
- id: 'gap-1',
162
- severity: 'major',
163
- category: 'completeness',
164
- description: 'Missing tasks',
165
- recommendation: 'Add tasks',
166
- location: 'tasks',
167
- },
168
- ],
169
- iteration: 1,
170
- checkedAt: Date.now(),
171
- };
172
- p.checks = [p.latestCheck];
182
+ const check = planner.grade(plan.id);
183
+ expect(check.score).toBeLessThan(90);
173
184
  expect(() => planner.approve(plan.id)).toThrow(PlanGradeRejectionError);
174
185
  });
175
186
 
@@ -184,68 +195,52 @@ describe('Planner', () => {
184
195
  const lenientPlanner = new Planner(join(tempDir, 'lenient-plans.json'), {
185
196
  minGradeForApproval: 'B',
186
197
  });
187
- const plan = lenientPlanner.create({ objective: 'B-grade plan', scope: 'test' });
188
- // Set a B grade check
189
- const p = lenientPlanner.get(plan.id)!;
190
- p.latestCheck = {
191
- checkId: 'chk-test-b',
192
- planId: plan.id,
193
- grade: 'B',
194
- score: 82,
195
- gaps: [],
196
- iteration: 1,
197
- checkedAt: Date.now(),
198
- };
199
- p.checks = [p.latestCheck];
200
- // B grade should pass with B threshold
198
+ const plan = lenientPlanner.create({
199
+ objective: 'B-grade plan with enough detail',
200
+ scope: 'Test scope with clear boundary',
201
+ decisions: ['Use TypeScript for compile-time safety'],
202
+ tasks: [
203
+ { title: 'Task 1', description: 'Implement the first part of the change' },
204
+ { title: 'Task 2', description: 'Validate the second part of the change' },
205
+ ],
206
+ });
207
+ const check = lenientPlanner.grade(plan.id);
208
+ expect(check.grade).toBe('B');
201
209
  const approved = lenientPlanner.approve(plan.id);
202
210
  expect(approved.status).toBe('approved');
203
211
  });
204
212
 
205
213
  it('should reject with PlanGradeRejectionError containing gap details', () => {
206
- const plan = planner.create({ objective: 'Gap details', scope: 'test' });
207
- const p = planner.get(plan.id)!;
208
- const testGaps = [
209
- {
210
- id: 'gap-crit',
211
- severity: 'critical' as const,
212
- category: 'structure',
213
- description: 'Missing critical structure',
214
- recommendation: 'Fix structure',
215
- location: 'tasks',
216
- },
217
- {
218
- id: 'gap-maj',
219
- severity: 'major' as const,
220
- category: 'completeness',
221
- description: 'Incomplete scope',
222
- recommendation: 'Add scope details',
223
- location: 'scope',
224
- },
225
- ];
226
- p.latestCheck = {
227
- checkId: 'chk-test-gaps',
228
- planId: plan.id,
229
- grade: 'C',
230
- score: 65,
231
- gaps: testGaps,
232
- iteration: 1,
233
- checkedAt: Date.now(),
234
- };
235
- p.checks = [p.latestCheck];
214
+ const plan = planner.create({ objective: '', scope: '' });
215
+ const check = planner.grade(plan.id);
236
216
  try {
237
217
  planner.approve(plan.id);
238
218
  expect.unreachable('Should have thrown');
239
219
  } catch (err) {
240
220
  expect(err).toBeInstanceOf(PlanGradeRejectionError);
241
221
  const rejection = err as PlanGradeRejectionError;
242
- expect(rejection.grade).toBe('C');
243
- expect(rejection.score).toBe(65);
222
+ expect(rejection.grade).toBe(check.grade);
223
+ expect(rejection.score).toBe(check.score);
244
224
  expect(rejection.minGrade).toBe('A');
245
- expect(rejection.gaps).toHaveLength(2);
225
+ expect(rejection.gaps).toEqual(check.gaps);
246
226
  expect(rejection.message).toContain('below the minimum required grade A');
247
227
  }
248
228
  });
229
+
230
+ it('should respect grading performed by another planner instance', () => {
231
+ const strictPlanner = new Planner(join(tempDir, 'shared-plans.json'), {
232
+ minGradeForApproval: 'A',
233
+ });
234
+ const gradingPlanner = new Planner(join(tempDir, 'shared-plans.json'), {
235
+ minGradeForApproval: 'A',
236
+ });
237
+ const plan = strictPlanner.create({ objective: '', scope: '' });
238
+
239
+ const check = gradingPlanner.grade(plan.id);
240
+
241
+ expect(check.grade).toBe('F');
242
+ expect(() => strictPlanner.approve(plan.id)).toThrow(PlanGradeRejectionError);
243
+ });
249
244
  });
250
245
 
251
246
  describe('startExecution', () => {
@@ -592,10 +587,11 @@ describe('Planner', () => {
592
587
  { title: 'Task C', description: 'Third task (not in cycle)' },
593
588
  ],
594
589
  });
595
- // Manually create circular deps
596
- const p = planner.get(plan.id)!;
597
- p.tasks[0].dependsOn = ['task-2'];
598
- p.tasks[1].dependsOn = ['task-1'];
590
+ planner.splitTasks(plan.id, [
591
+ { title: 'Task A', description: 'First task in the cycle', dependsOn: ['task-2'] },
592
+ { title: 'Task B', description: 'Second task in the cycle', dependsOn: ['task-1'] },
593
+ { title: 'Task C', description: 'Third task (not in cycle)' },
594
+ ]);
599
595
  const check = planner.grade(plan.id);
600
596
  const circGap = check.gaps.find((g) => g.description.includes('Circular'));
601
597
  expect(circGap).toBeDefined();
@@ -77,8 +77,38 @@ export class Planner {
77
77
  }
78
78
  }
79
79
 
80
- private save(): void {
80
+ private refresh(): void {
81
+ this.store = this.load();
82
+ }
83
+
84
+ private mergeLatestStore(deletedPlanIds: string[] = []): void {
85
+ const deleted = new Set(deletedPlanIds);
86
+ const latest = this.load();
87
+ const merged = new Map<string, Plan>();
88
+
89
+ for (const plan of latest.plans) {
90
+ if (!deleted.has(plan.id)) {
91
+ merged.set(plan.id, plan);
92
+ }
93
+ }
94
+
95
+ for (const plan of this.store.plans) {
96
+ if (deleted.has(plan.id)) continue;
97
+ const existing = merged.get(plan.id);
98
+ if (!existing || plan.updatedAt >= existing.updatedAt) {
99
+ merged.set(plan.id, plan);
100
+ }
101
+ }
102
+
103
+ this.store = {
104
+ version: latest.version ?? this.store.version ?? '1.0',
105
+ plans: [...merged.values()],
106
+ };
107
+ }
108
+
109
+ private save(deletedPlanIds: string[] = []): void {
81
110
  mkdirSync(dirname(this.filePath), { recursive: true });
111
+ this.mergeLatestStore(deletedPlanIds);
82
112
  writeFileSync(this.filePath, JSON.stringify(this.store, null, 2), 'utf-8');
83
113
  }
84
114
 
@@ -88,8 +118,13 @@ export class Planner {
88
118
  plan.updatedAt = r.updatedAt;
89
119
  }
90
120
 
121
+ private findPlan(planId: string): Plan | null {
122
+ return this.store.plans.find((p) => p.id === planId) ?? null;
123
+ }
124
+
91
125
  private requirePlan(planId: string): Plan {
92
- const plan = this.get(planId);
126
+ this.refresh();
127
+ const plan = this.findPlan(planId);
93
128
  if (!plan) throw new Error(`Plan not found: ${planId}`);
94
129
  return plan;
95
130
  }
@@ -101,6 +136,7 @@ export class Planner {
101
136
  }
102
137
 
103
138
  create(params: Parameters<typeof createPlanObject>[0]): Plan {
139
+ this.refresh();
104
140
  const plan = createPlanObject(params);
105
141
  this.store.plans.push(plan);
106
142
  this.save();
@@ -108,18 +144,21 @@ export class Planner {
108
144
  }
109
145
 
110
146
  get(planId: string): Plan | null {
111
- return this.store.plans.find((p) => p.id === planId) ?? null;
147
+ this.refresh();
148
+ return this.findPlan(planId);
112
149
  }
113
150
 
114
151
  list(): Plan[] {
152
+ this.refresh();
115
153
  return [...this.store.plans];
116
154
  }
117
155
 
118
156
  remove(planId: string): boolean {
157
+ this.refresh();
119
158
  const idx = this.store.plans.findIndex((p) => p.id === planId);
120
159
  if (idx < 0) return false;
121
160
  this.store.plans.splice(idx, 1);
122
- this.save();
161
+ this.save([planId]);
123
162
  return true;
124
163
  }
125
164
 
@@ -146,6 +185,14 @@ export class Planner {
146
185
  return plan;
147
186
  }
148
187
 
188
+ patchPlan(planId: string, fields: Partial<Plan>): Plan {
189
+ const plan = this.requirePlan(planId);
190
+ Object.assign(plan, fields);
191
+ plan.updatedAt = Date.now();
192
+ this.save();
193
+ return plan;
194
+ }
195
+
149
196
  startExecution(planId: string): Plan {
150
197
  const plan = this.requirePlan(planId);
151
198
  this.transition(plan, 'executing');
@@ -220,10 +267,12 @@ export class Planner {
220
267
  }
221
268
 
222
269
  getExecuting(): Plan[] {
270
+ this.refresh();
223
271
  return this.store.plans.filter((p) => p.status === 'executing' || p.status === 'validating');
224
272
  }
225
273
 
226
274
  getActive(): Plan[] {
275
+ this.refresh();
227
276
  return this.store.plans.filter(
228
277
  (p) =>
229
278
  p.status === 'brainstorming' ||
@@ -435,6 +484,7 @@ export class Planner {
435
484
  }
436
485
 
437
486
  archive(olderThanDays?: number): Plan[] {
487
+ this.refresh();
438
488
  const cutoff =
439
489
  olderThanDays !== undefined
440
490
  ? Date.now() - olderThanDays * 24 * 60 * 60 * 1000
@@ -460,6 +510,7 @@ export class Planner {
460
510
  closedIds: string[];
461
511
  closedPlans: Array<{ id: string; previousStatus: string; reason: string }>;
462
512
  } {
513
+ this.refresh();
463
514
  const now = Date.now();
464
515
  const forceAll = olderThanMs === 0;
465
516
  const defaultTtl = forceAll ? 0 : 30 * 60 * 1000; // 30 minutes for draft/approved
@@ -522,6 +573,7 @@ export class Planner {
522
573
  totalTasks: number;
523
574
  tasksByStatus: Record<TaskStatus, number>;
524
575
  } {
576
+ this.refresh();
525
577
  const plans = this.store.plans;
526
578
  const byStatus = {
527
579
  brainstorming: 0,
@@ -1,6 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import {
3
- DRIFT_WEIGHTS,
4
3
  calculateDriftScore,
5
4
  computeExecutionSummary,
6
5
  buildReconciliationReport,
@@ -9,14 +8,6 @@ import {
9
8
  import type { DriftItem, PlanTask } from './planner-types.js';
10
9
 
11
10
  describe('reconciliation-engine', () => {
12
- describe('DRIFT_WEIGHTS', () => {
13
- it('has correct weights', () => {
14
- expect(DRIFT_WEIGHTS.high).toBe(20);
15
- expect(DRIFT_WEIGHTS.medium).toBe(10);
16
- expect(DRIFT_WEIGHTS.low).toBe(5);
17
- });
18
- });
19
-
20
11
  describe('calculateDriftScore', () => {
21
12
  it('returns 100 for no drift items', () => {
22
13
  expect(calculateDriftScore([])).toBe(100);
@@ -92,6 +83,7 @@ describe('reconciliation-engine', () => {
92
83
 
93
84
  describe('buildReconciliationReport', () => {
94
85
  it('builds a report with accuracy score', () => {
86
+ const before = Date.now();
95
87
  const report = buildReconciliationReport('plan-1', {
96
88
  actualOutcome: 'Done',
97
89
  driftItems: [{ type: 'skipped', description: 'x', impact: 'low', rationale: 'r' }],
@@ -100,7 +92,8 @@ describe('reconciliation-engine', () => {
100
92
  expect(report.accuracy).toBe(95);
101
93
  expect(report.driftItems).toHaveLength(1);
102
94
  expect(report.summary).toBe('Done');
103
- expect(report.reconciledAt).toBeGreaterThan(0);
95
+ expect(report.reconciledAt).toBeGreaterThanOrEqual(before);
96
+ expect(report.reconciledAt).toBeLessThanOrEqual(Date.now());
104
97
  });
105
98
  it('defaults to empty drift items', () => {
106
99
  const report = buildReconciliationReport('plan-2', { actualOutcome: 'OK' });
@@ -294,9 +294,4 @@ describe('assessTaskComplexity — reasoning', () => {
294
294
  const result = assess({ prompt: 'fix typo' });
295
295
  expect(result.reasoning).toContain('No complexity signals detected');
296
296
  });
297
-
298
- it('always returns 6 signals', () => {
299
- const result = assess({ prompt: 'anything' });
300
- expect(result.signals).toHaveLength(6);
301
- });
302
297
  });
@@ -25,6 +25,7 @@ describe('task-verifier', () => {
25
25
  const existing: TaskEvidence[] = [
26
26
  { criterion: 'cr1', content: 'result', type: 'description', submittedAt: 100 },
27
27
  ];
28
+ const before = Date.now();
28
29
  const result = createEvidence(existing, {
29
30
  criterion: 'cr2',
30
31
  content: 'output',
@@ -32,7 +33,8 @@ describe('task-verifier', () => {
32
33
  });
33
34
  expect(result).toHaveLength(2);
34
35
  expect(result[1].criterion).toBe('cr2');
35
- expect(result[1].submittedAt).toBeGreaterThan(0);
36
+ expect(result[1].submittedAt).toBeGreaterThanOrEqual(before);
37
+ expect(result[1].submittedAt).toBeLessThanOrEqual(Date.now());
36
38
  });
37
39
  it('does not mutate original array', () => {
38
40
  const existing: TaskEvidence[] = [];
@@ -181,10 +181,6 @@ describe('All generic playbooks', () => {
181
181
  // ─── Individual playbook tests ────────────────────────────────────
182
182
 
183
183
  describe('brainstormingPlaybook', () => {
184
- it('should have correct id', () => {
185
- expect(brainstormingPlaybook.id).toBe('generic-brainstorming');
186
- });
187
-
188
184
  it('should match BUILD and PLAN intents', () => {
189
185
  expect(brainstormingPlaybook.matchIntents).toContain('BUILD');
190
186
  expect(brainstormingPlaybook.matchIntents).toContain('PLAN');
@@ -221,10 +217,6 @@ describe('brainstormingPlaybook', () => {
221
217
  });
222
218
 
223
219
  describe('codeReviewPlaybook', () => {
224
- it('should have correct id', () => {
225
- expect(codeReviewPlaybook.id).toBe('generic-code-review');
226
- });
227
-
228
220
  it('should match REVIEW intent only', () => {
229
221
  expect(codeReviewPlaybook.matchIntents).toEqual(['REVIEW']);
230
222
  });
@@ -260,10 +252,6 @@ describe('codeReviewPlaybook', () => {
260
252
  });
261
253
 
262
254
  describe('onboardingPlaybook', () => {
263
- it('should have correct id', () => {
264
- expect(onboardingPlaybook.id).toBe('generic-onboarding');
265
- });
266
-
267
255
  it('should match PLAN intent', () => {
268
256
  expect(onboardingPlaybook.matchIntents).toContain('PLAN');
269
257
  });
@@ -288,10 +276,6 @@ describe('onboardingPlaybook', () => {
288
276
  });
289
277
 
290
278
  describe('subagentExecutionPlaybook', () => {
291
- it('should have correct id', () => {
292
- expect(subagentExecutionPlaybook.id).toBe('generic-subagent-execution');
293
- });
294
-
295
279
  it('should match BUILD and IMPROVE intents', () => {
296
280
  expect(subagentExecutionPlaybook.matchIntents).toContain('BUILD');
297
281
  expect(subagentExecutionPlaybook.matchIntents).toContain('IMPROVE');
@@ -314,10 +298,6 @@ describe('subagentExecutionPlaybook', () => {
314
298
  });
315
299
 
316
300
  describe('systematicDebuggingPlaybook', () => {
317
- it('should have correct id', () => {
318
- expect(systematicDebuggingPlaybook.id).toBe('generic-systematic-debugging');
319
- });
320
-
321
301
  it('should match FIX intent only', () => {
322
302
  expect(systematicDebuggingPlaybook.matchIntents).toEqual(['FIX']);
323
303
  });
@@ -361,10 +341,6 @@ describe('systematicDebuggingPlaybook', () => {
361
341
  });
362
342
 
363
343
  describe('tddPlaybook', () => {
364
- it('should have correct id', () => {
365
- expect(tddPlaybook.id).toBe('generic-tdd');
366
- });
367
-
368
344
  it('should match BUILD and FIX intents', () => {
369
345
  expect(tddPlaybook.matchIntents).toContain('BUILD');
370
346
  expect(tddPlaybook.matchIntents).toContain('FIX');
@@ -399,10 +375,6 @@ describe('tddPlaybook', () => {
399
375
  });
400
376
 
401
377
  describe('verificationPlaybook', () => {
402
- it('should have correct id', () => {
403
- expect(verificationPlaybook.id).toBe('generic-verification');
404
- });
405
-
406
378
  it('should match BUILD, FIX, IMPROVE, and DELIVER intents', () => {
407
379
  expect(verificationPlaybook.matchIntents).toContain('BUILD');
408
380
  expect(verificationPlaybook.matchIntents).toContain('FIX');
@@ -9,61 +9,6 @@ import { describe, it, expect } from 'vitest';
9
9
  import * as playbooksModule from './index.js';
10
10
 
11
11
  describe('playbooks barrel export', () => {
12
- it('should export getBuiltinPlaybook function', () => {
13
- expect(typeof playbooksModule.getBuiltinPlaybook).toBe('function');
14
- });
15
-
16
- it('should export getAllBuiltinPlaybooks function', () => {
17
- expect(typeof playbooksModule.getAllBuiltinPlaybooks).toBe('function');
18
- });
19
-
20
- it('should export scorePlaybook function', () => {
21
- expect(typeof playbooksModule.scorePlaybook).toBe('function');
22
- });
23
-
24
- it('should export mergePlaybooks function', () => {
25
- expect(typeof playbooksModule.mergePlaybooks).toBe('function');
26
- });
27
-
28
- it('should export matchPlaybooks function', () => {
29
- expect(typeof playbooksModule.matchPlaybooks).toBe('function');
30
- });
31
-
32
- it('should export playbookDefinitionToEntry function', () => {
33
- expect(typeof playbooksModule.playbookDefinitionToEntry).toBe('function');
34
- });
35
-
36
- it('should export entryToPlaybookDefinition function', () => {
37
- expect(typeof playbooksModule.entryToPlaybookDefinition).toBe('function');
38
- });
39
-
40
- it('should export seedDefaultPlaybooks function', () => {
41
- expect(typeof playbooksModule.seedDefaultPlaybooks).toBe('function');
42
- });
43
-
44
- it('should export PlaybookExecutor class', () => {
45
- expect(typeof playbooksModule.PlaybookExecutor).toBe('function');
46
- const executor = new playbooksModule.PlaybookExecutor();
47
- expect(executor).toBeInstanceOf(playbooksModule.PlaybookExecutor);
48
- });
49
-
50
- it('should not export unexpected runtime values', () => {
51
- const expectedExports = [
52
- 'getBuiltinPlaybook',
53
- 'getAllBuiltinPlaybooks',
54
- 'scorePlaybook',
55
- 'mergePlaybooks',
56
- 'matchPlaybooks',
57
- 'playbookDefinitionToEntry',
58
- 'entryToPlaybookDefinition',
59
- 'seedDefaultPlaybooks',
60
- 'PlaybookExecutor',
61
- ];
62
-
63
- const actualExports = Object.keys(playbooksModule);
64
- expect(actualExports.sort()).toEqual(expectedExports.sort());
65
- });
66
-
67
12
  it('should return playbooks from getAllBuiltinPlaybooks via barrel', () => {
68
13
  const playbooks = playbooksModule.getAllBuiltinPlaybooks();
69
14
  expect(playbooks.length).toBeGreaterThanOrEqual(6);