lex-agentic-memory 0.1.0

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 (267) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-memory.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/memory/archaeology/client.rb +19 -0
  8. data/lib/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine.rb +184 -0
  9. data/lib/legion/extensions/agentic/memory/archaeology/helpers/artifact.rb +146 -0
  10. data/lib/legion/extensions/agentic/memory/archaeology/helpers/constants.rb +95 -0
  11. data/lib/legion/extensions/agentic/memory/archaeology/helpers/excavation_site.rb +97 -0
  12. data/lib/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology.rb +86 -0
  13. data/lib/legion/extensions/agentic/memory/archaeology/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/memory/archaeology.rb +22 -0
  15. data/lib/legion/extensions/agentic/memory/compression/client.rb +15 -0
  16. data/lib/legion/extensions/agentic/memory/compression/helpers/compression_engine.rb +145 -0
  17. data/lib/legion/extensions/agentic/memory/compression/helpers/constants.rb +44 -0
  18. data/lib/legion/extensions/agentic/memory/compression/helpers/information_chunk.rb +81 -0
  19. data/lib/legion/extensions/agentic/memory/compression/runners/cognitive_compression.rb +76 -0
  20. data/lib/legion/extensions/agentic/memory/compression/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/memory/compression.rb +19 -0
  22. data/lib/legion/extensions/agentic/memory/echo/client.rb +19 -0
  23. data/lib/legion/extensions/agentic/memory/echo/helpers/constants.rb +57 -0
  24. data/lib/legion/extensions/agentic/memory/echo/helpers/echo.rb +95 -0
  25. data/lib/legion/extensions/agentic/memory/echo/helpers/echo_engine.rb +136 -0
  26. data/lib/legion/extensions/agentic/memory/echo/runners/cognitive_echo.rb +81 -0
  27. data/lib/legion/extensions/agentic/memory/echo/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/memory/echo.rb +19 -0
  29. data/lib/legion/extensions/agentic/memory/echo_chamber/client.rb +19 -0
  30. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber.rb +134 -0
  31. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine.rb +156 -0
  32. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/constants.rb +60 -0
  33. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/echo.rb +96 -0
  34. data/lib/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber.rb +84 -0
  35. data/lib/legion/extensions/agentic/memory/echo_chamber/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/memory/echo_chamber.rb +21 -0
  37. data/lib/legion/extensions/agentic/memory/episodic/actors/decay.rb +45 -0
  38. data/lib/legion/extensions/agentic/memory/episodic/client.rb +25 -0
  39. data/lib/legion/extensions/agentic/memory/episodic/helpers/constants.rb +36 -0
  40. data/lib/legion/extensions/agentic/memory/episodic/helpers/episode.rb +104 -0
  41. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_binding.rb +57 -0
  42. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_store.rb +141 -0
  43. data/lib/legion/extensions/agentic/memory/episodic/runners/episodic_buffer.rb +108 -0
  44. data/lib/legion/extensions/agentic/memory/episodic/version.rb +13 -0
  45. data/lib/legion/extensions/agentic/memory/episodic.rb +20 -0
  46. data/lib/legion/extensions/agentic/memory/hologram/client.rb +23 -0
  47. data/lib/legion/extensions/agentic/memory/hologram/helpers/constants.rb +67 -0
  48. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram.rb +118 -0
  49. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram_engine.rb +117 -0
  50. data/lib/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment.rb +70 -0
  51. data/lib/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram.rb +110 -0
  52. data/lib/legion/extensions/agentic/memory/hologram/version.rb +13 -0
  53. data/lib/legion/extensions/agentic/memory/hologram.rb +22 -0
  54. data/lib/legion/extensions/agentic/memory/immune_memory/client.rb +19 -0
  55. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/constants.rb +84 -0
  56. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/encounter.rb +54 -0
  57. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine.rb +171 -0
  58. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell.rb +97 -0
  59. data/lib/legion/extensions/agentic/memory/immune_memory/runners/cognitive_immune_memory.rb +76 -0
  60. data/lib/legion/extensions/agentic/memory/immune_memory/version.rb +13 -0
  61. data/lib/legion/extensions/agentic/memory/immune_memory.rb +20 -0
  62. data/lib/legion/extensions/agentic/memory/nostalgia/client.rb +31 -0
  63. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/constants.rb +68 -0
  64. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine.rb +148 -0
  65. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event.rb +44 -0
  66. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory.rb +97 -0
  67. data/lib/legion/extensions/agentic/memory/nostalgia/runners/analysis.rb +61 -0
  68. data/lib/legion/extensions/agentic/memory/nostalgia/runners/recall.rb +60 -0
  69. data/lib/legion/extensions/agentic/memory/nostalgia/version.rb +13 -0
  70. data/lib/legion/extensions/agentic/memory/nostalgia.rb +21 -0
  71. data/lib/legion/extensions/agentic/memory/offloading/client.rb +29 -0
  72. data/lib/legion/extensions/agentic/memory/offloading/helpers/constants.rb +55 -0
  73. data/lib/legion/extensions/agentic/memory/offloading/helpers/external_store.rb +82 -0
  74. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloaded_item.rb +65 -0
  75. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloading_engine.rb +138 -0
  76. data/lib/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading.rb +102 -0
  77. data/lib/legion/extensions/agentic/memory/offloading/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/memory/offloading.rb +20 -0
  79. data/lib/legion/extensions/agentic/memory/paleontology/client.rb +19 -0
  80. data/lib/legion/extensions/agentic/memory/paleontology/helpers/constants.rb +69 -0
  81. data/lib/legion/extensions/agentic/memory/paleontology/helpers/excavation.rb +62 -0
  82. data/lib/legion/extensions/agentic/memory/paleontology/helpers/fossil.rb +125 -0
  83. data/lib/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine.rb +170 -0
  84. data/lib/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology.rb +93 -0
  85. data/lib/legion/extensions/agentic/memory/paleontology/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/memory/paleontology.rb +22 -0
  87. data/lib/legion/extensions/agentic/memory/palimpsest/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer.rb +81 -0
  89. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/constants.rb +56 -0
  90. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest.rb +124 -0
  91. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine.rb +117 -0
  92. data/lib/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest.rb +105 -0
  93. data/lib/legion/extensions/agentic/memory/palimpsest/version.rb +13 -0
  94. data/lib/legion/extensions/agentic/memory/palimpsest.rb +21 -0
  95. data/lib/legion/extensions/agentic/memory/reserve/client.rb +19 -0
  96. data/lib/legion/extensions/agentic/memory/reserve/helpers/constants.rb +58 -0
  97. data/lib/legion/extensions/agentic/memory/reserve/helpers/pathway.rb +112 -0
  98. data/lib/legion/extensions/agentic/memory/reserve/helpers/reserve_engine.rb +163 -0
  99. data/lib/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve.rb +92 -0
  100. data/lib/legion/extensions/agentic/memory/reserve/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/memory/reserve.rb +19 -0
  102. data/lib/legion/extensions/agentic/memory/semantic/actors/decay.rb +45 -0
  103. data/lib/legion/extensions/agentic/memory/semantic/client.rb +28 -0
  104. data/lib/legion/extensions/agentic/memory/semantic/helpers/concept.rb +101 -0
  105. data/lib/legion/extensions/agentic/memory/semantic/helpers/constants.rb +51 -0
  106. data/lib/legion/extensions/agentic/memory/semantic/helpers/knowledge_store.rb +145 -0
  107. data/lib/legion/extensions/agentic/memory/semantic/runners/semantic_memory.rb +94 -0
  108. data/lib/legion/extensions/agentic/memory/semantic/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/memory/semantic.rb +20 -0
  110. data/lib/legion/extensions/agentic/memory/semantic_priming/client.rb +19 -0
  111. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/connection.rb +77 -0
  112. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/constants.rb +68 -0
  113. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network.rb +206 -0
  114. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node.rb +81 -0
  115. data/lib/legion/extensions/agentic/memory/semantic_priming/runners/semantic_priming.rb +120 -0
  116. data/lib/legion/extensions/agentic/memory/semantic_priming/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/memory/semantic_priming.rb +20 -0
  118. data/lib/legion/extensions/agentic/memory/semantic_satiation/client.rb +28 -0
  119. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/concept.rb +82 -0
  120. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/constants.rb +37 -0
  121. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine.rb +103 -0
  122. data/lib/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation.rb +92 -0
  123. data/lib/legion/extensions/agentic/memory/semantic_satiation/version.rb +13 -0
  124. data/lib/legion/extensions/agentic/memory/semantic_satiation.rb +19 -0
  125. data/lib/legion/extensions/agentic/memory/source_monitoring/actors/decay.rb +31 -0
  126. data/lib/legion/extensions/agentic/memory/source_monitoring/client.rb +28 -0
  127. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/constants.rb +66 -0
  128. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_record.rb +94 -0
  129. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker.rb +119 -0
  130. data/lib/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring.rb +95 -0
  131. data/lib/legion/extensions/agentic/memory/source_monitoring/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/memory/source_monitoring.rb +19 -0
  133. data/lib/legion/extensions/agentic/memory/trace/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/memory/trace/actors/tier_migration.rb +45 -0
  135. data/lib/legion/extensions/agentic/memory/trace/batch_decay.rb +44 -0
  136. data/lib/legion/extensions/agentic/memory/trace/client.rb +32 -0
  137. data/lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb +167 -0
  138. data/lib/legion/extensions/agentic/memory/trace/helpers/decay.rb +68 -0
  139. data/lib/legion/extensions/agentic/memory/trace/helpers/error_tracer.rb +94 -0
  140. data/lib/legion/extensions/agentic/memory/trace/helpers/store.rb +260 -0
  141. data/lib/legion/extensions/agentic/memory/trace/helpers/trace.rb +106 -0
  142. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000001_create_memory_traces.rb +31 -0
  143. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000002_create_memory_associations.rb +13 -0
  144. data/lib/legion/extensions/agentic/memory/trace/persistent_store.rb +94 -0
  145. data/lib/legion/extensions/agentic/memory/trace/quota.rb +55 -0
  146. data/lib/legion/extensions/agentic/memory/trace/runners/consolidation.rb +121 -0
  147. data/lib/legion/extensions/agentic/memory/trace/runners/traces.rb +105 -0
  148. data/lib/legion/extensions/agentic/memory/trace/version.rb +13 -0
  149. data/lib/legion/extensions/agentic/memory/trace.rb +54 -0
  150. data/lib/legion/extensions/agentic/memory/transfer/client.rb +28 -0
  151. data/lib/legion/extensions/agentic/memory/transfer/helpers/constants.rb +34 -0
  152. data/lib/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge.rb +74 -0
  153. data/lib/legion/extensions/agentic/memory/transfer/helpers/transfer_engine.rb +188 -0
  154. data/lib/legion/extensions/agentic/memory/transfer/runners/transfer_learning.rb +77 -0
  155. data/lib/legion/extensions/agentic/memory/transfer/version.rb +13 -0
  156. data/lib/legion/extensions/agentic/memory/transfer.rb +19 -0
  157. data/lib/legion/extensions/agentic/memory/version.rb +11 -0
  158. data/lib/legion/extensions/agentic/memory.rb +35 -0
  159. data/spec/legion/extensions/agentic/memory/archaeology/client_spec.rb +38 -0
  160. data/spec/legion/extensions/agentic/memory/archaeology/cognitive_archaeology_spec.rb +7 -0
  161. data/spec/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine_spec.rb +165 -0
  162. data/spec/legion/extensions/agentic/memory/archaeology/helpers/artifact_spec.rb +196 -0
  163. data/spec/legion/extensions/agentic/memory/archaeology/helpers/constants_spec.rb +42 -0
  164. data/spec/legion/extensions/agentic/memory/archaeology/helpers/excavation_site_spec.rb +115 -0
  165. data/spec/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology_spec.rb +96 -0
  166. data/spec/legion/extensions/agentic/memory/compression/helpers/compression_engine_spec.rb +138 -0
  167. data/spec/legion/extensions/agentic/memory/compression/helpers/constants_spec.rb +50 -0
  168. data/spec/legion/extensions/agentic/memory/compression/helpers/information_chunk_spec.rb +123 -0
  169. data/spec/legion/extensions/agentic/memory/compression/runners/cognitive_compression_spec.rb +80 -0
  170. data/spec/legion/extensions/agentic/memory/echo/client_spec.rb +20 -0
  171. data/spec/legion/extensions/agentic/memory/echo/cognitive_echo_spec.rb +7 -0
  172. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_engine_spec.rb +152 -0
  173. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_spec.rb +128 -0
  174. data/spec/legion/extensions/agentic/memory/echo/runners_spec.rb +80 -0
  175. data/spec/legion/extensions/agentic/memory/echo_chamber/client_spec.rb +84 -0
  176. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine_spec.rb +266 -0
  177. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_spec.rb +249 -0
  178. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/constants_spec.rb +130 -0
  179. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/echo_spec.rb +252 -0
  180. data/spec/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber_spec.rb +185 -0
  181. data/spec/legion/extensions/agentic/memory/episodic/client_spec.rb +80 -0
  182. data/spec/legion/extensions/agentic/memory/episodic/episodic_buffer_spec.rb +15 -0
  183. data/spec/legion/extensions/agentic/memory/episodic/helpers/constants_spec.rb +86 -0
  184. data/spec/legion/extensions/agentic/memory/episodic/helpers/episode_spec.rb +188 -0
  185. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_binding_spec.rb +114 -0
  186. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_store_spec.rb +201 -0
  187. data/spec/legion/extensions/agentic/memory/episodic/runners/episodic_buffer_spec.rb +208 -0
  188. data/spec/legion/extensions/agentic/memory/hologram/client_spec.rb +89 -0
  189. data/spec/legion/extensions/agentic/memory/hologram/cognitive_hologram_spec.rb +39 -0
  190. data/spec/legion/extensions/agentic/memory/hologram/helpers/constants_spec.rb +143 -0
  191. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_engine_spec.rb +268 -0
  192. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_spec.rb +256 -0
  193. data/spec/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment_spec.rb +213 -0
  194. data/spec/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram_spec.rb +239 -0
  195. data/spec/legion/extensions/agentic/memory/immune_memory/client_spec.rb +18 -0
  196. data/spec/legion/extensions/agentic/memory/immune_memory/cognitive_immune_memory_spec.rb +7 -0
  197. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/encounter_spec.rb +85 -0
  198. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine_spec.rb +203 -0
  199. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell_spec.rb +190 -0
  200. data/spec/legion/extensions/agentic/memory/immune_memory/runners_spec.rb +83 -0
  201. data/spec/legion/extensions/agentic/memory/nostalgia/client_spec.rb +71 -0
  202. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/constants_spec.rb +65 -0
  203. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine_spec.rb +191 -0
  204. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event_spec.rb +59 -0
  205. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory_spec.rb +134 -0
  206. data/spec/legion/extensions/agentic/memory/nostalgia/runners/analysis_spec.rb +97 -0
  207. data/spec/legion/extensions/agentic/memory/nostalgia/runners/recall_spec.rb +81 -0
  208. data/spec/legion/extensions/agentic/memory/offloading/client_spec.rb +30 -0
  209. data/spec/legion/extensions/agentic/memory/offloading/helpers/constants_spec.rb +71 -0
  210. data/spec/legion/extensions/agentic/memory/offloading/helpers/external_store_spec.rb +158 -0
  211. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloaded_item_spec.rb +123 -0
  212. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloading_engine_spec.rb +250 -0
  213. data/spec/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading_spec.rb +194 -0
  214. data/spec/legion/extensions/agentic/memory/paleontology/client_spec.rb +27 -0
  215. data/spec/legion/extensions/agentic/memory/paleontology/cognitive_paleontology_spec.rb +7 -0
  216. data/spec/legion/extensions/agentic/memory/paleontology/helpers/constants_spec.rb +33 -0
  217. data/spec/legion/extensions/agentic/memory/paleontology/helpers/excavation_spec.rb +71 -0
  218. data/spec/legion/extensions/agentic/memory/paleontology/helpers/fossil_spec.rb +134 -0
  219. data/spec/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine_spec.rb +148 -0
  220. data/spec/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology_spec.rb +89 -0
  221. data/spec/legion/extensions/agentic/memory/palimpsest/client_spec.rb +58 -0
  222. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer_spec.rb +150 -0
  223. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/constants_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine_spec.rb +164 -0
  225. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_spec.rb +172 -0
  226. data/spec/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest_spec.rb +165 -0
  227. data/spec/legion/extensions/agentic/memory/reserve/client_spec.rb +37 -0
  228. data/spec/legion/extensions/agentic/memory/reserve/helpers/pathway_spec.rb +165 -0
  229. data/spec/legion/extensions/agentic/memory/reserve/helpers/reserve_engine_spec.rb +196 -0
  230. data/spec/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve_spec.rb +96 -0
  231. data/spec/legion/extensions/agentic/memory/semantic/client_spec.rb +35 -0
  232. data/spec/legion/extensions/agentic/memory/semantic/helpers/concept_spec.rb +119 -0
  233. data/spec/legion/extensions/agentic/memory/semantic/helpers/knowledge_store_spec.rb +140 -0
  234. data/spec/legion/extensions/agentic/memory/semantic/runners/semantic_memory_spec.rb +103 -0
  235. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/connection_spec.rb +98 -0
  236. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network_spec.rb +208 -0
  237. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node_spec.rb +125 -0
  238. data/spec/legion/extensions/agentic/memory/semantic_priming/semantic_priming_spec.rb +7 -0
  239. data/spec/legion/extensions/agentic/memory/semantic_satiation/client_spec.rb +19 -0
  240. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/concept_spec.rb +167 -0
  241. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/constants_spec.rb +81 -0
  242. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine_spec.rb +189 -0
  243. data/spec/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation_spec.rb +176 -0
  244. data/spec/legion/extensions/agentic/memory/source_monitoring/client_spec.rb +25 -0
  245. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_record_spec.rb +152 -0
  246. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker_spec.rb +139 -0
  247. data/spec/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring_spec.rb +102 -0
  248. data/spec/legion/extensions/agentic/memory/trace/actors/decay_spec.rb +62 -0
  249. data/spec/legion/extensions/agentic/memory/trace/actors/tier_migration_spec.rb +62 -0
  250. data/spec/legion/extensions/agentic/memory/trace/batch_decay_spec.rb +26 -0
  251. data/spec/legion/extensions/agentic/memory/trace/client_spec.rb +62 -0
  252. data/spec/legion/extensions/agentic/memory/trace/helpers/decay_spec.rb +134 -0
  253. data/spec/legion/extensions/agentic/memory/trace/helpers/store_spec.rb +217 -0
  254. data/spec/legion/extensions/agentic/memory/trace/helpers/trace_spec.rb +120 -0
  255. data/spec/legion/extensions/agentic/memory/trace/local_persistence_spec.rb +255 -0
  256. data/spec/legion/extensions/agentic/memory/trace/memory_spec.rb +11 -0
  257. data/spec/legion/extensions/agentic/memory/trace/persistent_store_spec.rb +50 -0
  258. data/spec/legion/extensions/agentic/memory/trace/quota_spec.rb +56 -0
  259. data/spec/legion/extensions/agentic/memory/trace/runners/consolidation_spec.rb +118 -0
  260. data/spec/legion/extensions/agentic/memory/trace/runners/traces_spec.rb +69 -0
  261. data/spec/legion/extensions/agentic/memory/transfer/client_spec.rb +18 -0
  262. data/spec/legion/extensions/agentic/memory/transfer/helpers/constants_spec.rb +41 -0
  263. data/spec/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge_spec.rb +134 -0
  264. data/spec/legion/extensions/agentic/memory/transfer/helpers/transfer_engine_spec.rb +299 -0
  265. data/spec/legion/extensions/agentic/memory/transfer/runners/transfer_learning_spec.rb +144 -0
  266. data/spec/spec_helper.rb +46 -0
  267. metadata +351 -0
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Paleontology
8
+ module Helpers
9
+ class PaleontologyEngine
10
+ def initialize
11
+ @fossils = {}
12
+ @excavations = {}
13
+ @extinction_log = []
14
+ end
15
+
16
+ def record_extinction(fossil_type:, domain:, content:,
17
+ extinction_cause:, stratum_depth: 0,
18
+ significance: nil, **)
19
+ validate_capacity!(:fossils, Constants::MAX_FOSSILS)
20
+ fossil = Fossil.new(
21
+ fossil_type: fossil_type,
22
+ domain: domain,
23
+ content: content,
24
+ extinction_cause: extinction_cause,
25
+ stratum_depth: stratum_depth,
26
+ significance: significance
27
+ )
28
+ @fossils[fossil.id] = fossil
29
+ @extinction_log << { fossil_id: fossil.id, at: Time.now.utc }
30
+ fossil
31
+ end
32
+
33
+ def begin_excavation(target_stratum:)
34
+ validate_capacity!(:excavations, Constants::MAX_EXCAVATIONS)
35
+ exc = Excavation.new(target_stratum: target_stratum)
36
+ @excavations[exc.id] = exc
37
+ exc
38
+ end
39
+
40
+ def excavate!(excavation_id:)
41
+ exc = fetch_excavation!(excavation_id)
42
+ raise ArgumentError, 'excavation already completed' if exc.completed?
43
+
44
+ matching = stratum_fossils(exc.target_stratum)
45
+ return nil if matching.empty?
46
+
47
+ fossil = matching.sample
48
+ exc.record_find!(fossil)
49
+ fossil
50
+ end
51
+
52
+ def complete_excavation(excavation_id:)
53
+ exc = fetch_excavation!(excavation_id)
54
+ exc.complete!
55
+ exc
56
+ end
57
+
58
+ def erode_all!(rate: Constants::FOSSILIZATION_RATE)
59
+ @fossils.each_value { |f| f.erode!(rate: rate) }
60
+ prune_imprints!
61
+ @fossils.size
62
+ end
63
+
64
+ def link_lineage(fossil_id:, ancestor_id:)
65
+ fossil = fetch_fossil!(fossil_id)
66
+ fetch_fossil!(ancestor_id)
67
+ fossil.link_lineage(ancestor_id)
68
+ fossil
69
+ end
70
+
71
+ def fossils_by_type(fossil_type)
72
+ @fossils.values.select { |f| f.fossil_type == fossil_type.to_sym }
73
+ end
74
+
75
+ def fossils_by_cause(cause)
76
+ @fossils.values.select { |f| f.extinction_cause == cause.to_sym }
77
+ end
78
+
79
+ def fossils_by_era(era)
80
+ @fossils.values.select { |f| f.era == era.to_sym }
81
+ end
82
+
83
+ def keystone_fossils(limit: 10)
84
+ @fossils.values
85
+ .select(&:keystone?)
86
+ .sort_by { |f| -f.significance }
87
+ .first(limit)
88
+ end
89
+
90
+ def extinction_timeline
91
+ @extinction_log.sort_by { |e| e[:at] }
92
+ end
93
+
94
+ def mass_extinction?(threshold: 5, window: 60)
95
+ cutoff = Time.now.utc - window
96
+ recent = @extinction_log.count { |e| e[:at] >= cutoff }
97
+ recent >= threshold
98
+ end
99
+
100
+ def all_fossils
101
+ @fossils.values
102
+ end
103
+
104
+ def all_excavations
105
+ @excavations.values
106
+ end
107
+
108
+ def paleontology_report
109
+ {
110
+ total_fossils: @fossils.size,
111
+ total_excavations: @excavations.size,
112
+ type_breakdown: count_by(:fossil_type, Constants::FOSSIL_TYPES),
113
+ cause_breakdown: count_by(:extinction_cause, Constants::EXTINCTION_CAUSES),
114
+ era_breakdown: count_by(:era, Constants::ERA_NAMES),
115
+ avg_preservation: avg_field(:preservation),
116
+ avg_significance: avg_field(:significance),
117
+ keystone_count: @fossils.values.count(&:keystone?),
118
+ imprint_count: @fossils.values.count(&:imprint?),
119
+ ancient_count: @fossils.values.count(&:ancient?),
120
+ mass_extinction: mass_extinction?
121
+ }
122
+ end
123
+
124
+ private
125
+
126
+ def fetch_fossil!(id)
127
+ @fossils.fetch(id) do
128
+ raise ArgumentError, "fossil not found: #{id.inspect}"
129
+ end
130
+ end
131
+
132
+ def fetch_excavation!(id)
133
+ @excavations.fetch(id) do
134
+ raise ArgumentError, "excavation not found: #{id.inspect}"
135
+ end
136
+ end
137
+
138
+ def validate_capacity!(type, max)
139
+ collection = type == :fossils ? @fossils : @excavations
140
+ return if collection.size < max
141
+
142
+ raise ArgumentError, "#{type} capacity reached (max #{max})"
143
+ end
144
+
145
+ def stratum_fossils(depth)
146
+ @fossils.values.select { |f| f.stratum_depth == depth }
147
+ end
148
+
149
+ def prune_imprints!
150
+ @fossils.delete_if { |_, f| f.preservation <= 0.0 }
151
+ end
152
+
153
+ def count_by(attr, values)
154
+ values.to_h do |v|
155
+ [v, @fossils.values.count { |f| f.public_send(attr) == v }]
156
+ end
157
+ end
158
+
159
+ def avg_field(field)
160
+ return 0.0 if @fossils.empty?
161
+
162
+ (@fossils.values.sum(&field) / @fossils.size).round(10)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Paleontology
8
+ module Runners
9
+ module CognitivePaleontology
10
+ extend self
11
+
12
+ def record_extinction(fossil_type:, domain:, content:,
13
+ extinction_cause:, stratum_depth: 0,
14
+ significance: nil, engine: nil, **)
15
+ eng = resolve_engine(engine)
16
+ fossil = eng.record_extinction(
17
+ fossil_type: fossil_type, domain: domain, content: content,
18
+ extinction_cause: extinction_cause,
19
+ stratum_depth: stratum_depth, significance: significance
20
+ )
21
+ { success: true, fossil: fossil.to_h }
22
+ rescue ArgumentError => e
23
+ { success: false, error: e.message }
24
+ end
25
+
26
+ def begin_excavation(target_stratum:, engine: nil, **)
27
+ eng = resolve_engine(engine)
28
+ exc = eng.begin_excavation(target_stratum: target_stratum)
29
+ { success: true, excavation: exc.to_h }
30
+ rescue ArgumentError => e
31
+ { success: false, error: e.message }
32
+ end
33
+
34
+ def excavate(excavation_id:, engine: nil, **)
35
+ eng = resolve_engine(engine)
36
+ fossil = eng.excavate!(excavation_id: excavation_id)
37
+ if fossil
38
+ { success: true, fossil: fossil.to_h }
39
+ else
40
+ { success: true, fossil: nil, message: 'no fossils at this stratum' }
41
+ end
42
+ rescue ArgumentError => e
43
+ { success: false, error: e.message }
44
+ end
45
+
46
+ def complete_excavation(excavation_id:, engine: nil, **)
47
+ eng = resolve_engine(engine)
48
+ exc = eng.complete_excavation(excavation_id: excavation_id)
49
+ { success: true, excavation: exc.to_h }
50
+ rescue ArgumentError => e
51
+ { success: false, error: e.message }
52
+ end
53
+
54
+ def list_fossils(engine: nil, fossil_type: nil,
55
+ extinction_cause: nil, **)
56
+ eng = resolve_engine(engine)
57
+ results = filter_fossils(eng.all_fossils,
58
+ fossil_type: fossil_type,
59
+ extinction_cause: extinction_cause)
60
+ { success: true, fossils: results.map(&:to_h),
61
+ count: results.size }
62
+ end
63
+
64
+ def paleontology_status(engine: nil, **)
65
+ eng = resolve_engine(engine)
66
+ { success: true, report: eng.paleontology_report }
67
+ end
68
+
69
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
70
+
71
+ private
72
+
73
+ def filter_fossils(fossils, fossil_type:, extinction_cause:)
74
+ r = fossils
75
+ r = r.select { |f| f.fossil_type == fossil_type.to_sym } if fossil_type
76
+ r = r.select { |f| f.extinction_cause == extinction_cause.to_sym } if extinction_cause
77
+ r
78
+ end
79
+
80
+ def resolve_engine(engine)
81
+ engine || default_engine
82
+ end
83
+
84
+ def default_engine
85
+ @default_engine ||= Helpers::PaleontologyEngine.new
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Paleontology
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative 'paleontology/version'
6
+ require_relative 'paleontology/helpers/constants'
7
+ require_relative 'paleontology/helpers/fossil'
8
+ require_relative 'paleontology/helpers/excavation'
9
+ require_relative 'paleontology/helpers/paleontology_engine'
10
+ require_relative 'paleontology/runners/cognitive_paleontology'
11
+ require_relative 'paleontology/client'
12
+
13
+ module Legion
14
+ module Extensions
15
+ module Agentic
16
+ module Memory
17
+ module Paleontology
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Palimpsest
8
+ class Client
9
+ include Runners::CognitivePalimpsest
10
+
11
+ def initialize(engine: nil)
12
+ @default_engine = engine || Helpers::PalimpsestEngine.new
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'time'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Agentic
9
+ module Memory
10
+ module Palimpsest
11
+ module Helpers
12
+ class BeliefLayer
13
+ include Constants
14
+
15
+ attr_reader :id, :content, :confidence, :domain, :version, :author,
16
+ :timestamp, :superseded_by
17
+
18
+ def initialize(content:, confidence: DEFAULT_CONFIDENCE, domain: :unknown,
19
+ version: 1, author: :system)
20
+ @id = ::SecureRandom.uuid
21
+ @content = content
22
+ @confidence = confidence.to_f.clamp(0.0, 1.0)
23
+ @domain = domain
24
+ @version = version
25
+ @author = author
26
+ @timestamp = ::Time.now.utc
27
+ @superseded_by = nil
28
+ end
29
+
30
+ def supersede!(next_layer_id)
31
+ @superseded_by = next_layer_id
32
+ end
33
+
34
+ def superseded?
35
+ !@superseded_by.nil?
36
+ end
37
+
38
+ def ghost?
39
+ superseded? && @confidence > GHOST_THRESHOLD
40
+ end
41
+
42
+ def dissipated?
43
+ superseded? && @confidence <= GHOST_THRESHOLD
44
+ end
45
+
46
+ def erode!(rate: EROSION_RATE)
47
+ @confidence = (@confidence - rate).clamp(0.0, 1.0).round(10)
48
+ end
49
+
50
+ def confidence_label
51
+ Constants.label_for(CONFIDENCE_LABELS, @confidence)
52
+ end
53
+
54
+ def ghost_label
55
+ return :not_ghost unless superseded?
56
+
57
+ Constants.label_for(GHOST_LABELS, @confidence)
58
+ end
59
+
60
+ def to_h
61
+ {
62
+ id: @id,
63
+ content: @content,
64
+ confidence: @confidence.round(4),
65
+ domain: @domain,
66
+ version: @version,
67
+ author: @author,
68
+ timestamp: @timestamp.iso8601,
69
+ superseded_by: @superseded_by,
70
+ superseded: superseded?,
71
+ ghost: ghost?,
72
+ label: confidence_label
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Palimpsest
8
+ module Helpers
9
+ module Constants
10
+ MAX_PALIMPSESTS = 200
11
+ MAX_LAYERS_PER_TOPIC = 20
12
+ DEFAULT_CONFIDENCE = 0.7
13
+ GHOST_THRESHOLD = 0.1
14
+ EROSION_RATE = 0.05
15
+ GHOST_DECAY = 0.02
16
+
17
+ LAYER_DOMAINS = %i[
18
+ factual procedural normative identity
19
+ relational emotional strategic unknown
20
+ ].freeze
21
+
22
+ CONFIDENCE_LABELS = {
23
+ (0.9..) => :certain,
24
+ (0.7...0.9) => :high,
25
+ (0.5...0.7) => :moderate,
26
+ (0.3...0.5) => :low,
27
+ (0.1...0.3) => :faint,
28
+ (..0.1) => :ghost
29
+ }.freeze
30
+
31
+ GHOST_LABELS = {
32
+ (0.5..) => :strong_ghost,
33
+ (0.3...0.5) => :moderate_ghost,
34
+ (0.1...0.3) => :faint_ghost,
35
+ (..0.1) => :dissipated
36
+ }.freeze
37
+
38
+ DRIFT_LABELS = {
39
+ (0.7..) => :radical,
40
+ (0.4...0.7) => :major,
41
+ (0.2...0.4) => :moderate,
42
+ (0.05...0.2) => :minor,
43
+ (..0.05) => :stable
44
+ }.freeze
45
+
46
+ def self.label_for(labels_hash, value)
47
+ labels_hash.each { |range, label| return label if range.cover?(value) }
48
+ labels_hash.values.last
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'time'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Agentic
9
+ module Memory
10
+ module Palimpsest
11
+ module Helpers
12
+ class Palimpsest
13
+ include Constants
14
+
15
+ attr_reader :id, :topic, :domain, :current_layer, :historical_layers,
16
+ :created_at, :overwrite_count
17
+
18
+ def initialize(topic:, domain: :unknown)
19
+ @id = ::SecureRandom.uuid
20
+ @topic = topic
21
+ @domain = domain
22
+ @current_layer = nil
23
+ @historical_layers = []
24
+ @created_at = ::Time.now.utc
25
+ @overwrite_count = 0
26
+ @version_counter = 0
27
+ end
28
+
29
+ def overwrite!(new_content, confidence: DEFAULT_CONFIDENCE, author: :system)
30
+ return false if at_layer_limit?
31
+
32
+ @version_counter += 1
33
+ new_layer = BeliefLayer.new(
34
+ content: new_content,
35
+ confidence: confidence,
36
+ domain: @domain,
37
+ version: @version_counter,
38
+ author: author
39
+ )
40
+
41
+ if @current_layer
42
+ @current_layer.supersede!(new_layer.id)
43
+ @historical_layers << @current_layer
44
+ end
45
+
46
+ @current_layer = new_layer
47
+ @overwrite_count += 1
48
+ new_layer
49
+ end
50
+
51
+ def peek_through(depth: 1)
52
+ return [] if @historical_layers.empty?
53
+
54
+ @historical_layers.last([depth, @historical_layers.size].min).reverse
55
+ end
56
+
57
+ def erode_current!(rate: EROSION_RATE)
58
+ return nil unless @current_layer
59
+
60
+ @current_layer.erode!(rate: rate)
61
+ @current_layer.confidence
62
+ end
63
+
64
+ def ghost_layers
65
+ @historical_layers.select(&:ghost?)
66
+ end
67
+
68
+ def all_layers
69
+ layers = @historical_layers.dup
70
+ layers << @current_layer if @current_layer
71
+ layers
72
+ end
73
+
74
+ def restoration_strength
75
+ ghosts = ghost_layers
76
+ return 0.0 if ghosts.empty?
77
+
78
+ total = ghosts.sum(&:confidence)
79
+ (total / ghosts.size).round(10)
80
+ end
81
+
82
+ def belief_drift
83
+ return 0.0 unless @current_layer && @historical_layers.any?
84
+
85
+ origin = @historical_layers.first
86
+ (@current_layer.confidence - origin.confidence).abs.round(10)
87
+ end
88
+
89
+ def drift_label
90
+ Constants.label_for(DRIFT_LABELS, belief_drift)
91
+ end
92
+
93
+ def decay_ghosts!(rate: GHOST_DECAY)
94
+ @historical_layers.each { |layer| layer.erode!(rate: rate) if layer.superseded? }
95
+ end
96
+
97
+ def to_h
98
+ {
99
+ id: @id,
100
+ topic: @topic,
101
+ domain: @domain,
102
+ layer_count: all_layers.size,
103
+ overwrite_count: @overwrite_count,
104
+ ghost_count: ghost_layers.size,
105
+ restoration_strength: restoration_strength.round(4),
106
+ belief_drift: belief_drift.round(4),
107
+ drift_label: drift_label,
108
+ current_layer: @current_layer&.to_h,
109
+ created_at: @created_at.iso8601
110
+ }
111
+ end
112
+
113
+ private
114
+
115
+ def at_layer_limit?
116
+ all_layers.size >= MAX_LAYERS_PER_TOPIC
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Palimpsest
8
+ module Helpers
9
+ class PalimpsestEngine
10
+ include Constants
11
+
12
+ attr_reader :palimpsests
13
+
14
+ def initialize
15
+ @palimpsests = {}
16
+ end
17
+
18
+ def create(topic:, domain: :unknown)
19
+ return nil if @palimpsests.size >= MAX_PALIMPSESTS
20
+ return nil if @palimpsests.key?(topic)
21
+
22
+ p = Palimpsest.new(topic: topic, domain: domain)
23
+ @palimpsests[topic] = p
24
+ p
25
+ end
26
+
27
+ def overwrite(topic:, content:, confidence: DEFAULT_CONFIDENCE, author: :system)
28
+ p = find_or_create(topic)
29
+ return nil unless p
30
+
31
+ p.overwrite!(content, confidence: confidence, author: author)
32
+ end
33
+
34
+ def peek_through(topic:, depth: 1)
35
+ p = @palimpsests[topic]
36
+ return [] unless p
37
+
38
+ p.peek_through(depth: depth).map(&:to_h)
39
+ end
40
+
41
+ def erode(topic:, rate: EROSION_RATE)
42
+ p = @palimpsests[topic]
43
+ return nil unless p
44
+
45
+ p.erode_current!(rate: rate)
46
+ end
47
+
48
+ def ghost_layers_for(topic:)
49
+ p = @palimpsests[topic]
50
+ return [] unless p
51
+
52
+ p.ghost_layers.map(&:to_h)
53
+ end
54
+
55
+ def all_ghost_layers
56
+ @palimpsests.flat_map { |_topic, p| p.ghost_layers.map(&:to_h) }
57
+ end
58
+
59
+ def domain_archaeology(domain:)
60
+ @palimpsests.each_with_object([]) do |(_topic, p), results|
61
+ next unless p.domain == domain
62
+
63
+ p.all_layers.each { |layer| results << layer.to_h.merge(topic: p.topic) }
64
+ end
65
+ end
66
+
67
+ def belief_drift(topic:)
68
+ p = @palimpsests[topic]
69
+ return nil unless p
70
+
71
+ { drift: p.belief_drift.round(4), label: p.drift_label }
72
+ end
73
+
74
+ def overwrite_frequency(topic:)
75
+ p = @palimpsests[topic]
76
+ return nil unless p
77
+
78
+ p.overwrite_count
79
+ end
80
+
81
+ def most_rewritten(limit: 10)
82
+ sorted = @palimpsests.values.sort_by { |p| -p.overwrite_count }
83
+ sorted.first(limit).map(&:to_h)
84
+ end
85
+
86
+ def decay_all!(ghost_rate: GHOST_DECAY)
87
+ @palimpsests.each_value { |p| p.decay_ghosts!(rate: ghost_rate) }
88
+ end
89
+
90
+ def palimpsest_report
91
+ total_ghosts = @palimpsests.values.sum { |p| p.ghost_layers.size }
92
+ avg_drift = if @palimpsests.empty?
93
+ 0.0
94
+ else
95
+ total = @palimpsests.values.sum(&:belief_drift)
96
+ (total / @palimpsests.size).round(4)
97
+ end
98
+ {
99
+ palimpsest_count: @palimpsests.size,
100
+ total_ghosts: total_ghosts,
101
+ average_drift: avg_drift,
102
+ most_rewritten: most_rewritten(limit: 5)
103
+ }
104
+ end
105
+
106
+ private
107
+
108
+ def find_or_create(topic)
109
+ @palimpsests[topic] || create(topic: topic)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end