@soleri/core 9.15.0 → 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 (288) 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/capabilities/chain-mapping.d.ts.map +1 -1
  8. package/dist/capabilities/chain-mapping.js +5 -4
  9. package/dist/capabilities/chain-mapping.js.map +1 -1
  10. package/dist/capabilities/registry.d.ts +6 -0
  11. package/dist/capabilities/registry.d.ts.map +1 -1
  12. package/dist/capabilities/registry.js +3 -2
  13. package/dist/capabilities/registry.js.map +1 -1
  14. package/dist/context/context-engine.js +1 -1
  15. package/dist/context/context-engine.js.map +1 -1
  16. package/dist/engine/core-ops.d.ts.map +1 -1
  17. package/dist/engine/core-ops.js +38 -1
  18. package/dist/engine/core-ops.js.map +1 -1
  19. package/dist/flows/epilogue.d.ts +5 -1
  20. package/dist/flows/epilogue.d.ts.map +1 -1
  21. package/dist/flows/epilogue.js +11 -3
  22. package/dist/flows/epilogue.js.map +1 -1
  23. package/dist/flows/executor.d.ts.map +1 -1
  24. package/dist/flows/executor.js +13 -5
  25. package/dist/flows/executor.js.map +1 -1
  26. package/dist/flows/index.d.ts +1 -2
  27. package/dist/flows/index.d.ts.map +1 -1
  28. package/dist/flows/index.js +1 -0
  29. package/dist/flows/index.js.map +1 -1
  30. package/dist/flows/plan-builder.d.ts +17 -1
  31. package/dist/flows/plan-builder.d.ts.map +1 -1
  32. package/dist/flows/plan-builder.js +67 -6
  33. package/dist/flows/plan-builder.js.map +1 -1
  34. package/dist/flows/probes.d.ts +1 -1
  35. package/dist/flows/probes.d.ts.map +1 -1
  36. package/dist/flows/probes.js +15 -3
  37. package/dist/flows/probes.js.map +1 -1
  38. package/dist/flows/types.d.ts +31 -4
  39. package/dist/flows/types.d.ts.map +1 -1
  40. package/dist/flows/types.js +6 -1
  41. package/dist/flows/types.js.map +1 -1
  42. package/dist/index.d.ts +8 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +7 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/packs/pack-installer.d.ts.map +1 -1
  47. package/dist/packs/pack-installer.js +28 -2
  48. package/dist/packs/pack-installer.js.map +1 -1
  49. package/dist/planning/planner-types.d.ts +2 -0
  50. package/dist/planning/planner-types.d.ts.map +1 -1
  51. package/dist/planning/planner.d.ts +1 -0
  52. package/dist/planning/planner.d.ts.map +1 -1
  53. package/dist/planning/planner.js +7 -0
  54. package/dist/planning/planner.js.map +1 -1
  55. package/dist/playbooks/playbook-executor.d.ts +10 -1
  56. package/dist/playbooks/playbook-executor.d.ts.map +1 -1
  57. package/dist/playbooks/playbook-executor.js +8 -2
  58. package/dist/playbooks/playbook-executor.js.map +1 -1
  59. package/dist/playbooks/playbook-types.d.ts +8 -0
  60. package/dist/playbooks/playbook-types.d.ts.map +1 -1
  61. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  62. package/dist/runtime/admin-extra-ops.js +30 -0
  63. package/dist/runtime/admin-extra-ops.js.map +1 -1
  64. package/dist/runtime/admin-ops.d.ts.map +1 -1
  65. package/dist/runtime/admin-ops.js +60 -21
  66. package/dist/runtime/admin-ops.js.map +1 -1
  67. package/dist/runtime/admin-setup-ops.d.ts +11 -0
  68. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  69. package/dist/runtime/admin-setup-ops.js +87 -17
  70. package/dist/runtime/admin-setup-ops.js.map +1 -1
  71. package/dist/runtime/capture-ops.d.ts.map +1 -1
  72. package/dist/runtime/capture-ops.js +38 -12
  73. package/dist/runtime/capture-ops.js.map +1 -1
  74. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  75. package/dist/runtime/facades/brain-facade.js +16 -4
  76. package/dist/runtime/facades/brain-facade.js.map +1 -1
  77. package/dist/runtime/facades/context-facade.d.ts.map +1 -1
  78. package/dist/runtime/facades/context-facade.js +9 -3
  79. package/dist/runtime/facades/context-facade.js.map +1 -1
  80. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/memory-facade.js +20 -7
  82. package/dist/runtime/facades/memory-facade.js.map +1 -1
  83. package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
  84. package/dist/runtime/facades/orchestrate-facade.js +12 -0
  85. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  86. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  87. package/dist/runtime/facades/plan-facade.js +113 -4
  88. package/dist/runtime/facades/plan-facade.js.map +1 -1
  89. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  90. package/dist/runtime/facades/vault-facade.js +24 -3
  91. package/dist/runtime/facades/vault-facade.js.map +1 -1
  92. package/dist/runtime/orchestrate-ops.d.ts +21 -0
  93. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  94. package/dist/runtime/orchestrate-ops.js +132 -38
  95. package/dist/runtime/orchestrate-ops.js.map +1 -1
  96. package/dist/runtime/schema-helpers.d.ts.map +1 -1
  97. package/dist/runtime/schema-helpers.js +4 -0
  98. package/dist/runtime/schema-helpers.js.map +1 -1
  99. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  100. package/dist/runtime/vault-linking-ops.js +16 -3
  101. package/dist/runtime/vault-linking-ops.js.map +1 -1
  102. package/dist/scheduler/cron-validator.d.ts +15 -0
  103. package/dist/scheduler/cron-validator.d.ts.map +1 -0
  104. package/dist/scheduler/cron-validator.js +93 -0
  105. package/dist/scheduler/cron-validator.js.map +1 -0
  106. package/dist/scheduler/platform-linux.d.ts +14 -0
  107. package/dist/scheduler/platform-linux.d.ts.map +1 -0
  108. package/dist/scheduler/platform-linux.js +107 -0
  109. package/dist/scheduler/platform-linux.js.map +1 -0
  110. package/dist/scheduler/platform-macos.d.ts +15 -0
  111. package/dist/scheduler/platform-macos.d.ts.map +1 -0
  112. package/dist/scheduler/platform-macos.js +131 -0
  113. package/dist/scheduler/platform-macos.js.map +1 -0
  114. package/dist/scheduler/scheduler-ops.d.ts +14 -0
  115. package/dist/scheduler/scheduler-ops.d.ts.map +1 -0
  116. package/dist/scheduler/scheduler-ops.js +77 -0
  117. package/dist/scheduler/scheduler-ops.js.map +1 -0
  118. package/dist/scheduler/scheduler.d.ts +55 -0
  119. package/dist/scheduler/scheduler.d.ts.map +1 -0
  120. package/dist/scheduler/scheduler.js +144 -0
  121. package/dist/scheduler/scheduler.js.map +1 -0
  122. package/dist/scheduler/types.d.ts +48 -0
  123. package/dist/scheduler/types.d.ts.map +1 -0
  124. package/dist/scheduler/types.js +6 -0
  125. package/dist/scheduler/types.js.map +1 -0
  126. package/dist/skills/sync-skills.d.ts +11 -0
  127. package/dist/skills/sync-skills.d.ts.map +1 -1
  128. package/dist/skills/sync-skills.js +132 -38
  129. package/dist/skills/sync-skills.js.map +1 -1
  130. package/dist/utils/worktree-reaper.d.ts +38 -0
  131. package/dist/utils/worktree-reaper.d.ts.map +1 -0
  132. package/dist/utils/worktree-reaper.js +85 -0
  133. package/dist/utils/worktree-reaper.js.map +1 -0
  134. package/dist/vault/scope-detector.d.ts.map +1 -1
  135. package/dist/vault/scope-detector.js +37 -4
  136. package/dist/vault/scope-detector.js.map +1 -1
  137. package/dist/vault/vault-entries.d.ts.map +1 -1
  138. package/dist/vault/vault-entries.js +3 -1
  139. package/dist/vault/vault-entries.js.map +1 -1
  140. package/package.json +1 -1
  141. package/src/agency/agency-manager.test.ts +4 -4
  142. package/src/agency/default-rules.test.ts +0 -13
  143. package/src/brain/brain-intelligence.test.ts +0 -5
  144. package/src/brain/second-brain-features.test.ts +2 -14
  145. package/src/capabilities/chain-mapping.test.ts +1 -6
  146. package/src/capabilities/chain-mapping.ts +6 -4
  147. package/src/capabilities/registry.test.ts +1 -1
  148. package/src/capabilities/registry.ts +9 -2
  149. package/src/chat/agent-loop.test.ts +1 -1
  150. package/src/chat/chat-enhanced.test.ts +0 -8
  151. package/src/claudemd/compose.test.ts +0 -5
  152. package/src/context/context-engine.test.ts +0 -1
  153. package/src/context/context-engine.ts +1 -1
  154. package/src/control/intent-router.test.ts +2 -2
  155. package/src/curator/tag-manager.test.ts +0 -4
  156. package/src/domain-packs/types.test.ts +0 -5
  157. package/src/dream/dream.test.ts +0 -7
  158. package/src/enforcement/registry.test.ts +2 -2
  159. package/src/engine/core-ops.test.ts +4 -22
  160. package/src/engine/core-ops.ts +36 -1
  161. package/src/engine/module-manifest.test.ts +1 -31
  162. package/src/engine/register-engine.test.ts +3 -33
  163. package/src/errors/retry.test.ts +3 -1
  164. package/src/flows/chain-runner.test.ts +0 -6
  165. package/src/flows/context-router.test.ts +3 -3
  166. package/src/flows/epilogue.test.ts +40 -2
  167. package/src/flows/epilogue.ts +11 -2
  168. package/src/flows/executor.test.ts +48 -2
  169. package/src/flows/executor.ts +15 -5
  170. package/src/flows/index.ts +1 -3
  171. package/src/flows/plan-builder.test.ts +201 -0
  172. package/src/flows/plan-builder.ts +81 -5
  173. package/src/flows/probes.ts +17 -3
  174. package/src/flows/types.ts +31 -2
  175. package/src/health/health-registry.test.ts +3 -1
  176. package/src/index.ts +17 -0
  177. package/src/intake/dedup-gate.test.ts +2 -6
  178. package/src/intake/text-ingester.test.ts +3 -4
  179. package/src/llm/llm-client.test.ts +1 -1
  180. package/src/llm/utils.test.ts +1 -1
  181. package/src/migrations/migration-runner.test.ts +0 -1
  182. package/src/operator/operator-context-store.test.ts +0 -13
  183. package/src/operator/operator-profile.test.ts +2 -20
  184. package/src/packs/pack-installer.ts +28 -2
  185. package/src/packs/pack-system.test.ts +2 -2
  186. package/src/persona/defaults.test.ts +19 -19
  187. package/src/planning/gap-passes.test.ts +0 -46
  188. package/src/planning/gap-patterns.test.ts +0 -42
  189. package/src/planning/goal-ancestry.test.ts +3 -1
  190. package/src/planning/plan-lifecycle.test.ts +15 -7
  191. package/src/planning/planner-types.ts +2 -0
  192. package/src/planning/planner.ts +8 -0
  193. package/src/planning/reconciliation-engine.test.ts +3 -10
  194. package/src/planning/task-complexity-assessor.test.ts +0 -5
  195. package/src/planning/task-verifier.test.ts +3 -1
  196. package/src/playbooks/generic/generic-playbooks.test.ts +0 -28
  197. package/src/playbooks/index.test.ts +0 -55
  198. package/src/playbooks/playbook-executor.test.ts +76 -0
  199. package/src/playbooks/playbook-executor.ts +24 -3
  200. package/src/playbooks/playbook-types.ts +8 -0
  201. package/src/plugins/plugin-registry.test.ts +6 -2
  202. package/src/project/project-registry.test.ts +2 -0
  203. package/src/queue/async-infrastructure.test.ts +6 -4
  204. package/src/queue/job-queue.test.ts +13 -7
  205. package/src/runtime/admin-extra-ops.test.ts +35 -30
  206. package/src/runtime/admin-extra-ops.ts +30 -0
  207. package/src/runtime/admin-ops.test.ts +0 -4
  208. package/src/runtime/admin-ops.ts +63 -21
  209. package/src/runtime/admin-setup-ops.test.ts +185 -13
  210. package/src/runtime/admin-setup-ops.ts +86 -16
  211. package/src/runtime/archive-ops.test.ts +0 -28
  212. package/src/runtime/branching-ops.test.ts +0 -17
  213. package/src/runtime/capture-ops.test.ts +41 -16
  214. package/src/runtime/capture-ops.ts +78 -46
  215. package/src/runtime/chain-ops.test.ts +0 -21
  216. package/src/runtime/facades/admin-facade.test.ts +0 -34
  217. package/src/runtime/facades/agency-facade.test.ts +0 -39
  218. package/src/runtime/facades/archive-facade.test.ts +0 -43
  219. package/src/runtime/facades/brain-facade.test.ts +8 -99
  220. package/src/runtime/facades/brain-facade.ts +29 -12
  221. package/src/runtime/facades/branching-facade.test.ts +30 -17
  222. package/src/runtime/facades/chat-facade.test.ts +0 -91
  223. package/src/runtime/facades/chat-service-ops.test.ts +0 -24
  224. package/src/runtime/facades/chat-session-ops.test.ts +0 -12
  225. package/src/runtime/facades/chat-transport-ops.test.ts +0 -23
  226. package/src/runtime/facades/context-facade.test.ts +0 -17
  227. package/src/runtime/facades/context-facade.ts +11 -4
  228. package/src/runtime/facades/control-facade.test.ts +0 -30
  229. package/src/runtime/facades/curator-facade.test.ts +0 -33
  230. package/src/runtime/facades/intake-facade.test.ts +0 -33
  231. package/src/runtime/facades/links-facade.test.ts +0 -37
  232. package/src/runtime/facades/loop-facade.test.ts +0 -26
  233. package/src/runtime/facades/memory-facade.test.ts +0 -18
  234. package/src/runtime/facades/memory-facade.ts +27 -11
  235. package/src/runtime/facades/operator-facade.test.ts +0 -31
  236. package/src/runtime/facades/orchestrate-facade.test.ts +0 -21
  237. package/src/runtime/facades/orchestrate-facade.ts +12 -0
  238. package/src/runtime/facades/plan-facade.test.ts +7 -32
  239. package/src/runtime/facades/plan-facade.ts +137 -4
  240. package/src/runtime/facades/review-facade.test.ts +1 -49
  241. package/src/runtime/facades/sync-facade.test.ts +24 -41
  242. package/src/runtime/facades/tier-facade.test.ts +30 -22
  243. package/src/runtime/facades/vault-facade.test.ts +0 -41
  244. package/src/runtime/facades/vault-facade.ts +26 -3
  245. package/src/runtime/grading-ops.test.ts +0 -27
  246. package/src/runtime/intake-ops.test.ts +0 -19
  247. package/src/runtime/loop-ops.test.ts +0 -48
  248. package/src/runtime/memory-cross-project-ops.test.ts +0 -14
  249. package/src/runtime/memory-extra-ops.test.ts +4 -8
  250. package/src/runtime/orchestrate-ops.test.ts +238 -19
  251. package/src/runtime/orchestrate-ops.ts +166 -41
  252. package/src/runtime/pack-ops.test.ts +0 -26
  253. package/src/runtime/planning-extra-ops.test.ts +2 -14
  254. package/src/runtime/playbook-ops-execution.test.ts +9 -20
  255. package/src/runtime/playbook-ops.test.ts +4 -67
  256. package/src/runtime/review-ops.test.ts +0 -15
  257. package/src/runtime/schema-helpers.ts +4 -0
  258. package/src/runtime/sync-ops.test.ts +0 -18
  259. package/src/runtime/tier-ops.test.ts +0 -21
  260. package/src/runtime/vault-extra-ops.test.ts +0 -12
  261. package/src/runtime/vault-linking-ops.test.ts +0 -4
  262. package/src/runtime/vault-linking-ops.ts +26 -8
  263. package/src/runtime/vault-sharing-ops.test.ts +0 -9
  264. package/src/scheduler/cron-validator.ts +101 -0
  265. package/src/scheduler/platform-linux.ts +122 -0
  266. package/src/scheduler/platform-macos.ts +150 -0
  267. package/src/scheduler/scheduler-ops.ts +77 -0
  268. package/src/scheduler/scheduler.test.ts +247 -0
  269. package/src/scheduler/scheduler.ts +174 -0
  270. package/src/scheduler/types.ts +52 -0
  271. package/src/skills/__tests__/sync-skills.test.ts +6 -17
  272. package/src/skills/global-claude-md.test.ts +113 -0
  273. package/src/skills/sync-skills.ts +143 -35
  274. package/src/skills/validate-skills.test.ts +12 -11
  275. package/src/telemetry/telemetry.test.ts +1 -0
  276. package/src/transport/http-server.test.ts +3 -0
  277. package/src/transport/session-manager.test.ts +3 -1
  278. package/src/transport/token-auth.test.ts +6 -9
  279. package/src/transport/ws-server.test.ts +10 -2
  280. package/src/utils/worktree-reaper.ts +113 -0
  281. package/src/vault/__tests__/vault-characterization.test.ts +0 -108
  282. package/src/vault/linking.test.ts +0 -2
  283. package/src/vault/playbook.test.ts +4 -1
  284. package/src/vault/scope-detector.test.ts +3 -1
  285. package/src/vault/scope-detector.ts +42 -4
  286. package/src/vault/vault-connect.test.ts +1 -1
  287. package/src/vault/vault-entries.ts +3 -1
  288. package/src/vault/vault.test.ts +23 -8
@@ -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);
@@ -217,6 +217,82 @@ describe('PlaybookExecutor', () => {
217
217
  const result = executor.complete(sessionId);
218
218
  expect('error' in result).toBe(true);
219
219
  });
220
+
221
+ // ── evidence source ──────────────────────────────────────────────
222
+
223
+ describe('evidence source', () => {
224
+ function makePlaybookWithUserGate(): PlaybookDefinition {
225
+ return makePlaybook({
226
+ gates: [
227
+ {
228
+ phase: 'completion',
229
+ requirement: 'User confirmed result',
230
+ checkType: 'user-confirm',
231
+ requiresUserEvidence: true,
232
+ },
233
+ ],
234
+ });
235
+ }
236
+
237
+ it('agent-source evidence fails a requiresUserEvidence gate', () => {
238
+ const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
239
+ for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
240
+
241
+ const result = executor.complete(sessionId, {
242
+ gateResults: { 'user-confirm': { satisfied: true, source: 'agent' } },
243
+ });
244
+
245
+ expect('error' in result).toBe(false);
246
+ if ('error' in result) return;
247
+
248
+ expect(result.gatesPassed).toBe(false);
249
+ expect(result.unsatisfiedGates[0]).toContain('user-confirm');
250
+ expect(result.unsatisfiedGates[0]).toContain('requires user confirmation');
251
+ });
252
+
253
+ it('user-source evidence satisfies a requiresUserEvidence gate', () => {
254
+ const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
255
+ for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
256
+
257
+ const result = executor.complete(sessionId, {
258
+ gateResults: { 'user-confirm': { satisfied: true, source: 'user' } },
259
+ });
260
+
261
+ expect('error' in result).toBe(false);
262
+ if ('error' in result) return;
263
+
264
+ expect(result.gatesPassed).toBe(true);
265
+ expect(result.unsatisfiedGates).toHaveLength(0);
266
+ });
267
+
268
+ it('plain boolean true satisfies a gate without requiresUserEvidence', () => {
269
+ const { sessionId, totalSteps } = executor.start(makePlaybook());
270
+ for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
271
+
272
+ const result = executor.complete(sessionId, {
273
+ gateResults: { 'test-pass': true },
274
+ });
275
+
276
+ expect('error' in result).toBe(false);
277
+ if ('error' in result) return;
278
+ expect(result.gatesPassed).toBe(true);
279
+ });
280
+
281
+ it('plain boolean true does not satisfy a requiresUserEvidence gate', () => {
282
+ const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
283
+ for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
284
+
285
+ // bare true = no source = treated as agent
286
+ const result = executor.complete(sessionId, {
287
+ gateResults: { 'user-confirm': true },
288
+ });
289
+
290
+ expect('error' in result).toBe(false);
291
+ if ('error' in result) return;
292
+ expect(result.gatesPassed).toBe(false);
293
+ expect(result.unsatisfiedGates[0]).toContain('requires user confirmation');
294
+ });
295
+ });
220
296
  });
221
297
 
222
298
  // ─── getSession / listSessions ──────────────────────────────────
@@ -72,6 +72,16 @@ export interface CompleteResult {
72
72
  duration: number;
73
73
  }
74
74
 
75
+ /**
76
+ * Evidence record for a gate result.
77
+ * `source` distinguishes agent-collected evidence from explicit user confirmation.
78
+ */
79
+ export interface GateEvidence {
80
+ satisfied: boolean;
81
+ /** Who produced the evidence. Defaults to 'agent' when omitted. */
82
+ source?: 'agent' | 'user';
83
+ }
84
+
75
85
  // =============================================================================
76
86
  // STEP PARSER
77
87
  // =============================================================================
@@ -227,7 +237,7 @@ export class PlaybookExecutor {
227
237
  */
228
238
  complete(
229
239
  sessionId: string,
230
- options?: { abort?: boolean; gateResults?: Record<string, boolean> },
240
+ options?: { abort?: boolean; gateResults?: Record<string, boolean | GateEvidence> },
231
241
  ): CompleteResult | { error: string } {
232
242
  const session = this.sessions.get(sessionId);
233
243
  if (!session) return { error: `Session not found: ${sessionId}` };
@@ -251,8 +261,19 @@ export class PlaybookExecutor {
251
261
  const completionGates = session.gates.filter((g) => g.phase === 'completion');
252
262
  const unsatisfiedGates: string[] = [];
253
263
  for (const gate of completionGates) {
254
- if (!gateResults[gate.checkType]) {
255
- unsatisfiedGates.push(`${gate.checkType}: ${gate.requirement}`);
264
+ const raw = gateResults[gate.checkType];
265
+ const evidence: GateEvidence =
266
+ typeof raw === 'object' && raw !== null ? raw : { satisfied: !!raw, source: 'agent' };
267
+
268
+ const satisfied =
269
+ evidence.satisfied && !(gate.requiresUserEvidence && evidence.source !== 'user');
270
+
271
+ if (!satisfied) {
272
+ const reason =
273
+ gate.requiresUserEvidence && evidence.satisfied && evidence.source !== 'user'
274
+ ? `${gate.checkType}: ${gate.requirement} (requires user confirmation)`
275
+ : `${gate.checkType}: ${gate.requirement}`;
276
+ unsatisfiedGates.push(reason);
256
277
  }
257
278
  }
258
279
 
@@ -48,6 +48,14 @@ export interface PlaybookGate {
48
48
  requirement: string;
49
49
  /** Check type to create/validate */
50
50
  checkType: string;
51
+ /** Whether this gate blocks progression (blocking) or is advisory only (advisory). Defaults to 'blocking'. */
52
+ severity?: 'blocking' | 'advisory';
53
+ /**
54
+ * When true, only user-sourced evidence satisfies this gate.
55
+ * Agent-collected evidence (e.g. automated test runs) does not count.
56
+ * Defaults to false.
57
+ */
58
+ requiresUserEvidence?: boolean;
51
59
  }
52
60
 
53
61
  // =============================================================================
@@ -47,13 +47,15 @@ describe('PluginRegistry — colocated', () => {
47
47
 
48
48
  describe('register', () => {
49
49
  it('registers a plugin and sets initial status to registered', () => {
50
+ const before = Date.now();
50
51
  const loaded = makeLoaded();
51
52
  const registered = registry.register(loaded);
52
53
 
53
54
  expect(registered.id).toBe('test-plugin');
54
55
  expect(registered.status).toBe('registered');
55
56
  expect(registered.facades).toEqual([]);
56
- expect(registered.registeredAt).toBeGreaterThan(0);
57
+ expect(registered.registeredAt).toBeGreaterThanOrEqual(before);
58
+ expect(registered.registeredAt).toBeLessThanOrEqual(Date.now());
57
59
  });
58
60
 
59
61
  it('throws when registering duplicate id', () => {
@@ -86,11 +88,13 @@ describe('PluginRegistry — colocated', () => {
86
88
  }),
87
89
  });
88
90
 
91
+ const before = Date.now();
89
92
  registry.register(loaded);
90
93
  const result = await registry.activate('test-plugin', makeContext(loaded));
91
94
 
92
95
  expect(result.status).toBe('active');
93
- expect(result.activatedAt).toBeGreaterThan(0);
96
+ expect(result.activatedAt).toBeGreaterThanOrEqual(before);
97
+ expect(result.activatedAt).toBeLessThanOrEqual(Date.now());
94
98
  expect(result.facades).toHaveLength(1);
95
99
  expect(result.facades[0].name).toBe('my_facade');
96
100
  expect(result.facades[0].ops).toHaveLength(2);
@@ -214,10 +214,12 @@ describe('ProjectRegistry', () => {
214
214
 
215
215
  describe('register', () => {
216
216
  it('creates a new project and returns it', () => {
217
+ const before = Date.now();
217
218
  const proj = registry.register('/tmp/myproj', 'My Project');
218
219
  expect(proj.path).toBe('/tmp/myproj');
219
220
  expect(proj.name).toBe('My Project');
220
221
  expect(proj.id).toBeTruthy();
222
+ expect(proj.registeredAt).toBeGreaterThanOrEqual(before);
221
223
  expect(proj.registeredAt).toBeLessThanOrEqual(Date.now());
222
224
  });
223
225
 
@@ -104,8 +104,7 @@ describe('JobQueue', () => {
104
104
 
105
105
  it('enqueue creates a job and returns ID', () => {
106
106
  const id = queue.enqueue('tag-normalize', { entryId: 'e1' });
107
- expect(id).toBeTruthy();
108
- expect(id.length).toBeGreaterThan(0);
107
+ expect(id).toHaveLength(12);
109
108
  });
110
109
 
111
110
  it('dequeue returns oldest pending job', () => {
@@ -200,16 +199,18 @@ describe('JobQueue', () => {
200
199
 
201
200
  it('getStats returns correct counts', () => {
202
201
  const stats = queue.getStats();
203
- expect(stats.total).toBeGreaterThan(0);
202
+ expect(typeof stats.total).toBe('number');
204
203
  expect(typeof stats.pending).toBe('number');
205
204
  expect(typeof stats.running).toBe('number');
206
205
  expect(typeof stats.completed).toBe('number');
207
206
  expect(typeof stats.failed).toBe('number');
207
+ expect(stats.total).toBe(stats.pending + stats.running + stats.completed + stats.failed);
208
208
  });
209
209
 
210
210
  it('purge removes old completed/failed jobs', () => {
211
211
  // purge with 0 days should remove all completed/failed
212
212
  const purged = queue.purge(0);
213
+ expect(typeof purged).toBe('number');
213
214
  expect(purged).toBeGreaterThanOrEqual(0);
214
215
  });
215
216
  });
@@ -275,7 +276,8 @@ describe('PipelineRunner', () => {
275
276
  const status = runner.getStatus();
276
277
  expect(status.running).toBe(false); // Not started yet
277
278
  expect(status.pollIntervalMs).toBe(100);
278
- expect(status.jobsProcessed).toBeGreaterThanOrEqual(1);
279
+ // At this point: process-test succeeded = 1 processed; flaky-type threw so it's retried, not counted
280
+ expect(status.jobsProcessed).toBe(1);
279
281
  });
280
282
 
281
283
  it('start/stop controls background polling', async () => {
@@ -131,11 +131,14 @@ describe('JobQueue', () => {
131
131
  queue = new JobQueue(provider);
132
132
  });
133
133
 
134
- it('initializes the table on construction', () => {
135
- expect(provider.execSql).toHaveBeenCalledTimes(1);
136
- expect((provider.execSql as ReturnType<typeof vi.fn>).mock.calls[0][0]).toContain(
137
- 'CREATE TABLE IF NOT EXISTS job_queue',
138
- );
134
+ it('initializes the job_queue table on construction', () => {
135
+ // Verify the correct DDL was executed — not just that something was called
136
+ const ddl = (provider.execSql as ReturnType<typeof vi.fn>).mock.calls[0]?.[0] as
137
+ | string
138
+ | undefined;
139
+ expect(ddl).toContain('CREATE TABLE IF NOT EXISTS job_queue');
140
+ expect(ddl).toContain('status');
141
+ expect(ddl).toContain('retry_count');
139
142
  });
140
143
 
141
144
  describe('enqueue', () => {
@@ -281,7 +284,8 @@ describe('JobQueue', () => {
281
284
  queue.dequeue();
282
285
  queue.complete(id);
283
286
  const stats = queue.getStats();
284
- expect(stats.total).toBeGreaterThan(0);
287
+ expect(stats.total).toBe(stats.pending + stats.running + stats.completed + stats.failed);
288
+ expect(stats.completed).toBeGreaterThanOrEqual(1);
285
289
  });
286
290
  });
287
291
 
@@ -321,7 +325,9 @@ describe('JobQueue', () => {
321
325
  queue.complete(id1);
322
326
  queue.fail(id2, 'err');
323
327
  const deleted = queue.purge(30);
324
- expect(deleted).toBeGreaterThanOrEqual(0);
328
+ // The mock purges all completed/failed — 2 were just created
329
+ expect(typeof deleted).toBe('number');
330
+ expect(deleted).toBeGreaterThanOrEqual(2);
325
331
  });
326
332
  });
327
333
  });
@@ -95,19 +95,6 @@ describe('createAdminExtraOps', () => {
95
95
  ops = createAdminExtraOps(runtime);
96
96
  });
97
97
 
98
- it('returns at least 24 ops', () => {
99
- expect(ops.length).toBeGreaterThanOrEqual(24);
100
- });
101
-
102
- it('all ops have name, description, auth, and handler', () => {
103
- for (const op of ops) {
104
- expect(op.name).toBeTruthy();
105
- expect(op.description).toBeTruthy();
106
- expect(['read', 'write', 'admin']).toContain(op.auth);
107
- expect(typeof op.handler).toBe('function');
108
- }
109
- });
110
-
111
98
  describe('admin_telemetry', () => {
112
99
  it('returns telemetry stats', async () => {
113
100
  const result = await findOp(ops, 'admin_telemetry').handler({});
@@ -441,7 +428,7 @@ describe('createAdminExtraOps', () => {
441
428
  subsystem: 'nonexistent',
442
429
  })) as Record<string, unknown>;
443
430
  expect(result.error).toContain('Unknown subsystem');
444
- expect(result.available).toBeDefined();
431
+ expect(result.available).toEqual(['vault', 'brain']); // keys from mock snapshot.subsystems
445
432
  });
446
433
  });
447
434
 
@@ -481,33 +468,51 @@ describe('createAdminExtraOps', () => {
481
468
  });
482
469
 
483
470
  describe('admin_persistence_info', () => {
484
- it('returns backend and table counts', async () => {
485
- const result = (await findOp(ops, 'admin_persistence_info').handler({})) as Record<
486
- string,
487
- unknown
488
- >;
471
+ it('returns backend and table counts for each known table', async () => {
472
+ const result = (await findOp(ops, 'admin_persistence_info').handler({})) as {
473
+ backend: string;
474
+ tables: Record<string, number>;
475
+ };
489
476
  expect(result.backend).toBe('sqlite');
490
- expect(result.tables).toBeDefined();
477
+ // Mock provider.get returns { count: 42 } for every table
478
+ expect(result.tables).toEqual({
479
+ entries: 42,
480
+ entries_archive: 42,
481
+ memories: 42,
482
+ projects: 42,
483
+ brain_vocabulary: 42,
484
+ brain_feedback: 42,
485
+ });
491
486
  });
492
487
  });
493
488
 
494
489
  describe('admin_setup_check', () => {
495
- it('returns readiness with per-subsystem checks', async () => {
496
- const result = (await findOp(ops, 'admin_setup_check').handler({})) as Record<
497
- string,
498
- unknown
499
- >;
490
+ it('returns ready: true with vault, brain, llm, and health checks all passing', async () => {
491
+ const result = (await findOp(ops, 'admin_setup_check').handler({})) as {
492
+ agentId: string;
493
+ ready: boolean;
494
+ checks: Record<string, { ok: boolean }>;
495
+ };
500
496
  expect(result.agentId).toBe('test-agent');
501
- expect(result.checks).toBeDefined();
502
- expect(typeof result.ready).toBe('boolean');
497
+ expect(result.ready).toBe(true);
498
+ expect(result.checks.vault.ok).toBe(true);
499
+ expect(result.checks.brain.ok).toBe(true);
500
+ expect(result.checks.llm.ok).toBe(true);
503
501
  });
504
502
  });
505
503
 
506
504
  describe('admin_setup_run', () => {
507
- it('runs setup actions', async () => {
508
- const result = (await findOp(ops, 'admin_setup_run').handler({})) as Record<string, unknown>;
505
+ it('runs all three setup actions and returns their names', async () => {
506
+ const result = (await findOp(ops, 'admin_setup_run').handler({})) as {
507
+ setup: boolean;
508
+ actions: string[];
509
+ };
509
510
  expect(result.setup).toBe(true);
510
- expect((result.actions as string[]).length).toBeGreaterThan(0);
511
+ expect(result.actions).toEqual([
512
+ 'brain_vocabulary_rebuilt',
513
+ 'fts_index_rebuilt',
514
+ 'templates_reloaded',
515
+ ]);
511
516
  });
512
517
  });
513
518
  });
@@ -849,5 +849,35 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
849
849
  };
850
850
  },
851
851
  },
852
+ {
853
+ name: 'worktree_status',
854
+ description: 'List stale .claude/worktrees/ entries without removing them.',
855
+ auth: 'read' as const,
856
+ schema: z.object({
857
+ projectPath: z.string().optional().default('.'),
858
+ }),
859
+ handler: async (params) => {
860
+ const { worktreeStatus } = await import('../utils/worktree-reaper.js');
861
+ const { resolve } = await import('node:path');
862
+ const projectPath = resolve((params.projectPath as string) ?? '.');
863
+ const status = worktreeStatus(projectPath);
864
+ return { ...status, projectPath };
865
+ },
866
+ },
867
+ {
868
+ name: 'worktree_reap',
869
+ description: 'Remove stale .claude/worktrees/ entries and prune git worktree refs.',
870
+ auth: 'write' as const,
871
+ schema: z.object({
872
+ projectPath: z.string().optional().default('.'),
873
+ }),
874
+ handler: async (params) => {
875
+ const { worktreeReap } = await import('../utils/worktree-reaper.js');
876
+ const { resolve } = await import('node:path');
877
+ const projectPath = resolve((params.projectPath as string) ?? '.');
878
+ const report = worktreeReap(projectPath);
879
+ return { ...report, projectPath };
880
+ },
881
+ },
852
882
  ];
853
883
  }
@@ -70,10 +70,6 @@ describe('createAdminOps', () => {
70
70
  ops = createAdminOps(rt);
71
71
  });
72
72
 
73
- it('returns ops array', () => {
74
- expect(ops.length).toBeGreaterThan(0);
75
- });
76
-
77
73
  // ─── admin_health ─────────────────────────────────────────────
78
74
 
79
75
  describe('admin_health', () => {