@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
@@ -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', () => {
@@ -5,8 +5,9 @@
5
5
  * runtime state. No new modules needed — uses existing runtime parts.
6
6
  */
7
7
 
8
- import { readFileSync, statSync } from 'node:fs';
8
+ import { readFileSync, statSync, existsSync, readdirSync } from 'node:fs';
9
9
  import { join, dirname } from 'node:path';
10
+ import { homedir } from 'node:os';
10
11
  import { fileURLToPath } from 'node:url';
11
12
  import type { OpDefinition } from '../facades/types.js';
12
13
  import type { AgentRuntime } from './types.js';
@@ -147,20 +148,21 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
147
148
  };
148
149
  }
149
150
  // Fallback — just describe admin ops
151
+ const adminOps = [
152
+ 'admin_health',
153
+ 'admin_tool_list',
154
+ 'admin_config',
155
+ 'admin_vault_size',
156
+ 'admin_uptime',
157
+ 'admin_version',
158
+ 'admin_reset_cache',
159
+ 'admin_diagnostic',
160
+ ];
150
161
  return {
151
- count: 8,
152
- ops: {
153
- admin: [
154
- 'admin_health',
155
- 'admin_tool_list',
156
- 'admin_config',
157
- 'admin_vault_size',
158
- 'admin_uptime',
159
- 'admin_version',
160
- 'admin_reset_cache',
161
- 'admin_diagnostic',
162
- ],
163
- },
162
+ count: adminOps.length,
163
+ scope: 'admin-only',
164
+ note: 'Pass _allOps for full system op count',
165
+ ops: { admin: adminOps },
164
166
  routing: buildRoutingHints(),
165
167
  };
166
168
  },
@@ -380,7 +382,7 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
380
382
  });
381
383
  }
382
384
 
383
- // 7. Skills
385
+ // 7. Skills — check discovered vs registered in .claude/skills/
384
386
  try {
385
387
  const agentDir = runtime.config.agentDir;
386
388
  const skillsDirs = agentDir ? [join(agentDir, 'skills')] : [];
@@ -388,12 +390,52 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
388
390
  const installedPacks = packInstaller.list();
389
391
  const packSkillCount = installedPacks.reduce((sum, p) => sum + p.skills.length, 0);
390
392
  const totalSkills = agentSkills.length + packSkillCount;
391
- const skillStatus = totalSkills > 0 ? 'ok' : agentDir ? 'warn' : 'ok';
392
- checks.push({
393
- name: 'skills',
394
- status: skillStatus,
395
- detail: `${totalSkills} skills (${agentSkills.length} agent, ${packSkillCount} pack)`,
396
- });
393
+
394
+ // Check registration status in .claude/skills/
395
+ const claudeSkillsDir = join(homedir(), '.claude', 'skills');
396
+ let registeredCount = 0;
397
+ let brokenCount = 0;
398
+ const unregistered: string[] = [];
399
+
400
+ if (existsSync(claudeSkillsDir)) {
401
+ try {
402
+ const registered = readdirSync(claudeSkillsDir, { withFileTypes: true });
403
+ registeredCount = registered.length;
404
+ for (const entry of registered) {
405
+ if (entry.isSymbolicLink()) {
406
+ try {
407
+ statSync(join(claudeSkillsDir, entry.name));
408
+ } catch {
409
+ brokenCount++;
410
+ }
411
+ }
412
+ }
413
+ } catch {
414
+ // Can't read .claude/skills/ — skip registration check
415
+ }
416
+ }
417
+
418
+ for (const skill of agentSkills) {
419
+ const skillRegisteredDir = join(claudeSkillsDir, skill.name);
420
+ if (!existsSync(skillRegisteredDir)) {
421
+ unregistered.push(skill.name);
422
+ }
423
+ }
424
+
425
+ const hasIssues = unregistered.length > 0 || brokenCount > 0;
426
+ // Warn only when agentDir is set but no skills exist anywhere (local OR global)
427
+ const hasAnySkills = totalSkills > 0 || registeredCount > 0;
428
+ const skillStatus = !hasAnySkills && agentDir ? 'warn' : hasIssues ? 'warn' : 'ok';
429
+ const detail = [
430
+ `${totalSkills} discovered (${agentSkills.length} agent, ${packSkillCount} pack)`,
431
+ `${registeredCount} registered in .claude/skills/`,
432
+ ...(unregistered.length > 0
433
+ ? [`${unregistered.length} unregistered: ${unregistered.join(', ')}`]
434
+ : []),
435
+ ...(brokenCount > 0 ? [`${brokenCount} broken links`] : []),
436
+ ].join(' — ');
437
+
438
+ checks.push({ name: 'skills', status: skillStatus, detail });
397
439
  } catch (err) {
398
440
  checks.push({
399
441
  name: 'skills',