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