@holoscript/framework 6.0.3

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 (329) hide show
  1. package/ALL-test-results.json +1 -0
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/ROADMAP.md +175 -0
  5. package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
  6. package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
  7. package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
  8. package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
  9. package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
  10. package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
  11. package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
  12. package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
  13. package/dist/agents/index.cjs +4790 -0
  14. package/dist/agents/index.d.cts +1788 -0
  15. package/dist/agents/index.d.ts +1788 -0
  16. package/dist/agents/index.js +4695 -0
  17. package/dist/ai/index.cjs +5347 -0
  18. package/dist/ai/index.d.cts +1753 -0
  19. package/dist/ai/index.d.ts +1753 -0
  20. package/dist/ai/index.js +5244 -0
  21. package/dist/behavior.cjs +449 -0
  22. package/dist/behavior.d.cts +130 -0
  23. package/dist/behavior.d.ts +130 -0
  24. package/dist/behavior.js +407 -0
  25. package/dist/economy/index.cjs +3659 -0
  26. package/dist/economy/index.d.cts +747 -0
  27. package/dist/economy/index.d.ts +747 -0
  28. package/dist/economy/index.js +3617 -0
  29. package/dist/implementations-D9T3un9D.d.cts +236 -0
  30. package/dist/implementations-D9T3un9D.d.ts +236 -0
  31. package/dist/index.cjs +24550 -0
  32. package/dist/index.d.cts +1729 -0
  33. package/dist/index.d.ts +1729 -0
  34. package/dist/index.js +24277 -0
  35. package/dist/learning/index.cjs +219 -0
  36. package/dist/learning/index.d.cts +104 -0
  37. package/dist/learning/index.d.ts +104 -0
  38. package/dist/learning/index.js +189 -0
  39. package/dist/negotiation/index.cjs +970 -0
  40. package/dist/negotiation/index.d.cts +610 -0
  41. package/dist/negotiation/index.d.ts +610 -0
  42. package/dist/negotiation/index.js +931 -0
  43. package/dist/skills/index.cjs +1118 -0
  44. package/dist/skills/index.d.cts +289 -0
  45. package/dist/skills/index.d.ts +289 -0
  46. package/dist/skills/index.js +1079 -0
  47. package/dist/swarm/index.cjs +5268 -0
  48. package/dist/swarm/index.d.cts +2433 -0
  49. package/dist/swarm/index.d.ts +2433 -0
  50. package/dist/swarm/index.js +5221 -0
  51. package/dist/training/index.cjs +2745 -0
  52. package/dist/training/index.d.cts +1734 -0
  53. package/dist/training/index.d.ts +1734 -0
  54. package/dist/training/index.js +2687 -0
  55. package/extract-failures.js +10 -0
  56. package/package.json +82 -0
  57. package/src/__tests__/bounty-marketplace.test.ts +374 -0
  58. package/src/__tests__/delegation.test.ts +144 -0
  59. package/src/__tests__/distributed-claimer.test.ts +147 -0
  60. package/src/__tests__/done-log-audit.test.ts +342 -0
  61. package/src/__tests__/framework.test.ts +865 -0
  62. package/src/__tests__/goal-synthesizer.test.ts +236 -0
  63. package/src/__tests__/presence.test.ts +223 -0
  64. package/src/__tests__/protocol-agent.test.ts +254 -0
  65. package/src/__tests__/revenue-splitter.test.ts +114 -0
  66. package/src/__tests__/scenario-driven-todo.test.ts +197 -0
  67. package/src/__tests__/self-improve.test.ts +349 -0
  68. package/src/__tests__/service-lifecycle.test.ts +237 -0
  69. package/src/__tests__/skill-router.test.ts +121 -0
  70. package/src/agents/AgentManifest.ts +493 -0
  71. package/src/agents/AgentRegistry.ts +475 -0
  72. package/src/agents/AgentTypes.ts +585 -0
  73. package/src/agents/AgentWalletRegistry.ts +83 -0
  74. package/src/agents/AuthenticatedCRDT.ts +388 -0
  75. package/src/agents/CapabilityMatcher.ts +453 -0
  76. package/src/agents/CrossRealityHandoff.ts +305 -0
  77. package/src/agents/CulturalMemory.ts +454 -0
  78. package/src/agents/FederatedRegistryAdapter.ts +429 -0
  79. package/src/agents/NormEngine.ts +450 -0
  80. package/src/agents/OrchestratorAgent.ts +414 -0
  81. package/src/agents/SkillWorkflowEngine.ts +472 -0
  82. package/src/agents/TaskDelegationService.ts +551 -0
  83. package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
  84. package/src/agents/__tests__/AgentManifest.test.ts +182 -0
  85. package/src/agents/__tests__/AgentModule.test.ts +864 -0
  86. package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
  87. package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
  88. package/src/agents/__tests__/AgentTypes.test.ts +534 -0
  89. package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
  90. package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
  91. package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
  92. package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
  93. package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
  94. package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
  95. package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
  96. package/src/agents/__tests__/NormEngine.test.ts +276 -0
  97. package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
  98. package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
  99. package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
  100. package/src/agents/index.ts +107 -0
  101. package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
  102. package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
  103. package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
  104. package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
  105. package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
  106. package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
  107. package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
  108. package/src/agents/spatial-comms/index.ts +66 -0
  109. package/src/ai/AIAdapter.ts +313 -0
  110. package/src/ai/AICopilot.ts +331 -0
  111. package/src/ai/AIOutputValidator.ts +203 -0
  112. package/src/ai/BTNodes.ts +239 -0
  113. package/src/ai/BehaviorSelector.ts +135 -0
  114. package/src/ai/BehaviorTree.ts +153 -0
  115. package/src/ai/Blackboard.ts +165 -0
  116. package/src/ai/GenerationAnalytics.ts +461 -0
  117. package/src/ai/GenerationCache.ts +265 -0
  118. package/src/ai/GoalPlanner.ts +165 -0
  119. package/src/ai/HoloScriptGenerator.ts +580 -0
  120. package/src/ai/InfluenceMap.ts +180 -0
  121. package/src/ai/NavMesh.ts +168 -0
  122. package/src/ai/PerceptionSystem.ts +178 -0
  123. package/src/ai/PromptTemplates.ts +453 -0
  124. package/src/ai/SemanticSearchService.ts +80 -0
  125. package/src/ai/StateMachine.ts +196 -0
  126. package/src/ai/SteeringBehavior.ts +150 -0
  127. package/src/ai/SteeringBehaviors.ts +244 -0
  128. package/src/ai/TrainingDataGenerator.ts +1082 -0
  129. package/src/ai/UtilityAI.ts +145 -0
  130. package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
  131. package/src/ai/__tests__/AIAdapter.test.ts +109 -0
  132. package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
  133. package/src/ai/__tests__/AICopilot.test.ts +178 -0
  134. package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
  135. package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
  136. package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
  137. package/src/ai/__tests__/BTNodes.test.ts +263 -0
  138. package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
  139. package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
  140. package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
  141. package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
  142. package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
  143. package/src/ai/__tests__/Blackboard.test.ts +183 -0
  144. package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
  145. package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
  146. package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
  147. package/src/ai/__tests__/GenerationCache.test.ts +171 -0
  148. package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
  149. package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
  150. package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
  151. package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
  152. package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
  153. package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
  154. package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
  155. package/src/ai/__tests__/NavMesh.test.ts +159 -0
  156. package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
  157. package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
  158. package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
  159. package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
  160. package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
  161. package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
  162. package/src/ai/__tests__/StateMachine.test.ts +163 -0
  163. package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
  164. package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
  165. package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
  166. package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
  167. package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
  168. package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
  169. package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
  170. package/src/ai/__tests__/UtilityAI.test.ts +155 -0
  171. package/src/ai/__tests__/adapters.prod.test.ts +263 -0
  172. package/src/ai/__tests__/adapters.test.ts +320 -0
  173. package/src/ai/adapters.ts +1585 -0
  174. package/src/ai/index.ts +130 -0
  175. package/src/behavior/BehaviorPresets.ts +140 -0
  176. package/src/behavior/BehaviorTree.ts +236 -0
  177. package/src/behavior/StateMachine.ts +176 -0
  178. package/src/behavior/StateTrait.ts +67 -0
  179. package/src/behavior/index.ts +8 -0
  180. package/src/behavior.ts +8 -0
  181. package/src/board/audit.ts +284 -0
  182. package/src/board/board-ops.ts +336 -0
  183. package/src/board/board-types.ts +302 -0
  184. package/src/board/index.ts +69 -0
  185. package/src/define-agent.ts +46 -0
  186. package/src/define-team.ts +33 -0
  187. package/src/delegation.ts +265 -0
  188. package/src/distributed-claimer.ts +228 -0
  189. package/src/economy/AgentBudgetEnforcer.ts +464 -0
  190. package/src/economy/BountyManager.ts +185 -0
  191. package/src/economy/CreatorRevenueAggregator.ts +460 -0
  192. package/src/economy/InvisibleWallet.ts +82 -0
  193. package/src/economy/KnowledgeMarketplace.ts +193 -0
  194. package/src/economy/PaymentWebhookService.ts +512 -0
  195. package/src/economy/RevenueSplitter.ts +156 -0
  196. package/src/economy/SubscriptionManager.ts +546 -0
  197. package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
  198. package/src/economy/UsageMeter.ts +440 -0
  199. package/src/economy/_core-stubs.ts +219 -0
  200. package/src/economy/index.ts +100 -0
  201. package/src/economy/x402-facilitator.ts +1978 -0
  202. package/src/index.ts +348 -0
  203. package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
  204. package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
  205. package/src/knowledge/brain.ts +167 -0
  206. package/src/knowledge/consolidation.ts +581 -0
  207. package/src/knowledge/knowledge-consolidator.ts +510 -0
  208. package/src/knowledge/knowledge-store.ts +616 -0
  209. package/src/learning/MemoryConsolidator.ts +102 -0
  210. package/src/learning/MemoryScorer.ts +69 -0
  211. package/src/learning/ProceduralCompiler.ts +45 -0
  212. package/src/learning/SemanticClusterer.ts +66 -0
  213. package/src/learning/index.ts +8 -0
  214. package/src/llm/llm-adapter.ts +159 -0
  215. package/src/mesh/index.ts +309 -0
  216. package/src/negotiation/NegotiationProtocol.ts +694 -0
  217. package/src/negotiation/NegotiationTypes.ts +473 -0
  218. package/src/negotiation/VotingMechanisms.ts +691 -0
  219. package/src/negotiation/index.ts +49 -0
  220. package/src/protocol/goal-synthesizer.ts +317 -0
  221. package/src/protocol/implementations.ts +474 -0
  222. package/src/protocol/micro-phase-decomposer.ts +299 -0
  223. package/src/protocol/micro-step-decomposer.test.ts +306 -0
  224. package/src/protocol-agent.test.ts +353 -0
  225. package/src/protocol-agent.ts +670 -0
  226. package/src/self-improve/absorb-scanner.ts +252 -0
  227. package/src/self-improve/evolution-engine.ts +149 -0
  228. package/src/self-improve/framework-absorber.ts +214 -0
  229. package/src/self-improve/index.ts +50 -0
  230. package/src/self-improve/prompt-optimizer.ts +212 -0
  231. package/src/self-improve/test-generator.ts +175 -0
  232. package/src/skill-router.ts +186 -0
  233. package/src/skills/index.ts +5 -0
  234. package/src/skills/skill-md-bridge.ts +1699 -0
  235. package/src/swarm/ACOEngine.ts +261 -0
  236. package/src/swarm/CollectiveIntelligence.ts +383 -0
  237. package/src/swarm/ContributionSynthesizer.ts +481 -0
  238. package/src/swarm/LeaderElection.ts +393 -0
  239. package/src/swarm/PSOEngine.ts +206 -0
  240. package/src/swarm/QuorumPolicy.ts +173 -0
  241. package/src/swarm/SwarmCoordinator.ts +335 -0
  242. package/src/swarm/SwarmManager.ts +442 -0
  243. package/src/swarm/SwarmMembership.ts +456 -0
  244. package/src/swarm/VotingRound.ts +255 -0
  245. package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
  246. package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
  247. package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
  248. package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
  249. package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
  250. package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
  251. package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
  252. package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
  253. package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
  254. package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
  255. package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
  256. package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
  257. package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
  258. package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
  259. package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
  260. package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
  261. package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
  262. package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
  263. package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
  264. package/src/swarm/__tests__/VotingRound.test.ts +174 -0
  265. package/src/swarm/analytics/SwarmInspector.ts +476 -0
  266. package/src/swarm/analytics/SwarmMetrics.ts +449 -0
  267. package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
  268. package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
  269. package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
  270. package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
  271. package/src/swarm/analytics/index.ts +7 -0
  272. package/src/swarm/index.ts +69 -0
  273. package/src/swarm/messaging/BroadcastChannel.ts +509 -0
  274. package/src/swarm/messaging/GossipProtocol.ts +565 -0
  275. package/src/swarm/messaging/SwarmEventBus.ts +443 -0
  276. package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
  277. package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
  278. package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
  279. package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
  280. package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
  281. package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
  282. package/src/swarm/messaging/index.ts +8 -0
  283. package/src/swarm/spatial/FlockingBehavior.ts +462 -0
  284. package/src/swarm/spatial/FormationController.ts +500 -0
  285. package/src/swarm/spatial/Vector3.ts +170 -0
  286. package/src/swarm/spatial/ZoneClaiming.ts +509 -0
  287. package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
  288. package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
  289. package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
  290. package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
  291. package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
  292. package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
  293. package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
  294. package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
  295. package/src/swarm/spatial/index.ts +28 -0
  296. package/src/team.ts +1245 -0
  297. package/src/training/LRScheduler.ts +377 -0
  298. package/src/training/QualityScoringPipeline.ts +139 -0
  299. package/src/training/SoftDedup.ts +461 -0
  300. package/src/training/SparsityMonitor.ts +685 -0
  301. package/src/training/SparsityMonitorTypes.ts +209 -0
  302. package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
  303. package/src/training/SpatialTrainingDataTypes.ts +216 -0
  304. package/src/training/TrainingPipelineConfig.ts +215 -0
  305. package/src/training/constants.ts +94 -0
  306. package/src/training/index.ts +138 -0
  307. package/src/training/schema.ts +147 -0
  308. package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
  309. package/src/training/scripts/generate-spatial-dataset.ts +521 -0
  310. package/src/training/training/data/novel-use-cases.jsonl +153 -0
  311. package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
  312. package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
  313. package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
  314. package/src/training/trainingmonkey/index.ts +26 -0
  315. package/src/training/trait-mappings.ts +157 -0
  316. package/src/types/core-stubs.d.ts +113 -0
  317. package/src/types.ts +304 -0
  318. package/test-output.txt +0 -0
  319. package/test-result.json +1 -0
  320. package/tsc-errors.txt +4 -0
  321. package/tsc_output.txt +0 -0
  322. package/tsconfig.json +14 -0
  323. package/tsup-learning-esm.config.ts +12 -0
  324. package/tsup.config.ts +21 -0
  325. package/typescript-errors-2.txt +0 -0
  326. package/typescript-errors.txt +22 -0
  327. package/vitest-log-utf8.txt +268 -0
  328. package/vitest-log.txt +0 -0
  329. package/vitest.config.ts +8 -0
@@ -0,0 +1,164 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ACOEngine, type ACOConfig } from '../ACOEngine';
3
+
4
+ // ─── helpers ────────────────────────────────────────────────────────────────
5
+
6
+ function mkACO(cfg?: Partial<ACOConfig>) {
7
+ return new ACOEngine(cfg);
8
+ }
9
+
10
+ /** Build an NxN distance matrix with given cost between all pairs */
11
+ function uniformMatrix(n: number, cost = 1): number[][] {
12
+ return Array.from({ length: n }, (_, i) =>
13
+ Array.from({ length: n }, (_, j) => (i === j ? 0 : cost))
14
+ );
15
+ }
16
+
17
+ /** Build an asymmetric matrix where path 0→1→2→...→n-1 is cheapest */
18
+ function directedMatrix(n: number): number[][] {
19
+ const m = uniformMatrix(n, 100); // default high cost
20
+ for (let i = 0; i < n - 1; i++) {
21
+ m[i][i + 1] = 1; // cheap forward path
22
+ m[i + 1][i] = 1; // and reverse for symmetric support
23
+ }
24
+ return m;
25
+ }
26
+
27
+ // ─── tests ───────────────────────────────────────────────────────────────────
28
+
29
+ describe('ACOEngine — construction / defaultConfig', () => {
30
+ it('creates with empty config', () => expect(() => mkACO()).not.toThrow());
31
+ it('creates with partial config', () => {
32
+ expect(() => mkACO({ antCount: 5, maxIterations: 5 })).not.toThrow();
33
+ });
34
+ });
35
+
36
+ describe('ACOEngine — getRecommendedAntCount', () => {
37
+ const aco = mkACO();
38
+ it('returns min 10 for small node count', () => {
39
+ expect(aco.getRecommendedAntCount(2)).toBeGreaterThanOrEqual(10);
40
+ });
41
+ it('returns max 50 for large node count', () => {
42
+ expect(aco.getRecommendedAntCount(200)).toBeLessThanOrEqual(50);
43
+ });
44
+ it('roughly equals node count in mid range', () => {
45
+ expect(aco.getRecommendedAntCount(25)).toBe(25);
46
+ });
47
+ it('returns integer', () => {
48
+ const r = aco.getRecommendedAntCount(15);
49
+ expect(r).toBe(Math.round(r));
50
+ });
51
+ });
52
+
53
+ describe('ACOEngine — optimize result shape', () => {
54
+ it('returns all required fields', async () => {
55
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
56
+ const result = await aco.optimize(3, uniformMatrix(3));
57
+ expect(result).toHaveProperty('bestPath');
58
+ expect(result).toHaveProperty('bestCost');
59
+ expect(result).toHaveProperty('converged');
60
+ expect(result).toHaveProperty('iterations');
61
+ expect(result).toHaveProperty('costHistory');
62
+ });
63
+ it('bestPath length equals node count', async () => {
64
+ const n = 4;
65
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
66
+ const result = await aco.optimize(n, uniformMatrix(n));
67
+ expect(result.bestPath).toHaveLength(n);
68
+ });
69
+ it('bestPath visits each node exactly once', async () => {
70
+ const n = 5;
71
+ const aco = mkACO({ antCount: 5, maxIterations: 5 });
72
+ const result = await aco.optimize(n, uniformMatrix(n));
73
+ const sorted = [...result.bestPath].sort((a, b) => a - b);
74
+ expect(sorted).toEqual([0, 1, 2, 3, 4]);
75
+ });
76
+ it('iterations > 0', async () => {
77
+ const aco = mkACO({ antCount: 3, maxIterations: 5 });
78
+ const result = await aco.optimize(3, uniformMatrix(3));
79
+ expect(result.iterations).toBeGreaterThan(0);
80
+ });
81
+ it('costHistory is non-empty array', async () => {
82
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
83
+ const result = await aco.optimize(3, uniformMatrix(3));
84
+ expect(Array.isArray(result.costHistory)).toBe(true);
85
+ expect(result.costHistory.length).toBeGreaterThan(0);
86
+ });
87
+ it('bestCost equals min of costHistory', async () => {
88
+ const aco = mkACO({ antCount: 5, maxIterations: 5 });
89
+ const result = await aco.optimize(3, uniformMatrix(3, 2));
90
+ const minHistory = Math.min(...result.costHistory);
91
+ expect(result.bestCost).toBeCloseTo(minHistory, 9);
92
+ });
93
+ it('bestCost is a finite positive number', async () => {
94
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
95
+ const result = await aco.optimize(4, uniformMatrix(4));
96
+ expect(isFinite(result.bestCost)).toBe(true);
97
+ expect(result.bestCost).toBeGreaterThan(0);
98
+ });
99
+ it('converged = boolean', async () => {
100
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
101
+ const result = await aco.optimize(3, uniformMatrix(3));
102
+ expect(typeof result.converged).toBe('boolean');
103
+ });
104
+ });
105
+
106
+ describe('ACOEngine — convergence behavior', () => {
107
+ it('converges on uniform matrix in many iterations', async () => {
108
+ // With uniform costs and many iterations, ACO should converge
109
+ const aco = mkACO({ antCount: 10, maxIterations: 50, convergenceThreshold: 0.001 });
110
+ const result = await aco.optimize(3, uniformMatrix(3));
111
+ expect(result.converged).toBe(true);
112
+ });
113
+ it('does not converge with only 5 iterations (needs > 10)', async () => {
114
+ const aco = mkACO({ antCount: 5, maxIterations: 5 });
115
+ const result = await aco.optimize(4, uniformMatrix(4));
116
+ expect(result.converged).toBe(false);
117
+ });
118
+ });
119
+
120
+ describe('ACOEngine — path quality', () => {
121
+ it('finds the low-cost path in a directed matrix', async () => {
122
+ // For directed matrix, 0→1→2→3 costs 3 (each edge=1), any other cost=100
123
+ const n = 4;
124
+ const aco = mkACO({ antCount: 20, maxIterations: 30 });
125
+ const result = await aco.optimize(n, directedMatrix(n));
126
+ // The best path cost should be n-1 (consecutive edges at cost 1)
127
+ expect(result.bestCost).toBeLessThanOrEqual(n - 1 + 0.01); // allow float rounding
128
+ });
129
+ it('single node — bestPath is [0], bestCost = 0', async () => {
130
+ const aco = mkACO({ antCount: 2, maxIterations: 2 });
131
+ const result = await aco.optimize(1, [[0]]);
132
+ expect(result.bestPath).toEqual([0]);
133
+ expect(result.bestCost).toBe(0);
134
+ });
135
+ it('two nodes — bestPath visits both', async () => {
136
+ const aco = mkACO({ antCount: 3, maxIterations: 3 });
137
+ const result = await aco.optimize(2, [
138
+ [0, 5],
139
+ [5, 0],
140
+ ]);
141
+ expect(result.bestPath).toHaveLength(2);
142
+ expect(new Set(result.bestPath).size).toBe(2);
143
+ });
144
+ });
145
+
146
+ describe('ACOEngine — config sensitivity', () => {
147
+ it('high evaporation rate does not crash', async () => {
148
+ const aco = mkACO({ evaporationRate: 0.9, antCount: 3, maxIterations: 3 });
149
+ await expect(aco.optimize(3, uniformMatrix(3))).resolves.toBeDefined();
150
+ });
151
+ it('low alpha (pheromone ignored) does not crash', async () => {
152
+ const aco = mkACO({ alpha: 0.01, antCount: 3, maxIterations: 3 });
153
+ await expect(aco.optimize(3, uniformMatrix(3))).resolves.toBeDefined();
154
+ });
155
+ it('high elitistWeight does not crash', async () => {
156
+ const aco = mkACO({ elitistWeight: 10, antCount: 3, maxIterations: 3 });
157
+ await expect(aco.optimize(3, uniformMatrix(3))).resolves.toBeDefined();
158
+ });
159
+ it('large antCount works correctly', async () => {
160
+ const aco = mkACO({ antCount: 50, maxIterations: 3 });
161
+ const result = await aco.optimize(4, uniformMatrix(4));
162
+ expect(result.bestPath).toHaveLength(4);
163
+ });
164
+ });
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { ACOEngine, type ACOConfig } from '../ACOEngine';
3
+
4
+ describe('ACOEngine', () => {
5
+ let engine: ACOEngine;
6
+
7
+ beforeEach(() => {
8
+ engine = new ACOEngine();
9
+ });
10
+
11
+ describe('constructor', () => {
12
+ it('should create with default config', () => {
13
+ expect(engine).toBeDefined();
14
+ });
15
+
16
+ it('should accept custom config', () => {
17
+ const customConfig: Partial<ACOConfig> = {
18
+ antCount: 30,
19
+ maxIterations: 150,
20
+ alpha: 1.5,
21
+ };
22
+ const customEngine = new ACOEngine(customConfig);
23
+ expect(customEngine).toBeDefined();
24
+ });
25
+ });
26
+
27
+ describe('optimize', () => {
28
+ it('should find path through all nodes', async () => {
29
+ const nodes = 5;
30
+ // Simple distance matrix
31
+ const distanceMatrix = Array.from({ length: nodes }, (_, i) =>
32
+ Array.from({ length: nodes }, (_, j) => (i === j ? 0 : Math.abs(i - j)))
33
+ );
34
+
35
+ const result = await engine.optimize(nodes, distanceMatrix);
36
+
37
+ expect(result.bestPath).toHaveLength(nodes);
38
+ expect(new Set(result.bestPath).size).toBe(nodes); // All unique
39
+ });
40
+
41
+ it('should find optimal path for simple TSP', async () => {
42
+ // 4 nodes in a line: 0-1-2-3
43
+ // Optimal path: 0,1,2,3 or reverse (cost = 3)
44
+ const distanceMatrix = [
45
+ [0, 1, 2, 3],
46
+ [1, 0, 1, 2],
47
+ [2, 1, 0, 1],
48
+ [3, 2, 1, 0],
49
+ ];
50
+
51
+ const result = await engine.optimize(4, distanceMatrix);
52
+
53
+ // Should find a reasonably good path
54
+ expect(result.bestCost).toBeLessThanOrEqual(6); // Worst case is 6
55
+ });
56
+
57
+ it('should improve over iterations', async () => {
58
+ const nodes = 6;
59
+ const distanceMatrix = Array.from({ length: nodes }, () =>
60
+ Array.from({ length: nodes }, () => Math.random() * 10)
61
+ );
62
+ // Zero diagonal
63
+ for (let i = 0; i < nodes; i++) {
64
+ distanceMatrix[i][i] = 0;
65
+ }
66
+
67
+ const result = await engine.optimize(nodes, distanceMatrix);
68
+
69
+ expect(result.costHistory.length).toBeGreaterThan(1);
70
+ // Later costs should be <= earlier costs (or equal)
71
+ const firstCost = result.costHistory[0];
72
+ const lastCost = result.costHistory[result.costHistory.length - 1];
73
+ expect(lastCost).toBeLessThanOrEqual(firstCost);
74
+ });
75
+
76
+ it('should detect convergence', async () => {
77
+ // Uniform distances - should converge quickly
78
+ const nodes = 4;
79
+ const distanceMatrix = Array.from({ length: nodes }, (_, i) =>
80
+ Array.from({ length: nodes }, (_, j) => (i === j ? 0 : 1))
81
+ );
82
+
83
+ const result = await engine.optimize(nodes, distanceMatrix);
84
+
85
+ expect(result.converged || result.iterations >= 10).toBe(true);
86
+ });
87
+
88
+ it('should handle asymmetric distances', async () => {
89
+ const distanceMatrix = [
90
+ [0, 1, 5],
91
+ [5, 0, 1],
92
+ [1, 5, 0],
93
+ ];
94
+
95
+ const result = await engine.optimize(3, distanceMatrix);
96
+
97
+ expect(result.bestPath).toHaveLength(3);
98
+ expect(result.bestCost).toBeGreaterThan(0);
99
+ });
100
+ });
101
+
102
+ describe('getRecommendedAntCount', () => {
103
+ it('should return at least 10 for small problems', () => {
104
+ expect(engine.getRecommendedAntCount(3)).toBeGreaterThanOrEqual(10);
105
+ });
106
+
107
+ it('should cap at 50 for large problems', () => {
108
+ expect(engine.getRecommendedAntCount(100)).toBeLessThanOrEqual(50);
109
+ });
110
+
111
+ it('should scale with node count', () => {
112
+ const small = engine.getRecommendedAntCount(5);
113
+ const large = engine.getRecommendedAntCount(30);
114
+ expect(large).toBeGreaterThanOrEqual(small);
115
+ });
116
+ });
117
+ });
@@ -0,0 +1,296 @@
1
+ /**
2
+ * CollectiveIntelligence — Production Tests
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+ import { CollectiveIntelligence } from '../CollectiveIntelligence';
6
+ import type { IHiveContribution } from '@holoscript/core';
7
+
8
+ function make(cfg = {}) {
9
+ return new CollectiveIntelligence(cfg);
10
+ }
11
+
12
+ function contrib(type: IHiveContribution['type'], content: string, confidence = 0.8) {
13
+ return { agentId: 'a1', type, content, confidence } as Omit<
14
+ IHiveContribution,
15
+ 'id' | 'timestamp'
16
+ >;
17
+ }
18
+
19
+ describe('CollectiveIntelligence — createSession', () => {
20
+ it('creates session with id', () => {
21
+ const ci = make();
22
+ const s = ci.createSession('topic', 'goal', 'a1');
23
+ expect(s.id).toBeTruthy();
24
+ expect(s.id.startsWith('hive-')).toBe(true);
25
+ });
26
+ it('session has correct topic/goal/initiator', () => {
27
+ const ci = make();
28
+ const s = ci.createSession('AI safety', 'solve it', 'a1');
29
+ expect(s.topic).toBe('AI safety');
30
+ expect(s.goal).toBe('solve it');
31
+ expect(s.initiator).toBe('a1');
32
+ });
33
+ it('session status=active', () => {
34
+ const ci = make();
35
+ expect(ci.createSession('t', 'g', 'a1').status).toBe('active');
36
+ });
37
+ it('initiator is a participant', () => {
38
+ const ci = make();
39
+ const s = ci.createSession('t', 'g', 'a1');
40
+ expect(s.participants).toContain('a1');
41
+ });
42
+ it('contributions starts empty', () => {
43
+ const ci = make();
44
+ expect(ci.createSession('t', 'g', 'a1').contributions).toHaveLength(0);
45
+ });
46
+ it('getSession returns created session', () => {
47
+ const ci = make();
48
+ const s = ci.createSession('t', 'g', 'a1');
49
+ expect(ci.getSession(s.id)).toBe(s);
50
+ });
51
+ it('getSession unknown=undefined', () => {
52
+ expect(make().getSession('ghost')).toBeUndefined();
53
+ });
54
+ it('getActiveSessions includes new session', () => {
55
+ const ci = make();
56
+ ci.createSession('t', 'g', 'a1');
57
+ expect(ci.getActiveSessions()).toHaveLength(1);
58
+ });
59
+ });
60
+
61
+ describe('CollectiveIntelligence — join / leave', () => {
62
+ it('join adds participant', () => {
63
+ const ci = make();
64
+ const s = ci.createSession('t', 'g', 'a1');
65
+ ci.join(s.id, 'a2');
66
+ expect(ci.getSession(s.id)!.participants).toContain('a2');
67
+ });
68
+ it('join idempotent for duplicate', () => {
69
+ const ci = make();
70
+ const s = ci.createSession('t', 'g', 'a1');
71
+ ci.join(s.id, 'a1'); // already there
72
+ expect(ci.getSession(s.id)!.participants).toHaveLength(1);
73
+ });
74
+ it('join unknown session throws', () => {
75
+ expect(() => make().join('ghost', 'a1')).toThrow();
76
+ });
77
+ it('join full session throws', () => {
78
+ const ci = make({ maxParticipants: 1 });
79
+ const s = ci.createSession('t', 'g', 'a1');
80
+ expect(() => ci.join(s.id, 'a2')).toThrow();
81
+ });
82
+ it('leave removes participant', () => {
83
+ const ci = make();
84
+ const s = ci.createSession('t', 'g', 'a1');
85
+ ci.join(s.id, 'a2');
86
+ ci.leave(s.id, 'a2');
87
+ expect(ci.getSession(s.id)!.participants).not.toContain('a2');
88
+ });
89
+ it('leave unknown session throws', () => {
90
+ expect(() => make().leave('ghost', 'a1')).toThrow();
91
+ });
92
+ });
93
+
94
+ describe('CollectiveIntelligence — contribute', () => {
95
+ it('returns IHiveContribution with id/timestamp', () => {
96
+ const ci = make();
97
+ const s = ci.createSession('t', 'g', 'a1');
98
+ const c = ci.contribute(s.id, contrib('idea', 'use caching'));
99
+ expect(c.id).toBeTruthy();
100
+ expect(c.timestamp).toBeGreaterThan(0);
101
+ });
102
+ it('contribution stored in session', () => {
103
+ const ci = make();
104
+ const s = ci.createSession('t', 'g', 'a1');
105
+ ci.contribute(s.id, contrib('idea', 'scale'));
106
+ expect(ci.getSession(s.id)!.contributions).toHaveLength(1);
107
+ });
108
+ it('non-participant cannot contribute', () => {
109
+ const ci = make();
110
+ const s = ci.createSession('t', 'g', 'a1');
111
+ expect(() =>
112
+ ci.contribute(s.id, { agentId: 'stranger', type: 'idea', content: 'x', confidence: 0.5 })
113
+ ).toThrow();
114
+ });
115
+ it('contribute unknown session throws', () => {
116
+ expect(() => make().contribute('ghost', contrib('idea', 'x'))).toThrow();
117
+ });
118
+ it('getAgentContributions returns only that agent', () => {
119
+ const ci = make();
120
+ const s = ci.createSession('t', 'g', 'a1');
121
+ ci.join(s.id, 'a2');
122
+ ci.contribute(s.id, contrib('idea', 'c1'));
123
+ ci.contribute(s.id, { agentId: 'a2', type: 'critique', content: 'c2', confidence: 0.7 });
124
+ expect(ci.getAgentContributions(s.id, 'a1')).toHaveLength(1);
125
+ });
126
+ it('contribute to closed session throws', () => {
127
+ const ci = make();
128
+ const s = ci.createSession('t', 'g', 'a1');
129
+ ci.closeSession(s.id);
130
+ expect(() => ci.contribute(s.id, contrib('idea', 'x'))).toThrow();
131
+ });
132
+ });
133
+
134
+ describe('CollectiveIntelligence — vote', () => {
135
+ it('vote does not throw', () => {
136
+ const ci = make();
137
+ const s = ci.createSession('t', 'g', 'a1');
138
+ ci.join(s.id, 'a2');
139
+ const c = ci.contribute(s.id, contrib('idea', 'scale'));
140
+ expect(() => ci.vote(s.id, c.id, 'a2', 'support')).not.toThrow();
141
+ });
142
+ it('non-participant cannot vote', () => {
143
+ const ci = make();
144
+ const s = ci.createSession('t', 'g', 'a1');
145
+ const c = ci.contribute(s.id, contrib('idea', 'scale'));
146
+ expect(() => ci.vote(s.id, c.id, 'outsider', 'support')).toThrow();
147
+ });
148
+ it('vote on unknown session throws', () => {
149
+ expect(() => make().vote('ghost', 'c1', 'a1', 'support')).toThrow();
150
+ });
151
+ it('getVotingResults returns array for valid session', () => {
152
+ const ci = make();
153
+ const s = ci.createSession('t', 'g', 'a1');
154
+ ci.contribute(s.id, contrib('idea', 'scale'));
155
+ const results = ci.getVotingResults(s.id);
156
+ expect(Array.isArray(results)).toBe(true);
157
+ });
158
+ it('getTopContribution returns highest voted', () => {
159
+ const ci = make();
160
+ const s = ci.createSession('t', 'g', 'a1');
161
+ ci.join(s.id, 'a2');
162
+ const c1 = ci.contribute(s.id, contrib('idea', 'best idea'));
163
+ const c2 = ci.contribute(s.id, {
164
+ agentId: 'a2',
165
+ type: 'idea',
166
+ content: 'other',
167
+ confidence: 0.5,
168
+ });
169
+ ci.vote(s.id, c1.id, 'a2', 'support');
170
+ const top = ci.getTopContribution(s.id);
171
+ expect(top?.id).toBe(c1.id);
172
+ });
173
+ });
174
+
175
+ describe('CollectiveIntelligence — synthesize', () => {
176
+ it('returns empty result when below synthesisMinContributions', () => {
177
+ const ci = make({ synthesisMinContributions: 3 });
178
+ const s = ci.createSession('t', 'g', 'a1');
179
+ ci.contribute(s.id, contrib('idea', 'x'));
180
+ const r = ci.synthesize(s.id);
181
+ expect(r.confidence).toBe(0);
182
+ expect(r.synthesizedContent).toBe('');
183
+ });
184
+ it('returns non-empty when min contributions met', () => {
185
+ const ci = make({ synthesisMinContributions: 2 });
186
+ const s = ci.createSession('t', 'g', 'a1');
187
+ ci.contribute(s.id, contrib('idea', 'implement caching layer'));
188
+ ci.contribute(s.id, contrib('solution', 'use Redis for cache'));
189
+ const r = ci.synthesize(s.id);
190
+ expect(r.synthesizedContent.length).toBeGreaterThan(0);
191
+ expect(r.confidence).toBeGreaterThan(0);
192
+ });
193
+ it('synthesize unknown session throws', () => {
194
+ expect(() => make().synthesize('ghost')).toThrow();
195
+ });
196
+ it('metadata totals correct', () => {
197
+ const ci = make({ synthesisMinContributions: 2 });
198
+ const s = ci.createSession('t', 'g', 'a1');
199
+ ci.contribute(s.id, contrib('idea', 'an idea'));
200
+ ci.contribute(s.id, contrib('solution', 'a solution'));
201
+ const r = ci.synthesize(s.id);
202
+ expect(r.metadata.totalContributions).toBe(2);
203
+ expect(r.metadata.ideaCount).toBe(1);
204
+ expect(r.metadata.solutionCount).toBe(1);
205
+ });
206
+ });
207
+
208
+ describe('CollectiveIntelligence — resolve / closeSession', () => {
209
+ it('resolve sets session status=resolved', () => {
210
+ const ci = make();
211
+ const s = ci.createSession('t', 'g', 'a1');
212
+ ci.resolve(s.id, 'final decision');
213
+ expect(ci.getSession(s.id)!.status).toBe('resolved');
214
+ expect(ci.getSession(s.id)!.resolution).toBe('final decision');
215
+ });
216
+ it('resolve unknown session throws', () => {
217
+ expect(() => make().resolve('ghost', 'x')).toThrow();
218
+ });
219
+ it('closeSession sets status=closed', () => {
220
+ const ci = make();
221
+ const s = ci.createSession('t', 'g', 'a1');
222
+ ci.closeSession(s.id);
223
+ expect(ci.getSession(s.id)!.status).toBe('closed');
224
+ });
225
+ it('closeSession unknown session throws', () => {
226
+ expect(() => make().closeSession('ghost')).toThrow();
227
+ });
228
+ it('resolved session not in getActiveSessions', () => {
229
+ const ci = make();
230
+ const s = ci.createSession('t', 'g', 'a1');
231
+ ci.resolve(s.id, 'done');
232
+ expect(ci.getActiveSessions()).toHaveLength(0);
233
+ });
234
+ });
235
+
236
+ describe('CollectiveIntelligence — getSessionStats', () => {
237
+ it('returns undefined for unknown session', () => {
238
+ expect(make().getSessionStats('ghost')).toBeUndefined();
239
+ });
240
+ it('returns correct participantCount and contributionCount', () => {
241
+ const ci = make();
242
+ const s = ci.createSession('t', 'g', 'a1');
243
+ ci.join(s.id, 'a2');
244
+ ci.contribute(s.id, contrib('idea', 'x'));
245
+ const stats = ci.getSessionStats(s.id)!;
246
+ expect(stats.participantCount).toBe(2);
247
+ expect(stats.contributionCount).toBe(1);
248
+ });
249
+ it('contributionsByType correct', () => {
250
+ const ci = make();
251
+ const s = ci.createSession('t', 'g', 'a1');
252
+ ci.contribute(s.id, contrib('idea', 'x'));
253
+ ci.contribute(s.id, contrib('critique', 'y'));
254
+ const stats = ci.getSessionStats(s.id)!;
255
+ expect(stats.contributionsByType.idea).toBe(1);
256
+ expect(stats.contributionsByType.critique).toBe(1);
257
+ });
258
+ it('averageConfidence computed', () => {
259
+ const ci = make();
260
+ const s = ci.createSession('t', 'g', 'a1');
261
+ ci.contribute(s.id, contrib('idea', 'x', 0.6));
262
+ ci.contribute(s.id, contrib('idea', 'y', 0.8));
263
+ const stats = ci.getSessionStats(s.id)!;
264
+ expect(stats.averageConfidence).toBeCloseTo(0.7, 5);
265
+ });
266
+ });
267
+
268
+ describe('CollectiveIntelligence — checkForConsensus', () => {
269
+ it('returns false for empty session', () => {
270
+ const ci = make();
271
+ const s = ci.createSession('t', 'g', 'a1');
272
+ expect(ci.checkForConsensus(s.id)).toBe(false);
273
+ });
274
+ it('checkForConsensus unknown session returns false', () => {
275
+ expect(make().checkForConsensus('ghost')).toBe(false);
276
+ });
277
+ });
278
+
279
+ describe('CollectiveIntelligence — findSimilarContributions', () => {
280
+ it('returns empty for unknown session', () => {
281
+ expect(make().findSimilarContributions('ghost', 'c1')).toHaveLength(0);
282
+ });
283
+ it('returns empty for unknown contribution', () => {
284
+ const ci = make();
285
+ const s = ci.createSession('t', 'g', 'a1');
286
+ expect(ci.findSimilarContributions(s.id, 'ghost')).toHaveLength(0);
287
+ });
288
+ it('finds similar contributions by content overlap', () => {
289
+ const ci = make();
290
+ const s = ci.createSession('t', 'g', 'a1');
291
+ const c1 = ci.contribute(s.id, contrib('idea', 'implement redis caching system'));
292
+ ci.contribute(s.id, contrib('idea', 'setup redis caching service'));
293
+ const similar = ci.findSimilarContributions(s.id, c1.id);
294
+ expect(similar.length).toBeGreaterThanOrEqual(0); // may or may not find based on jaccard threshold
295
+ });
296
+ });