htm 0.0.1

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 (155) hide show
  1. checksums.yaml +7 -0
  2. data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
  3. data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
  4. data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
  5. data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
  6. data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
  7. data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
  8. data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
  9. data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
  10. data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
  11. data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
  12. data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
  13. data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
  14. data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
  15. data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
  16. data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
  17. data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
  18. data/.architecture/members.yml +144 -0
  19. data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
  20. data/.architecture/reviews/initial-system-analysis.md +330 -0
  21. data/.envrc +32 -0
  22. data/.irbrc +145 -0
  23. data/CHANGELOG.md +150 -0
  24. data/COMMITS.md +196 -0
  25. data/LICENSE +21 -0
  26. data/README.md +1347 -0
  27. data/Rakefile +51 -0
  28. data/SETUP.md +268 -0
  29. data/config/database.yml +67 -0
  30. data/db/migrate/20250101000001_enable_extensions.rb +14 -0
  31. data/db/migrate/20250101000002_create_robots.rb +14 -0
  32. data/db/migrate/20250101000003_create_nodes.rb +42 -0
  33. data/db/migrate/20250101000005_create_tags.rb +38 -0
  34. data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
  35. data/db/schema.sql +473 -0
  36. data/db/seed_data/README.md +100 -0
  37. data/db/seed_data/presidents.md +136 -0
  38. data/db/seed_data/states.md +151 -0
  39. data/db/seeds.rb +208 -0
  40. data/dbdoc/README.md +173 -0
  41. data/dbdoc/public.node_stats.md +48 -0
  42. data/dbdoc/public.node_stats.svg +41 -0
  43. data/dbdoc/public.node_tags.md +40 -0
  44. data/dbdoc/public.node_tags.svg +112 -0
  45. data/dbdoc/public.nodes.md +54 -0
  46. data/dbdoc/public.nodes.svg +118 -0
  47. data/dbdoc/public.nodes_tags.md +39 -0
  48. data/dbdoc/public.nodes_tags.svg +112 -0
  49. data/dbdoc/public.ontology_structure.md +48 -0
  50. data/dbdoc/public.ontology_structure.svg +38 -0
  51. data/dbdoc/public.operations_log.md +42 -0
  52. data/dbdoc/public.operations_log.svg +130 -0
  53. data/dbdoc/public.relationships.md +39 -0
  54. data/dbdoc/public.relationships.svg +41 -0
  55. data/dbdoc/public.robot_activity.md +46 -0
  56. data/dbdoc/public.robot_activity.svg +35 -0
  57. data/dbdoc/public.robots.md +35 -0
  58. data/dbdoc/public.robots.svg +90 -0
  59. data/dbdoc/public.schema_migrations.md +29 -0
  60. data/dbdoc/public.schema_migrations.svg +26 -0
  61. data/dbdoc/public.tags.md +35 -0
  62. data/dbdoc/public.tags.svg +60 -0
  63. data/dbdoc/public.topic_relationships.md +45 -0
  64. data/dbdoc/public.topic_relationships.svg +32 -0
  65. data/dbdoc/schema.json +1437 -0
  66. data/dbdoc/schema.svg +154 -0
  67. data/docs/api/database.md +806 -0
  68. data/docs/api/embedding-service.md +532 -0
  69. data/docs/api/htm.md +797 -0
  70. data/docs/api/index.md +259 -0
  71. data/docs/api/long-term-memory.md +1096 -0
  72. data/docs/api/working-memory.md +665 -0
  73. data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
  74. data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
  75. data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
  76. data/docs/architecture/adrs/004-hive-mind.md +437 -0
  77. data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
  78. data/docs/architecture/adrs/006-context-assembly.md +496 -0
  79. data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
  80. data/docs/architecture/adrs/008-robot-identification.md +625 -0
  81. data/docs/architecture/adrs/009-never-forget.md +648 -0
  82. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
  83. data/docs/architecture/adrs/011-pgai-integration.md +494 -0
  84. data/docs/architecture/adrs/index.md +215 -0
  85. data/docs/architecture/hive-mind.md +736 -0
  86. data/docs/architecture/index.md +351 -0
  87. data/docs/architecture/overview.md +538 -0
  88. data/docs/architecture/two-tier-memory.md +873 -0
  89. data/docs/assets/css/custom.css +83 -0
  90. data/docs/assets/images/htm-core-components.svg +63 -0
  91. data/docs/assets/images/htm-database-schema.svg +93 -0
  92. data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
  93. data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
  94. data/docs/assets/images/htm-layered-architecture.svg +71 -0
  95. data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
  96. data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
  97. data/docs/assets/images/htm.jpg +0 -0
  98. data/docs/assets/images/htm_demo.gif +0 -0
  99. data/docs/assets/js/mathjax.js +18 -0
  100. data/docs/assets/videos/htm_video.mp4 +0 -0
  101. data/docs/database_rake_tasks.md +322 -0
  102. data/docs/development/contributing.md +787 -0
  103. data/docs/development/index.md +336 -0
  104. data/docs/development/schema.md +596 -0
  105. data/docs/development/setup.md +719 -0
  106. data/docs/development/testing.md +819 -0
  107. data/docs/guides/adding-memories.md +824 -0
  108. data/docs/guides/context-assembly.md +1009 -0
  109. data/docs/guides/getting-started.md +577 -0
  110. data/docs/guides/index.md +118 -0
  111. data/docs/guides/long-term-memory.md +941 -0
  112. data/docs/guides/multi-robot.md +866 -0
  113. data/docs/guides/recalling-memories.md +927 -0
  114. data/docs/guides/search-strategies.md +953 -0
  115. data/docs/guides/working-memory.md +717 -0
  116. data/docs/index.md +214 -0
  117. data/docs/installation.md +477 -0
  118. data/docs/multi_framework_support.md +519 -0
  119. data/docs/quick-start.md +655 -0
  120. data/docs/setup_local_database.md +302 -0
  121. data/docs/using_rake_tasks_in_your_app.md +383 -0
  122. data/examples/basic_usage.rb +93 -0
  123. data/examples/cli_app/README.md +317 -0
  124. data/examples/cli_app/htm_cli.rb +270 -0
  125. data/examples/custom_llm_configuration.rb +183 -0
  126. data/examples/example_app/Rakefile +71 -0
  127. data/examples/example_app/app.rb +206 -0
  128. data/examples/sinatra_app/Gemfile +21 -0
  129. data/examples/sinatra_app/app.rb +335 -0
  130. data/lib/htm/active_record_config.rb +113 -0
  131. data/lib/htm/configuration.rb +342 -0
  132. data/lib/htm/database.rb +594 -0
  133. data/lib/htm/embedding_service.rb +115 -0
  134. data/lib/htm/errors.rb +34 -0
  135. data/lib/htm/job_adapter.rb +154 -0
  136. data/lib/htm/jobs/generate_embedding_job.rb +65 -0
  137. data/lib/htm/jobs/generate_tags_job.rb +82 -0
  138. data/lib/htm/long_term_memory.rb +965 -0
  139. data/lib/htm/models/node.rb +109 -0
  140. data/lib/htm/models/node_tag.rb +33 -0
  141. data/lib/htm/models/robot.rb +52 -0
  142. data/lib/htm/models/tag.rb +76 -0
  143. data/lib/htm/railtie.rb +76 -0
  144. data/lib/htm/sinatra.rb +157 -0
  145. data/lib/htm/tag_service.rb +135 -0
  146. data/lib/htm/tasks.rb +38 -0
  147. data/lib/htm/version.rb +5 -0
  148. data/lib/htm/working_memory.rb +182 -0
  149. data/lib/htm.rb +400 -0
  150. data/lib/tasks/db.rake +19 -0
  151. data/lib/tasks/htm.rake +147 -0
  152. data/lib/tasks/jobs.rake +312 -0
  153. data/mkdocs.yml +190 -0
  154. data/scripts/install_local_database.sh +309 -0
  155. metadata +341 -0
@@ -0,0 +1,866 @@
1
+ # Multi-Robot Usage
2
+
3
+ HTM's "hive mind" architecture enables multiple robots to share knowledge through a common long-term memory. This guide covers setting up multi-robot systems, attribution tracking, and collaboration patterns.
4
+
5
+ ## Understanding the Hive Mind
6
+
7
+ In HTM, all robots share the same long-term memory database but maintain separate working memories:
8
+
9
+ ![HTM Hive Mind Architecture](../assets/images/htm-hive-mind-architecture.svg)
10
+
11
+ **Key Principles:**
12
+
13
+ - **Shared Knowledge**: All memories are accessible to all robots
14
+ - **Private Working Memory**: Each robot has its own active context
15
+ - **Full Attribution**: Track which robot added each memory
16
+ - **Collective Intelligence**: Robots learn from each other's experiences
17
+
18
+ ## Setting Up Multiple Robots
19
+
20
+ ### Basic Multi-Robot Setup
21
+
22
+ ```ruby
23
+ # Robot 1: Research Assistant
24
+ research_bot = HTM.new(
25
+ robot_name: "Research Assistant",
26
+ robot_id: "research-001",
27
+ working_memory_size: 128_000
28
+ )
29
+
30
+ # Robot 2: Code Helper
31
+ code_bot = HTM.new(
32
+ robot_name: "Code Helper",
33
+ robot_id: "code-001",
34
+ working_memory_size: 128_000
35
+ )
36
+
37
+ # Robot 3: Documentation Writer
38
+ docs_bot = HTM.new(
39
+ robot_name: "Docs Writer",
40
+ robot_id: "docs-001",
41
+ working_memory_size: 64_000
42
+ )
43
+
44
+ # Each robot can access shared knowledge
45
+ research_bot.add_node(
46
+ "finding_001",
47
+ "Research shows PostgreSQL outperforms MongoDB for ACID workloads",
48
+ type: :fact,
49
+ importance: 8.0,
50
+ tags: ["research", "database"]
51
+ )
52
+
53
+ # Code bot can access this finding
54
+ findings = code_bot.recall(
55
+ timeframe: "last hour",
56
+ topic: "database performance"
57
+ )
58
+
59
+ # Docs bot can document it
60
+ docs_bot.add_node(
61
+ "doc_001",
62
+ "PostgreSQL performance documented based on research findings",
63
+ type: :context,
64
+ importance: 6.0,
65
+ tags: ["documentation", "database"],
66
+ related_to: ["finding_001"]
67
+ )
68
+ ```
69
+
70
+ ## Robot Identification
71
+
72
+ ### Session IDs vs Persistent IDs
73
+
74
+ Choose the right identification strategy:
75
+
76
+ ```ruby
77
+ # Strategy 1: Persistent Robot (recommended for production)
78
+ persistent_bot = HTM.new(
79
+ robot_name: "Production Assistant",
80
+ robot_id: "prod-assistant-001" # Fixed, reusable
81
+ )
82
+
83
+ # Strategy 2: Session-based Robot (for temporary workflows)
84
+ session_id = SecureRandom.uuid
85
+ session_bot = HTM.new(
86
+ robot_name: "Temp Session",
87
+ robot_id: "session-#{session_id}" # Unique per session
88
+ )
89
+
90
+ # Strategy 3: User-specific Robot
91
+ user_id = "alice"
92
+ user_bot = HTM.new(
93
+ robot_name: "Alice's Assistant",
94
+ robot_id: "user-#{user_id}-assistant"
95
+ )
96
+ ```
97
+
98
+ !!! tip "Naming Conventions"
99
+ - **Production robots**: `service-purpose-001` (e.g., `api-assistant-001`)
100
+ - **User robots**: `user-{user_id}-{purpose}` (e.g., `user-alice-assistant`)
101
+ - **Session robots**: `session-{uuid}` (e.g., `session-abc123...`)
102
+ - **Team robots**: `team-{name}-{purpose}` (e.g., `team-eng-reviewer`)
103
+
104
+ ### Robot Registry
105
+
106
+ All robots are automatically registered:
107
+
108
+ ```ruby
109
+ # Robots are registered when created
110
+ bot = HTM.new(robot_name: "My Bot", robot_id: "bot-001")
111
+
112
+ # Query robot registry
113
+ config = HTM::Database.default_config
114
+ conn = PG.connect(config)
115
+
116
+ result = conn.exec("SELECT * FROM robots ORDER BY last_active DESC")
117
+
118
+ puts "Registered robots:"
119
+ result.each do |row|
120
+ puts "#{row['name']} (#{row['id']})"
121
+ puts " Created: #{row['created_at']}"
122
+ puts " Last active: #{row['last_active']}"
123
+ puts
124
+ end
125
+
126
+ conn.close
127
+ ```
128
+
129
+ ## Attribution Tracking
130
+
131
+ ### Who Said What?
132
+
133
+ Track which robot contributed which memories:
134
+
135
+ ```ruby
136
+ # Add memories from different robots
137
+ alpha = HTM.new(robot_name: "Alpha", robot_id: "alpha")
138
+ beta = HTM.new(robot_name: "Beta", robot_id: "beta")
139
+
140
+ alpha.add_node("alpha_001", "Alpha's insight about caching", type: :fact)
141
+ beta.add_node("beta_001", "Beta's approach to testing", type: :fact)
142
+
143
+ # Query by robot
144
+ def memories_by_robot(robot_id)
145
+ config = HTM::Database.default_config
146
+ conn = PG.connect(config)
147
+
148
+ result = conn.exec_params(
149
+ "SELECT key, value, type FROM nodes WHERE robot_id = $1",
150
+ [robot_id]
151
+ )
152
+
153
+ memories = result.to_a
154
+ conn.close
155
+ memories
156
+ end
157
+
158
+ alpha_memories = memories_by_robot("alpha")
159
+ puts "Alpha contributed #{alpha_memories.length} memories"
160
+ ```
161
+
162
+ ### Which Robot Said...?
163
+
164
+ Use HTM's built-in attribution tracking:
165
+
166
+ ```ruby
167
+ # Find which robots discussed a topic
168
+ breakdown = htm.which_robot_said("PostgreSQL")
169
+
170
+ puts "Robots that discussed PostgreSQL:"
171
+ breakdown.each do |robot_id, count|
172
+ puts " #{robot_id}: #{count} mentions"
173
+ end
174
+
175
+ # Example output:
176
+ # Robots that discussed PostgreSQL:
177
+ # research-001: 15 mentions
178
+ # code-001: 8 mentions
179
+ # docs-001: 3 mentions
180
+ ```
181
+
182
+ ### Conversation Timeline
183
+
184
+ See the chronological conversation across robots:
185
+
186
+ ```ruby
187
+ timeline = htm.conversation_timeline("architecture decisions", limit: 50)
188
+
189
+ puts "Architecture discussion timeline:"
190
+ timeline.each do |entry|
191
+ puts "#{entry[:timestamp]} - #{entry[:robot]}"
192
+ puts " [#{entry[:type]}] #{entry[:content][0..100]}..."
193
+ puts
194
+ end
195
+ ```
196
+
197
+ ## Collaboration Patterns
198
+
199
+ ### Pattern 1: Specialized Roles
200
+
201
+ Each robot has a specific role and expertise:
202
+
203
+ ```ruby
204
+ class MultiRobotSystem
205
+ def initialize
206
+ @researcher = HTM.new(
207
+ robot_name: "Researcher",
208
+ robot_id: "researcher-001"
209
+ )
210
+
211
+ @developer = HTM.new(
212
+ robot_name: "Developer",
213
+ robot_id: "developer-001"
214
+ )
215
+
216
+ @reviewer = HTM.new(
217
+ robot_name: "Reviewer",
218
+ robot_id: "reviewer-001"
219
+ )
220
+ end
221
+
222
+ def process_feature_request(feature)
223
+ # 1. Researcher gathers requirements
224
+ @researcher.add_node(
225
+ "research_#{feature}",
226
+ "Research findings for #{feature}",
227
+ type: :fact,
228
+ importance: 8.0,
229
+ tags: ["research", feature]
230
+ )
231
+
232
+ # 2. Developer recalls research and implements
233
+ research = @developer.recall(
234
+ timeframe: "last hour",
235
+ topic: "research #{feature}"
236
+ )
237
+
238
+ @developer.add_node(
239
+ "impl_#{feature}",
240
+ "Implementation plan based on research",
241
+ type: :decision,
242
+ importance: 9.0,
243
+ tags: ["implementation", feature],
244
+ related_to: ["research_#{feature}"]
245
+ )
246
+
247
+ # 3. Reviewer checks work
248
+ work = @reviewer.recall(
249
+ timeframe: "last hour",
250
+ topic: feature
251
+ )
252
+
253
+ @reviewer.add_node(
254
+ "review_#{feature}",
255
+ "Code review findings",
256
+ type: :context,
257
+ importance: 7.0,
258
+ tags: ["review", feature]
259
+ )
260
+ end
261
+ end
262
+
263
+ system = MultiRobotSystem.new
264
+ system.process_feature_request("user-authentication")
265
+ ```
266
+
267
+ ### Pattern 2: Shift Handoff
268
+
269
+ Robots pass context between shifts:
270
+
271
+ ```ruby
272
+ class ShiftHandoff
273
+ def initialize
274
+ @current_shift = nil
275
+ end
276
+
277
+ def start_shift(shift_name)
278
+ @current_shift = HTM.new(
279
+ robot_name: "#{shift_name} Bot",
280
+ robot_id: "shift-#{shift_name.downcase}"
281
+ )
282
+
283
+ # Recall context from previous shift
284
+ handoff = @current_shift.recall(
285
+ timeframe: "last 24 hours",
286
+ topic: "shift handoff urgent",
287
+ strategy: :hybrid,
288
+ limit: 20
289
+ )
290
+
291
+ puts "#{shift_name} shift starting"
292
+ puts "Received #{handoff.length} items from previous shift"
293
+
294
+ handoff
295
+ end
296
+
297
+ def end_shift(summary)
298
+ # Document shift handoff
299
+ @current_shift.add_node(
300
+ "handoff_#{Time.now.to_i}",
301
+ summary,
302
+ type: :context,
303
+ importance: 9.0,
304
+ tags: ["shift-handoff", "urgent"]
305
+ )
306
+
307
+ puts "Shift handoff documented"
308
+ end
309
+ end
310
+
311
+ # Usage
312
+ handoff = ShiftHandoff.new
313
+
314
+ # Morning shift
315
+ morning = handoff.start_shift("Morning")
316
+ # ... do morning work
317
+ handoff.end_shift("Three critical bugs fixed, deploy scheduled for 2pm")
318
+
319
+ # Afternoon shift
320
+ afternoon = handoff.start_shift("Afternoon")
321
+ # ... receives morning's summary
322
+ ```
323
+
324
+ ### Pattern 3: Expert Consultation
325
+
326
+ Specialized experts provide knowledge:
327
+
328
+ ```ruby
329
+ class ExpertSystem
330
+ def initialize
331
+ @experts = {
332
+ database: HTM.new(
333
+ robot_name: "Database Expert",
334
+ robot_id: "expert-database"
335
+ ),
336
+ security: HTM.new(
337
+ robot_name: "Security Expert",
338
+ robot_id: "expert-security"
339
+ ),
340
+ performance: HTM.new(
341
+ robot_name: "Performance Expert",
342
+ robot_id: "expert-performance"
343
+ )
344
+ }
345
+
346
+ @general = HTM.new(
347
+ robot_name: "General Assistant",
348
+ robot_id: "assistant-general"
349
+ )
350
+ end
351
+
352
+ def consult(topic)
353
+ # Determine which expert to consult
354
+ expert_type = determine_expert(topic)
355
+ expert = @experts[expert_type]
356
+
357
+ # Get expert knowledge
358
+ knowledge = expert.recall(
359
+ timeframe: "all time",
360
+ topic: topic,
361
+ strategy: :hybrid,
362
+ limit: 10
363
+ )
364
+
365
+ # General assistant learns from expert
366
+ knowledge.each do |k|
367
+ @general.add_node(
368
+ "learned_#{SecureRandom.hex(4)}",
369
+ "Learned from #{expert_type} expert: #{k['value']}",
370
+ type: :fact,
371
+ importance: k['importance'],
372
+ tags: ["learned", expert_type.to_s],
373
+ related_to: [k['key']]
374
+ )
375
+ end
376
+
377
+ knowledge
378
+ end
379
+
380
+ private
381
+
382
+ def determine_expert(topic)
383
+ # Simple keyword matching
384
+ case topic.downcase
385
+ when /database|sql|query/
386
+ :database
387
+ when /security|auth|encryption/
388
+ :security
389
+ when /performance|speed|optimization/
390
+ :performance
391
+ else
392
+ :database # default
393
+ end
394
+ end
395
+ end
396
+
397
+ system = ExpertSystem.new
398
+ knowledge = system.consult("PostgreSQL query optimization")
399
+ ```
400
+
401
+ ### Pattern 4: Collaborative Decision Making
402
+
403
+ Multiple robots contribute to decisions:
404
+
405
+ ```ruby
406
+ class CollaborativeDecision
407
+ def initialize(topic)
408
+ @topic = topic
409
+ @participants = []
410
+ end
411
+
412
+ def add_participant(name, role)
413
+ bot = HTM.new(
414
+ robot_name: "#{name} (#{role})",
415
+ robot_id: "decision-#{role.downcase}-#{SecureRandom.hex(4)}"
416
+ )
417
+ @participants << { name: name, role: role, bot: bot }
418
+ bot
419
+ end
420
+
421
+ def gather_input(bot, opinion)
422
+ bot.add_node(
423
+ "opinion_#{SecureRandom.hex(4)}",
424
+ opinion,
425
+ type: :context,
426
+ importance: 8.0,
427
+ tags: ["decision", @topic, "opinion"]
428
+ )
429
+ end
430
+
431
+ def make_decision(decision_maker)
432
+ # Recall all opinions
433
+ opinions = decision_maker.recall(
434
+ timeframe: "last hour",
435
+ topic: "decision #{@topic} opinion",
436
+ strategy: :hybrid,
437
+ limit: 50
438
+ )
439
+
440
+ puts "#{decision_maker.robot_name} considering:"
441
+ opinions.each do |opinion|
442
+ puts "- #{opinion['value'][0..100]}..."
443
+ end
444
+
445
+ # Document final decision
446
+ decision_maker.add_node(
447
+ "decision_#{@topic}_final",
448
+ "Final decision on #{@topic} after considering team input",
449
+ type: :decision,
450
+ importance: 10.0,
451
+ tags: ["decision", @topic, "final"]
452
+ )
453
+ end
454
+ end
455
+
456
+ # Usage
457
+ decision = CollaborativeDecision.new("database-choice")
458
+
459
+ # Gather input
460
+ developer = decision.add_participant("Alice", "Developer")
461
+ decision.gather_input(developer, "PostgreSQL for reliability")
462
+
463
+ architect = decision.add_participant("Bob", "Architect")
464
+ decision.gather_input(architect, "PostgreSQL for ACID compliance")
465
+
466
+ dba = decision.add_participant("Carol", "DBA")
467
+ decision.gather_input(dba, "PostgreSQL for operational maturity")
468
+
469
+ # Make decision
470
+ lead = decision.add_participant("Dave", "TechLead")
471
+ decision.make_decision(lead)
472
+ ```
473
+
474
+ ## Shared vs Private Knowledge
475
+
476
+ ### Sharing Strategies
477
+
478
+ Control what gets shared:
479
+
480
+ ```ruby
481
+ class SmartSharing
482
+ def initialize(robot_id)
483
+ @htm = HTM.new(robot_name: "Smart Bot", robot_id: robot_id)
484
+ @private_prefix = "private_#{robot_id}_"
485
+ end
486
+
487
+ def add_shared(key, value, **opts)
488
+ # Shared with all robots
489
+ @htm.add_node(key, value, **opts.merge(
490
+ tags: (opts[:tags] || []) + ["shared"]
491
+ ))
492
+ end
493
+
494
+ def add_private(key, value, **opts)
495
+ # Use robot-specific key prefix
496
+ private_key = "#{@private_prefix}#{key}"
497
+ @htm.add_node(private_key, value, **opts.merge(
498
+ tags: (opts[:tags] || []) + ["private"],
499
+ importance: (opts[:importance] || 5.0)
500
+ ))
501
+ end
502
+
503
+ def recall_shared(topic)
504
+ # Only shared knowledge
505
+ @htm.recall(
506
+ timeframe: "all time",
507
+ topic: "shared #{topic}",
508
+ strategy: :hybrid
509
+ ).select { |m| m['tags']&.include?("shared") }
510
+ end
511
+
512
+ def recall_private(topic)
513
+ # Only my private knowledge
514
+ @htm.recall(
515
+ timeframe: "all time",
516
+ topic: topic,
517
+ strategy: :hybrid
518
+ ).select { |m| m['key'].start_with?(@private_prefix) }
519
+ end
520
+ end
521
+
522
+ # Usage
523
+ bot1 = SmartSharing.new("bot-001")
524
+ bot1.add_shared("shared_fact", "Everyone should know this", type: :fact)
525
+ bot1.add_private("my_thought", "Private thought", type: :context)
526
+
527
+ bot2 = SmartSharing.new("bot-002")
528
+ shared = bot2.recall_shared("fact") # Can see shared_fact
529
+ private = bot2.recall_private("thought") # Won't see bot1's private thoughts
530
+ ```
531
+
532
+ ## Cross-Robot Queries
533
+
534
+ ### Finding Robot Activity
535
+
536
+ ```ruby
537
+ # Get all robots and their activity
538
+ def get_robot_activity
539
+ config = HTM::Database.default_config
540
+ conn = PG.connect(config)
541
+
542
+ result = conn.exec(
543
+ <<~SQL
544
+ SELECT
545
+ r.id,
546
+ r.name,
547
+ COUNT(n.id) as memory_count,
548
+ MAX(n.created_at) as last_memory,
549
+ r.last_active
550
+ FROM robots r
551
+ LEFT JOIN nodes n ON r.id = n.robot_id
552
+ GROUP BY r.id, r.name, r.last_active
553
+ ORDER BY r.last_active DESC
554
+ SQL
555
+ )
556
+
557
+ robots = result.to_a
558
+ conn.close
559
+ robots
560
+ end
561
+
562
+ # Display activity
563
+ robots = get_robot_activity
564
+ puts "Robot Activity Report:"
565
+ robots.each do |r|
566
+ puts "\n#{r['name']} (#{r['id']})"
567
+ puts " Memories: #{r['memory_count']}"
568
+ puts " Last memory: #{r['last_memory']}"
569
+ puts " Last active: #{r['last_active']}"
570
+ end
571
+ ```
572
+
573
+ ### Cross-Robot Search
574
+
575
+ ```ruby
576
+ def search_across_robots(topic, limit_per_robot: 5)
577
+ config = HTM::Database.default_config
578
+ conn = PG.connect(config)
579
+
580
+ # Get all robots
581
+ robots = conn.exec("SELECT id, name FROM robots")
582
+
583
+ results = {}
584
+
585
+ robots.each do |robot|
586
+ # Search memories from this robot
587
+ stmt = conn.prepare(
588
+ "search_#{robot['id']}",
589
+ <<~SQL
590
+ SELECT key, value, type, importance, created_at
591
+ FROM nodes
592
+ WHERE robot_id = $1
593
+ AND to_tsvector('english', value) @@ plainto_tsquery('english', $2)
594
+ ORDER BY importance DESC
595
+ LIMIT $3
596
+ SQL
597
+ )
598
+
599
+ robot_results = conn.exec_prepared(
600
+ "search_#{robot['id']}",
601
+ [robot['id'], topic, limit_per_robot]
602
+ )
603
+
604
+ results[robot['name']] = robot_results.to_a
605
+ end
606
+
607
+ conn.close
608
+ results
609
+ end
610
+
611
+ # Usage
612
+ results = search_across_robots("authentication")
613
+ results.each do |robot_name, memories|
614
+ puts "\n=== #{robot_name} ==="
615
+ memories.each do |m|
616
+ puts "- [#{m['type']}] #{m['value'][0..80]}..."
617
+ end
618
+ end
619
+ ```
620
+
621
+ ## Monitoring Multi-Robot Systems
622
+
623
+ ### Dashboard
624
+
625
+ ```ruby
626
+ class MultiRobotDashboard
627
+ def initialize
628
+ @config = HTM::Database.default_config
629
+ end
630
+
631
+ def summary
632
+ conn = PG.connect(@config)
633
+
634
+ # Total stats
635
+ total_robots = conn.exec("SELECT COUNT(*) FROM robots").first['count'].to_i
636
+ total_memories = conn.exec("SELECT COUNT(*) FROM nodes").first['count'].to_i
637
+
638
+ # Per-robot breakdown
639
+ breakdown = conn.exec(
640
+ <<~SQL
641
+ SELECT
642
+ r.name,
643
+ COUNT(n.id) as memories,
644
+ AVG(n.importance) as avg_importance,
645
+ MAX(n.created_at) as last_contribution
646
+ FROM robots r
647
+ LEFT JOIN nodes n ON r.id = n.robot_id
648
+ GROUP BY r.id, r.name
649
+ ORDER BY memories DESC
650
+ SQL
651
+ ).to_a
652
+
653
+ conn.close
654
+
655
+ {
656
+ total_robots: total_robots,
657
+ total_memories: total_memories,
658
+ breakdown: breakdown
659
+ }
660
+ end
661
+
662
+ def print_summary
663
+ data = summary
664
+
665
+ puts "=== Multi-Robot System Dashboard ==="
666
+ puts "Total robots: #{data[:total_robots]}"
667
+ puts "Total memories: #{data[:total_memories]}"
668
+ puts "\nPer-robot breakdown:"
669
+
670
+ data[:breakdown].each do |robot|
671
+ puts "\n#{robot['name']}"
672
+ puts " Memories: #{robot['memories']}"
673
+ puts " Avg importance: #{robot['avg_importance'].to_f.round(2)}"
674
+ puts " Last contribution: #{robot['last_contribution']}"
675
+ end
676
+ end
677
+ end
678
+
679
+ dashboard = MultiRobotDashboard.new
680
+ dashboard.print_summary
681
+ ```
682
+
683
+ ## Best Practices
684
+
685
+ ### 1. Clear Robot Roles
686
+
687
+ ```ruby
688
+ # Good: Clear, specific roles
689
+ researcher = HTM.new(robot_name: "Research Specialist", robot_id: "research-001")
690
+ coder = HTM.new(robot_name: "Code Generator", robot_id: "coder-001")
691
+
692
+ # Avoid: Vague roles
693
+ bot1 = HTM.new(robot_name: "Bot 1", robot_id: "bot1")
694
+ ```
695
+
696
+ ### 2. Consistent Naming
697
+
698
+ ```ruby
699
+ # Good: Consistent naming scheme
700
+ class RobotFactory
701
+ def self.create(service, purpose, instance = "001")
702
+ HTM.new(
703
+ robot_name: "#{service.capitalize} #{purpose.capitalize}",
704
+ robot_id: "#{service}-#{purpose}-#{instance}"
705
+ )
706
+ end
707
+ end
708
+
709
+ api_assistant = RobotFactory.create("api", "assistant", "001")
710
+ api_validator = RobotFactory.create("api", "validator", "001")
711
+ ```
712
+
713
+ ### 3. Attribution in Content
714
+
715
+ ```ruby
716
+ # Include attribution in the content itself
717
+ bot.add_node(
718
+ "finding_001",
719
+ "Research by #{bot.robot_name}: PostgreSQL outperforms MongoDB",
720
+ type: :fact,
721
+ importance: 8.0
722
+ )
723
+ ```
724
+
725
+ ### 4. Regular Reconciliation
726
+
727
+ ```ruby
728
+ # Periodically sync understanding across robots
729
+ def sync_robots(*robots)
730
+ # Find recent high-importance memories
731
+ shared_knowledge = robots.first.recall(
732
+ timeframe: "last 24 hours",
733
+ topic: "important shared",
734
+ strategy: :hybrid,
735
+ limit: 50
736
+ ).select { |m| m['importance'].to_f >= 8.0 }
737
+
738
+ puts "Syncing #{shared_knowledge.length} important memories across #{robots.length} robots"
739
+ end
740
+ ```
741
+
742
+ ### 5. Clean Up Inactive Robots
743
+
744
+ ```ruby
745
+ def cleanup_inactive_robots(days: 30)
746
+ config = HTM::Database.default_config
747
+ conn = PG.connect(config)
748
+
749
+ cutoff = Time.now - (days * 24 * 3600)
750
+
751
+ result = conn.exec_params(
752
+ "SELECT id, name FROM robots WHERE last_active < $1",
753
+ [cutoff]
754
+ )
755
+
756
+ puts "Inactive robots (last active > #{days} days):"
757
+ result.each do |robot|
758
+ puts "- #{robot['name']} (#{robot['id']})"
759
+ end
760
+
761
+ conn.close
762
+ end
763
+
764
+ cleanup_inactive_robots(days: 90)
765
+ ```
766
+
767
+ ## Complete Example
768
+
769
+ ```ruby
770
+ require 'htm'
771
+
772
+ # Create a multi-robot development team
773
+ class DevTeam
774
+ def initialize
775
+ @analyst = HTM.new(
776
+ robot_name: "Requirements Analyst",
777
+ robot_id: "team-analyst-001"
778
+ )
779
+
780
+ @developer = HTM.new(
781
+ robot_name: "Senior Developer",
782
+ robot_id: "team-developer-001"
783
+ )
784
+
785
+ @tester = HTM.new(
786
+ robot_name: "QA Tester",
787
+ robot_id: "team-tester-001"
788
+ )
789
+ end
790
+
791
+ def process_feature(feature_name)
792
+ puts "\n=== Processing Feature: #{feature_name} ==="
793
+
794
+ # 1. Analyst documents requirements
795
+ puts "\n1. Analyst gathering requirements..."
796
+ @analyst.add_node(
797
+ "req_#{feature_name}",
798
+ "Requirements for #{feature_name}: Must support OAuth2",
799
+ type: :fact,
800
+ importance: 9.0,
801
+ tags: ["requirements", feature_name]
802
+ )
803
+
804
+ # 2. Developer recalls requirements and designs
805
+ puts "\n2. Developer reviewing requirements..."
806
+ requirements = @developer.recall(
807
+ timeframe: "last hour",
808
+ topic: "requirements #{feature_name}"
809
+ )
810
+
811
+ puts "Found #{requirements.length} requirements"
812
+
813
+ @developer.add_node(
814
+ "design_#{feature_name}",
815
+ "Design for #{feature_name} based on requirements",
816
+ type: :decision,
817
+ importance: 9.0,
818
+ tags: ["design", feature_name],
819
+ related_to: ["req_#{feature_name}"]
820
+ )
821
+
822
+ # 3. Tester recalls everything and creates test plan
823
+ puts "\n3. Tester creating test plan..."
824
+ context = @tester.recall(
825
+ timeframe: "last hour",
826
+ topic: feature_name,
827
+ strategy: :hybrid
828
+ )
829
+
830
+ puts "Tester reviewed #{context.length} items"
831
+
832
+ @tester.add_node(
833
+ "test_#{feature_name}",
834
+ "Test plan for #{feature_name}",
835
+ type: :context,
836
+ importance: 8.0,
837
+ tags: ["testing", feature_name],
838
+ related_to: ["design_#{feature_name}", "req_#{feature_name}"]
839
+ )
840
+
841
+ # 4. Show collaboration
842
+ puts "\n4. Collaboration summary:"
843
+ timeline = @analyst.conversation_timeline(feature_name)
844
+ timeline.each do |entry|
845
+ puts "- #{entry[:robot]}: #{entry[:type]}"
846
+ end
847
+
848
+ # 5. Show attribution
849
+ puts "\n5. Who contributed:"
850
+ breakdown = @analyst.which_robot_said(feature_name)
851
+ breakdown.each do |robot_id, count|
852
+ puts "- #{robot_id}: #{count} memories"
853
+ end
854
+ end
855
+ end
856
+
857
+ # Run the team
858
+ team = DevTeam.new
859
+ team.process_feature("oauth-integration")
860
+ ```
861
+
862
+ ## Next Steps
863
+
864
+ - [**Context Assembly**](context-assembly.md) - Build context from multi-robot memories
865
+ - [**Long-term Memory**](long-term-memory.md) - Understand the shared storage layer
866
+ - [**Search Strategies**](search-strategies.md) - Find relevant memories across robots