@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
@@ -51,14 +51,6 @@ describe('Vault Characterization Tests', () => {
51
51
  });
52
52
 
53
53
  describe('constructor', () => {
54
- it('creates an in-memory vault', () => {
55
- const v = new Vault(':memory:');
56
- expect(v).toBeInstanceOf(Vault);
57
- v.close();
58
- });
59
- it('stamps FORMAT_VERSION', () => {
60
- expect(Vault.FORMAT_VERSION).toBe(1);
61
- });
62
54
  it('createWithSQLite factory', () => {
63
55
  const v = Vault.createWithSQLite(':memory:');
64
56
  expect(v).toBeInstanceOf(Vault);
@@ -83,26 +75,11 @@ describe('Vault Characterization Tests', () => {
83
75
  });
84
76
 
85
77
  describe('seed', () => {
86
- it('inserts entries', () => {
87
- expect(vault.seed([makeEntry({ id: 'seed-1' })])).toBe(1);
88
- });
89
78
  it('upserts on conflict', () => {
90
79
  vault.seed([makeEntry({ id: 'u1', title: 'Old' })]);
91
80
  vault.seed([makeEntry({ id: 'u1', title: 'New' })]);
92
81
  expect(vault.get('u1')?.title).toBe('New');
93
82
  });
94
- it('handles multiple', () => {
95
- expect(
96
- vault.seed([makeEntry({ id: 'a1' }), makeEntry({ id: 'a2' }), makeEntry({ id: 'a3' })]),
97
- ).toBe(3);
98
- });
99
- });
100
-
101
- describe('add', () => {
102
- it('delegates to seed', () => {
103
- vault.add(makeEntry({ id: 'add-1' }));
104
- expect(vault.get('add-1')).toBeTruthy();
105
- });
106
83
  });
107
84
 
108
85
  describe('get', () => {
@@ -172,10 +149,6 @@ describe('Vault Characterization Tests', () => {
172
149
  });
173
150
 
174
151
  describe('list', () => {
175
- it('returns entries', () => {
176
- vault.seed([makeEntry({ id: 'l1' }), makeEntry({ id: 'l2' })]);
177
- expect(vault.list().length).toBe(2);
178
- });
179
152
  it('filters by domain', () => {
180
153
  vault.seed([
181
154
  makeEntry({ id: 'ld1', domain: 'react' }),
@@ -245,13 +218,6 @@ describe('Vault Characterization Tests', () => {
245
218
  ]);
246
219
  });
247
220
  });
248
- describe('getRecent', () => {
249
- it('returns entries', () => {
250
- vault.seed([makeEntry({ id: 'r1' })]);
251
- vault.seed([makeEntry({ id: 'r2' })]);
252
- expect(vault.getRecent(5).length).toBe(2);
253
- });
254
- });
255
221
 
256
222
  describe('setTemporal', () => {
257
223
  it('sets temporal fields', () => {
@@ -319,30 +285,7 @@ describe('Vault Characterization Tests', () => {
319
285
  expect(vault.findByContentHash('x')).toBeNull();
320
286
  });
321
287
  });
322
- describe('contentHashStats', () => {
323
- it('zeros for empty', () => {
324
- const s = vault.contentHashStats();
325
- expect(s.total).toBe(0);
326
- });
327
- it('counts', () => {
328
- vault.seed([
329
- makeEntry({ id: 'c1', title: 'First' }),
330
- makeEntry({ id: 'c2', title: 'Second' }),
331
- ]);
332
- const s = vault.contentHashStats();
333
- expect(s.total).toBe(2);
334
- expect(s.uniqueHashes).toBe(2);
335
- });
336
- });
337
288
 
338
- describe('exportAll', () => {
339
- it('exports all', () => {
340
- vault.seed([makeEntry({ id: 'e1' }), makeEntry({ id: 'e2' })]);
341
- const r = vault.exportAll();
342
- expect(r.count).toBe(2);
343
- expect(typeof r.exportedAt).toBe('number');
344
- });
345
- });
346
289
  describe('getAgeReport', () => {
347
290
  it('empty report', () => {
348
291
  const r = vault.getAgeReport();
@@ -389,12 +332,6 @@ describe('Vault Characterization Tests', () => {
389
332
  });
390
333
  });
391
334
 
392
- describe('optimize', () => {
393
- it('returns status', () => {
394
- const r = vault.optimize();
395
- expect(typeof r.vacuumed).toBe('boolean');
396
- });
397
- });
398
335
  describe('rebuildFtsIndex', () => {
399
336
  it('does not throw', () => {
400
337
  expect(() => vault.rebuildFtsIndex()).not.toThrow();
@@ -402,10 +339,6 @@ describe('Vault Characterization Tests', () => {
402
339
  });
403
340
 
404
341
  describe('registerProject', () => {
405
- it('registers new', () => {
406
- const p = vault.registerProject('/t', 'test');
407
- expect(p.sessionCount).toBe(1);
408
- });
409
342
  it('increments on re-register', () => {
410
343
  vault.registerProject('/t');
411
344
  expect(vault.registerProject('/t').sessionCount).toBe(2);
@@ -424,21 +357,7 @@ describe('Vault Characterization Tests', () => {
424
357
  expect(vault.getProject('/t')!.name).toBe('T');
425
358
  });
426
359
  });
427
- describe('listProjects', () => {
428
- it('lists all', () => {
429
- vault.registerProject('/a');
430
- vault.registerProject('/b');
431
- expect(vault.listProjects().length).toBe(2);
432
- });
433
- });
434
360
 
435
- describe('captureMemory', () => {
436
- it('creates with id', () => {
437
- const m = vault.captureMemory(makeMemoryInput());
438
- expect(m.id).toMatch(/^mem-/);
439
- expect(m.archivedAt).toBeNull();
440
- });
441
- });
442
361
  describe('getMemory', () => {
443
362
  it('returns null', () => {
444
363
  expect(vault.getMemory('x')).toBeNull();
@@ -489,9 +408,6 @@ describe('Vault Characterization Tests', () => {
489
408
  });
490
409
 
491
410
  describe('memoryStats', () => {
492
- it('zeros for empty', () => {
493
- expect(vault.memoryStats().total).toBe(0);
494
- });
495
411
  it('counts', () => {
496
412
  vault.captureMemory(makeMemoryInput({ projectPath: '/a', type: 'session' }));
497
413
  vault.captureMemory(makeMemoryInput({ projectPath: '/a', type: 'lesson' }));
@@ -502,20 +418,6 @@ describe('Vault Characterization Tests', () => {
502
418
  });
503
419
  });
504
420
 
505
- describe('memoryStatsDetailed', () => {
506
- it('includes extended fields', () => {
507
- vault.captureMemory(makeMemoryInput());
508
- const s = vault.memoryStatsDetailed();
509
- expect(typeof s.oldest).toBe('number');
510
- expect(s.archivedCount).toBe(0);
511
- });
512
- });
513
- describe('exportMemories', () => {
514
- it('exports', () => {
515
- vault.captureMemory(makeMemoryInput());
516
- expect(vault.exportMemories().length).toBe(1);
517
- });
518
- });
519
421
  describe('importMemories', () => {
520
422
  it('imports and deduplicates', () => {
521
423
  vault.captureMemory(makeMemoryInput());
@@ -560,16 +462,6 @@ describe('Vault Characterization Tests', () => {
560
462
  });
561
463
  });
562
464
 
563
- describe('getProvider', () => {
564
- it('returns provider', () => {
565
- expect(vault.getProvider().backend).toBe('sqlite');
566
- });
567
- });
568
- describe('getDb', () => {
569
- it('returns db', () => {
570
- expect(vault.getDb()).toBeTruthy();
571
- });
572
- });
573
465
  describe('close', () => {
574
466
  it('does not throw', () => {
575
467
  const v = new Vault(':memory:');
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Default canonical tag taxonomy for Soleri agents.
3
+ *
4
+ * These tags represent the most common knowledge domains. When a vault is
5
+ * configured with tagConstraintMode 'suggest' or 'enforce', incoming tags
6
+ * are mapped to the nearest entry in this list via edit-distance matching.
7
+ *
8
+ * To use these defaults in your agent runtime config:
9
+ * import { DEFAULT_CANONICAL_TAGS } from '@soleri/core';
10
+ * // ...
11
+ * canonicalTags: DEFAULT_CANONICAL_TAGS,
12
+ * tagConstraintMode: 'suggest',
13
+ */
14
+ export const DEFAULT_CANONICAL_TAGS: string[] = [
15
+ 'architecture',
16
+ 'typescript',
17
+ 'react',
18
+ 'testing',
19
+ 'workflow',
20
+ 'design-tokens',
21
+ 'accessibility',
22
+ 'performance',
23
+ 'security',
24
+ 'planning',
25
+ 'soleri',
26
+ 'vault',
27
+ 'mcp',
28
+ 'claude-code',
29
+ 'ai',
30
+ 'learning',
31
+ 'gamification',
32
+ 'education',
33
+ 'adhd',
34
+ 'routing',
35
+ 'orchestration',
36
+ 'skills',
37
+ 'automation',
38
+ 'git',
39
+ 'database',
40
+ 'api',
41
+ 'authentication',
42
+ 'subagent',
43
+ 'design-system',
44
+ 'component',
45
+ 'frontend',
46
+ 'backend',
47
+ 'tooling',
48
+ 'monorepo',
49
+ 'refactoring',
50
+ 'debugging',
51
+ 'deployment',
52
+ 'configuration',
53
+ 'documentation',
54
+ 'pattern',
55
+ 'anti-pattern',
56
+ 'principle',
57
+ 'decision',
58
+ 'migration',
59
+ 'plugin',
60
+ 'hook',
61
+ 'schema',
62
+ 'pipeline',
63
+ 'ingestion',
64
+ ];
@@ -355,7 +355,6 @@ describe('LinkManager', () => {
355
355
  const result = mgr.backfillLinks();
356
356
  expect(result.processed).toBe(0);
357
357
  expect(result.linksCreated).toBe(0);
358
- expect(result.durationMs).toBeGreaterThanOrEqual(0);
359
358
  });
360
359
 
361
360
  it('dry run populates preview array', () => {
@@ -385,7 +384,6 @@ describe('LinkManager', () => {
385
384
  if (result.processed > 0 && result.preview) {
386
385
  expect(Array.isArray(result.preview)).toBe(true);
387
386
  }
388
- expect(result.durationMs).toBeGreaterThanOrEqual(0);
389
387
  });
390
388
 
391
389
  it('calls onProgress callback', () => {
@@ -95,7 +95,10 @@ describe('validatePlaybook', () => {
95
95
  const steps = [makeStep({ title: '', description: '' }, 1)];
96
96
  const result = validatePlaybook(makePlaybook({ title: '', steps }));
97
97
  expect(result.valid).toBe(false);
98
- expect(result.errors.length).toBeGreaterThanOrEqual(3);
98
+ expect(result.errors).toHaveLength(3);
99
+ expect(result.errors).toContain('Playbook title must not be empty');
100
+ expect(result.errors).toContain('Step 1 title must not be empty');
101
+ expect(result.errors).toContain('Step 1 description must not be empty');
99
102
  });
100
103
  });
101
104
 
@@ -165,11 +165,13 @@ describe('detectScope', () => {
165
165
  const result = detectScope(
166
166
  makeInput({ description: 'Use focus ring for keyboard navigation' }),
167
167
  );
168
+ expect(result.signals.length).toBeGreaterThan(0);
168
169
  for (const signal of result.signals) {
169
170
  expect(signal).toHaveProperty('tier');
170
171
  expect(signal).toHaveProperty('source');
171
172
  expect(signal).toHaveProperty('indicator');
172
173
  expect(signal).toHaveProperty('weight');
174
+ // Weight must be in (0, 1] range
173
175
  expect(signal.weight).toBeGreaterThan(0);
174
176
  expect(signal.weight).toBeLessThanOrEqual(1);
175
177
  }
@@ -179,7 +181,7 @@ describe('detectScope', () => {
179
181
  const result = detectScope(
180
182
  makeInput({ description: 'Accessibility best practice for a11y compliance' }),
181
183
  );
182
- expect(result.reason.length).toBeGreaterThan(0);
184
+ expect(result.reason).toMatch(/accessibility|a11y/i);
183
185
  expect(result.reason).not.toContain('defaulting');
184
186
  });
185
187
  });
@@ -49,7 +49,27 @@ const TEAM_CONTENT_PATTERNS: Array<{ pattern: RegExp; weight: number; desc: stri
49
49
  { pattern: /error\s+handling|error\s+boundary/i, weight: 0.6, desc: 'error handling' },
50
50
  { pattern: /touch\s+target|tap\s+target|fitts/i, weight: 0.8, desc: 'UX touch targets' },
51
51
  { pattern: /focus\s+(ring|state|indicator)/i, weight: 0.8, desc: 'focus states' },
52
- { pattern: /best\s+practice|anti.?pattern/i, weight: 0.65, desc: 'best practice' },
52
+ // Reduced weight: "best practice" alone is too weak to push to team tier
53
+ { pattern: /best\s+practice|anti.?pattern/i, weight: 0.4, desc: 'best practice' },
54
+ ];
55
+
56
+ /**
57
+ * Project-specific negative signals — agent/tool names and framework-specific patterns
58
+ * that indicate the entry is project-scoped, not universally applicable.
59
+ */
60
+ const PROJECT_SPECIFIC_PATTERNS: Array<{ pattern: RegExp; weight: number; desc: string }> = [
61
+ { pattern: /\bsoleri\b/i, weight: 0.9, desc: 'Soleri project reference' },
62
+ { pattern: /\bernesto\b/i, weight: 0.9, desc: 'Ernesto agent reference' },
63
+ { pattern: /\bsalvador\b/i, weight: 0.9, desc: 'Salvador agent reference' },
64
+ {
65
+ pattern: /\bskill\s+(file|system|pack|registry|sync)/i,
66
+ weight: 0.7,
67
+ desc: 'skill system specific',
68
+ },
69
+ { pattern: /vault\s+(entry|search|op|facade)/i, weight: 0.65, desc: 'vault system specific' },
70
+ { pattern: /domain\s+pack|hook\s+pack/i, weight: 0.7, desc: 'Soleri pack system' },
71
+ { pattern: /plan\s+(grade|facade|lifecycle)/i, weight: 0.65, desc: 'Soleri planning system' },
72
+ { pattern: /soleri\s+(agent|cli|core|forge)/i, weight: 0.95, desc: 'Soleri package reference' },
53
73
  ];
54
74
 
55
75
  const PROJECT_CONTENT_PATTERNS: Array<{ pattern: RegExp; weight: number; desc: string }> = [
@@ -128,6 +148,11 @@ function analyzeContent(text: string): ScopeSignal[] {
128
148
  signals.push({ tier: 'project', source: 'content', indicator: desc, weight });
129
149
  }
130
150
  }
151
+ for (const { pattern, weight, desc } of PROJECT_SPECIFIC_PATTERNS) {
152
+ if (pattern.test(text)) {
153
+ signals.push({ tier: 'project', source: 'content', indicator: desc, weight });
154
+ }
155
+ }
131
156
  for (const { pattern, weight, desc } of AGENT_CONTENT_PATTERNS) {
132
157
  if (pattern.test(text)) {
133
158
  signals.push({ tier: 'agent', source: 'content', indicator: desc, weight });
@@ -165,7 +190,11 @@ function analyzeTags(tags: string[]): ScopeSignal[] {
165
190
  return signals;
166
191
  }
167
192
 
168
- function computeConfidence(scores: Record<ScopeTier, number>, winner: ScopeTier): ConfidenceLevel {
193
+ function computeConfidence(
194
+ scores: Record<ScopeTier, number>,
195
+ winner: ScopeTier,
196
+ signals: ScopeSignal[],
197
+ ): ConfidenceLevel {
169
198
  const winScore = scores[winner];
170
199
  const others = Object.entries(scores)
171
200
  .filter(([t]) => t !== winner)
@@ -173,7 +202,16 @@ function computeConfidence(scores: Record<ScopeTier, number>, winner: ScopeTier)
173
202
  const runnerUp = Math.max(...others, 0);
174
203
 
175
204
  if (winScore === 0) return 'LOW';
176
- if (runnerUp === 0 && winScore >= 0.5) return 'HIGH';
205
+ if (runnerUp === 0 && winScore >= 0.5) {
206
+ // Cap team tier at MEDIUM when only generic/methodology signals drove the win
207
+ if (winner === 'team') {
208
+ const genericOnly = signals
209
+ .filter((s) => s.tier === 'team')
210
+ .every((s) => s.indicator === 'best practice' || s.source === 'category');
211
+ if (genericOnly) return 'MEDIUM';
212
+ }
213
+ return 'HIGH';
214
+ }
177
215
  const ratio = winScore / (winScore + runnerUp);
178
216
  if (ratio >= 0.7 && winScore >= 1.0) return 'HIGH';
179
217
  if (ratio >= 0.55) return 'MEDIUM';
@@ -205,7 +243,7 @@ export function detectScope(input: ScopeInput): ScopeDetectionResult {
205
243
  }
206
244
  }
207
245
 
208
- const confidence = computeConfidence(scores, winner);
246
+ const confidence = computeConfidence(scores, winner, signals);
209
247
  const topSignals = signals
210
248
  .filter((s) => s.tier === winner)
211
249
  .sort((a, b) => b.weight - a.weight)
@@ -0,0 +1,214 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ computeEditDistance,
4
+ normalizeTag,
5
+ normalizeTags,
6
+ isMetadataTag,
7
+ } from './tag-normalizer.js';
8
+
9
+ // ─── computeEditDistance ────────────────────────────────────────────────────
10
+
11
+ describe('computeEditDistance', () => {
12
+ it('returns 0 for identical strings', () => {
13
+ expect(computeEditDistance('workflow', 'workflow')).toBe(0);
14
+ });
15
+
16
+ it('returns length of b for empty a', () => {
17
+ expect(computeEditDistance('', 'abc')).toBe(3);
18
+ });
19
+
20
+ it('returns length of a for empty b', () => {
21
+ expect(computeEditDistance('abc', '')).toBe(3);
22
+ });
23
+
24
+ it('single insertion: workflow → workflows', () => {
25
+ expect(computeEditDistance('workflow', 'workflows')).toBe(1);
26
+ });
27
+
28
+ it('single deletion: testing → testin', () => {
29
+ expect(computeEditDistance('testing', 'testin')).toBe(1);
30
+ });
31
+
32
+ it('single substitution: arch → arcs', () => {
33
+ expect(computeEditDistance('arch', 'arcs')).toBe(1);
34
+ });
35
+
36
+ it("distance 1: architcture → architecture (single insertion - missing 'e')", () => {
37
+ expect(computeEditDistance('architcture', 'architecture')).toBe(1);
38
+ });
39
+
40
+ it('distance 2: archtecrure → architecture (two edits)', () => {
41
+ expect(computeEditDistance('archtecrure', 'architecture')).toBe(2);
42
+ });
43
+
44
+ it('large distance: typescript → javascript', () => {
45
+ expect(computeEditDistance('typescript', 'javascript')).toBeGreaterThan(3);
46
+ });
47
+
48
+ it('is symmetric', () => {
49
+ expect(computeEditDistance('foo', 'bar')).toBe(computeEditDistance('bar', 'foo'));
50
+ });
51
+ });
52
+
53
+ // ─── isMetadataTag ──────────────────────────────────────────────────────────
54
+
55
+ describe('isMetadataTag', () => {
56
+ it('returns true when tag matches a prefix', () => {
57
+ expect(isMetadataTag('source:article', ['source:'])).toBe(true);
58
+ });
59
+
60
+ it('returns true for exact prefix match', () => {
61
+ expect(isMetadataTag('source:ingested', ['source:'])).toBe(true);
62
+ });
63
+
64
+ it('returns false when no prefix matches', () => {
65
+ expect(isMetadataTag('typescript', ['source:'])).toBe(false);
66
+ });
67
+
68
+ it('returns false with empty prefix list', () => {
69
+ expect(isMetadataTag('source:article', [])).toBe(false);
70
+ });
71
+
72
+ it('supports multiple prefixes', () => {
73
+ expect(isMetadataTag('meta:foo', ['source:', 'meta:'])).toBe(true);
74
+ });
75
+ });
76
+
77
+ // ─── normalizeTag ───────────────────────────────────────────────────────────
78
+
79
+ const CANONICAL = ['architecture', 'typescript', 'workflow', 'testing', 'performance'];
80
+
81
+ describe('normalizeTag — mode: off', () => {
82
+ it('returns tag as-is regardless of canonical list', () => {
83
+ expect(normalizeTag('workflows', CANONICAL, 'off')).toBe('workflows');
84
+ });
85
+
86
+ it('passes through noise words in off mode', () => {
87
+ expect(normalizeTag('new', CANONICAL, 'off')).toBe('new');
88
+ });
89
+ });
90
+
91
+ describe('normalizeTag — noise stripping', () => {
92
+ it('drops version strings (v1.2)', () => {
93
+ expect(normalizeTag('v1.2', CANONICAL, 'suggest')).toBeNull();
94
+ });
95
+
96
+ it('drops version strings (v10)', () => {
97
+ expect(normalizeTag('v10', CANONICAL, 'suggest')).toBeNull();
98
+ });
99
+
100
+ it('drops generic noise word: new', () => {
101
+ expect(normalizeTag('new', CANONICAL, 'suggest')).toBeNull();
102
+ });
103
+
104
+ it('drops generic noise word: via', () => {
105
+ expect(normalizeTag('via', CANONICAL, 'suggest')).toBeNull();
106
+ });
107
+
108
+ it('drops generic noise word: raw', () => {
109
+ expect(normalizeTag('raw', CANONICAL, 'enforce')).toBeNull();
110
+ });
111
+ });
112
+
113
+ describe('normalizeTag — mode: suggest', () => {
114
+ it('returns canonical for exact match', () => {
115
+ expect(normalizeTag('typescript', CANONICAL, 'suggest')).toBe('typescript');
116
+ });
117
+
118
+ it('maps within edit-distance 2 to canonical', () => {
119
+ // 'workflows' is distance 1 from 'workflow'
120
+ expect(normalizeTag('workflows', CANONICAL, 'suggest')).toBe('workflow');
121
+ });
122
+
123
+ it('lowercases tag before matching', () => {
124
+ expect(normalizeTag('TypeScript', CANONICAL, 'suggest')).toBe('typescript');
125
+ });
126
+
127
+ it('passes through unknown tag with no close canonical (suggest passthrough)', () => {
128
+ const result = normalizeTag('gamification', CANONICAL, 'suggest');
129
+ // No match within distance 2 — passthrough
130
+ expect(result).toBe('gamification');
131
+ });
132
+
133
+ it('returns null for noise even in suggest mode', () => {
134
+ expect(normalizeTag('one', CANONICAL, 'suggest')).toBeNull();
135
+ });
136
+ });
137
+
138
+ describe('normalizeTag — mode: enforce', () => {
139
+ it('returns canonical for exact match', () => {
140
+ expect(normalizeTag('testing', CANONICAL, 'enforce')).toBe('testing');
141
+ });
142
+
143
+ it('maps within edit-distance 3 to canonical', () => {
144
+ // 'archtecrure' is 2 away from 'architecture'
145
+ expect(normalizeTag('archtecrure', CANONICAL, 'enforce')).toBe('architecture');
146
+ });
147
+
148
+ it('returns null for tag with no match within distance 3', () => {
149
+ // 'gamification' is far from all CANONICAL entries
150
+ expect(normalizeTag('gamification', CANONICAL, 'enforce')).toBeNull();
151
+ });
152
+
153
+ it('returns null for noise words', () => {
154
+ expect(normalizeTag('full', CANONICAL, 'enforce')).toBeNull();
155
+ });
156
+ });
157
+
158
+ describe('normalizeTag — empty canonical list', () => {
159
+ it('suggest mode: passes through non-noise tags', () => {
160
+ expect(normalizeTag('react', [], 'suggest')).toBe('react');
161
+ });
162
+
163
+ it('enforce mode: drops all tags (no canonical to match)', () => {
164
+ expect(normalizeTag('react', [], 'enforce')).toBeNull();
165
+ });
166
+ });
167
+
168
+ // ─── normalizeTags ──────────────────────────────────────────────────────────
169
+
170
+ describe('normalizeTags', () => {
171
+ it('deduplicates tags that map to the same canonical', () => {
172
+ // Both 'workflows' and 'workflow' normalize to 'workflow'
173
+ const result = normalizeTags(['workflows', 'workflow'], CANONICAL, 'suggest');
174
+ expect(result).toEqual(['workflow']);
175
+ });
176
+
177
+ it('preserves metadata tags unchanged', () => {
178
+ const result = normalizeTags(['source:article', 'typescript'], CANONICAL, 'enforce', [
179
+ 'source:',
180
+ ]);
181
+ expect(result).toContain('source:article');
182
+ expect(result).toContain('typescript');
183
+ });
184
+
185
+ it('metadata tags bypass canonical normalization in enforce mode', () => {
186
+ // 'source:mytype' does not match any canonical — but it should be kept
187
+ const result = normalizeTags(['source:mytype'], CANONICAL, 'enforce', ['source:']);
188
+ expect(result).toEqual(['source:mytype']);
189
+ });
190
+
191
+ it('returns empty array when all tags are noise', () => {
192
+ const result = normalizeTags(['new', 'via', 'raw', 'v1.2'], CANONICAL, 'suggest');
193
+ expect(result).toEqual([]);
194
+ });
195
+
196
+ it('in off mode, returns tags unchanged', () => {
197
+ const input = ['new', 'workflows', 'v1.2'];
198
+ const result = normalizeTags(input, CANONICAL, 'off');
199
+ expect(result).toEqual(input);
200
+ });
201
+
202
+ it('batch normalizes a mixed tag list', () => {
203
+ const result = normalizeTags(
204
+ ['TypeScript', 'workflows', 'new', 'source:article'],
205
+ CANONICAL,
206
+ 'suggest',
207
+ ['source:'],
208
+ );
209
+ expect(result).toContain('typescript');
210
+ expect(result).toContain('workflow');
211
+ expect(result).toContain('source:article');
212
+ expect(result).not.toContain('new');
213
+ });
214
+ });