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,570 @@
1
+ # ADR-009: Never-Forget Philosophy with Explicit Deletion
2
+
3
+ **Status**: Accepted
4
+
5
+ **Date**: 2025-10-25
6
+
7
+ **Decision Makers**: Dewayne VanHoozer, Claude (Anthropic)
8
+
9
+ ---
10
+
11
+ ## ⚠️ UPDATE (2025-10-28)
12
+
13
+ **References to TimescaleDB compression in this ADR are now historical.**
14
+
15
+ After initial struggles with database configuration, the decision was made to drop the TimescaleDB extension as it was not providing sufficient value for the current proof-of-concept applications. The never-forget philosophy remains unchanged, but storage growth is now managed through standard PostgreSQL archival strategies instead of TimescaleDB compression policies.
16
+
17
+ See [ADR-001](001-use-postgresql-timescaledb-storage.md) for details on the TimescaleDB removal.
18
+
19
+ ---
20
+
21
+ ## Context
22
+
23
+ Traditional memory systems for LLMs face a critical design decision: when should memories be deleted?
24
+
25
+ Alternative approaches:
26
+
27
+ 1. **Automatic deletion**: LRU cache eviction, TTL expiration, capacity limits
28
+ 2. **Never delete**: Unlimited growth, storage costs, degraded performance
29
+ 3. **Manual deletion**: User explicitly deletes memories
30
+ 4. **Hybrid**: Automatic archival + manual deletion for permanent removal
31
+
32
+ Key challenges:
33
+
34
+ - **LLM context loss**: Deleting memories loses valuable knowledge
35
+ - **User surprise**: Automatic deletion feels like "forgetting" without consent
36
+ - **Debugging**: Hard to debug if memories disappear automatically
37
+ - **Storage costs**: Unlimited storage is expensive
38
+ - **Performance**: Large datasets slow down queries
39
+
40
+ HTM's core purpose is to provide **persistent, never-forgetting memory** for LLM robots. The philosophy: "never forget unless explicitly told."
41
+
42
+ ## Decision
43
+
44
+ We will implement a **never-forget philosophy** where:
45
+
46
+ 1. ✅ **Memories are never automatically deleted**
47
+ 2. ✅ **Eviction only moves memories from working → long-term storage**
48
+ 3. ✅ **Deletion requires explicit user confirmation**
49
+ 4. ✅ **Confirmation must be `:confirmed` symbol to prevent accidental deletion**
50
+ 5. ✅ **All deletions are logged for audit trail**
51
+
52
+ ### Deletion API
53
+
54
+ ```ruby
55
+ # Attempting to delete without confirmation raises error
56
+ htm.forget("key_to_delete")
57
+ # => ArgumentError: Must pass confirm: :confirmed to delete
58
+
59
+ # Explicit confirmation required
60
+ htm.forget("key_to_delete", confirm: :confirmed)
61
+ # => true (deleted successfully)
62
+ ```
63
+
64
+ ### Implementation
65
+
66
+ ```ruby
67
+ def forget(key, confirm: false)
68
+ raise ArgumentError, "Must pass confirm: :confirmed to delete" unless confirm == :confirmed
69
+
70
+ node_id = @long_term_memory.get_node_id(key)
71
+
72
+ # Log operation BEFORE deleting (audit trail)
73
+ @long_term_memory.log_operation(
74
+ operation: 'forget',
75
+ node_id: node_id,
76
+ robot_id: @robot_id,
77
+ details: { key: key }
78
+ )
79
+
80
+ # Delete from long-term memory and working memory
81
+ @long_term_memory.delete(key)
82
+ @working_memory.remove(key)
83
+
84
+ update_robot_activity
85
+ true
86
+ end
87
+ ```
88
+
89
+ ### Eviction vs Deletion
90
+
91
+ **Eviction (automatic, safe)**:
92
+ - Triggered by working memory capacity limit
93
+ - Moves memories from working memory → long-term memory
94
+ - NO data loss, memories remain recallable
95
+ - Logged as 'evict' operation
96
+
97
+ **Deletion (explicit, destructive)**:
98
+ - Triggered only by user calling `forget(confirm: :confirmed)`
99
+ - Removes memory from both working and long-term storage
100
+ - PERMANENT data loss
101
+ - Logged as 'forget' operation
102
+
103
+ ## Rationale
104
+
105
+ ### Why Never-Forget?
106
+
107
+ **LLMs need long-term context**:
108
+ - Architectural decisions made months ago still matter
109
+ - User preferences should persist across sessions
110
+ - Bug fixes and resolutions are valuable knowledge
111
+ - Conversation history builds understanding over time
112
+
113
+ **Automatic deletion causes problems**:
114
+ - ❌ **Surprise**: User asks "didn't we discuss this?" → memory gone
115
+ - ❌ **Debugging**: Can't debug deleted memories
116
+ - ❌ **Inconsistency**: Same query returns different results over time
117
+ - ❌ **Lost knowledge**: Critical information disappears silently
118
+
119
+ **Two-tier architecture enables never-forget**:
120
+ - Working memory: Token-limited, evicts to long-term
121
+ - Long-term memory: Unlimited, persistent PostgreSQL
122
+ - Eviction ≠ deletion, just moves to cold storage
123
+ - Recall brings memories back to working memory
124
+
125
+ ### Why Explicit Confirmation?
126
+
127
+ **Prevent accidental deletion**:
128
+ ```ruby
129
+ # Easy typo or mistake
130
+ htm.forget("important_key") # REJECTED - raises error
131
+
132
+ # Must be intentional
133
+ htm.forget("important_key", confirm: :confirmed) # Allowed
134
+ ```
135
+
136
+ **Confirmation is a speed bump**:
137
+ - Forces user to think before deleting
138
+ - Symbol `:confirmed` (not boolean) prevents `confirm: true` shortcuts
139
+ - Clear intent signal in code review
140
+
141
+ **Audit trail for safety**:
142
+ - All deletions logged with robot_id and timestamp
143
+ - Can investigate "who deleted this?"
144
+ - Provides recovery information (log has the deleted value)
145
+
146
+ ### Why Log Before Deleting?
147
+
148
+ **Foreign key constraint safety**:
149
+ ```ruby
150
+ # Log operation BEFORE deleting
151
+ @long_term_memory.log_operation(
152
+ operation: 'forget',
153
+ node_id: node_id, # Still exists
154
+ robot_id: @robot_id,
155
+ details: { key: key }
156
+ )
157
+
158
+ # Now safe to delete
159
+ @long_term_memory.delete(key)
160
+ ```
161
+
162
+ **Audit trail preservation**:
163
+ - Deletion log entry survives even if something goes wrong
164
+ - Can reconstruct what was deleted and when
165
+ - Supports future "undo delete" feature
166
+
167
+ ## Consequences
168
+
169
+ ### Positive
170
+
171
+ ✅ **Never lose knowledge**: Memories persist unless explicitly deleted
172
+ ✅ **Predictable behavior**: No surprise deletions, no data loss
173
+ ✅ **Debugging friendly**: All memories available for analysis
174
+ ✅ **Audit trail**: Every deletion logged with who/when/what
175
+ ✅ **Safe eviction**: Working memory overflow doesn't lose data
176
+ ✅ **Recallable**: Evicted memories return via recall()
177
+ ✅ **Intentional deletion**: Confirmation prevents accidents
178
+
179
+ ### Negative
180
+
181
+ ❌ **Unbounded growth**: Database grows indefinitely without cleanup
182
+ ❌ **Storage costs**: Long-term storage has financial cost
183
+ ❌ **Query performance**: Larger datasets slow down searches
184
+ ❌ **Manual cleanup**: User must periodically delete unneeded memories
185
+ ❌ **No automatic expiration**: Can't set TTL for temporary memories
186
+ ❌ **Privacy concerns**: Sensitive data persists until deleted
187
+
188
+ ### Neutral
189
+
190
+ ➡️ **User responsibility**: User must manage memory lifecycle
191
+ ➡️ **Explicit is better**: Pythonic philosophy, clear intent
192
+ ➡️ **Retention policies**: Future feature, not v1
193
+
194
+ ## Design Decisions
195
+
196
+ ### Decision: Confirmation Symbol (`:confirmed`) Instead of Boolean
197
+ **Rationale**:
198
+
199
+ - Boolean `confirm: true` is too easy to add casually
200
+ - Symbol `:confirmed` requires deliberate intent
201
+ - Harder to accidentally pass `true` vs `:confirmed`
202
+
203
+ **Alternative**: `confirm: true`
204
+ **Rejected**: Too casual, easy to misuse
205
+
206
+ **Alternative**: `confirm: "I am sure"`
207
+ **Rejected**: String matching is fragile
208
+
209
+ ### Decision: Raise Error on Missing Confirmation
210
+ **Rationale**: Fail-safe default, loud failure prevents data loss
211
+
212
+ ```ruby
213
+ htm.forget("key") # Raises ArgumentError
214
+ ```
215
+
216
+ **Alternative**: Silently ignore (return false)
217
+ **Rejected**: Silent failures are dangerous
218
+
219
+ **Alternative**: Prompt user for confirmation
220
+ **Rejected**: Not appropriate for library code
221
+
222
+ ### Decision: Log Before Delete (Not After)
223
+ **Rationale**: Avoid foreign key constraint violations
224
+
225
+ **Alternative**: Log after delete
226
+ **Rejected**: Foreign key violation if node_id referenced
227
+
228
+ **Alternative**: Allow NULL node_id in logs
229
+ **Rejected**: Lose referential integrity
230
+
231
+ ### Decision: Eviction Preserves in Long-Term Memory
232
+ **Rationale**: Core never-forget philosophy
233
+
234
+ **Alternative**: Eviction = deletion
235
+ **Rejected**: Violates never-forget principle
236
+
237
+ **Alternative**: Archive to separate table
238
+ **Deferred**: Can optimize with archival tables later
239
+
240
+ ### Decision: No TTL (Time-To-Live) Feature
241
+ **Rationale**: Simplicity, never-forget philosophy
242
+
243
+ **Alternative**: Optional TTL per memory
244
+ **Deferred**: Can add later if needed
245
+
246
+ ## Use Cases
247
+
248
+ ### Use Case 1: Accidental Deletion Attempt
249
+ ```ruby
250
+ # User typo or mistake
251
+ htm.forget("important_decision")
252
+
253
+ # Result: ArgumentError raised
254
+ # => ArgumentError: Must pass confirm: :confirmed to delete
255
+
256
+ # Memory remains safe
257
+ ```
258
+
259
+ ### Use Case 2: Intentional Deletion
260
+ ```ruby
261
+ # User wants to delete temporary test data
262
+ htm.add_node("test_key", "temporary test data", importance: 1.0)
263
+
264
+ # Later: delete intentionally
265
+ htm.forget("test_key", confirm: :confirmed)
266
+ # => true (deleted)
267
+
268
+ # Deletion logged for audit trail
269
+ ```
270
+
271
+ ### Use Case 3: Eviction (Not Deletion)
272
+ ```ruby
273
+ # Working memory full (128,000 tokens)
274
+ # Add large new memory (10,000 tokens)
275
+
276
+ htm.add_node("new_large_memory", large_text, importance: 7.0)
277
+
278
+ # Result: HTM evicts low-importance memories to make space
279
+ # Evicted memories moved to long-term storage (NOT deleted)
280
+ # Can be recalled later:
281
+
282
+ memories = htm.recall(timeframe: "last month", topic: "evicted topic")
283
+ # => Evicted memories returned
284
+ ```
285
+
286
+ ### Use Case 4: Audit Trail Query
287
+ ```ruby
288
+ # Who deleted this memory?
289
+ deleted_logs = db.exec(<<~SQL)
290
+ SELECT robot_id, created_at, details
291
+ FROM operations_log
292
+ WHERE operation = 'forget'
293
+ AND details->>'key' = 'important_key'
294
+ SQL
295
+
296
+ # Result:
297
+ # robot_id: "f47ac10b-..."
298
+ # created_at: 2025-10-25 14:32:15
299
+ # details: {"key": "important_key"}
300
+ ```
301
+
302
+ ### Use Case 5: Bulk Cleanup (Manual)
303
+ ```ruby
304
+ # User wants to clean up old test data
305
+ test_keys = [
306
+ "test_001",
307
+ "test_002",
308
+ "test_003"
309
+ ]
310
+
311
+ test_keys.each do |key|
312
+ htm.forget(key, confirm: :confirmed)
313
+ end
314
+
315
+ # All deletions logged individually
316
+ # User must explicitly confirm each deletion
317
+ ```
318
+
319
+ ### Use Case 6: Never-Forget in Practice
320
+ ```ruby
321
+ # Session 1: Important decision
322
+ htm.add_node("decision_001",
323
+ "We decided to use PostgreSQL for HTM storage",
324
+ type: :decision,
325
+ importance: 10.0)
326
+
327
+ # ... 90 days later, many sessions, many memories added ...
328
+ # Working memory evicted this decision to long-term storage
329
+
330
+ # Session 100: User asks about database choice
331
+ memories = htm.recall(timeframe: "last 3 months", topic: "database storage")
332
+
333
+ # Result: Decision recalled from long-term memory
334
+ # Never forgotten, always available
335
+ ```
336
+
337
+ ## Deletion Lifecycle
338
+
339
+ ### 1. User Initiates Deletion
340
+ ```ruby
341
+ htm.forget("key_to_delete", confirm: :confirmed)
342
+ ```
343
+
344
+ ### 2. Validation
345
+ ```ruby
346
+ raise ArgumentError, "Must pass confirm: :confirmed to delete" unless confirm == :confirmed
347
+ ```
348
+
349
+ ### 3. Retrieve Node ID
350
+ ```ruby
351
+ node_id = @long_term_memory.get_node_id("key_to_delete")
352
+ # => 42
353
+ ```
354
+
355
+ ### 4. Log Operation (Before Deletion)
356
+ ```ruby
357
+ @long_term_memory.log_operation(
358
+ operation: 'forget',
359
+ node_id: 42, # Still exists at this point
360
+ robot_id: @robot_id,
361
+ details: { key: "key_to_delete" }
362
+ )
363
+ ```
364
+
365
+ ### 5. Delete from Long-Term Memory
366
+ ```sql
367
+ DELETE FROM nodes WHERE key = 'key_to_delete'
368
+
369
+ -- Cascades to:
370
+ -- - relationships (foreign key cascade)
371
+ -- - tags (foreign key cascade)
372
+ ```
373
+
374
+ ### 6. Remove from Working Memory
375
+ ```ruby
376
+ @working_memory.remove("key_to_delete")
377
+ ```
378
+
379
+ ### 7. Update Robot Activity
380
+ ```ruby
381
+ @long_term_memory.update_robot_activity(@robot_id)
382
+ ```
383
+
384
+ ### 8. Return Success
385
+ ```ruby
386
+ return true
387
+ ```
388
+
389
+ ## Performance Characteristics
390
+
391
+ ### Deletion Performance
392
+
393
+ - **Node ID lookup**: O(log n) with index on key
394
+ - **Log operation**: O(1) insert
395
+ - **Delete query**: O(1) with primary key
396
+ - **Cascade deletes**: O(m) where m = related records
397
+ - **Working memory remove**: O(1) hash delete
398
+ - **Total**: < 10ms for typical deletion
399
+
400
+ ### Audit Log Growth
401
+
402
+ - **One log entry per deletion**: Minimal overhead
403
+ - **Log table indexed**: Fast queries by operation, robot_id, timestamp
404
+ - **Partitioning**: Can partition by timestamp if needed
405
+
406
+ ### Storage Growth (Never-Forget)
407
+
408
+ - **Long-term memory**: Grows unbounded without cleanup
409
+ - **Typical growth**: ~100-1000 nodes per day (varies widely)
410
+ - **Storage**: ~1-10 KB per node (text + embedding)
411
+ - **Annual growth estimate**: ~365-3650 MB per year
412
+
413
+ ## Risks and Mitigations
414
+
415
+ ### Risk: Unbounded Storage Growth
416
+
417
+ - **Risk**: Database grows indefinitely, storage costs increase
418
+ - **Likelihood**: High (by design, never-forget)
419
+ - **Impact**: Medium (storage costs, query slowdown)
420
+ - **Mitigation**:
421
+ - Monitor database size
422
+ - Implement archival strategies (future)
423
+ - Document cleanup procedures
424
+ - Compression policies (TimescaleDB)
425
+ - User-driven cleanup with bulk delete utilities
426
+
427
+ ### Risk: Accidental Deletion Despite Confirmation
428
+
429
+ - **Risk**: User confirms deletion by mistake
430
+ - **Likelihood**: Low (confirmation is speed bump)
431
+ - **Impact**: High (permanent data loss)
432
+ - **Mitigation**:
433
+ - Audit log preserves what was deleted
434
+ - Future: "undo delete" within time window
435
+ - Future: "soft delete" with archival table
436
+ - Document deletion is permanent
437
+
438
+ ### Risk: Performance Degradation
439
+
440
+ - **Risk**: Large dataset slows down queries
441
+ - **Likelihood**: Medium (depends on usage)
442
+ - **Impact**: Medium (slower recall)
443
+ - **Mitigation**:
444
+ - Indexes on key, robot_id, created_at, embedding
445
+ - TimescaleDB compression for old data
446
+ - Archival to separate table (future)
447
+ - Partitioning by time range
448
+
449
+ ### Risk: Privacy Concerns
450
+
451
+ - **Risk**: Sensitive data persists indefinitely
452
+ - **Likelihood**: Medium (users may store sensitive info)
453
+ - **Impact**: High (privacy violation)
454
+ - **Mitigation**:
455
+ - Document data retention clearly
456
+ - Provide secure deletion utilities
457
+ - Encryption at rest (PostgreSQL)
458
+ - User awareness of never-forget philosophy
459
+
460
+ ## Future Enhancements
461
+
462
+ ### Soft Delete (Archival)
463
+ ```ruby
464
+ # Mark as deleted instead of hard delete
465
+ htm.archive("key_to_archive", confirm: :confirmed)
466
+
467
+ # Archived memories excluded from queries
468
+ # But recoverable if needed
469
+ htm.unarchive("key_to_archive")
470
+ ```
471
+
472
+ ### Undo Delete (Time Window)
473
+ ```ruby
474
+ # Soft delete with 30-day recovery window
475
+ htm.forget("key", confirm: :confirmed)
476
+
477
+ # Within 30 days: undo
478
+ htm.undo_forget("key")
479
+
480
+ # After 30 days: permanent deletion
481
+ ```
482
+
483
+ ### Retention Policies
484
+ ```ruby
485
+ # Automatic archival based on age and importance
486
+ htm.configure_retention(
487
+ archive_after_days: 365,
488
+ min_importance: 5.0 # Don't archive high-importance
489
+ )
490
+ ```
491
+
492
+ ### Bulk Delete Utilities
493
+ ```ruby
494
+ # Delete all nodes matching criteria
495
+ HTM::Cleanup.delete_by_tag("temporary", confirm: :confirmed)
496
+ HTM::Cleanup.delete_older_than(1.year.ago, confirm: :confirmed)
497
+ HTM::Cleanup.delete_by_robot("robot-123", confirm: :confirmed)
498
+ ```
499
+
500
+ ### Encryption for Sensitive Data
501
+ ```ruby
502
+ # Encrypt sensitive memories
503
+ htm.add_node("api_key", sensitive_value,
504
+ encrypt: true,
505
+ importance: 10.0)
506
+
507
+ # Automatically encrypted in database
508
+ # Decrypted on retrieval
509
+ ```
510
+
511
+ ### Audit Log Analysis
512
+ ```ruby
513
+ # Analyze deletion patterns
514
+ HTM::Analytics.deletion_report(timeframe: "last month")
515
+
516
+ # Who deletes the most?
517
+ # What types of memories are deleted?
518
+ # When are deletions happening?
519
+ ```
520
+
521
+ ## Alternatives Considered
522
+
523
+ ### Automatic TTL (Time-To-Live)
524
+ **Pros**: Automatic cleanup, predictable storage
525
+ **Cons**: Violates never-forget, surprise deletions
526
+ **Decision**: ❌ Rejected - contradicts core philosophy
527
+
528
+ ### LRU Cache Eviction with Deletion
529
+ **Pros**: Simple, automatic capacity management
530
+ **Cons**: Data loss, surprise deletions
531
+ **Decision**: ❌ Rejected - eviction should not delete
532
+
533
+ ### No Deletion API (Truly Never Delete)
534
+ **Pros**: Simplest never-forget implementation
535
+ **Cons**: No escape hatch for mistakes, privacy issues
536
+ **Decision**: ❌ Rejected - need explicit deletion for edge cases
537
+
538
+ ### Confirmation via Prompt
539
+ **Pros**: Most user-friendly, hard to misuse
540
+ **Cons**: Not appropriate for library code, breaks automation
541
+ **Decision**: ❌ Rejected - library should not prompt
542
+
543
+ ### Soft Delete by Default
544
+ **Pros**: Recoverable, safer than hard delete
545
+ **Cons**: Complexity, storage overhead, unclear semantics
546
+ **Decision**: 🔄 Deferred - consider for v2
547
+
548
+ ## References
549
+
550
+ - [Never Forget Principle](https://en.wikipedia.org/wiki/Persistence_(computer_science))
551
+ - [Audit Logging Best Practices](https://owasp.org/www-community/Audit_Logging)
552
+ - [Soft Deletion Pattern](https://en.wikipedia.org/wiki/Soft_deletion)
553
+ - [GDPR Right to Erasure](https://gdpr.eu/right-to-be-forgotten/)
554
+ - [Data Retention Policies](https://en.wikipedia.org/wiki/Data_retention)
555
+
556
+ ## Review Notes
557
+
558
+ **Systems Architect**: ✅ Never-forget philosophy is core value proposition. Explicit deletion is correct.
559
+
560
+ **Security Specialist**: ⚠️ Document data retention clearly. Consider encryption for sensitive data. GDPR implications?
561
+
562
+ **Domain Expert**: ✅ Two-tier architecture enables never-forget without performance penalty. Smart design.
563
+
564
+ **Ruby Expert**: ✅ Symbol confirmation (`:confirmed`) is idiomatic Ruby. Better than boolean.
565
+
566
+ **AI Engineer**: ✅ Persistent memory is critical for LLM context. Automatic deletion would degrade performance.
567
+
568
+ **Performance Specialist**: ⚠️ Monitor storage growth. Plan for archival strategies. Compression will help.
569
+
570
+ **Database Architect**: ✅ Log-before-delete prevents foreign key violations. Consider partitioning for large datasets.