htm 0.0.18 → 0.0.30

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -1
  3. data/README.md +12 -0
  4. data/Rakefile +104 -18
  5. data/db/migrate/00001_enable_extensions.rb +9 -5
  6. data/db/migrate/00002_create_robots.rb +18 -6
  7. data/db/migrate/00003_create_file_sources.rb +30 -17
  8. data/db/migrate/00004_create_nodes.rb +60 -48
  9. data/db/migrate/00005_create_tags.rb +24 -12
  10. data/db/migrate/00006_create_node_tags.rb +28 -13
  11. data/db/migrate/00007_create_robot_nodes.rb +40 -26
  12. data/db/schema.sql +17 -1
  13. data/db/seeds.rb +34 -34
  14. data/docs/api/embedding-service.md +140 -110
  15. data/docs/api/yard/HTM/ActiveRecordConfig.md +6 -0
  16. data/docs/api/yard/HTM/Config.md +173 -0
  17. data/docs/api/yard/HTM/ConfigSection.md +28 -0
  18. data/docs/api/yard/HTM/Database.md +1 -1
  19. data/docs/api/yard/HTM/Railtie.md +2 -2
  20. data/docs/api/yard/HTM.md +0 -57
  21. data/docs/api/yard/index.csv +76 -61
  22. data/docs/api/yard-reference.md +2 -1
  23. data/docs/architecture/adrs/003-ollama-embeddings.md +45 -36
  24. data/docs/architecture/adrs/004-hive-mind.md +1 -1
  25. data/docs/architecture/adrs/008-robot-identification.md +1 -1
  26. data/docs/architecture/index.md +11 -9
  27. data/docs/architecture/overview.md +11 -7
  28. data/docs/assets/images/balanced-strategy-decay.svg +41 -0
  29. data/docs/assets/images/class-hierarchy.svg +1 -1
  30. data/docs/assets/images/eviction-priority.svg +43 -0
  31. data/docs/assets/images/exception-hierarchy.svg +2 -2
  32. data/docs/assets/images/hive-mind-shared-memory.svg +52 -0
  33. data/docs/assets/images/htm-architecture-overview.svg +3 -3
  34. data/docs/assets/images/htm-core-components.svg +4 -4
  35. data/docs/assets/images/htm-layered-architecture.svg +1 -1
  36. data/docs/assets/images/htm-memory-addition-flow.svg +2 -2
  37. data/docs/assets/images/htm-memory-recall-flow.svg +2 -2
  38. data/docs/assets/images/memory-topology.svg +53 -0
  39. data/docs/assets/images/two-tier-memory-architecture.svg +55 -0
  40. data/docs/database/naming-convention.md +244 -0
  41. data/docs/database_rake_tasks.md +31 -0
  42. data/docs/development/rake-tasks.md +80 -35
  43. data/docs/development/setup.md +76 -44
  44. data/docs/examples/basic-usage.md +133 -0
  45. data/docs/examples/config-files.md +170 -0
  46. data/docs/examples/file-loading.md +208 -0
  47. data/docs/examples/index.md +116 -0
  48. data/docs/examples/llm-configuration.md +168 -0
  49. data/docs/examples/mcp-client.md +172 -0
  50. data/docs/examples/rails-integration.md +173 -0
  51. data/docs/examples/robot-groups.md +210 -0
  52. data/docs/examples/sinatra-integration.md +218 -0
  53. data/docs/examples/standalone-app.md +216 -0
  54. data/docs/examples/telemetry.md +224 -0
  55. data/docs/examples/timeframes.md +143 -0
  56. data/docs/getting-started/installation.md +97 -40
  57. data/docs/getting-started/quick-start.md +28 -11
  58. data/docs/guides/configuration.md +515 -0
  59. data/docs/guides/file-loading.md +322 -0
  60. data/docs/guides/getting-started.md +40 -9
  61. data/docs/guides/index.md +3 -3
  62. data/docs/guides/mcp-server.md +100 -13
  63. data/docs/guides/propositions.md +264 -0
  64. data/docs/guides/recalling-memories.md +4 -4
  65. data/docs/guides/search-strategies.md +3 -3
  66. data/docs/guides/tags.md +318 -0
  67. data/docs/guides/telemetry.md +229 -0
  68. data/docs/index.md +8 -16
  69. data/docs/{architecture → robots}/hive-mind.md +8 -111
  70. data/docs/robots/index.md +73 -0
  71. data/docs/{guides → robots}/multi-robot.md +3 -3
  72. data/docs/{guides → robots}/robot-groups.md +8 -7
  73. data/docs/{architecture → robots}/two-tier-memory.md +13 -149
  74. data/docs/robots/why-robots.md +85 -0
  75. data/examples/.envrc +6 -0
  76. data/examples/.gitignore +2 -0
  77. data/examples/00_create_examples_db.rb +94 -0
  78. data/examples/{basic_usage.rb → 01_basic_usage.rb} +12 -16
  79. data/examples/{custom_llm_configuration.rb → 03_custom_llm_configuration.rb} +13 -3
  80. data/examples/{file_loader_usage.rb → 04_file_loader_usage.rb} +11 -14
  81. data/examples/{timeframe_demo.rb → 05_timeframe_demo.rb} +10 -3
  82. data/examples/{example_app → 06_example_app}/app.rb +15 -15
  83. data/examples/{cli_app → 07_cli_app}/htm_cli.rb +15 -22
  84. data/examples/08_sinatra_app/Gemfile.lock +241 -0
  85. data/examples/{sinatra_app → 08_sinatra_app}/app.rb +19 -18
  86. data/examples/{mcp_client.rb → 09_mcp_client.rb} +5 -8
  87. data/examples/{telemetry → 10_telemetry}/SETUP_README.md +1 -1
  88. data/examples/{telemetry → 10_telemetry}/demo.rb +14 -10
  89. data/examples/11_robot_groups/README.md +335 -0
  90. data/examples/{robot_groups → 11_robot_groups/lib}/robot_worker.rb +17 -3
  91. data/examples/{robot_groups → 11_robot_groups}/multi_process.rb +9 -9
  92. data/examples/{robot_groups → 11_robot_groups}/same_process.rb +9 -12
  93. data/examples/{rails_app → 12_rails_app}/Gemfile +3 -0
  94. data/examples/{rails_app → 12_rails_app}/Gemfile.lock +87 -58
  95. data/examples/{rails_app → 12_rails_app}/app/controllers/dashboard_controller.rb +10 -6
  96. data/examples/{rails_app → 12_rails_app}/app/controllers/files_controller.rb +5 -5
  97. data/examples/{rails_app → 12_rails_app}/app/controllers/memories_controller.rb +11 -7
  98. data/examples/{rails_app → 12_rails_app}/app/controllers/robots_controller.rb +8 -8
  99. data/examples/12_rails_app/app/controllers/tags_controller.rb +36 -0
  100. data/examples/{rails_app → 12_rails_app}/app/views/dashboard/index.html.erb +2 -2
  101. data/examples/{rails_app → 12_rails_app}/app/views/files/new.html.erb +5 -2
  102. data/examples/{rails_app → 12_rails_app}/app/views/memories/_memory_card.html.erb +3 -3
  103. data/examples/{rails_app → 12_rails_app}/app/views/memories/deleted.html.erb +3 -3
  104. data/examples/{rails_app → 12_rails_app}/app/views/memories/edit.html.erb +3 -3
  105. data/examples/{rails_app → 12_rails_app}/app/views/memories/show.html.erb +4 -4
  106. data/examples/{rails_app → 12_rails_app}/app/views/robots/index.html.erb +2 -2
  107. data/examples/{rails_app → 12_rails_app}/app/views/robots/show.html.erb +4 -4
  108. data/examples/{rails_app → 12_rails_app}/app/views/search/index.html.erb +1 -1
  109. data/examples/{rails_app → 12_rails_app}/app/views/tags/index.html.erb +2 -2
  110. data/examples/{rails_app → 12_rails_app}/app/views/tags/show.html.erb +1 -1
  111. data/examples/12_rails_app/config/initializers/htm.rb +7 -0
  112. data/examples/12_rails_app/config/initializers/rack.rb +5 -0
  113. data/examples/README.md +230 -211
  114. data/examples/examples_helper.rb +138 -0
  115. data/lib/htm/config/builder.rb +167 -0
  116. data/lib/htm/config/database.rb +317 -0
  117. data/lib/htm/config/defaults.yml +41 -13
  118. data/lib/htm/config/section.rb +74 -0
  119. data/lib/htm/config/validator.rb +83 -0
  120. data/lib/htm/config.rb +65 -361
  121. data/lib/htm/database.rb +85 -127
  122. data/lib/htm/errors.rb +14 -0
  123. data/lib/htm/integrations/sinatra.rb +13 -44
  124. data/lib/htm/job_adapter.rb +75 -1
  125. data/lib/htm/jobs/generate_embedding_job.rb +3 -4
  126. data/lib/htm/jobs/generate_propositions_job.rb +4 -5
  127. data/lib/htm/jobs/generate_tags_job.rb +16 -15
  128. data/lib/htm/loaders/defaults_loader.rb +23 -0
  129. data/lib/htm/loaders/markdown_loader.rb +17 -15
  130. data/lib/htm/loaders/xdg_config_loader.rb +9 -9
  131. data/lib/htm/long_term_memory/fulltext_search.rb +14 -14
  132. data/lib/htm/long_term_memory/hybrid_search.rb +396 -229
  133. data/lib/htm/long_term_memory/node_operations.rb +24 -23
  134. data/lib/htm/long_term_memory/relevance_scorer.rb +23 -20
  135. data/lib/htm/long_term_memory/robot_operations.rb +4 -4
  136. data/lib/htm/long_term_memory/tag_operations.rb +91 -77
  137. data/lib/htm/long_term_memory/vector_search.rb +4 -5
  138. data/lib/htm/long_term_memory.rb +13 -13
  139. data/lib/htm/mcp/cli.rb +115 -8
  140. data/lib/htm/mcp/resources.rb +4 -3
  141. data/lib/htm/mcp/server.rb +5 -4
  142. data/lib/htm/mcp/tools.rb +37 -28
  143. data/lib/htm/migration.rb +72 -0
  144. data/lib/htm/models/file_source.rb +52 -31
  145. data/lib/htm/models/node.rb +224 -108
  146. data/lib/htm/models/node_tag.rb +49 -28
  147. data/lib/htm/models/robot.rb +38 -27
  148. data/lib/htm/models/robot_node.rb +63 -35
  149. data/lib/htm/models/tag.rb +126 -123
  150. data/lib/htm/observability.rb +45 -41
  151. data/lib/htm/proposition_service.rb +76 -7
  152. data/lib/htm/railtie.rb +2 -2
  153. data/lib/htm/robot_group.rb +30 -18
  154. data/lib/htm/sequel_config.rb +215 -0
  155. data/lib/htm/sql_builder.rb +14 -16
  156. data/lib/htm/tag_service.rb +78 -0
  157. data/lib/htm/tasks.rb +3 -0
  158. data/lib/htm/version.rb +1 -1
  159. data/lib/htm/workflows/remember_workflow.rb +213 -0
  160. data/lib/htm.rb +27 -22
  161. data/lib/tasks/db.rake +0 -2
  162. data/lib/tasks/doc.rake +2 -2
  163. data/lib/tasks/files.rake +11 -18
  164. data/lib/tasks/htm.rake +190 -62
  165. data/lib/tasks/jobs.rake +179 -54
  166. data/lib/tasks/tags.rake +8 -13
  167. data/mkdocs.yml +33 -8
  168. data/scripts/backfill_parent_tags.rb +376 -0
  169. data/scripts/normalize_plural_tags.rb +335 -0
  170. metadata +168 -86
  171. data/docs/api/yard/HTM/Configuration.md +0 -240
  172. data/docs/telemetry.md +0 -391
  173. data/examples/rails_app/app/controllers/tags_controller.rb +0 -30
  174. data/examples/sinatra_app/Gemfile.lock +0 -166
  175. data/lib/htm/active_record_config.rb +0 -104
  176. /data/examples/{config_file_example → 02_config_file_example}/README.md +0 -0
  177. /data/examples/{config_file_example → 02_config_file_example}/config/htm.local.yml +0 -0
  178. /data/examples/{config_file_example → 02_config_file_example}/custom_config.yml +0 -0
  179. /data/examples/{config_file_example → 02_config_file_example}/show_config.rb +0 -0
  180. /data/examples/{example_app → 06_example_app}/Rakefile +0 -0
  181. /data/examples/{cli_app → 07_cli_app}/README.md +0 -0
  182. /data/examples/{sinatra_app → 08_sinatra_app}/Gemfile +0 -0
  183. /data/examples/{telemetry → 10_telemetry}/README.md +0 -0
  184. /data/examples/{telemetry → 10_telemetry}/grafana/dashboards/htm-metrics.json +0 -0
  185. /data/examples/{rails_app → 12_rails_app}/.gitignore +0 -0
  186. /data/examples/{rails_app → 12_rails_app}/Procfile.dev +0 -0
  187. /data/examples/{rails_app → 12_rails_app}/README.md +0 -0
  188. /data/examples/{rails_app → 12_rails_app}/Rakefile +0 -0
  189. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/application.css +0 -0
  190. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/inter-font.css +0 -0
  191. /data/examples/{rails_app → 12_rails_app}/app/controllers/application_controller.rb +0 -0
  192. /data/examples/{rails_app → 12_rails_app}/app/controllers/search_controller.rb +0 -0
  193. /data/examples/{rails_app → 12_rails_app}/app/javascript/application.js +0 -0
  194. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/application.js +0 -0
  195. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/index.js +0 -0
  196. /data/examples/{rails_app → 12_rails_app}/app/views/files/index.html.erb +0 -0
  197. /data/examples/{rails_app → 12_rails_app}/app/views/files/show.html.erb +0 -0
  198. /data/examples/{rails_app → 12_rails_app}/app/views/layouts/application.html.erb +0 -0
  199. /data/examples/{rails_app → 12_rails_app}/app/views/memories/index.html.erb +0 -0
  200. /data/examples/{rails_app → 12_rails_app}/app/views/memories/new.html.erb +0 -0
  201. /data/examples/{rails_app → 12_rails_app}/app/views/robots/new.html.erb +0 -0
  202. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_navbar.html.erb +0 -0
  203. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_stat_card.html.erb +0 -0
  204. /data/examples/{rails_app → 12_rails_app}/bin/dev +0 -0
  205. /data/examples/{rails_app → 12_rails_app}/bin/rails +0 -0
  206. /data/examples/{rails_app → 12_rails_app}/bin/rake +0 -0
  207. /data/examples/{rails_app → 12_rails_app}/config/application.rb +0 -0
  208. /data/examples/{rails_app → 12_rails_app}/config/boot.rb +0 -0
  209. /data/examples/{rails_app → 12_rails_app}/config/database.yml +0 -0
  210. /data/examples/{rails_app → 12_rails_app}/config/environment.rb +0 -0
  211. /data/examples/{rails_app → 12_rails_app}/config/importmap.rb +0 -0
  212. /data/examples/{rails_app → 12_rails_app}/config/routes.rb +0 -0
  213. /data/examples/{rails_app → 12_rails_app}/config/tailwind.config.js +0 -0
  214. /data/examples/{rails_app → 12_rails_app}/config.ru +0 -0
  215. /data/examples/{rails_app → 12_rails_app}/log/.keep +0 -0
  216. /data/examples/{rails_app → 12_rails_app}/tmp/local_secret.txt +0 -0
@@ -0,0 +1,52 @@
1
+ <svg viewBox="0 0 900 600" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
2
+ <!-- Title -->
3
+ <text x="450" y="30" text-anchor="middle" fill="#E0E0E0" font-size="18" font-weight="bold">Hive Mind: Shared Long-Term Memory</text>
4
+
5
+ <!-- Central Database -->
6
+ <ellipse cx="450" cy="300" rx="180" ry="120" fill="rgba(156, 39, 176, 0.2)" stroke="#9C27B0" stroke-width="3"/>
7
+ <text x="450" y="280" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Long-Term Memory</text>
8
+ <text x="450" y="305" text-anchor="middle" fill="#B0B0B0" font-size="12">PostgreSQL</text>
9
+ <text x="450" y="325" text-anchor="middle" fill="#B0B0B0" font-size="12">Shared Global Database</text>
10
+ <text x="450" y="345" text-anchor="middle" fill="#4CAF50" font-size="13" font-weight="bold">All Robots Access Here</text>
11
+
12
+ <!-- Robot 1: Code Helper -->
13
+ <rect x="50" y="80" width="200" height="100" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="2" rx="5"/>
14
+ <text x="150" y="110" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 1: Code Helper</text>
15
+ <text x="150" y="135" text-anchor="middle" fill="#B0B0B0" font-size="11">ID: robot-abc123</text>
16
+ <text x="150" y="155" text-anchor="middle" fill="#B0B0B0" font-size="11">Own Working Memory</text>
17
+
18
+ <!-- Robot 2: Research Assistant -->
19
+ <rect x="650" y="80" width="200" height="100" fill="rgba(76, 175, 80, 0.2)" stroke="#4CAF50" stroke-width="2" rx="5"/>
20
+ <text x="750" y="110" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 2: Research Bot</text>
21
+ <text x="750" y="135" text-anchor="middle" fill="#B0B0B0" font-size="11">ID: robot-xyz789</text>
22
+ <text x="750" y="155" text-anchor="middle" fill="#B0B0B0" font-size="11">Own Working Memory</text>
23
+
24
+ <!-- Robot 3: Chat Companion -->
25
+ <rect x="50" y="450" width="200" height="100" fill="rgba(255, 152, 0, 0.2)" stroke="#FF9800" stroke-width="2" rx="5"/>
26
+ <text x="150" y="480" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 3: Chat Bot</text>
27
+ <text x="150" y="505" text-anchor="middle" fill="#B0B0B0" font-size="11">ID: robot-def456</text>
28
+ <text x="150" y="525" text-anchor="middle" fill="#B0B0B0" font-size="11">Own Working Memory</text>
29
+
30
+ <!-- Robot 4: Design Assistant -->
31
+ <rect x="650" y="450" width="200" height="100" fill="rgba(244, 67, 54, 0.2)" stroke="#F44336" stroke-width="2" rx="5"/>
32
+ <text x="750" y="480" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 4: Designer</text>
33
+ <text x="750" y="505" text-anchor="middle" fill="#B0B0B0" font-size="11">ID: robot-ghi012</text>
34
+ <text x="750" y="525" text-anchor="middle" fill="#B0B0B0" font-size="11">Own Working Memory</text>
35
+
36
+ <!-- Connections to central database -->
37
+ <line x1="150" y1="180" x2="320" y2="240" stroke="#2196F3" stroke-width="3"/>
38
+ <line x1="750" y1="180" x2="580" y2="240" stroke="#4CAF50" stroke-width="3"/>
39
+ <line x1="150" y1="450" x2="320" y2="360" stroke="#FF9800" stroke-width="3"/>
40
+ <line x1="750" y1="450" x2="580" y2="360" stroke="#F44336" stroke-width="3"/>
41
+
42
+ <!-- Labels on connections -->
43
+ <text x="235" y="210" fill="#2196F3" font-size="10">read/write</text>
44
+ <text x="650" y="210" fill="#4CAF50" font-size="10">read/write</text>
45
+ <text x="235" y="410" fill="#FF9800" font-size="10">read/write</text>
46
+ <text x="650" y="410" fill="#F44336" font-size="10">read/write</text>
47
+
48
+ <!-- Key benefit -->
49
+ <rect x="300" y="520" width="300" height="60" fill="rgba(76, 175, 80, 0.1)" stroke="#4CAF50" stroke-width="2" rx="5"/>
50
+ <text x="450" y="545" text-anchor="middle" fill="#4CAF50" font-size="13" font-weight="bold">Knowledge Sharing:</text>
51
+ <text x="450" y="565" text-anchor="middle" fill="#B0B0B0" font-size="11">All robots see all memories</text>
52
+ </svg>
@@ -46,8 +46,8 @@
46
46
  <!-- Embedding Service -->
47
47
  <rect x="500" y="520" width="280" height="100" fill="rgba(255, 152, 0, 0.3)" stroke="#FF9800" stroke-width="3" rx="8"/>
48
48
  <text x="640" y="555" text-anchor="middle" fill="#FFFFFF" font-size="16" font-weight="bold">Embedding Service</text>
49
- <text x="640" y="580" text-anchor="middle" fill="#B0B0B0" font-size="13">(Ollama/RubyLLM)</text>
50
- <text x="640" y="603" text-anchor="middle" fill="#B0B0B0" font-size="13">gpt-oss model</text>
49
+ <text x="640" y="580" text-anchor="middle" fill="#B0B0B0" font-size="13">(RubyLLM Multi-Provider)</text>
50
+ <text x="640" y="603" text-anchor="middle" fill="#B0B0B0" font-size="13">Configurable model</text>
51
51
 
52
52
  <!-- Notes -->
53
53
  <rect x="30" y="500" width="360" height="160" fill="rgba(33, 150, 243, 0.1)" stroke="#2196F3" stroke-width="2" rx="5" stroke-dasharray="5,5"/>
@@ -56,7 +56,7 @@
56
56
  <text x="40" y="570" fill="#B0B0B0" font-size="11">• <tspan fill="#2196F3" font-weight="bold">HTM API</tspan>: Main interface for memory operations</text>
57
57
  <text x="40" y="590" fill="#B0B0B0" font-size="11">• <tspan fill="#2196F3" font-weight="bold">Working Memory</tspan>: Fast, token-limited cache</text>
58
58
  <text x="40" y="610" fill="#B0B0B0" font-size="11">• <tspan fill="#9C27B0" font-weight="bold">Long-term Memory</tspan>: Durable PostgreSQL storage</text>
59
- <text x="40" y="630" fill="#B0B0B0" font-size="11">• <tspan fill="#FF9800" font-weight="bold">Embedding Service</tspan>: Vector generation via Ollama</text>
59
+ <text x="40" y="630" fill="#B0B0B0" font-size="11">• <tspan fill="#FF9800" font-weight="bold">Embedding Service</tspan>: Vector generation via RubyLLM</text>
60
60
  <text x="40" y="650" fill="#B0B0B0" font-size="11">• <tspan fill="#FFC107" font-weight="bold">Eviction/Recall</tspan>: Automatic memory management</text>
61
61
 
62
62
  <!-- Markers -->
@@ -44,13 +44,13 @@
44
44
  <text x="630" y="310" fill="#B0B0B0" font-size="11">@provider: Symbol</text>
45
45
  <text x="630" y="330" fill="#B0B0B0" font-size="11">@model: String</text>
46
46
  <text x="630" y="350" fill="#B0B0B0" font-size="11">@dimensions: Integer</text>
47
- <text x="630" y="370" fill="#B0B0B0" font-size="11">@ollama_url: String</text>
47
+ <text x="630" y="370" fill="#B0B0B0" font-size="11">@provider_url: String</text>
48
48
  <line x1="620" y1="380" x2="840" y2="380" stroke="#FF9800" stroke-width="1"/>
49
49
  <text x="630" y="400" fill="#4CAF50" font-size="11">+embed(text)</text>
50
50
  <text x="630" y="420" fill="#B0B0B0" font-size="11">+count_tokens(text)</text>
51
- <text x="630" y="440" fill="#B0B0B0" font-size="11">+embed_with_ollama()</text>
52
- <text x="630" y="460" fill="#B0B0B0" font-size="11">+embed_with_openai()</text>
53
- <text x="630" y="480" fill="#FFC107" font-size="10" font-style="italic">// Client-side generation</text>
51
+ <text x="630" y="440" fill="#B0B0B0" font-size="11">+generate_embedding()</text>
52
+ <text x="630" y="460" fill="#B0B0B0" font-size="11">+validate_response()</text>
53
+ <text x="630" y="480" fill="#FFC107" font-size="10" font-style="italic">// RubyLLM multi-provider</text>
54
54
 
55
55
  <!-- Relationships -->
56
56
  <line x1="450" y1="150" x2="170" y2="250" stroke="#2196F3" stroke-width="2"/>
@@ -37,7 +37,7 @@
37
37
  <!-- Layer 4: Services Layer -->
38
38
  <rect x="100" y="440" width="290" height="100" fill="rgba(244, 67, 54, 0.2)" stroke="#F44336" stroke-width="2" rx="5"/>
39
39
  <text x="245" y="465" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Embedding Service</text>
40
- <text x="245" y="490" text-anchor="middle" fill="#B0B0B0" font-size="11">Ollama • OpenAI • Cohere</text>
40
+ <text x="245" y="490" text-anchor="middle" fill="#B0B0B0" font-size="11">RubyLLM (Multi-Provider)</text>
41
41
  <text x="245" y="510" text-anchor="middle" fill="#B0B0B0" font-size="11">Vector Generation</text>
42
42
  <text x="245" y="530" text-anchor="middle" fill="#B0B0B0" font-size="11">Token Counting</text>
43
43
 
@@ -14,9 +14,9 @@
14
14
  <rect x="470" y="80" width="190" height="60" fill="rgba(255, 152, 0, 0.3)" stroke="#FF9800" stroke-width="3" rx="8"/>
15
15
  <text x="565" y="115" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">EmbeddingService</text>
16
16
 
17
- <!-- Ollama/OpenAI -->
17
+ <!-- LLM Provider -->
18
18
  <rect x="710" y="80" width="180" height="60" fill="rgba(255, 193, 7, 0.3)" stroke="#FFC107" stroke-width="3" rx="8"/>
19
- <text x="800" y="115" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">Ollama/OpenAI</text>
19
+ <text x="800" y="115" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">LLM Provider</text>
20
20
 
21
21
  <!-- LongTermMemory -->
22
22
  <rect x="280" y="260" width="180" height="60" fill="rgba(156, 39, 176, 0.3)" stroke="#9C27B0" stroke-width="3" rx="8"/>
@@ -19,9 +19,9 @@
19
19
  <rect x="240" y="180" width="190" height="60" fill="rgba(255, 152, 0, 0.3)" stroke="#2196F3" stroke-width="3" rx="8"/>
20
20
  <text x="335" y="215" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">EmbeddingService</text>
21
21
 
22
- <!-- Ollama/OpenAI -->
22
+ <!-- LLM Provider -->
23
23
  <rect x="480" y="180" width="180" height="60" fill="rgba(255, 193, 7, 0.3)" stroke="#FFC107" stroke-width="3" rx="8"/>
24
- <text x="570" y="215" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">Ollama/OpenAI</text>
24
+ <text x="570" y="215" text-anchor="middle" fill="#FFFFFF" font-size="14" font-weight="bold">LLM Provider</text>
25
25
 
26
26
  <!-- LongTermMemory -->
27
27
  <rect x="240" y="300" width="180" height="60" fill="rgba(156, 39, 176, 0.3)" stroke="#9C27B0" stroke-width="3" rx="8"/>
@@ -0,0 +1,53 @@
1
+ <svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
2
+ <!-- Title -->
3
+ <text x="400" y="30" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Memory Topology: Shared LTM + Local WM</text>
4
+
5
+ <!-- Legend -->
6
+ <rect x="50" y="50" width="20" height="20" fill="rgba(156, 39, 176, 0.3)" stroke="#9C27B0"/>
7
+ <text x="80" y="65" fill="#B0B0B0" font-size="12">Shared (Global)</text>
8
+ <rect x="200" y="50" width="20" height="20" fill="rgba(33, 150, 243, 0.3)" stroke="#2196F3"/>
9
+ <text x="230" y="65" fill="#B0B0B0" font-size="12">Per-Robot (Local)</text>
10
+
11
+ <!-- Robot 1 -->
12
+ <g transform="translate(0, 100)">
13
+ <text x="150" y="0" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 1 (Process 1)</text>
14
+ <rect x="50" y="20" width="200" height="80" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="2" rx="5"/>
15
+ <text x="150" y="50" text-anchor="middle" fill="#E0E0E0" font-size="12">Working Memory</text>
16
+ <text x="150" y="70" text-anchor="middle" fill="#B0B0B0" font-size="10">In-memory, token-limited</text>
17
+ <text x="150" y="85" text-anchor="middle" fill="#B0B0B0" font-size="10">Independent</text>
18
+ </g>
19
+
20
+ <!-- Robot 2 -->
21
+ <g transform="translate(300, 100)">
22
+ <text x="150" y="0" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Robot 2 (Process 2)</text>
23
+ <rect x="50" y="20" width="200" height="80" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="2" rx="5"/>
24
+ <text x="150" y="50" text-anchor="middle" fill="#E0E0E0" font-size="12">Working Memory</text>
25
+ <text x="150" y="70" text-anchor="middle" fill="#B0B0B0" font-size="10">In-memory, token-limited</text>
26
+ <text x="150" y="85" text-anchor="middle" fill="#B0B0B0" font-size="10">Independent</text>
27
+ </g>
28
+
29
+ <!-- Shared Long-Term Memory -->
30
+ <rect x="150" y="280" width="500" height="150" fill="rgba(156, 39, 176, 0.2)" stroke="#9C27B0" stroke-width="3" rx="5"/>
31
+ <text x="400" y="310" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Long-Term Memory (Shared)</text>
32
+ <text x="400" y="340" text-anchor="middle" fill="#B0B0B0" font-size="12">PostgreSQL</text>
33
+ <text x="400" y="365" text-anchor="middle" fill="#B0B0B0" font-size="12">All robots read/write here</text>
34
+ <text x="400" y="390" text-anchor="middle" fill="#B0B0B0" font-size="12">Memories attributed with robot_id</text>
35
+ <text x="400" y="410" text-anchor="middle" fill="#4CAF50" font-size="12" font-weight="bold">Single Source of Truth</text>
36
+
37
+ <!-- Connections -->
38
+ <line x1="150" y1="200" x2="300" y2="280" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow-purple)"/>
39
+ <line x1="450" y1="200" x2="400" y2="280" stroke="#9C27B0" stroke-width="2" marker-end="url(#arrow-purple)"/>
40
+
41
+ <text x="225" y="240" fill="#9C27B0" font-size="10">read/write</text>
42
+ <text x="425" y="240" fill="#9C27B0" font-size="10">read/write</text>
43
+
44
+ <defs>
45
+ <marker id="arrow-purple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
46
+ <polygon points="0 0, 10 3, 0 6" fill="#9C27B0"/>
47
+ </marker>
48
+ </defs>
49
+
50
+ <!-- Key Point -->
51
+ <rect x="100" y="460" width="600" height="30" fill="rgba(76, 175, 80, 0.1)" stroke="#4CAF50" stroke-width="1" rx="3"/>
52
+ <text x="400" y="480" text-anchor="middle" fill="#4CAF50" font-size="12">Each robot has fast local cache (WM) + access to global knowledge (LTM)</text>
53
+ </svg>
@@ -0,0 +1,55 @@
1
+ <svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
2
+ <!-- Title -->
3
+ <text x="400" y="30" text-anchor="middle" fill="#E0E0E0" font-size="18" font-weight="bold">Two-Tier Memory Architecture</text>
4
+
5
+ <!-- Working Memory (Hot Tier) -->
6
+ <rect x="50" y="80" width="300" height="180" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="3" rx="5"/>
7
+ <text x="200" y="110" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Working Memory (Hot)</text>
8
+ <text x="80" y="140" fill="#B0B0B0" font-size="12">Capacity: Token-limited (128K)</text>
9
+ <text x="80" y="160" fill="#B0B0B0" font-size="12">Storage: In-memory Ruby Hash</text>
10
+ <text x="80" y="180" fill="#B0B0B0" font-size="12">Speed: O(1) lookups</text>
11
+ <text x="80" y="200" fill="#B0B0B0" font-size="12">Lifetime: Process lifetime</text>
12
+ <text x="80" y="220" fill="#B0B0B0" font-size="12">Eviction: Importance + Recency</text>
13
+ <text x="80" y="240" fill="#4CAF50" font-size="12" font-weight="bold">Fast, Token-Aware, Volatile</text>
14
+
15
+ <!-- Long-Term Memory (Cold Tier) -->
16
+ <rect x="450" y="80" width="300" height="180" fill="rgba(156, 39, 176, 0.2)" stroke="#9C27B0" stroke-width="3" rx="5"/>
17
+ <text x="600" y="110" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Long-Term Memory (Cold)</text>
18
+ <text x="480" y="140" fill="#B0B0B0" font-size="12">Capacity: Unlimited</text>
19
+ <text x="480" y="160" fill="#B0B0B0" font-size="12">Storage: PostgreSQL + TimescaleDB</text>
20
+ <text x="480" y="180" fill="#B0B0B0" font-size="12">Speed: O(log n) with indexes</text>
21
+ <text x="480" y="200" fill="#B0B0B0" font-size="12">Lifetime: Permanent</text>
22
+ <text x="480" y="220" fill="#B0B0B0" font-size="12">Retrieval: RAG (semantic + temporal)</text>
23
+ <text x="480" y="240" fill="#4CAF50" font-size="12" font-weight="bold">Durable, Searchable, Persistent</text>
24
+
25
+ <!-- Data Flow: Add Memory -->
26
+ <path d="M 200 280 L 200 320 L 400 320 L 400 280" stroke="#4CAF50" stroke-width="3" fill="none" marker-end="url(#arrow-green)"/>
27
+ <text x="300" y="310" text-anchor="middle" fill="#4CAF50" font-size="12" font-weight="bold">Add Memory</text>
28
+ <text x="300" y="330" text-anchor="middle" fill="#B0B0B0" font-size="10">(Stored in both tiers)</text>
29
+
30
+ <!-- Data Flow: Eviction -->
31
+ <path d="M 350 360 L 600 360" stroke="#FF9800" stroke-width="3" marker-end="url(#arrow-orange)"/>
32
+ <text x="475" y="350" text-anchor="middle" fill="#FF9800" font-size="12" font-weight="bold">Eviction</text>
33
+ <text x="475" y="380" text-anchor="middle" fill="#B0B0B0" font-size="10">(Token limit → move to LTM only)</text>
34
+
35
+ <!-- Data Flow: Recall -->
36
+ <path d="M 600 400 L 200 400" stroke="#9C27B0" stroke-width="3" marker-end="url(#arrow-purple)"/>
37
+ <text x="400" y="390" text-anchor="middle" fill="#9C27B0" font-size="12" font-weight="bold">Recall</text>
38
+ <text x="400" y="420" text-anchor="middle" fill="#B0B0B0" font-size="10">(RAG search → load back to WM)</text>
39
+
40
+ <!-- Never Forget Note -->
41
+ <rect x="150" y="450" width="500" height="40" fill="rgba(76, 175, 80, 0.1)" stroke="#4CAF50" stroke-width="1" rx="3"/>
42
+ <text x="400" y="475" text-anchor="middle" fill="#4CAF50" font-size="13" font-weight="bold">Never Forget: Evicted memories stay in LTM forever (explicit deletion only)</text>
43
+
44
+ <defs>
45
+ <marker id="arrow-green" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
46
+ <polygon points="0 0, 10 3, 0 6" fill="#4CAF50"/>
47
+ </marker>
48
+ <marker id="arrow-orange" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
49
+ <polygon points="0 0, 10 3, 0 6" fill="#FF9800"/>
50
+ </marker>
51
+ <marker id="arrow-purple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
52
+ <polygon points="0 0, 10 3, 0 6" fill="#9C27B0"/>
53
+ </marker>
54
+ </defs>
55
+ </svg>
@@ -0,0 +1,244 @@
1
+ # Database Naming Convention
2
+
3
+ HTM enforces a strict database naming convention to prevent accidental data corruption or loss from operating on the wrong database.
4
+
5
+ ## The Convention
6
+
7
+ Database names **must** follow this exact format:
8
+
9
+ ```
10
+ {service_name}_{environment}
11
+ ```
12
+
13
+ Where:
14
+ - `service_name` is the value of `config.service.name` (default: `htm`)
15
+ - `environment` is the value of `HTM_ENV` (or `RAILS_ENV` / `RACK_ENV` fallback)
16
+
17
+ ## Valid Examples
18
+
19
+ | Service Name | Environment | Expected Database Name |
20
+ |--------------|-------------|------------------------|
21
+ | `htm` | `development` | `htm_development` |
22
+ | `htm` | `test` | `htm_test` |
23
+ | `htm` | `production` | `htm_production` |
24
+ | `payroll` | `development` | `payroll_development` |
25
+ | `payroll` | `production` | `payroll_production` |
26
+
27
+ ## Why This Matters
28
+
29
+ Without strict enforcement, dangerous misconfigurations can go undetected:
30
+
31
+ ### Scenario 1: Environment Mismatch
32
+
33
+ ```bash
34
+ # Developer thinks they're in test, but connected to production
35
+ HTM_ENV=test
36
+ HTM_DATABASE__URL="postgresql://user@host/htm_production"
37
+
38
+ rake htm:db:drop # DISASTER: Drops production database!
39
+ ```
40
+
41
+ With the naming convention enforced, this command fails immediately:
42
+
43
+ ```
44
+ Error: Database name does not follow naming convention!
45
+
46
+ Database names must be: {service_name}_{environment}
47
+
48
+ Service name: htm
49
+ Environment: test
50
+ Expected: htm_test
51
+ Actual: htm_production
52
+ ```
53
+
54
+ ### Scenario 2: Service Mismatch
55
+
56
+ ```bash
57
+ # HTM configured to use another application's database
58
+ HTM_ENV=production
59
+ # service.name = "htm" (default)
60
+ HTM_DATABASE__URL="postgresql://user@host/payroll_production"
61
+
62
+ rake htm:db:setup # DISASTER: Corrupts payroll application's data!
63
+ ```
64
+
65
+ With enforcement, this fails:
66
+
67
+ ```
68
+ Error: Database name does not follow naming convention!
69
+
70
+ Service name: htm
71
+ Environment: production
72
+ Expected: htm_production
73
+ Actual: payroll_production
74
+ ```
75
+
76
+ ## How Enforcement Works
77
+
78
+ ### Validation Points
79
+
80
+ The naming convention is validated at these points:
81
+
82
+ 1. **All rake tasks** that depend on `htm:db:validate` (setup, migrate, drop, etc.)
83
+ 2. **Programmatic access** via `HTM.config.validate_database_name!`
84
+
85
+ ### No Bypass Option
86
+
87
+ There is no way to skip this validation. If your database name doesn't match the convention, you must either:
88
+
89
+ 1. Rename your database to match the convention
90
+ 2. Change `HTM_ENV` to match the database suffix
91
+ 3. Change `config.service.name` to match the database prefix
92
+
93
+ ## Configuration
94
+
95
+ ### Setting the Service Name
96
+
97
+ The service name defaults to `htm`. To use a different name:
98
+
99
+ **Via environment variable:**
100
+ ```bash
101
+ export HTM_SERVICE__NAME="myapp"
102
+ ```
103
+
104
+ **Via configuration file (`config/htm.yml`):**
105
+ ```yaml
106
+ service:
107
+ name: myapp
108
+ ```
109
+
110
+ **Via Ruby configuration:**
111
+ ```ruby
112
+ HTM.configure do |config|
113
+ config.service.name = "myapp"
114
+ end
115
+ ```
116
+
117
+ ### Setting the Environment
118
+
119
+ Environment is determined by (in priority order):
120
+
121
+ 1. `HTM_ENV`
122
+ 2. `RAILS_ENV`
123
+ 3. `RACK_ENV`
124
+ 4. Default: `development`
125
+
126
+ **Valid environments:**
127
+ - `development`
128
+ - `test`
129
+ - `production`
130
+
131
+ These correspond to the top-level keys in `config/defaults.yml`.
132
+
133
+ ## Validation Methods
134
+
135
+ ### Check if Database Name is Valid
136
+
137
+ ```ruby
138
+ config = HTM.config
139
+
140
+ # Boolean check
141
+ if config.valid_database_name?
142
+ puts "Database name is correct"
143
+ else
144
+ puts "Expected: #{config.expected_database_name}"
145
+ puts "Actual: #{config.actual_database_name}"
146
+ end
147
+ ```
148
+
149
+ ### Raise Error on Invalid Name
150
+
151
+ ```ruby
152
+ config = HTM.config
153
+
154
+ # Raises HTM::ConfigurationError if invalid
155
+ config.validate_database_name!
156
+ ```
157
+
158
+ ### Get Expected and Actual Names
159
+
160
+ ```ruby
161
+ config = HTM.config
162
+
163
+ config.expected_database_name # => "htm_test"
164
+ config.actual_database_name # => Extracted from URL or config
165
+ ```
166
+
167
+ ## Rake Task Validation
168
+
169
+ All database-related rake tasks run validation automatically:
170
+
171
+ ```bash
172
+ # These all validate the naming convention first:
173
+ rake htm:db:setup
174
+ rake htm:db:migrate
175
+ rake htm:db:drop
176
+ rake htm:db:reset
177
+ rake htm:db:create
178
+ ```
179
+
180
+ To validate manually without performing any operation:
181
+
182
+ ```bash
183
+ rake htm:db:validate
184
+ ```
185
+
186
+ ## Migration Guide
187
+
188
+ If you have existing databases that don't follow the convention:
189
+
190
+ ### Option 1: Rename the Database
191
+
192
+ ```bash
193
+ # PostgreSQL
194
+ psql -c "ALTER DATABASE old_name RENAME TO htm_development;"
195
+ ```
196
+
197
+ ### Option 2: Export and Import
198
+
199
+ ```bash
200
+ # Export from old database
201
+ pg_dump old_database > backup.sql
202
+
203
+ # Create new database with correct name
204
+ createdb htm_development
205
+
206
+ # Import to new database
207
+ psql htm_development < backup.sql
208
+ ```
209
+
210
+ ### Option 3: Change Your Service Name
211
+
212
+ If your database is named `myapp_production`, set your service name to match:
213
+
214
+ ```bash
215
+ export HTM_SERVICE__NAME="myapp"
216
+ ```
217
+
218
+ ## Error Messages
219
+
220
+ When validation fails, you'll see a clear error message:
221
+
222
+ ```
223
+ Error: Database name 'wrong_db' does not match expected 'htm_test'.
224
+ Database names must follow the convention: {service_name}_{environment}
225
+ Service name: htm
226
+ Environment: test
227
+ Expected: htm_test
228
+ Actual: wrong_db
229
+
230
+ Either:
231
+ - Set HTM_DATABASE__URL to point to 'htm_test'
232
+ - Set HTM_DATABASE__NAME=htm_test
233
+ - Change HTM_ENV to match the database suffix
234
+ ```
235
+
236
+ ## Summary
237
+
238
+ The strict database naming convention:
239
+
240
+ - **Prevents** accidental operations on wrong environments
241
+ - **Prevents** cross-application database corruption
242
+ - **Requires** exact match of `{service_name}_{environment}`
243
+ - **Has no bypass** - you must fix the configuration
244
+ - **Validates automatically** on all database operations
@@ -219,6 +219,37 @@ $ rake htm:db:reset
219
219
  # Runs drop (with confirmation) then setup
220
220
  ```
221
221
 
222
+ #### `rake htm:db:purge_all`
223
+ Permanently removes all soft-deleted records from all tables.
224
+
225
+ **What it does:**
226
+ - Removes soft-deleted nodes, node_tags, and robot_nodes
227
+ - Removes orphaned join table entries (pointing to non-existent nodes)
228
+ - Removes orphaned propositions (where source_node_id no longer exists)
229
+ - Removes orphaned robots (with no associated memory nodes)
230
+ - Deletes in correct order for referential integrity
231
+
232
+ **Safety:** Prompts for confirmation before deletion
233
+
234
+ ```bash
235
+ $ rake htm:db:purge_all
236
+
237
+ HTM Purge All Soft-Deleted Records
238
+ ============================================================
239
+
240
+ Records to permanently delete:
241
+ --------------------------------------------------------------
242
+ Soft-deleted nodes: 23
243
+ Soft-deleted node_tags: 45
244
+ Orphaned propositions: 5
245
+ Orphaned robots (no nodes): 2
246
+ --------------------------------------------------------------
247
+ Total records to delete: 75
248
+
249
+ Proceed with permanent deletion? (yes/no): yes
250
+ ✓ Purge complete!
251
+ ```
252
+
222
253
  ---
223
254
 
224
255
  ## Environment Variables