htm 0.0.1 → 0.0.10

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/.aigcm_msg +1 -0
  3. data/.architecture/reviews/comprehensive-codebase-review.md +577 -0
  4. data/.claude/settings.local.json +92 -0
  5. data/.envrc +1 -0
  6. data/.irbrc +283 -80
  7. data/.tbls.yml +31 -0
  8. data/CHANGELOG.md +314 -16
  9. data/CLAUDE.md +603 -0
  10. data/README.md +76 -5
  11. data/Rakefile +5 -0
  12. data/SETUP.md +132 -101
  13. data/db/migrate/{20250101000001_enable_extensions.rb → 00001_enable_extensions.rb} +0 -1
  14. data/db/migrate/00002_create_robots.rb +11 -0
  15. data/db/migrate/00003_create_file_sources.rb +20 -0
  16. data/db/migrate/00004_create_nodes.rb +65 -0
  17. data/db/migrate/00005_create_tags.rb +13 -0
  18. data/db/migrate/00006_create_node_tags.rb +18 -0
  19. data/db/migrate/00007_create_robot_nodes.rb +26 -0
  20. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +12 -0
  21. data/db/schema.sql +390 -36
  22. data/docs/api/database.md +19 -232
  23. data/docs/api/embedding-service.md +1 -7
  24. data/docs/api/htm.md +305 -364
  25. data/docs/api/index.md +1 -7
  26. data/docs/api/long-term-memory.md +342 -590
  27. data/docs/api/yard/HTM/ActiveRecordConfig.md +23 -0
  28. data/docs/api/yard/HTM/AuthorizationError.md +11 -0
  29. data/docs/api/yard/HTM/CircuitBreaker.md +92 -0
  30. data/docs/api/yard/HTM/CircuitBreakerOpenError.md +34 -0
  31. data/docs/api/yard/HTM/Configuration.md +175 -0
  32. data/docs/api/yard/HTM/Database.md +99 -0
  33. data/docs/api/yard/HTM/DatabaseError.md +14 -0
  34. data/docs/api/yard/HTM/EmbeddingError.md +18 -0
  35. data/docs/api/yard/HTM/EmbeddingService.md +58 -0
  36. data/docs/api/yard/HTM/Error.md +11 -0
  37. data/docs/api/yard/HTM/JobAdapter.md +39 -0
  38. data/docs/api/yard/HTM/LongTermMemory.md +342 -0
  39. data/docs/api/yard/HTM/NotFoundError.md +17 -0
  40. data/docs/api/yard/HTM/Observability.md +107 -0
  41. data/docs/api/yard/HTM/QueryTimeoutError.md +19 -0
  42. data/docs/api/yard/HTM/Railtie.md +27 -0
  43. data/docs/api/yard/HTM/ResourceExhaustedError.md +13 -0
  44. data/docs/api/yard/HTM/TagError.md +18 -0
  45. data/docs/api/yard/HTM/TagService.md +67 -0
  46. data/docs/api/yard/HTM/Timeframe/Result.md +24 -0
  47. data/docs/api/yard/HTM/Timeframe.md +40 -0
  48. data/docs/api/yard/HTM/TimeframeExtractor/Result.md +24 -0
  49. data/docs/api/yard/HTM/TimeframeExtractor.md +45 -0
  50. data/docs/api/yard/HTM/ValidationError.md +20 -0
  51. data/docs/api/yard/HTM/WorkingMemory.md +131 -0
  52. data/docs/api/yard/HTM.md +80 -0
  53. data/docs/api/yard/index.csv +179 -0
  54. data/docs/api/yard-reference.md +51 -0
  55. data/docs/architecture/adrs/001-postgresql-timescaledb.md +1 -1
  56. data/docs/architecture/adrs/003-ollama-embeddings.md +1 -1
  57. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +2 -27
  58. data/docs/architecture/adrs/index.md +2 -13
  59. data/docs/architecture/hive-mind.md +165 -166
  60. data/docs/architecture/index.md +2 -2
  61. data/docs/architecture/overview.md +5 -171
  62. data/docs/architecture/two-tier-memory.md +1 -35
  63. data/docs/assets/images/adr-010-current-architecture.svg +37 -0
  64. data/docs/assets/images/adr-010-proposed-architecture.svg +48 -0
  65. data/docs/assets/images/adr-dependency-tree.svg +93 -0
  66. data/docs/assets/images/class-hierarchy.svg +55 -0
  67. data/docs/assets/images/exception-hierarchy.svg +45 -0
  68. data/docs/assets/images/htm-architecture-overview.svg +83 -0
  69. data/docs/assets/images/htm-complete-memory-flow.svg +160 -0
  70. data/docs/assets/images/htm-context-assembly-flow.svg +148 -0
  71. data/docs/assets/images/htm-eviction-process.svg +141 -0
  72. data/docs/assets/images/htm-memory-addition-flow.svg +138 -0
  73. data/docs/assets/images/htm-memory-recall-flow.svg +152 -0
  74. data/docs/assets/images/htm-node-states.svg +123 -0
  75. data/docs/assets/images/project-structure.svg +78 -0
  76. data/docs/assets/images/test-directory-structure.svg +38 -0
  77. data/{dbdoc → docs/database}/README.md +127 -125
  78. data/docs/database/public.file_sources.md +42 -0
  79. data/docs/database/public.file_sources.svg +211 -0
  80. data/{dbdoc → docs/database}/public.node_tags.md +7 -8
  81. data/docs/database/public.node_tags.svg +239 -0
  82. data/{dbdoc → docs/database}/public.nodes.md +22 -17
  83. data/docs/database/public.nodes.svg +271 -0
  84. data/docs/database/public.robot_nodes.md +46 -0
  85. data/docs/database/public.robot_nodes.svg +243 -0
  86. data/{dbdoc → docs/database}/public.robots.md +2 -3
  87. data/docs/database/public.robots.svg +161 -0
  88. data/docs/database/public.tags.svg +139 -0
  89. data/{dbdoc → docs/database}/schema.json +941 -630
  90. data/docs/database/schema.svg +282 -0
  91. data/docs/development/index.md +1 -29
  92. data/docs/development/schema.md +134 -309
  93. data/docs/development/testing.md +1 -9
  94. data/docs/getting-started/index.md +47 -0
  95. data/docs/{installation.md → getting-started/installation.md} +2 -2
  96. data/docs/{quick-start.md → getting-started/quick-start.md} +5 -5
  97. data/docs/guides/adding-memories.md +295 -643
  98. data/docs/guides/recalling-memories.md +36 -1
  99. data/docs/guides/search-strategies.md +85 -51
  100. data/docs/images/htm-er-diagram.svg +156 -0
  101. data/docs/index.md +16 -31
  102. data/docs/multi_framework_support.md +4 -4
  103. data/examples/README.md +280 -0
  104. data/examples/basic_usage.rb +18 -16
  105. data/examples/cli_app/htm_cli.rb +146 -8
  106. data/examples/cli_app/temp.log +93 -0
  107. data/examples/custom_llm_configuration.rb +1 -2
  108. data/examples/example_app/app.rb +11 -14
  109. data/examples/file_loader_usage.rb +177 -0
  110. data/examples/robot_groups/lib/robot_group.rb +419 -0
  111. data/examples/robot_groups/lib/working_memory_channel.rb +140 -0
  112. data/examples/robot_groups/multi_process.rb +286 -0
  113. data/examples/robot_groups/robot_worker.rb +136 -0
  114. data/examples/robot_groups/same_process.rb +229 -0
  115. data/examples/sinatra_app/Gemfile +1 -0
  116. data/examples/sinatra_app/Gemfile.lock +166 -0
  117. data/examples/sinatra_app/app.rb +219 -24
  118. data/examples/timeframe_demo.rb +276 -0
  119. data/lib/htm/active_record_config.rb +10 -3
  120. data/lib/htm/circuit_breaker.rb +202 -0
  121. data/lib/htm/configuration.rb +313 -80
  122. data/lib/htm/database.rb +67 -36
  123. data/lib/htm/embedding_service.rb +39 -2
  124. data/lib/htm/errors.rb +131 -11
  125. data/lib/htm/{sinatra.rb → integrations/sinatra.rb} +87 -12
  126. data/lib/htm/job_adapter.rb +10 -3
  127. data/lib/htm/jobs/generate_embedding_job.rb +5 -4
  128. data/lib/htm/jobs/generate_tags_job.rb +4 -0
  129. data/lib/htm/loaders/markdown_loader.rb +263 -0
  130. data/lib/htm/loaders/paragraph_chunker.rb +112 -0
  131. data/lib/htm/long_term_memory.rb +601 -321
  132. data/lib/htm/models/file_source.rb +99 -0
  133. data/lib/htm/models/node.rb +116 -12
  134. data/lib/htm/models/robot.rb +53 -4
  135. data/lib/htm/models/robot_node.rb +51 -0
  136. data/lib/htm/models/tag.rb +302 -0
  137. data/lib/htm/observability.rb +395 -0
  138. data/lib/htm/tag_service.rb +60 -3
  139. data/lib/htm/tasks.rb +29 -0
  140. data/lib/htm/timeframe.rb +194 -0
  141. data/lib/htm/timeframe_extractor.rb +307 -0
  142. data/lib/htm/version.rb +1 -1
  143. data/lib/htm/working_memory.rb +165 -70
  144. data/lib/htm.rb +352 -133
  145. data/lib/tasks/doc.rake +300 -0
  146. data/lib/tasks/files.rake +299 -0
  147. data/lib/tasks/htm.rake +188 -2
  148. data/lib/tasks/jobs.rake +10 -12
  149. data/lib/tasks/tags.rake +194 -0
  150. data/mkdocs.yml +91 -9
  151. data/notes/ARCHITECTURE_REVIEW.md +1167 -0
  152. data/notes/IMPLEMENTATION_SUMMARY.md +606 -0
  153. data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +451 -0
  154. data/notes/next_steps.md +100 -0
  155. data/notes/plan.md +627 -0
  156. data/notes/tag_ontology_enhancement_ideas.md +222 -0
  157. data/notes/timescaledb_removal_summary.md +200 -0
  158. metadata +177 -37
  159. data/db/migrate/20250101000002_create_robots.rb +0 -14
  160. data/db/migrate/20250101000003_create_nodes.rb +0 -42
  161. data/db/migrate/20250101000005_create_tags.rb +0 -38
  162. data/db/migrate/20250101000007_add_node_vector_indexes.rb +0 -30
  163. data/dbdoc/public.node_tags.svg +0 -112
  164. data/dbdoc/public.nodes.svg +0 -118
  165. data/dbdoc/public.robots.svg +0 -90
  166. data/dbdoc/public.tags.svg +0 -60
  167. data/dbdoc/schema.svg +0 -154
  168. data/{dbdoc → docs/database}/public.node_stats.md +0 -0
  169. data/{dbdoc → docs/database}/public.node_stats.svg +0 -0
  170. data/{dbdoc → docs/database}/public.nodes_tags.md +0 -0
  171. data/{dbdoc → docs/database}/public.nodes_tags.svg +0 -0
  172. data/{dbdoc → docs/database}/public.ontology_structure.md +0 -0
  173. data/{dbdoc → docs/database}/public.ontology_structure.svg +0 -0
  174. data/{dbdoc → docs/database}/public.operations_log.md +0 -0
  175. data/{dbdoc → docs/database}/public.operations_log.svg +0 -0
  176. data/{dbdoc → docs/database}/public.relationships.md +0 -0
  177. data/{dbdoc → docs/database}/public.relationships.svg +0 -0
  178. data/{dbdoc → docs/database}/public.robot_activity.md +0 -0
  179. data/{dbdoc → docs/database}/public.robot_activity.svg +0 -0
  180. data/{dbdoc → docs/database}/public.schema_migrations.md +0 -0
  181. data/{dbdoc → docs/database}/public.schema_migrations.svg +0 -0
  182. data/{dbdoc → docs/database}/public.tags.md +3 -3
  183. /data/{dbdoc → docs/database}/public.topic_relationships.md +0 -0
  184. /data/{dbdoc → docs/database}/public.topic_relationships.svg +0 -0
@@ -4,744 +4,425 @@ This guide covers everything you need to know about storing information in HTM e
4
4
 
5
5
  ## Basic Usage
6
6
 
7
- The primary method for adding memories is `add_node`:
7
+ The primary method for adding memories is `remember`:
8
8
 
9
9
  ```ruby
10
- node_id = htm.add_node(
11
- key, # Unique identifier
12
- value, # Content (string)
13
- type: :fact, # Memory type
14
- category: nil, # Optional category
15
- importance: 1.0, # Importance score (0.0-10.0)
16
- related_to: [], # Array of related node keys
17
- tags: [] # Array of tags
18
- )
10
+ node_id = htm.remember(content, tags: [], metadata: {})
19
11
  ```
20
12
 
21
- The method returns the database ID of the created node.
22
-
23
- ## Memory Types Deep Dive
24
-
25
- HTM supports six memory types, each optimized for specific use cases.
26
-
27
- ### :fact - Immutable Facts
28
-
29
- Facts are unchanging truths about the world, users, or systems.
30
-
31
- ```ruby
32
- # User information
33
- htm.add_node(
34
- "user_name",
35
- "The user's name is Alice Thompson",
36
- type: :fact,
37
- importance: 9.0,
38
- tags: ["user", "identity"]
39
- )
13
+ **Parameters:**
40
14
 
41
- # System configuration
42
- htm.add_node(
43
- "system_timezone",
44
- "System timezone is UTC",
45
- type: :fact,
46
- importance: 6.0,
47
- tags: ["system", "config"]
48
- )
15
+ | Parameter | Type | Default | Description |
16
+ |-----------|------|---------|-------------|
17
+ | `content` | String | *required* | The information to remember |
18
+ | `tags` | Array\<String\> | `[]` | Manual tags to assign (in addition to auto-extracted tags) |
19
+ | `metadata` | Hash | `{}` | Arbitrary key-value metadata stored as JSONB |
49
20
 
50
- # Domain knowledge
51
- htm.add_node(
52
- "fact_photosynthesis",
53
- "Photosynthesis converts light energy into chemical energy in plants",
54
- type: :fact,
55
- importance: 7.0,
56
- tags: ["biology", "science"]
57
- )
58
- ```
21
+ The method returns the database ID of the created node.
59
22
 
60
- !!! tip "When to Use :fact"
61
- - User profile information (name, email, preferences)
62
- - System configuration that rarely changes
63
- - Scientific facts or domain knowledge
64
- - Historical events
65
- - API endpoints and credentials
23
+ ## How Remember Works
66
24
 
67
- ### :context - Conversation State
25
+ When you call `remember()`:
68
26
 
69
- Context captures the current state of conversations or sessions.
27
+ 1. **Content hashing**: A SHA-256 hash of the content is computed
28
+ 2. **Deduplication check**: If a node with the same hash exists, reuse it
29
+ 3. **Node creation/linking**: Create new node OR link robot to existing node
30
+ 4. **Working memory**: Add node to working memory (evict if needed)
31
+ 5. **Background jobs**: Enqueue embedding and tag generation (async)
70
32
 
71
33
  ```ruby
72
- # Current conversation topic
73
- htm.add_node(
74
- "context_#{session_id}_001",
75
- "User is asking about database performance optimization",
76
- type: :context,
77
- importance: 6.0,
78
- tags: ["conversation", "current"]
79
- )
80
-
81
- # Conversation mood
82
- htm.add_node(
83
- "context_mood",
84
- "User seems frustrated with slow query times",
85
- type: :context,
86
- importance: 7.0,
87
- tags: ["conversation", "sentiment"]
88
- )
34
+ # First robot remembers something
35
+ node_id = htm.remember("PostgreSQL supports vector similarity search")
36
+ # => 123 (new node created)
89
37
 
90
- # Current task
91
- htm.add_node(
92
- "context_task",
93
- "Helping user optimize their PostgreSQL queries",
94
- type: :context,
95
- importance: 8.0,
96
- tags: ["task", "active"]
97
- )
38
+ # Same content remembered again (by same or different robot)
39
+ node_id = htm.remember("PostgreSQL supports vector similarity search")
40
+ # => 123 (same node_id returned, just updates remember_count)
98
41
  ```
99
42
 
100
- !!! tip "When to Use :context"
101
- - Current conversation topics
102
- - Session state
103
- - Temporary workflow status
104
- - User's current goals or questions
105
- - Conversation sentiment or mood
43
+ ## Content Types
106
44
 
107
- !!! note
108
- Context memories are typically lower importance (4-6) since they become outdated quickly. They'll naturally get evicted from working memory as new context arrives.
45
+ HTM doesn't enforce content types - just store meaningful text that stands alone:
109
46
 
110
- ### :code - Code Snippets and Patterns
111
-
112
- Store code examples, patterns, and technical solutions.
47
+ ### Facts
113
48
 
114
49
  ```ruby
115
- # Function example
116
- htm.add_node(
117
- "code_date_parser",
118
- <<~CODE,
119
- def parse_date(date_string)
120
- Date.parse(date_string)
121
- rescue ArgumentError
122
- nil
123
- end
124
- CODE
125
- type: :code,
126
- importance: 6.0,
127
- tags: ["ruby", "date", "parsing"]
128
- )
50
+ # User information
51
+ htm.remember("The user's name is Alice Thompson")
129
52
 
130
- # SQL query pattern
131
- htm.add_node(
132
- "code_user_query",
133
- <<~SQL,
134
- SELECT u.id, u.name, COUNT(o.id) as order_count
135
- FROM users u
136
- LEFT JOIN orders o ON u.id = o.user_id
137
- GROUP BY u.id, u.name
138
- HAVING COUNT(o.id) > 10
139
- SQL
140
- type: :code,
141
- category: "sql",
142
- importance: 7.0,
143
- tags: ["sql", "aggregation", "joins"]
144
- )
53
+ # System configuration
54
+ htm.remember("System timezone is UTC")
145
55
 
146
- # Configuration example
147
- htm.add_node(
148
- "code_redis_config",
149
- <<~YAML,
150
- redis:
151
- host: localhost
152
- port: 6379
153
- pool_size: 5
154
- timeout: 2
155
- YAML
156
- type: :code,
157
- category: "config",
158
- importance: 5.0,
159
- tags: ["redis", "configuration", "yaml"]
160
- )
56
+ # Domain knowledge
57
+ htm.remember("Photosynthesis converts light energy into chemical energy in plants")
161
58
  ```
162
59
 
163
- !!! tip "When to Use :code"
164
- - Reusable code snippets
165
- - Configuration examples
166
- - SQL queries and patterns
167
- - API request/response examples
168
- - Algorithm implementations
169
- - Regular expressions
170
-
171
- ### :preference - User Preferences
172
-
173
- Store user preferences and settings.
60
+ ### Preferences
174
61
 
175
62
  ```ruby
176
63
  # Communication style
177
- htm.add_node(
178
- "pref_communication",
179
- "User prefers concise answers with bullet points",
180
- type: :preference,
181
- importance: 8.0,
182
- tags: ["user", "communication", "style"]
183
- )
64
+ htm.remember("User prefers concise answers with bullet points")
184
65
 
185
66
  # Technical preferences
186
- htm.add_node(
187
- "pref_language",
188
- "User prefers Ruby over Python for scripting tasks",
189
- type: :preference,
190
- importance: 7.0,
191
- tags: ["user", "programming", "language"]
192
- )
193
-
194
- # UI preferences
195
- htm.add_node(
196
- "pref_theme",
197
- "User uses dark theme in their IDE",
198
- type: :preference,
199
- importance: 4.0,
200
- tags: ["user", "ui", "theme"]
201
- )
202
-
203
- # Work preferences
204
- htm.add_node(
205
- "pref_working_hours",
206
- "User typically codes in the morning, prefers design work in afternoon",
207
- type: :preference,
208
- importance: 5.0,
209
- tags: ["user", "schedule", "productivity"]
210
- )
67
+ htm.remember("User prefers Ruby over Python for scripting tasks")
211
68
  ```
212
69
 
213
- !!! tip "When to Use :preference"
214
- - Communication style preferences
215
- - Technical tool preferences
216
- - UI/UX preferences
217
- - Work habits and patterns
218
- - Learning style preferences
219
-
220
- ### :decision - Architectural Decisions
221
-
222
- Track important decisions with rationale.
70
+ ### Decisions
223
71
 
224
72
  ```ruby
225
73
  # Technology choice
226
- htm.add_node(
227
- "decision_database",
228
- <<~DECISION,
229
- Decision: Use PostgreSQL with TimescaleDB for HTM storage
230
-
231
- Rationale:
232
- - Excellent time-series optimization
233
- - Native vector search with pgvector
234
- - Strong consistency guarantees
235
- - Mature ecosystem
236
-
237
- Alternatives considered:
238
- - MongoDB (rejected: eventual consistency issues)
239
- - Redis (rejected: limited persistence)
240
- DECISION
241
- type: :decision,
242
- category: "architecture",
243
- importance: 9.5,
244
- tags: ["architecture", "database", "timescaledb"]
245
- )
74
+ htm.remember(<<~DECISION)
75
+ Decision: Use PostgreSQL with pgvector for HTM storage
76
+
77
+ Rationale:
78
+ - Excellent vector search via pgvector
79
+ - Strong consistency guarantees
80
+ - Mature ecosystem
81
+
82
+ Alternatives considered:
83
+ - MongoDB (rejected: eventual consistency issues)
84
+ - Redis (rejected: limited persistence)
85
+ DECISION
86
+ ```
246
87
 
247
- # Design pattern choice
248
- htm.add_node(
249
- "decision_memory_architecture",
250
- <<~DECISION,
251
- Decision: Implement two-tier memory (working + long-term)
252
-
253
- Rationale:
254
- - Working memory provides fast access
255
- - Long-term memory ensures durability
256
- - Mirrors human memory architecture
257
- - Allows token-limited LLM context
258
-
259
- Trade-offs:
260
- - Added complexity in synchronization
261
- - Eviction strategy needs tuning
262
- DECISION
263
- type: :decision,
264
- category: "architecture",
265
- importance: 10.0,
266
- tags: ["architecture", "memory", "design-pattern"]
267
- )
88
+ ### Code Snippets
268
89
 
269
- # Process decision
270
- htm.add_node(
271
- "decision_testing",
272
- "Decided to use Minitest over RSpec for simplicity and speed",
273
- type: :decision,
274
- category: "process",
275
- importance: 6.0,
276
- tags: ["testing", "tools"]
277
- )
90
+ ```ruby
91
+ # Function example
92
+ htm.remember(<<~CODE)
93
+ def parse_date(date_string)
94
+ Date.parse(date_string)
95
+ rescue ArgumentError
96
+ nil
97
+ end
98
+ CODE
99
+
100
+ # SQL query pattern
101
+ htm.remember(<<~SQL)
102
+ SELECT u.id, u.name, COUNT(o.id) as order_count
103
+ FROM users u
104
+ LEFT JOIN orders o ON u.id = o.user_id
105
+ GROUP BY u.id, u.name
106
+ HAVING COUNT(o.id) > 10
107
+ SQL
278
108
  ```
279
109
 
280
- !!! tip "When to Use :decision"
281
- - Technology selections
282
- - Architecture patterns
283
- - API design choices
284
- - Process decisions
285
- - Trade-off analysis results
110
+ ## Using Tags
286
111
 
287
- !!! note "Decision Template"
288
- Include: what was decided, why, alternatives considered, and trade-offs. This context helps future decision-making.
112
+ Tags provide hierarchical organization for your memories. HTM automatically extracts tags from content, but you can also specify manual tags.
289
113
 
290
- ### :question - Unresolved Questions
114
+ ### Hierarchical Tag Convention
291
115
 
292
- Track questions that need answering.
116
+ Use colons to create hierarchical namespaces:
293
117
 
294
118
  ```ruby
295
- # Technical question
296
- htm.add_node(
297
- "question_caching",
298
- "Should we implement Redis caching for frequently accessed memories?",
299
- type: :question,
300
- importance: 7.0,
301
- tags: ["performance", "caching", "open"]
302
- )
303
-
304
- # Design question
305
- htm.add_node(
306
- "question_auth",
307
- "How should we handle authentication for multi-robot scenarios?",
308
- type: :question,
309
- importance: 8.0,
310
- tags: ["security", "architecture", "open"]
119
+ # Manual tags with hierarchy
120
+ htm.remember(
121
+ "PostgreSQL 17 adds MERGE statement improvements",
122
+ tags: ["database:postgresql", "database:sql", "version:17"]
311
123
  )
312
124
 
313
- # Research question
314
- htm.add_node(
315
- "question_embeddings",
316
- "Would fine-tuning embeddings on our domain improve recall accuracy?",
317
- type: :question,
318
- importance: 6.0,
319
- tags: ["embeddings", "research", "open"]
320
- )
125
+ # Tags are used in hybrid search for relevance boosting
126
+ # A recall for "postgresql" will boost nodes with matching tags
321
127
  ```
322
128
 
323
- !!! tip "When to Use :question"
324
- - Open technical questions
325
- - Design uncertainties
326
- - Research topics to investigate
327
- - Feature requests to evaluate
328
- - Performance questions
329
-
330
- !!! tip "Closing Questions"
331
- When a question is answered, add a related decision node and mark the question as resolved by updating its tags.
332
-
333
- ## Importance Scoring Guidelines
129
+ ### Tag Naming Conventions
334
130
 
335
- The importance score (0.0-10.0) determines memory retention and eviction priority.
131
+ ```ruby
132
+ # Good: Consistent, lowercase, hierarchical
133
+ tags: ["database:postgresql", "architecture:api", "security:authentication"]
336
134
 
337
- ![Importance Scoring Framework](../assets/images/htm-importance-scoring-framework.svg)
135
+ # Avoid: Inconsistent casing, flat tags, vague terms
136
+ tags: ["PostgreSQL", "stuff", "misc"]
137
+ ```
338
138
 
339
- ### Scoring Framework
139
+ ### Common Tag Patterns
340
140
 
341
141
  ```ruby
342
- # Critical (9.0-10.0): Must never lose
343
- htm.add_node("api_key", "Production API key: ...", importance: 10.0)
344
- htm.add_node("decision_architecture", "Core architecture decision", importance: 9.5)
345
-
346
- # High (7.0-8.9): Very important, high retention
347
- htm.add_node("user_identity", "User's name and email", importance: 8.0)
348
- htm.add_node("major_decision", "Chose Rails for web framework", importance: 7.5)
349
-
350
- # Medium (4.0-6.9): Moderately important
351
- htm.add_node("code_snippet", "Useful utility function", importance: 6.0)
352
- htm.add_node("context_current", "Current conversation topic", importance: 5.0)
353
- htm.add_node("preference_minor", "Prefers tabs over spaces", importance: 4.0)
354
-
355
- # Low (1.0-3.9): Nice to have, can evict
356
- htm.add_node("temp_note", "Check logs later", importance: 3.0)
357
- htm.add_node("minor_context", "Mentioned weather briefly", importance: 2.0)
358
- htm.add_node("throwaway", "Temporary calculation result", importance: 1.0)
359
- ```
360
-
361
- ### Importance by Type
142
+ # Domain tags
143
+ tags: ["database:postgresql", "api:rest", "auth:jwt"]
362
144
 
363
- Typical importance ranges for each type:
145
+ # Layer tags
146
+ tags: ["layer:frontend", "layer:backend", "layer:infrastructure"]
364
147
 
365
- | Type | Typical Range | Example |
366
- |------|---------------|---------|
367
- | `:fact` | 7.0-10.0 | User identity, system facts |
368
- | `:decision` | 7.0-10.0 | Architecture, major choices |
369
- | `:preference` | 4.0-8.0 | User preferences |
370
- | `:code` | 4.0-7.0 | Code snippets, examples |
371
- | `:context` | 3.0-6.0 | Conversation state |
372
- | `:question` | 5.0-8.0 | Open questions |
148
+ # Technology tags
149
+ tags: ["tech:ruby", "tech:javascript", "tech:docker"]
373
150
 
374
- !!! warning "Importance Affects Eviction"
375
- When working memory is full, HTM evicts memories with lower importance first. Set importance thoughtfully based on long-term value.
151
+ # Project tags
152
+ tags: ["project:alpha", "project:beta"]
153
+ ```
376
154
 
377
- ## Adding Relationships
155
+ ### Automatic Tag Extraction
378
156
 
379
- Link related memories to build a knowledge graph:
157
+ When a node is created, a background job (GenerateTagsJob) automatically extracts hierarchical tags from the content using an LLM. This happens asynchronously.
380
158
 
381
159
  ```ruby
382
- # Add a decision
383
- htm.add_node(
384
- "decision_database",
385
- "Use PostgreSQL for data storage",
386
- type: :decision,
387
- importance: 9.0
388
- )
389
-
390
- # Add related implementation code
391
- htm.add_node(
392
- "code_db_connection",
393
- "PG.connect(ENV['DATABASE_URL'])",
394
- type: :code,
395
- importance: 6.0,
396
- related_to: ["decision_database"]
397
- )
398
-
399
- # Add related configuration
400
- htm.add_node(
401
- "fact_db_config",
402
- "Database uses connection pool of size 5",
403
- type: :fact,
404
- importance: 7.0,
405
- related_to: ["decision_database", "code_db_connection"]
406
- )
160
+ # Just provide content, tags are auto-extracted
161
+ htm.remember("We're using Redis for session caching with a 24-hour TTL")
162
+ # Background job might extract: ["database:redis", "caching:session", "performance"]
407
163
  ```
408
164
 
409
- !!! tip "Relationship Patterns"
410
- - Link implementation code to decisions
411
- - Connect questions to related facts
412
- - Link preferences to user facts
413
- - Connect related decisions (e.g., database choice → ORM choice)
165
+ ## Using Metadata
414
166
 
415
- ## Categorization with Tags
167
+ Metadata provides flexible key-value storage for arbitrary data that doesn't fit into tags. Unlike tags (which are for hierarchical categorization), metadata is for structured data like version numbers, priorities, source systems, or any custom attributes.
416
168
 
417
- Tags enable flexible organization and retrieval:
169
+ ### Basic Metadata Usage
418
170
 
419
171
  ```ruby
420
- # Use multiple tags for rich categorization
421
- htm.add_node(
422
- "decision_api_design",
423
- "RESTful API with JSON responses",
424
- type: :decision,
425
- importance: 8.0,
426
- tags: [
427
- "api", # Domain
428
- "rest", # Approach
429
- "architecture", # Category
430
- "backend", # Layer
431
- "json", # Format
432
- "http" # Protocol
433
- ]
172
+ # Store with metadata
173
+ htm.remember(
174
+ "User prefers dark mode",
175
+ metadata: { category: "preference", priority: "high" }
176
+ )
177
+
178
+ # Multiple metadata fields
179
+ htm.remember(
180
+ "API endpoint changed from /v1 to /v2",
181
+ metadata: {
182
+ category: "migration",
183
+ version: 2,
184
+ breaking_change: true,
185
+ affected_services: ["web", "mobile"]
186
+ }
434
187
  )
435
188
  ```
436
189
 
437
- ### Tag Naming Conventions
190
+ ### Metadata vs Tags
438
191
 
439
- ```ruby
440
- # Good: Consistent, lowercase, descriptive
441
- tags: ["user", "authentication", "security", "oauth"]
192
+ | Feature | Tags | Metadata |
193
+ |---------|------|----------|
194
+ | Structure | Hierarchical (colon-separated) | Flat key-value pairs |
195
+ | Type | String only | Any JSON type (string, number, boolean, array, object) |
196
+ | Search | Prefix matching (`LIKE 'ai:%'`) | JSONB containment (`@>`) |
197
+ | Purpose | Categorization & navigation | Arbitrary attributes & filtering |
198
+ | Auto-extraction | Yes (via LLM) | No (always explicit) |
442
199
 
443
- # Avoid: Inconsistent casing, vague terms
444
- tags: ["User", "auth", "stuff", "misc"]
445
- ```
446
-
447
- ### Common Tag Patterns
200
+ ### Common Metadata Patterns
448
201
 
449
202
  ```ruby
450
- # Domain tags
451
- tags: ["database", "api", "ui", "auth", "billing"]
203
+ # Version tracking
204
+ htm.remember("API uses OAuth 2.0", metadata: { version: 3, deprecated: false })
452
205
 
453
- # Layer tags
454
- tags: ["frontend", "backend", "infrastructure", "data"]
206
+ # Source tracking
207
+ htm.remember("Error rate is 0.1%", metadata: { source: "monitoring", dashboard: "errors" })
455
208
 
456
- # Status tags
457
- tags: ["active", "deprecated", "experimental", "stable"]
209
+ # Priority/importance
210
+ htm.remember("Deploy to prod on Fridays is forbidden", metadata: { priority: "critical" })
458
211
 
459
- # Priority tags
460
- tags: ["critical", "high-priority", "low-priority"]
212
+ # Environment-specific
213
+ htm.remember("Database connection limit is 100", metadata: { environment: "production" })
461
214
 
462
- # Project tags
463
- tags: ["project-alpha", "project-beta"]
215
+ # Combining with tags
216
+ htm.remember(
217
+ "Use connection pooling for better performance",
218
+ tags: ["database:postgresql", "performance"],
219
+ metadata: { priority: "high", reviewed: true, author: "dba-team" }
220
+ )
464
221
  ```
465
222
 
466
- ## Advanced Patterns
223
+ ### Querying by Metadata
467
224
 
468
- ### Timestamped Entries
469
-
470
- Create time-series logs:
225
+ Use the `metadata` parameter in `recall()` to filter by metadata:
471
226
 
472
227
  ```ruby
473
- def log_event(event_type, description)
474
- timestamp = Time.now.to_i
475
-
476
- htm.add_node(
477
- "event_#{event_type}_#{timestamp}",
478
- "#{event_type.upcase}: #{description}",
479
- type: :context,
480
- importance: 5.0,
481
- tags: ["event", event_type, "log"]
482
- )
483
- end
228
+ # Find all high-priority items
229
+ htm.recall("settings", metadata: { priority: "high" })
484
230
 
485
- log_event("error", "Database connection timeout")
486
- log_event("performance", "Query took 3.2 seconds")
487
- ```
231
+ # Find production-specific configurations
232
+ htm.recall("database", metadata: { environment: "production" })
488
233
 
489
- ### Versioned Information
234
+ # Combine with other filters
235
+ htm.recall(
236
+ "API changes",
237
+ timeframe: "last month",
238
+ metadata: { breaking_change: true },
239
+ strategy: :hybrid
240
+ )
241
+ ```
490
242
 
491
- Track changes over time:
243
+ Metadata filtering uses PostgreSQL's JSONB containment operator (`@>`), which means the node's metadata must contain all the key-value pairs you specify.
492
244
 
493
- ```ruby
494
- def update_fact(base_key, new_value, version)
495
- # Add versioned node
496
- htm.add_node(
497
- "#{base_key}_v#{version}",
498
- new_value,
499
- type: :fact,
500
- importance: 8.0,
501
- tags: ["versioned", "v#{version}"],
502
- related_to: version > 1 ? ["#{base_key}_v#{version-1}"] : []
503
- )
504
- end
245
+ ## Content Deduplication
505
246
 
506
- update_fact("user_email", "alice@example.com", 1)
507
- update_fact("user_email", "alice@newdomain.com", 2)
508
- ```
247
+ HTM automatically deduplicates content across all robots using SHA-256 hashing.
509
248
 
510
- ### Compound Memories
511
-
512
- Store structured information:
249
+ ### How It Works
513
250
 
514
251
  ```ruby
515
- # User profile as compound memory
516
- user_profile = {
517
- name: "Alice Thompson",
518
- email: "alice@example.com",
519
- role: "Senior Engineer",
520
- joined: "2023-01-15"
521
- }.map { |k, v| "#{k}: #{v}" }.join("\n")
522
-
523
- htm.add_node(
524
- "user_profile_001",
525
- user_profile,
526
- type: :fact,
527
- importance: 9.0,
528
- tags: ["user", "profile", "complete"]
529
- )
252
+ # Robot 1 remembers something
253
+ robot1 = HTM.new(robot_name: "assistant_1")
254
+ node_id = robot1.remember("Ruby 3.3 supports YJIT by default")
255
+ # => 123 (new node)
256
+
257
+ # Robot 2 remembers the same thing
258
+ robot2 = HTM.new(robot_name: "assistant_2")
259
+ node_id = robot2.remember("Ruby 3.3 supports YJIT by default")
260
+ # => 123 (same node_id! Content matched by hash)
530
261
  ```
531
262
 
532
- ### Conditional Importance
263
+ ### Robot-Node Association
533
264
 
534
- Adjust importance based on context:
265
+ Each robot-node relationship is tracked in `robot_nodes`:
535
266
 
536
267
  ```ruby
537
- def add_memory_with_context(key, value, type, base_importance, current_project)
538
- # Boost importance for current project
539
- importance = base_importance
540
- importance += 2.0 if tags.include?(current_project)
541
- importance = [importance, 10.0].min # Cap at 10.0
542
-
543
- htm.add_node(
544
- key,
545
- value,
546
- type: type,
547
- importance: importance,
548
- tags: [current_project, type.to_s]
549
- )
550
- end
268
+ # Check how many times a robot has "remembered" content
269
+ rn = HTM::Models::RobotNode.find_by(robot_id: htm.robot_id, node_id: node_id)
270
+ rn.remember_count # => 3 (remembered 3 times)
271
+ rn.first_remembered_at # => When first encountered
272
+ rn.last_remembered_at # => When last tried to remember
551
273
  ```
552
274
 
553
275
  ## Best Practices
554
276
 
555
- ### 1. Use Descriptive Keys
556
-
557
- ```ruby
558
- # Good: Descriptive and namespaced
559
- "user_profile_alice_001"
560
- "decision_database_selection"
561
- "code_authentication_jwt"
562
-
563
- # Bad: Vague or collision-prone
564
- "profile"
565
- "dec1"
566
- "code"
567
- ```
568
-
569
- ### 2. Be Consistent with Categories
277
+ ### 1. Make Content Self-Contained
570
278
 
571
279
  ```ruby
572
- # Define standard categories
573
- CATEGORIES = {
574
- architecture: "architecture",
575
- security: "security",
576
- performance: "performance",
577
- ui: "user-interface"
578
- }
579
-
580
- htm.add_node(
581
- key, value,
582
- category: CATEGORIES[:architecture]
280
+ # Good: Self-contained, understandable without context
281
+ htm.remember(
282
+ "Decided to use Redis for session storage because it provides fast access and automatic expiration"
583
283
  )
284
+
285
+ # Bad: Requires external context
286
+ htm.remember("Use Redis") # Why? For what?
584
287
  ```
585
288
 
586
- ### 3. Include Context in Values
289
+ ### 2. Include Rich Context
587
290
 
588
291
  ```ruby
589
- # Good: Self-contained
590
- htm.add_node(
591
- "decision_001",
592
- "Decided to use Redis for session storage because it provides fast access and automatic expiration",
593
- type: :decision
594
- )
595
-
596
- # Bad: Requires external context
597
- htm.add_node(
598
- "decision_001",
599
- "Use Redis", # Why? For what?
600
- type: :decision
601
- )
292
+ # Good: Includes rationale and alternatives
293
+ htm.remember(<<~DECISION)
294
+ Decision: Use OAuth 2.0 for authentication
295
+
296
+ Rationale:
297
+ - Industry standard
298
+ - Better security than basic auth
299
+ - Supports SSO
300
+
301
+ Alternatives considered:
302
+ - Basic auth (rejected: security concerns)
303
+ - Custom tokens (rejected: maintenance burden)
304
+ DECISION
602
305
  ```
603
306
 
604
- ### 4. Tag Generously
307
+ ### 3. Use Hierarchical Tags
605
308
 
606
309
  ```ruby
607
310
  # Good: Rich tags for multiple retrieval paths
608
- htm.add_node(
609
- "code_api_auth",
610
- "...",
611
- tags: ["api", "authentication", "security", "jwt", "middleware", "ruby"]
311
+ htm.remember(
312
+ "JWT tokens are stateless authentication tokens",
313
+ tags: ["auth:jwt", "security:tokens", "architecture:stateless"]
612
314
  )
613
315
 
614
- # Suboptimal: Minimal tags
615
- htm.add_node(
616
- "code_api_auth",
617
- "...",
618
- tags: ["code"]
619
- )
316
+ # Suboptimal: Flat or minimal tags
317
+ htm.remember("JWT info", tags: ["jwt"])
620
318
  ```
621
319
 
622
- ### 5. Use Relationships to Build Context
320
+ ### 4. Keep Content Focused
623
321
 
624
322
  ```ruby
625
- # Create a narrative with relationships
626
- decision_id = htm.add_node("decision_api", "Use GraphQL", type: :decision)
627
-
628
- htm.add_node(
629
- "question_api",
630
- "How to handle file uploads in GraphQL?",
631
- type: :question,
632
- related_to: ["decision_api"]
633
- )
323
+ # Good: One concept per memory
324
+ htm.remember("PostgreSQL's EXPLAIN ANALYZE shows actual execution times")
325
+ htm.remember("PostgreSQL's EXPLAIN shows the query plan without executing")
634
326
 
635
- htm.add_node(
636
- "code_upload",
637
- "GraphQL upload implementation",
638
- type: :code,
639
- related_to: ["decision_api", "question_api"]
640
- )
327
+ # Suboptimal: Multiple unrelated concepts
328
+ htm.remember("PostgreSQL has EXPLAIN and also supports JSON and has good performance")
641
329
  ```
642
330
 
643
- ## Common Pitfalls
644
-
645
- ### Pitfall 1: Duplicate Keys
331
+ ## Async Processing
646
332
 
647
- ```ruby
648
- # This will fail - keys must be unique
649
- htm.add_node("user_001", "Alice")
650
- htm.add_node("user_001", "Bob") # Error: key already exists
651
- ```
333
+ Embedding generation and tag extraction happen asynchronously:
652
334
 
653
- **Solution**: Use unique keys with timestamps or UUIDs:
335
+ ### Workflow
654
336
 
655
337
  ```ruby
656
- require 'securerandom'
338
+ # 1. Node created immediately (~15ms)
339
+ node_id = htm.remember("Important fact about databases")
340
+ # Returns immediately with node_id
657
341
 
658
- htm.add_node("user_#{SecureRandom.hex(4)}", "Alice")
659
- htm.add_node("user_#{SecureRandom.hex(4)}", "Bob")
342
+ # 2. Background jobs enqueue (async)
343
+ # - GenerateEmbeddingJob runs (~100ms)
344
+ # - GenerateTagsJob runs (~1 second)
345
+
346
+ # 3. Node is eventually enriched
347
+ # - embedding field populated (enables vector search)
348
+ # - tags associated (enables tag navigation and boosting)
660
349
  ```
661
350
 
662
- ### Pitfall 2: Too-High Importance
351
+ ### Immediate vs Eventual Capabilities
663
352
 
664
- ```ruby
665
- # Don't make everything critical
666
- htm.add_node("note", "Random thought", importance: 10.0) # Too high!
667
- ```
353
+ | Capability | Available | Notes |
354
+ |------------|-----------|-------|
355
+ | Full-text search | Immediately | Works on content |
356
+ | Basic retrieval | Immediately | By node ID |
357
+ | Vector search | After ~100ms | Needs embedding |
358
+ | Tag-enhanced search | After ~1s | Needs tags |
359
+ | Hybrid search | After ~1s | Needs embedding + tags |
668
360
 
669
- **Solution**: Reserve high importance (9-10) for truly critical data.
361
+ ## Working Memory Integration
670
362
 
671
- ### Pitfall 3: Missing Context
363
+ When you `remember()`, the node is automatically added to working memory:
672
364
 
673
365
  ```ruby
674
- # Bad: No context
675
- htm.add_node("decision", "Chose option A", type: :decision)
676
-
677
- # Good: Include rationale
678
- htm.add_node(
679
- "decision_auth",
680
- "Chose OAuth 2.0 for authentication because it provides better security and is industry standard",
681
- type: :decision
682
- )
366
+ # Remember adds to both LTM and WM
367
+ htm.remember("Important fact")
368
+
369
+ # Check working memory
370
+ stats = htm.working_memory.stats
371
+ puts "Nodes in WM: #{stats[:node_count]}"
372
+ puts "Token usage: #{stats[:utilization]}%"
683
373
  ```
684
374
 
685
- ### Pitfall 4: No Tags
375
+ ### Eviction
376
+
377
+ If working memory is full, older/less important nodes are evicted to make room:
686
378
 
687
379
  ```ruby
688
- # Harder to find later
689
- htm.add_node("code_001", "def foo...", type: :code)
690
-
691
- # Better: Tags enable multiple retrieval paths
692
- htm.add_node(
693
- "code_001",
694
- "def foo...",
695
- type: :code,
696
- tags: ["ruby", "functions", "utilities"]
697
- )
380
+ # Working memory has a token budget
381
+ htm = HTM.new(working_memory_size: 128_000) # 128K tokens
382
+
383
+ # As you remember more, older items may be evicted from WM
384
+ # They remain in LTM and can be recalled later
698
385
  ```
699
386
 
700
387
  ## Performance Considerations
701
388
 
702
389
  ### Batch Operations
703
390
 
704
- When adding many memories, consider transaction efficiency:
391
+ Each `remember()` call is a database operation. For bulk inserts:
705
392
 
706
393
  ```ruby
707
- # Instead of many individual adds
708
- memories = [
709
- {key: "fact_001", value: "...", type: :fact},
710
- {key: "fact_002", value: "...", type: :fact},
711
- # ... many more
394
+ # Multiple memories
395
+ facts = [
396
+ "PostgreSQL supports JSONB",
397
+ "PostgreSQL has excellent indexing",
398
+ "PostgreSQL handles concurrent writes well"
712
399
  ]
713
400
 
714
- # Add them efficiently
715
- memories.each do |m|
716
- htm.add_node(m[:key], m[:value], type: m[:type], importance: m[:importance])
401
+ facts.each do |fact|
402
+ htm.remember(fact)
717
403
  end
718
404
  ```
719
405
 
720
- !!! note
721
- Each `add_node` call generates embeddings via Ollama. For large batches, this can take time. Consider adding in the background or showing progress.
722
-
723
- ### Embedding Generation
406
+ ### Content Length
724
407
 
725
- Embedding generation has a cost:
408
+ Longer content takes more time to process:
726
409
 
727
410
  ```ruby
728
- # Short text: Fast (~50ms)
729
- htm.add_node("fact", "User name is Alice", ...)
411
+ # Short text: Fast (~15ms save, ~100ms embedding)
412
+ htm.remember("User name is Alice")
730
413
 
731
- # Long text: Slower (~500ms)
732
- htm.add_node("code", "..." * 1000, ...) # 1000 chars
414
+ # Long text: Slower (~15ms save, ~500ms embedding)
415
+ htm.remember("..." * 1000) # 1000 chars
733
416
  ```
734
417
 
735
- !!! tip
736
- For very long content (>1000 tokens), consider splitting into multiple nodes or summarizing.
418
+ For very long content (>1000 tokens), consider splitting into multiple memories.
737
419
 
738
420
  ## Next Steps
739
421
 
740
422
  Now that you know how to add memories effectively, learn about:
741
423
 
742
- - [**Recalling Memories**](recalling-memories.md) - Search and retrieve memories
743
424
  - [**Search Strategies**](search-strategies.md) - Optimize retrieval with different strategies
744
- - [**Context Assembly**](context-assembly.md) - Use memories with your LLM
425
+ - [**Recalling Memories**](recalling-memories.md) - Search and retrieve memories
745
426
 
746
427
  ## Complete Example
747
428
 
@@ -750,75 +431,46 @@ require 'htm'
750
431
 
751
432
  htm = HTM.new(robot_name: "Memory Demo")
752
433
 
753
- # Add a fact with rich metadata
754
- htm.add_node(
755
- "user_profile",
756
- "Alice Thompson is a senior software engineer specializing in distributed systems",
757
- type: :fact,
758
- category: "user",
759
- importance: 9.0,
760
- tags: ["user", "profile", "engineering"]
434
+ # Add a fact
435
+ htm.remember(
436
+ "Alice Thompson is a senior software engineer specializing in distributed systems"
761
437
  )
762
438
 
763
- # Add a related preference
764
- htm.add_node(
765
- "user_pref_tools",
439
+ # Add a preference with metadata
440
+ htm.remember(
766
441
  "Alice prefers Vim for editing and tmux for terminal management",
767
- type: :preference,
768
- importance: 7.0,
769
- tags: ["user", "tools", "preferences"],
770
- related_to: ["user_profile"]
442
+ metadata: { category: "preference", source: "user-interview" }
771
443
  )
772
444
 
773
- # Add a decision with context
774
- htm.add_node(
775
- "decision_messaging",
776
- <<~DECISION,
777
- Decision: Use RabbitMQ for async job processing
778
-
779
- Rationale:
780
- - Need reliable message delivery
781
- - Support for multiple consumer patterns
782
- - Excellent Ruby client library
783
-
784
- Alternatives:
785
- - Redis (simpler but less reliable)
786
- - Kafka (overkill for our scale)
787
- DECISION
788
- type: :decision,
789
- category: "architecture",
790
- importance: 8.5,
791
- tags: ["architecture", "messaging", "rabbitmq", "async"]
792
- )
445
+ # Add a decision with context, tags, and metadata
446
+ htm.remember(<<~DECISION, tags: ["architecture", "messaging"], metadata: { priority: "high", approved: true, version: 1 })
447
+ Decision: Use RabbitMQ for async job processing
793
448
 
794
- # Add implementation code
795
- htm.add_node(
796
- "code_rabbitmq_setup",
797
- <<~RUBY,
798
- require 'bunny'
799
-
800
- connection = Bunny.new(ENV['RABBITMQ_URL'])
801
- connection.start
802
-
803
- channel = connection.create_channel
804
- queue = channel.queue('jobs', durable: true)
805
- RUBY
806
- type: :code,
807
- importance: 6.0,
808
- tags: ["ruby", "rabbitmq", "setup", "code"],
809
- related_to: ["decision_messaging"]
810
- )
449
+ Rationale:
450
+ - Need reliable message delivery
451
+ - Support for multiple consumer patterns
452
+ - Excellent Ruby client library
811
453
 
812
- # Add an open question
813
- htm.add_node(
814
- "question_scaling",
815
- "Should we implement message partitioning for better scaling?",
816
- type: :question,
817
- importance: 7.0,
818
- tags: ["rabbitmq", "scaling", "performance", "open"],
819
- related_to: ["decision_messaging"]
820
- )
454
+ Alternatives:
455
+ - Redis (simpler but less reliable)
456
+ - Kafka (overkill for our scale)
457
+ DECISION
458
+
459
+ # Add implementation code with metadata
460
+ htm.remember(<<~RUBY, tags: ["code:ruby", "messaging:rabbitmq"], metadata: { language: "ruby", tested: true })
461
+ require 'bunny'
462
+
463
+ connection = Bunny.new(ENV['RABBITMQ_URL'])
464
+ connection.start
465
+
466
+ channel = connection.create_channel
467
+ queue = channel.queue('jobs', durable: true)
468
+ RUBY
469
+
470
+ puts "Added memories with relationships and rich metadata"
471
+ puts "Stats: #{HTM::Models::Node.count} total nodes"
821
472
 
822
- puts "Added 5 memories with relationships and rich metadata"
823
- puts "Stats: #{htm.memory_stats[:total_nodes]} total nodes"
473
+ # Query by metadata
474
+ high_priority = htm.recall("decisions", metadata: { priority: "high" })
475
+ puts "High priority decisions: #{high_priority.count}"
824
476
  ```