lex-agentic-attention 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 (315) 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-attention.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/attention/arousal/client.rb +27 -0
  8. data/lib/legion/extensions/agentic/attention/arousal/helpers/arousal_model.rb +84 -0
  9. data/lib/legion/extensions/agentic/attention/arousal/helpers/constants.rb +44 -0
  10. data/lib/legion/extensions/agentic/attention/arousal/runners/arousal.rb +119 -0
  11. data/lib/legion/extensions/agentic/attention/arousal/version.rb +13 -0
  12. data/lib/legion/extensions/agentic/attention/arousal.rb +18 -0
  13. data/lib/legion/extensions/agentic/attention/blindspot/client.rb +19 -0
  14. data/lib/legion/extensions/agentic/attention/blindspot/helpers/blindspot.rb +98 -0
  15. data/lib/legion/extensions/agentic/attention/blindspot/helpers/blindspot_engine.rb +174 -0
  16. data/lib/legion/extensions/agentic/attention/blindspot/helpers/constants.rb +63 -0
  17. data/lib/legion/extensions/agentic/attention/blindspot/helpers/knowledge_boundary.rb +62 -0
  18. data/lib/legion/extensions/agentic/attention/blindspot/runners/cognitive_blindspot.rb +103 -0
  19. data/lib/legion/extensions/agentic/attention/blindspot/version.rb +13 -0
  20. data/lib/legion/extensions/agentic/attention/blindspot.rb +20 -0
  21. data/lib/legion/extensions/agentic/attention/blink/client.rb +19 -0
  22. data/lib/legion/extensions/agentic/attention/blink/helpers/blink_engine.rb +171 -0
  23. data/lib/legion/extensions/agentic/attention/blink/helpers/constants.rb +46 -0
  24. data/lib/legion/extensions/agentic/attention/blink/helpers/stimulus.rb +68 -0
  25. data/lib/legion/extensions/agentic/attention/blink/runners/attentional_blink.rb +75 -0
  26. data/lib/legion/extensions/agentic/attention/blink/version.rb +13 -0
  27. data/lib/legion/extensions/agentic/attention/blink.rb +19 -0
  28. data/lib/legion/extensions/agentic/attention/economy/client.rb +28 -0
  29. data/lib/legion/extensions/agentic/attention/economy/helpers/attention_budget.rb +143 -0
  30. data/lib/legion/extensions/agentic/attention/economy/helpers/constants.rb +38 -0
  31. data/lib/legion/extensions/agentic/attention/economy/helpers/demand.rb +72 -0
  32. data/lib/legion/extensions/agentic/attention/economy/runners/attention_economy.rb +90 -0
  33. data/lib/legion/extensions/agentic/attention/economy/version.rb +13 -0
  34. data/lib/legion/extensions/agentic/attention/economy.rb +19 -0
  35. data/lib/legion/extensions/agentic/attention/feature_binding/actors/decay.rb +45 -0
  36. data/lib/legion/extensions/agentic/attention/feature_binding/client.rb +29 -0
  37. data/lib/legion/extensions/agentic/attention/feature_binding/helpers/binding_field.rb +124 -0
  38. data/lib/legion/extensions/agentic/attention/feature_binding/helpers/bound_object.rb +89 -0
  39. data/lib/legion/extensions/agentic/attention/feature_binding/helpers/constants.rb +52 -0
  40. data/lib/legion/extensions/agentic/attention/feature_binding/helpers/feature.rb +52 -0
  41. data/lib/legion/extensions/agentic/attention/feature_binding/runners/feature_binding.rb +102 -0
  42. data/lib/legion/extensions/agentic/attention/feature_binding/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/attention/feature_binding.rb +20 -0
  44. data/lib/legion/extensions/agentic/attention/focus/client.rb +30 -0
  45. data/lib/legion/extensions/agentic/attention/focus/helpers/constants.rb +38 -0
  46. data/lib/legion/extensions/agentic/attention/focus/helpers/focus.rb +78 -0
  47. data/lib/legion/extensions/agentic/attention/focus/helpers/focus_manager.rb +67 -0
  48. data/lib/legion/extensions/agentic/attention/focus/helpers/habituation.rb +61 -0
  49. data/lib/legion/extensions/agentic/attention/focus/runners/attention.rb +121 -0
  50. data/lib/legion/extensions/agentic/attention/focus/version.rb +13 -0
  51. data/lib/legion/extensions/agentic/attention/focus.rb +20 -0
  52. data/lib/legion/extensions/agentic/attention/kaleidoscope/client.rb +15 -0
  53. data/lib/legion/extensions/agentic/attention/kaleidoscope/helpers/constants.rb +48 -0
  54. data/lib/legion/extensions/agentic/attention/kaleidoscope/helpers/facet.rb +90 -0
  55. data/lib/legion/extensions/agentic/attention/kaleidoscope/helpers/kaleidoscope_engine.rb +142 -0
  56. data/lib/legion/extensions/agentic/attention/kaleidoscope/helpers/pattern.rb +110 -0
  57. data/lib/legion/extensions/agentic/attention/kaleidoscope/runners/cognitive_kaleidoscope.rb +91 -0
  58. data/lib/legion/extensions/agentic/attention/kaleidoscope/version.rb +13 -0
  59. data/lib/legion/extensions/agentic/attention/kaleidoscope.rb +22 -0
  60. data/lib/legion/extensions/agentic/attention/latent_inhibition/client.rb +28 -0
  61. data/lib/legion/extensions/agentic/attention/latent_inhibition/helpers/constants.rb +33 -0
  62. data/lib/legion/extensions/agentic/attention/latent_inhibition/helpers/inhibition_engine.rb +118 -0
  63. data/lib/legion/extensions/agentic/attention/latent_inhibition/helpers/stimulus_record.rb +70 -0
  64. data/lib/legion/extensions/agentic/attention/latent_inhibition/runners/latent_inhibition.rb +78 -0
  65. data/lib/legion/extensions/agentic/attention/latent_inhibition/version.rb +13 -0
  66. data/lib/legion/extensions/agentic/attention/latent_inhibition.rb +19 -0
  67. data/lib/legion/extensions/agentic/attention/lens/client.rb +29 -0
  68. data/lib/legion/extensions/agentic/attention/lens/helpers/constants.rb +58 -0
  69. data/lib/legion/extensions/agentic/attention/lens/helpers/lens.rb +99 -0
  70. data/lib/legion/extensions/agentic/attention/lens/helpers/lens_engine.rb +101 -0
  71. data/lib/legion/extensions/agentic/attention/lens/helpers/lens_stack.rb +134 -0
  72. data/lib/legion/extensions/agentic/attention/lens/runners/cognitive_lens.rb +90 -0
  73. data/lib/legion/extensions/agentic/attention/lens/version.rb +13 -0
  74. data/lib/legion/extensions/agentic/attention/lens.rb +22 -0
  75. data/lib/legion/extensions/agentic/attention/lighthouse/client.rb +25 -0
  76. data/lib/legion/extensions/agentic/attention/lighthouse/helpers/beacon.rb +76 -0
  77. data/lib/legion/extensions/agentic/attention/lighthouse/helpers/constants.rb +49 -0
  78. data/lib/legion/extensions/agentic/attention/lighthouse/helpers/fog.rb +74 -0
  79. data/lib/legion/extensions/agentic/attention/lighthouse/helpers/lighthouse_engine.rb +127 -0
  80. data/lib/legion/extensions/agentic/attention/lighthouse/runners/cognitive_lighthouse.rb +80 -0
  81. data/lib/legion/extensions/agentic/attention/lighthouse/version.rb +13 -0
  82. data/lib/legion/extensions/agentic/attention/lighthouse.rb +22 -0
  83. data/lib/legion/extensions/agentic/attention/priming/client.rb +21 -0
  84. data/lib/legion/extensions/agentic/attention/priming/helpers/activation_network.rb +130 -0
  85. data/lib/legion/extensions/agentic/attention/priming/helpers/concept_node.rb +92 -0
  86. data/lib/legion/extensions/agentic/attention/priming/helpers/constants.rb +54 -0
  87. data/lib/legion/extensions/agentic/attention/priming/runners/priming.rb +94 -0
  88. data/lib/legion/extensions/agentic/attention/priming/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/attention/priming.rb +19 -0
  90. data/lib/legion/extensions/agentic/attention/prism/client.rb +26 -0
  91. data/lib/legion/extensions/agentic/attention/prism/helpers/beam.rb +169 -0
  92. data/lib/legion/extensions/agentic/attention/prism/helpers/constants.rb +46 -0
  93. data/lib/legion/extensions/agentic/attention/prism/helpers/prism_engine.rb +136 -0
  94. data/lib/legion/extensions/agentic/attention/prism/helpers/spectral_component.rb +72 -0
  95. data/lib/legion/extensions/agentic/attention/prism/runners/cognitive_prism.rb +79 -0
  96. data/lib/legion/extensions/agentic/attention/prism/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/attention/prism.rb +22 -0
  98. data/lib/legion/extensions/agentic/attention/regulation/client.rb +19 -0
  99. data/lib/legion/extensions/agentic/attention/regulation/helpers/attention_controller.rb +157 -0
  100. data/lib/legion/extensions/agentic/attention/regulation/helpers/attention_target.rb +64 -0
  101. data/lib/legion/extensions/agentic/attention/regulation/helpers/constants.rb +41 -0
  102. data/lib/legion/extensions/agentic/attention/regulation/runners/attention_regulation.rb +88 -0
  103. data/lib/legion/extensions/agentic/attention/regulation/version.rb +13 -0
  104. data/lib/legion/extensions/agentic/attention/regulation.rb +20 -0
  105. data/lib/legion/extensions/agentic/attention/relevance_theory/helpers/client.rb +17 -0
  106. data/lib/legion/extensions/agentic/attention/relevance_theory/helpers/cognitive_input.rb +88 -0
  107. data/lib/legion/extensions/agentic/attention/relevance_theory/helpers/constants.rb +50 -0
  108. data/lib/legion/extensions/agentic/attention/relevance_theory/helpers/relevance_engine.rb +152 -0
  109. data/lib/legion/extensions/agentic/attention/relevance_theory/runners/relevance_theory.rb +98 -0
  110. data/lib/legion/extensions/agentic/attention/relevance_theory/version.rb +13 -0
  111. data/lib/legion/extensions/agentic/attention/relevance_theory.rb +19 -0
  112. data/lib/legion/extensions/agentic/attention/salience/client.rb +21 -0
  113. data/lib/legion/extensions/agentic/attention/salience/helpers/constants.rb +44 -0
  114. data/lib/legion/extensions/agentic/attention/salience/helpers/salience_map.rb +122 -0
  115. data/lib/legion/extensions/agentic/attention/salience/helpers/signal_integrator.rb +97 -0
  116. data/lib/legion/extensions/agentic/attention/salience/runners/salience.rb +70 -0
  117. data/lib/legion/extensions/agentic/attention/salience/version.rb +13 -0
  118. data/lib/legion/extensions/agentic/attention/salience.rb +19 -0
  119. data/lib/legion/extensions/agentic/attention/schema/actors/decay.rb +45 -0
  120. data/lib/legion/extensions/agentic/attention/schema/client.rb +28 -0
  121. data/lib/legion/extensions/agentic/attention/schema/helpers/attention_schema_model.rb +226 -0
  122. data/lib/legion/extensions/agentic/attention/schema/helpers/constants.rb +66 -0
  123. data/lib/legion/extensions/agentic/attention/schema/helpers/schema_item.rb +68 -0
  124. data/lib/legion/extensions/agentic/attention/schema/runners/attention_schema.rb +117 -0
  125. data/lib/legion/extensions/agentic/attention/schema/version.rb +13 -0
  126. data/lib/legion/extensions/agentic/attention/schema.rb +19 -0
  127. data/lib/legion/extensions/agentic/attention/sensory_gating/client.rb +15 -0
  128. data/lib/legion/extensions/agentic/attention/sensory_gating/helpers/constants.rb +44 -0
  129. data/lib/legion/extensions/agentic/attention/sensory_gating/helpers/gating_engine.rb +109 -0
  130. data/lib/legion/extensions/agentic/attention/sensory_gating/helpers/sensory_filter.rb +100 -0
  131. data/lib/legion/extensions/agentic/attention/sensory_gating/runners/sensory_gating.rb +73 -0
  132. data/lib/legion/extensions/agentic/attention/sensory_gating/version.rb +13 -0
  133. data/lib/legion/extensions/agentic/attention/sensory_gating.rb +19 -0
  134. data/lib/legion/extensions/agentic/attention/signal_detection/client.rb +28 -0
  135. data/lib/legion/extensions/agentic/attention/signal_detection/helpers/constants.rb +66 -0
  136. data/lib/legion/extensions/agentic/attention/signal_detection/helpers/detection_engine.rb +129 -0
  137. data/lib/legion/extensions/agentic/attention/signal_detection/helpers/detector.rb +136 -0
  138. data/lib/legion/extensions/agentic/attention/signal_detection/runners/signal_detection.rb +113 -0
  139. data/lib/legion/extensions/agentic/attention/signal_detection/version.rb +13 -0
  140. data/lib/legion/extensions/agentic/attention/signal_detection.rb +19 -0
  141. data/lib/legion/extensions/agentic/attention/spotlight/client.rb +29 -0
  142. data/lib/legion/extensions/agentic/attention/spotlight/helpers/attention_target.rb +61 -0
  143. data/lib/legion/extensions/agentic/attention/spotlight/helpers/constants.rb +44 -0
  144. data/lib/legion/extensions/agentic/attention/spotlight/helpers/spotlight.rb +78 -0
  145. data/lib/legion/extensions/agentic/attention/spotlight/helpers/spotlight_engine.rb +163 -0
  146. data/lib/legion/extensions/agentic/attention/spotlight/runners/attention_spotlight.rb +122 -0
  147. data/lib/legion/extensions/agentic/attention/spotlight/version.rb +13 -0
  148. data/lib/legion/extensions/agentic/attention/spotlight.rb +20 -0
  149. data/lib/legion/extensions/agentic/attention/subliminal/client.rb +19 -0
  150. data/lib/legion/extensions/agentic/attention/subliminal/helpers/constants.rb +74 -0
  151. data/lib/legion/extensions/agentic/attention/subliminal/helpers/influence_event.rb +44 -0
  152. data/lib/legion/extensions/agentic/attention/subliminal/helpers/subliminal_engine.rb +149 -0
  153. data/lib/legion/extensions/agentic/attention/subliminal/helpers/subliminal_trace.rb +101 -0
  154. data/lib/legion/extensions/agentic/attention/subliminal/runners/subliminal.rb +71 -0
  155. data/lib/legion/extensions/agentic/attention/subliminal/version.rb +13 -0
  156. data/lib/legion/extensions/agentic/attention/subliminal.rb +20 -0
  157. data/lib/legion/extensions/agentic/attention/surprise/client.rb +22 -0
  158. data/lib/legion/extensions/agentic/attention/surprise/helpers/constants.rb +35 -0
  159. data/lib/legion/extensions/agentic/attention/surprise/helpers/habituation_model.rb +62 -0
  160. data/lib/legion/extensions/agentic/attention/surprise/helpers/surprise_event.rb +43 -0
  161. data/lib/legion/extensions/agentic/attention/surprise/helpers/surprise_store.rb +74 -0
  162. data/lib/legion/extensions/agentic/attention/surprise/runners/surprise.rb +174 -0
  163. data/lib/legion/extensions/agentic/attention/surprise/version.rb +13 -0
  164. data/lib/legion/extensions/agentic/attention/surprise.rb +20 -0
  165. data/lib/legion/extensions/agentic/attention/switching/client.rb +19 -0
  166. data/lib/legion/extensions/agentic/attention/switching/helpers/constants.rb +64 -0
  167. data/lib/legion/extensions/agentic/attention/switching/helpers/switch_event.rb +60 -0
  168. data/lib/legion/extensions/agentic/attention/switching/helpers/switching_engine.rb +172 -0
  169. data/lib/legion/extensions/agentic/attention/switching/helpers/task_set.rb +91 -0
  170. data/lib/legion/extensions/agentic/attention/switching/runners/attention_switching.rb +92 -0
  171. data/lib/legion/extensions/agentic/attention/switching/version.rb +13 -0
  172. data/lib/legion/extensions/agentic/attention/switching.rb +20 -0
  173. data/lib/legion/extensions/agentic/attention/synesthesia/client.rb +29 -0
  174. data/lib/legion/extensions/agentic/attention/synesthesia/helpers/constants.rb +52 -0
  175. data/lib/legion/extensions/agentic/attention/synesthesia/helpers/sensory_mapping.rb +70 -0
  176. data/lib/legion/extensions/agentic/attention/synesthesia/helpers/synesthesia_engine.rb +177 -0
  177. data/lib/legion/extensions/agentic/attention/synesthesia/helpers/synesthetic_event.rb +49 -0
  178. data/lib/legion/extensions/agentic/attention/synesthesia/runners/cognitive_synesthesia.rb +83 -0
  179. data/lib/legion/extensions/agentic/attention/synesthesia/version.rb +13 -0
  180. data/lib/legion/extensions/agentic/attention/synesthesia.rb +20 -0
  181. data/lib/legion/extensions/agentic/attention/telescope/client.rb +15 -0
  182. data/lib/legion/extensions/agentic/attention/telescope/helpers/constants.rb +60 -0
  183. data/lib/legion/extensions/agentic/attention/telescope/helpers/observation.rb +68 -0
  184. data/lib/legion/extensions/agentic/attention/telescope/helpers/observatory_engine.rb +126 -0
  185. data/lib/legion/extensions/agentic/attention/telescope/helpers/telescope.rb +129 -0
  186. data/lib/legion/extensions/agentic/attention/telescope/runners/cognitive_telescope.rb +103 -0
  187. data/lib/legion/extensions/agentic/attention/telescope/version.rb +13 -0
  188. data/lib/legion/extensions/agentic/attention/telescope.rb +22 -0
  189. data/lib/legion/extensions/agentic/attention/version.rb +11 -0
  190. data/lib/legion/extensions/agentic/attention.rb +41 -0
  191. data/spec/legion/extensions/agentic/attention/arousal/client_spec.rb +42 -0
  192. data/spec/legion/extensions/agentic/attention/arousal/helpers/arousal_model_spec.rb +160 -0
  193. data/spec/legion/extensions/agentic/attention/arousal/helpers/constants_spec.rb +61 -0
  194. data/spec/legion/extensions/agentic/attention/arousal/runners/arousal_spec.rb +137 -0
  195. data/spec/legion/extensions/agentic/attention/blindspot/client_spec.rb +41 -0
  196. data/spec/legion/extensions/agentic/attention/blindspot/helpers/blindspot_engine_spec.rb +246 -0
  197. data/spec/legion/extensions/agentic/attention/blindspot/helpers/blindspot_spec.rb +204 -0
  198. data/spec/legion/extensions/agentic/attention/blindspot/helpers/constants_spec.rb +82 -0
  199. data/spec/legion/extensions/agentic/attention/blindspot/helpers/knowledge_boundary_spec.rb +104 -0
  200. data/spec/legion/extensions/agentic/attention/blindspot/runners/cognitive_blindspot_spec.rb +179 -0
  201. data/spec/legion/extensions/agentic/attention/blink/client_spec.rb +18 -0
  202. data/spec/legion/extensions/agentic/attention/blink/helpers/blink_engine_spec.rb +147 -0
  203. data/spec/legion/extensions/agentic/attention/blink/helpers/constants_spec.rb +27 -0
  204. data/spec/legion/extensions/agentic/attention/blink/helpers/stimulus_spec.rb +77 -0
  205. data/spec/legion/extensions/agentic/attention/blink/runners/attentional_blink_spec.rb +77 -0
  206. data/spec/legion/extensions/agentic/attention/economy/client_spec.rb +30 -0
  207. data/spec/legion/extensions/agentic/attention/economy/helpers/attention_budget_spec.rb +222 -0
  208. data/spec/legion/extensions/agentic/attention/economy/helpers/constants_spec.rb +112 -0
  209. data/spec/legion/extensions/agentic/attention/economy/helpers/demand_spec.rb +128 -0
  210. data/spec/legion/extensions/agentic/attention/economy/runners/attention_economy_spec.rb +163 -0
  211. data/spec/legion/extensions/agentic/attention/feature_binding/client_spec.rb +20 -0
  212. data/spec/legion/extensions/agentic/attention/feature_binding/helpers/binding_field_spec.rb +207 -0
  213. data/spec/legion/extensions/agentic/attention/feature_binding/helpers/bound_object_spec.rb +149 -0
  214. data/spec/legion/extensions/agentic/attention/feature_binding/helpers/feature_spec.rb +86 -0
  215. data/spec/legion/extensions/agentic/attention/feature_binding/runners/feature_binding_spec.rb +106 -0
  216. data/spec/legion/extensions/agentic/attention/focus/client_spec.rb +19 -0
  217. data/spec/legion/extensions/agentic/attention/focus/helpers/focus_manager_spec.rb +105 -0
  218. data/spec/legion/extensions/agentic/attention/focus/helpers/focus_spec.rb +148 -0
  219. data/spec/legion/extensions/agentic/attention/focus/helpers/habituation_spec.rb +97 -0
  220. data/spec/legion/extensions/agentic/attention/focus/runners/attention_spec.rb +134 -0
  221. data/spec/legion/extensions/agentic/attention/kaleidoscope/client_spec.rb +38 -0
  222. data/spec/legion/extensions/agentic/attention/kaleidoscope/helpers/constants_spec.rb +79 -0
  223. data/spec/legion/extensions/agentic/attention/kaleidoscope/helpers/facet_spec.rb +155 -0
  224. data/spec/legion/extensions/agentic/attention/kaleidoscope/helpers/kaleidoscope_engine_spec.rb +164 -0
  225. data/spec/legion/extensions/agentic/attention/kaleidoscope/helpers/pattern_spec.rb +157 -0
  226. data/spec/legion/extensions/agentic/attention/kaleidoscope/runners/cognitive_kaleidoscope_spec.rb +112 -0
  227. data/spec/legion/extensions/agentic/attention/latent_inhibition/client_spec.rb +49 -0
  228. data/spec/legion/extensions/agentic/attention/latent_inhibition/helpers/constants_spec.rb +57 -0
  229. data/spec/legion/extensions/agentic/attention/latent_inhibition/helpers/inhibition_engine_spec.rb +207 -0
  230. data/spec/legion/extensions/agentic/attention/latent_inhibition/helpers/stimulus_record_spec.rb +175 -0
  231. data/spec/legion/extensions/agentic/attention/latent_inhibition/runners/latent_inhibition_spec.rb +148 -0
  232. data/spec/legion/extensions/agentic/attention/lens/client_spec.rb +64 -0
  233. data/spec/legion/extensions/agentic/attention/lens/helpers/constants_spec.rb +91 -0
  234. data/spec/legion/extensions/agentic/attention/lens/helpers/lens_engine_spec.rb +158 -0
  235. data/spec/legion/extensions/agentic/attention/lens/helpers/lens_spec.rb +222 -0
  236. data/spec/legion/extensions/agentic/attention/lens/helpers/lens_stack_spec.rb +179 -0
  237. data/spec/legion/extensions/agentic/attention/lens/runners/cognitive_lens_spec.rb +182 -0
  238. data/spec/legion/extensions/agentic/attention/lighthouse/client_spec.rb +103 -0
  239. data/spec/legion/extensions/agentic/attention/lighthouse/helpers/beacon_spec.rb +170 -0
  240. data/spec/legion/extensions/agentic/attention/lighthouse/helpers/constants_spec.rb +94 -0
  241. data/spec/legion/extensions/agentic/attention/lighthouse/helpers/fog_spec.rb +166 -0
  242. data/spec/legion/extensions/agentic/attention/lighthouse/helpers/lighthouse_engine_spec.rb +238 -0
  243. data/spec/legion/extensions/agentic/attention/lighthouse/runners/cognitive_lighthouse_spec.rb +182 -0
  244. data/spec/legion/extensions/agentic/attention/priming/client_spec.rb +80 -0
  245. data/spec/legion/extensions/agentic/attention/priming/helpers/activation_network_spec.rb +176 -0
  246. data/spec/legion/extensions/agentic/attention/priming/helpers/concept_node_spec.rb +165 -0
  247. data/spec/legion/extensions/agentic/attention/priming/helpers/constants_spec.rb +51 -0
  248. data/spec/legion/extensions/agentic/attention/priming/runners/priming_spec.rb +141 -0
  249. data/spec/legion/extensions/agentic/attention/prism/client_spec.rb +86 -0
  250. data/spec/legion/extensions/agentic/attention/prism/helpers/beam_spec.rb +182 -0
  251. data/spec/legion/extensions/agentic/attention/prism/helpers/constants_spec.rb +93 -0
  252. data/spec/legion/extensions/agentic/attention/prism/helpers/prism_engine_spec.rb +253 -0
  253. data/spec/legion/extensions/agentic/attention/prism/helpers/spectral_component_spec.rb +171 -0
  254. data/spec/legion/extensions/agentic/attention/prism/runners/cognitive_prism_spec.rb +132 -0
  255. data/spec/legion/extensions/agentic/attention/regulation/client_spec.rb +58 -0
  256. data/spec/legion/extensions/agentic/attention/regulation/helpers/attention_controller_spec.rb +295 -0
  257. data/spec/legion/extensions/agentic/attention/regulation/helpers/attention_target_spec.rb +122 -0
  258. data/spec/legion/extensions/agentic/attention/regulation/runners/attention_regulation_spec.rb +147 -0
  259. data/spec/legion/extensions/agentic/attention/relevance_theory/helpers/cognitive_input_spec.rb +121 -0
  260. data/spec/legion/extensions/agentic/attention/relevance_theory/helpers/relevance_engine_spec.rb +149 -0
  261. data/spec/legion/extensions/agentic/attention/relevance_theory/relevance_theory_spec.rb +11 -0
  262. data/spec/legion/extensions/agentic/attention/relevance_theory/runners/relevance_theory_spec.rb +86 -0
  263. data/spec/legion/extensions/agentic/attention/salience/client_spec.rb +60 -0
  264. data/spec/legion/extensions/agentic/attention/salience/helpers/constants_spec.rb +100 -0
  265. data/spec/legion/extensions/agentic/attention/salience/helpers/salience_map_spec.rb +189 -0
  266. data/spec/legion/extensions/agentic/attention/salience/helpers/signal_integrator_spec.rb +149 -0
  267. data/spec/legion/extensions/agentic/attention/salience/runners/salience_spec.rb +143 -0
  268. data/spec/legion/extensions/agentic/attention/schema/client_spec.rb +47 -0
  269. data/spec/legion/extensions/agentic/attention/schema/helpers/attention_schema_model_spec.rb +219 -0
  270. data/spec/legion/extensions/agentic/attention/schema/helpers/schema_item_spec.rb +114 -0
  271. data/spec/legion/extensions/agentic/attention/schema/runners/attention_schema_spec.rb +185 -0
  272. data/spec/legion/extensions/agentic/attention/sensory_gating/helpers/gating_engine_spec.rb +112 -0
  273. data/spec/legion/extensions/agentic/attention/sensory_gating/helpers/sensory_filter_spec.rb +121 -0
  274. data/spec/legion/extensions/agentic/attention/sensory_gating/runners/sensory_gating_spec.rb +61 -0
  275. data/spec/legion/extensions/agentic/attention/signal_detection/client_spec.rb +20 -0
  276. data/spec/legion/extensions/agentic/attention/signal_detection/helpers/constants_spec.rb +85 -0
  277. data/spec/legion/extensions/agentic/attention/signal_detection/helpers/detection_engine_spec.rb +143 -0
  278. data/spec/legion/extensions/agentic/attention/signal_detection/helpers/detector_spec.rb +179 -0
  279. data/spec/legion/extensions/agentic/attention/signal_detection/runners/signal_detection_spec.rb +151 -0
  280. data/spec/legion/extensions/agentic/attention/spotlight/client_spec.rb +31 -0
  281. data/spec/legion/extensions/agentic/attention/spotlight/helpers/attention_target_spec.rb +121 -0
  282. data/spec/legion/extensions/agentic/attention/spotlight/helpers/constants_spec.rb +82 -0
  283. data/spec/legion/extensions/agentic/attention/spotlight/helpers/spotlight_engine_spec.rb +275 -0
  284. data/spec/legion/extensions/agentic/attention/spotlight/helpers/spotlight_spec.rb +196 -0
  285. data/spec/legion/extensions/agentic/attention/spotlight/runners/attention_spotlight_spec.rb +251 -0
  286. data/spec/legion/extensions/agentic/attention/subliminal/client_spec.rb +21 -0
  287. data/spec/legion/extensions/agentic/attention/subliminal/helpers/influence_event_spec.rb +58 -0
  288. data/spec/legion/extensions/agentic/attention/subliminal/helpers/subliminal_engine_spec.rb +161 -0
  289. data/spec/legion/extensions/agentic/attention/subliminal/helpers/subliminal_trace_spec.rb +168 -0
  290. data/spec/legion/extensions/agentic/attention/subliminal/runners_spec.rb +78 -0
  291. data/spec/legion/extensions/agentic/attention/subliminal/subliminal_spec.rb +7 -0
  292. data/spec/legion/extensions/agentic/attention/surprise/client_spec.rb +122 -0
  293. data/spec/legion/extensions/agentic/attention/surprise/helpers/constants_spec.rb +112 -0
  294. data/spec/legion/extensions/agentic/attention/surprise/helpers/habituation_model_spec.rb +127 -0
  295. data/spec/legion/extensions/agentic/attention/surprise/helpers/surprise_event_spec.rb +102 -0
  296. data/spec/legion/extensions/agentic/attention/surprise/helpers/surprise_store_spec.rb +171 -0
  297. data/spec/legion/extensions/agentic/attention/surprise/runners/surprise_spec.rb +266 -0
  298. data/spec/legion/extensions/agentic/attention/switching/attention_switching_spec.rb +7 -0
  299. data/spec/legion/extensions/agentic/attention/switching/helpers/switch_event_spec.rb +74 -0
  300. data/spec/legion/extensions/agentic/attention/switching/helpers/switching_engine_spec.rb +164 -0
  301. data/spec/legion/extensions/agentic/attention/switching/helpers/task_set_spec.rb +134 -0
  302. data/spec/legion/extensions/agentic/attention/synesthesia/client_spec.rb +57 -0
  303. data/spec/legion/extensions/agentic/attention/synesthesia/helpers/constants_spec.rb +89 -0
  304. data/spec/legion/extensions/agentic/attention/synesthesia/helpers/sensory_mapping_spec.rb +135 -0
  305. data/spec/legion/extensions/agentic/attention/synesthesia/helpers/synesthesia_engine_spec.rb +260 -0
  306. data/spec/legion/extensions/agentic/attention/synesthesia/helpers/synesthetic_event_spec.rb +83 -0
  307. data/spec/legion/extensions/agentic/attention/synesthesia/runners/cognitive_synesthesia_spec.rb +192 -0
  308. data/spec/legion/extensions/agentic/attention/telescope/client_spec.rb +30 -0
  309. data/spec/legion/extensions/agentic/attention/telescope/helpers/constants_spec.rb +108 -0
  310. data/spec/legion/extensions/agentic/attention/telescope/helpers/observation_spec.rb +138 -0
  311. data/spec/legion/extensions/agentic/attention/telescope/helpers/observatory_engine_spec.rb +197 -0
  312. data/spec/legion/extensions/agentic/attention/telescope/helpers/telescope_spec.rb +251 -0
  313. data/spec/legion/extensions/agentic/attention/telescope/runners/cognitive_telescope_spec.rb +191 -0
  314. data/spec/spec_helper.rb +46 -0
  315. metadata +399 -0
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Attention::Telescope::Helpers::Observation do
4
+ let(:telescope_id) { SecureRandom.uuid }
5
+
6
+ subject(:observation) do
7
+ described_class.new(
8
+ telescope_id: telescope_id,
9
+ target: 'Alpha Centauri',
10
+ distance: 0.5,
11
+ telescope_magnification: 50.0
12
+ )
13
+ end
14
+
15
+ describe '#initialize' do
16
+ it 'generates a uuid id' do
17
+ expect(observation.id).to match(/\A[0-9a-f-]{36}\z/)
18
+ end
19
+
20
+ it 'stores the telescope_id' do
21
+ expect(observation.telescope_id).to eq(telescope_id)
22
+ end
23
+
24
+ it 'stores the target as string' do
25
+ expect(observation.target).to eq('Alpha Centauri')
26
+ end
27
+
28
+ it 'stores the clamped distance' do
29
+ expect(observation.distance).to eq(0.5)
30
+ end
31
+
32
+ it 'clamps distance above 1.0' do
33
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
34
+ distance: 5.0, telescope_magnification: 10.0)
35
+ expect(obs.distance).to eq(1.0)
36
+ end
37
+
38
+ it 'clamps distance below 0.0' do
39
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
40
+ distance: -1.0, telescope_magnification: 10.0)
41
+ expect(obs.distance).to eq(0.0)
42
+ end
43
+
44
+ it 'sets recorded_at' do
45
+ expect(observation.recorded_at).to be_a(Time)
46
+ end
47
+
48
+ it 'computes a detail_level between 0 and 1' do
49
+ expect(observation.detail_level).to be_between(0.0, 1.0)
50
+ end
51
+ end
52
+
53
+ describe '#detail_level' do
54
+ it 'is higher with greater magnification' do
55
+ low_mag = described_class.new(telescope_id: telescope_id, target: 'x',
56
+ distance: 0.2, telescope_magnification: 10.0)
57
+ high_mag = described_class.new(telescope_id: telescope_id, target: 'x',
58
+ distance: 0.2, telescope_magnification: 90.0)
59
+ expect(high_mag.detail_level).to be > low_mag.detail_level
60
+ end
61
+
62
+ it 'is higher for closer targets' do
63
+ far = described_class.new(telescope_id: telescope_id, target: 'x',
64
+ distance: 0.9, telescope_magnification: 50.0)
65
+ near = described_class.new(telescope_id: telescope_id, target: 'x',
66
+ distance: 0.1, telescope_magnification: 50.0)
67
+ expect(near.detail_level).to be > far.detail_level
68
+ end
69
+
70
+ it 'is zero when at max distance with min magnification' do
71
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
72
+ distance: 1.0, telescope_magnification: 1.0)
73
+ expect(obs.detail_level).to eq(0.0)
74
+ end
75
+ end
76
+
77
+ describe '#significant?' do
78
+ it 'returns true when detail_level >= 0.7' do
79
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
80
+ distance: 0.1, telescope_magnification: 100.0)
81
+ expect(obs.significant?).to be true
82
+ end
83
+
84
+ it 'returns false when detail_level < 0.7' do
85
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
86
+ distance: 0.9, telescope_magnification: 1.0)
87
+ expect(obs.significant?).to be false
88
+ end
89
+ end
90
+
91
+ describe '#faint?' do
92
+ it 'returns true when detail_level < 0.3' do
93
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
94
+ distance: 0.9, telescope_magnification: 1.0)
95
+ expect(obs.faint?).to be true
96
+ end
97
+
98
+ it 'returns false for a well-resolved observation' do
99
+ # high magnification + very close distance = high detail (not faint)
100
+ obs = described_class.new(telescope_id: telescope_id, target: 'nearby',
101
+ distance: 0.05, telescope_magnification: 100.0)
102
+ expect(obs.faint?).to be false
103
+ end
104
+ end
105
+
106
+ describe '#distance_label' do
107
+ it 'returns a symbol' do
108
+ expect(observation.distance_label).to be_a(Symbol)
109
+ end
110
+
111
+ it 'returns :cosmic for very distant targets' do
112
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
113
+ distance: 0.95, telescope_magnification: 10.0)
114
+ expect(obs.distance_label).to eq(:cosmic)
115
+ end
116
+
117
+ it 'returns :myopic for very close targets' do
118
+ obs = described_class.new(telescope_id: telescope_id, target: 'x',
119
+ distance: 0.05, telescope_magnification: 10.0)
120
+ expect(obs.distance_label).to eq(:myopic)
121
+ end
122
+ end
123
+
124
+ describe '#to_h' do
125
+ it 'contains all expected keys' do
126
+ h = observation.to_h
127
+ %i[id telescope_id target distance distance_label
128
+ detail_level significant faint recorded_at].each do |key|
129
+ expect(h).to have_key(key)
130
+ end
131
+ end
132
+
133
+ it 'serializes significant correctly' do
134
+ h = observation.to_h
135
+ expect(h[:significant]).to eq(observation.significant?)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Attention::Telescope::Helpers::ObservatoryEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:telescope) { engine.create_telescope(lens_type: :refractor) }
7
+
8
+ describe '#create_telescope' do
9
+ it 'returns a Telescope instance' do
10
+ expect(telescope).to be_a(Legion::Extensions::Agentic::Attention::Telescope::Helpers::Telescope)
11
+ end
12
+
13
+ it 'stores the telescope' do
14
+ id = telescope.id
15
+ expect(engine.all_telescopes.map(&:id)).to include(id)
16
+ end
17
+
18
+ it 'raises when observatory is full' do
19
+ stub_const('Legion::Extensions::Agentic::Attention::Telescope::Helpers::Constants::MAX_TELESCOPES', 1)
20
+ engine.create_telescope(lens_type: :radio)
21
+ expect { engine.create_telescope(lens_type: :adaptive) }
22
+ .to raise_error(ArgumentError, /observatory full/)
23
+ end
24
+
25
+ it 'creates with custom aperture and magnification' do
26
+ t = engine.create_telescope(lens_type: :reflector, aperture: 0.9, magnification: 20.0)
27
+ expect(t.aperture).to eq(0.9)
28
+ expect(t.magnification).to eq(20.0)
29
+ end
30
+ end
31
+
32
+ describe '#zoom_in' do
33
+ it 'increases telescope magnification' do
34
+ old = telescope.magnification
35
+ engine.zoom_in(telescope_id: telescope.id, factor: 3.0)
36
+ expect(telescope.magnification).to be > old
37
+ end
38
+
39
+ it 'raises for unknown telescope' do
40
+ expect { engine.zoom_in(telescope_id: 'nope', factor: 2.0) }
41
+ .to raise_error(ArgumentError, /telescope not found/)
42
+ end
43
+ end
44
+
45
+ describe '#zoom_out' do
46
+ it 'decreases magnification after zoom_in' do
47
+ engine.zoom_in(telescope_id: telescope.id, factor: 10.0)
48
+ old = telescope.magnification
49
+ engine.zoom_out(telescope_id: telescope.id, factor: 2.0)
50
+ expect(telescope.magnification).to be < old
51
+ end
52
+
53
+ it 'raises for unknown telescope' do
54
+ expect { engine.zoom_out(telescope_id: 'nope', factor: 2.0) }
55
+ .to raise_error(ArgumentError, /telescope not found/)
56
+ end
57
+ end
58
+
59
+ describe '#focus_telescope' do
60
+ it 'sets focal_distance on the telescope' do
61
+ engine.focus_telescope(telescope_id: telescope.id, target_distance: 0.6)
62
+ expect(telescope.focal_distance).to eq(0.6)
63
+ end
64
+
65
+ it 'raises for unknown telescope' do
66
+ expect { engine.focus_telescope(telescope_id: 'x', target_distance: 0.5) }
67
+ .to raise_error(ArgumentError, /telescope not found/)
68
+ end
69
+ end
70
+
71
+ describe '#observe' do
72
+ it 'returns an Observation' do
73
+ obs = engine.observe(telescope_id: telescope.id, target: 'Mars', distance: 0.4)
74
+ expect(obs).to be_a(Legion::Extensions::Agentic::Attention::Telescope::Helpers::Observation)
75
+ end
76
+
77
+ it 'stores the observation' do
78
+ engine.observe(telescope_id: telescope.id, target: 'Venus', distance: 0.3)
79
+ expect(engine.all_observations.size).to eq(1)
80
+ end
81
+
82
+ it 'records target and distance' do
83
+ obs = engine.observe(telescope_id: telescope.id, target: 'Jupiter', distance: 0.7)
84
+ expect(obs.target).to eq('Jupiter')
85
+ expect(obs.distance).to eq(0.7)
86
+ end
87
+
88
+ it 'raises for unknown telescope' do
89
+ expect { engine.observe(telescope_id: 'nope', target: 'x', distance: 0.5) }
90
+ .to raise_error(ArgumentError, /telescope not found/)
91
+ end
92
+
93
+ it 'raises when observation log is full' do
94
+ stub_const('Legion::Extensions::Agentic::Attention::Telescope::Helpers::Constants::MAX_OBSERVATIONS', 1)
95
+ engine.observe(telescope_id: telescope.id, target: 'a', distance: 0.1)
96
+ expect { engine.observe(telescope_id: telescope.id, target: 'b', distance: 0.2) }
97
+ .to raise_error(ArgumentError, /observation log full/)
98
+ end
99
+ end
100
+
101
+ describe '#survey_mode!' do
102
+ it 'resets telescope to wide-field (magnification == 1.0)' do
103
+ engine.zoom_in(telescope_id: telescope.id, factor: 20.0)
104
+ engine.survey_mode!(telescope_id: telescope.id)
105
+ expect(telescope.magnification).to eq(1.0)
106
+ end
107
+
108
+ it 'raises for unknown telescope' do
109
+ expect { engine.survey_mode!(telescope_id: 'x') }
110
+ .to raise_error(ArgumentError, /telescope not found/)
111
+ end
112
+ end
113
+
114
+ describe '#deep_focus!' do
115
+ it 'zooms telescope to MAX_MAGNIFICATION' do
116
+ engine.deep_focus!(telescope_id: telescope.id)
117
+ expect(telescope.magnification).to eq(100.0)
118
+ end
119
+
120
+ it 'sets deep_field? to true' do
121
+ engine.deep_focus!(telescope_id: telescope.id)
122
+ expect(telescope.deep_field?).to be true
123
+ end
124
+
125
+ it 'raises for unknown telescope' do
126
+ expect { engine.deep_focus!(telescope_id: 'x') }
127
+ .to raise_error(ArgumentError, /telescope not found/)
128
+ end
129
+ end
130
+
131
+ describe '#all_telescopes' do
132
+ it 'returns all created telescopes' do
133
+ engine.create_telescope(lens_type: :radio)
134
+ engine.create_telescope(lens_type: :adaptive)
135
+ expect(engine.all_telescopes.size).to eq(2)
136
+ end
137
+ end
138
+
139
+ describe '#all_observations' do
140
+ it 'returns a copy of all observations' do
141
+ engine.observe(telescope_id: telescope.id, target: 'a', distance: 0.1)
142
+ engine.observe(telescope_id: telescope.id, target: 'b', distance: 0.2)
143
+ expect(engine.all_observations.size).to eq(2)
144
+ end
145
+ end
146
+
147
+ describe '#significant_observations' do
148
+ it 'returns only significant observations' do
149
+ # significant: high magnification + low distance
150
+ engine.zoom_in(telescope_id: telescope.id, factor: 100.0)
151
+ engine.observe(telescope_id: telescope.id, target: 'near', distance: 0.05)
152
+ # faint: low magnification + far distance
153
+ t2 = engine.create_telescope(lens_type: :radio, magnification: 1.0)
154
+ engine.observe(telescope_id: t2.id, target: 'far', distance: 0.99)
155
+ sig = engine.significant_observations
156
+ expect(sig.all?(&:significant?)).to be true
157
+ end
158
+ end
159
+
160
+ describe '#observatory_report' do
161
+ it 'returns a hash with all expected keys' do
162
+ telescope
163
+ engine.observe(telescope_id: telescope.id, target: 'x', distance: 0.5)
164
+ report = engine.observatory_report
165
+ %i[total_telescopes total_observations significant_count faint_count
166
+ avg_detail deepest_observation widest_telescope].each do |k|
167
+ expect(report).to have_key(k)
168
+ end
169
+ end
170
+
171
+ it 'counts telescopes and observations correctly' do
172
+ telescope
173
+ engine.observe(telescope_id: telescope.id, target: 'a', distance: 0.3)
174
+ engine.observe(telescope_id: telescope.id, target: 'b', distance: 0.6)
175
+ report = engine.observatory_report
176
+ expect(report[:total_telescopes]).to eq(1)
177
+ expect(report[:total_observations]).to eq(2)
178
+ end
179
+
180
+ it 'returns nil deepest_observation when no observations exist' do
181
+ telescope
182
+ report = engine.observatory_report
183
+ expect(report[:deepest_observation]).to be_nil
184
+ end
185
+
186
+ it 'returns nil widest_telescope when no telescopes exist' do
187
+ report = engine.observatory_report
188
+ expect(report[:widest_telescope]).to be_nil
189
+ end
190
+
191
+ it 'reports avg_detail as 0.0 when no observations' do
192
+ telescope
193
+ report = engine.observatory_report
194
+ expect(report[:avg_detail]).to eq(0.0)
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Attention::Telescope::Helpers::Telescope do
4
+ subject(:telescope) { described_class.new(lens_type: :refractor) }
5
+
6
+ describe '#initialize' do
7
+ it 'generates a uuid id' do
8
+ expect(telescope.id).to match(/\A[0-9a-f-]{36}\z/)
9
+ end
10
+
11
+ it 'stores the lens_type as symbol' do
12
+ expect(telescope.lens_type).to eq(:refractor)
13
+ end
14
+
15
+ it 'defaults aperture to 0.5' do
16
+ expect(telescope.aperture).to eq(0.5)
17
+ end
18
+
19
+ it 'defaults magnification to 1.0' do
20
+ expect(telescope.magnification).to eq(1.0)
21
+ end
22
+
23
+ it 'defaults tracking to false' do
24
+ expect(telescope.tracking).to be false
25
+ end
26
+
27
+ it 'starts with nil focal_distance' do
28
+ expect(telescope.focal_distance).to be_nil
29
+ end
30
+
31
+ it 'sets created_at' do
32
+ expect(telescope.created_at).to be_a(Time)
33
+ end
34
+
35
+ it 'accepts custom aperture' do
36
+ t = described_class.new(lens_type: :reflector, aperture: 0.8)
37
+ expect(t.aperture).to eq(0.8)
38
+ end
39
+
40
+ it 'clamps aperture to 0.1 minimum' do
41
+ t = described_class.new(lens_type: :radio, aperture: 0.0)
42
+ expect(t.aperture).to eq(0.1)
43
+ end
44
+
45
+ it 'clamps aperture to 1.0 maximum' do
46
+ t = described_class.new(lens_type: :adaptive, aperture: 5.0)
47
+ expect(t.aperture).to eq(1.0)
48
+ end
49
+
50
+ it 'clamps magnification to BASE_MAGNIFICATION minimum' do
51
+ t = described_class.new(lens_type: :catadioptric, magnification: 0.0)
52
+ expect(t.magnification).to eq(1.0)
53
+ end
54
+
55
+ it 'clamps magnification to MAX_MAGNIFICATION maximum' do
56
+ t = described_class.new(lens_type: :reflector, magnification: 999.0)
57
+ expect(t.magnification).to eq(100.0)
58
+ end
59
+
60
+ it 'rejects unknown lens types' do
61
+ expect { described_class.new(lens_type: :laser) }
62
+ .to raise_error(ArgumentError, /unknown lens type/)
63
+ end
64
+
65
+ it 'accepts tracking: true' do
66
+ t = described_class.new(lens_type: :radio, tracking: true)
67
+ expect(t.tracking).to be true
68
+ end
69
+ end
70
+
71
+ describe '#zoom_in!' do
72
+ it 'increases magnification' do
73
+ old = telescope.magnification
74
+ telescope.zoom_in!(2.0)
75
+ expect(telescope.magnification).to be > old
76
+ end
77
+
78
+ it 'doubles magnification with factor 2' do
79
+ telescope.zoom_in!(2.0)
80
+ expect(telescope.magnification).to eq(2.0)
81
+ end
82
+
83
+ it 'does not exceed MAX_MAGNIFICATION' do
84
+ telescope.zoom_in!(1000.0)
85
+ expect(telescope.magnification).to eq(100.0)
86
+ end
87
+
88
+ it 'returns self for chaining' do
89
+ expect(telescope.zoom_in!(2.0)).to eq(telescope)
90
+ end
91
+ end
92
+
93
+ describe '#zoom_out!' do
94
+ it 'decreases magnification' do
95
+ telescope.zoom_in!(10.0)
96
+ old = telescope.magnification
97
+ telescope.zoom_out!(2.0)
98
+ expect(telescope.magnification).to be < old
99
+ end
100
+
101
+ it 'does not go below BASE_MAGNIFICATION' do
102
+ telescope.zoom_out!(1000.0)
103
+ expect(telescope.magnification).to eq(1.0)
104
+ end
105
+
106
+ it 'returns self for chaining' do
107
+ expect(telescope.zoom_out!(2.0)).to eq(telescope)
108
+ end
109
+ end
110
+
111
+ describe '#field_of_view' do
112
+ it 'is 1.0 at BASE_MAGNIFICATION with aperture 1.0' do
113
+ t = described_class.new(lens_type: :refractor, aperture: 1.0)
114
+ expect(t.field_of_view).to eq(1.0)
115
+ end
116
+
117
+ it 'decreases when magnification increases' do
118
+ wide = telescope.field_of_view
119
+ telescope.zoom_in!(10.0)
120
+ expect(telescope.field_of_view).to be < wide
121
+ end
122
+
123
+ it 'is larger with higher aperture at same magnification' do
124
+ t1 = described_class.new(lens_type: :refractor, aperture: 0.3)
125
+ t2 = described_class.new(lens_type: :refractor, aperture: 0.9)
126
+ expect(t2.field_of_view).to be > t1.field_of_view
127
+ end
128
+ end
129
+
130
+ describe '#clarity' do
131
+ it 'returns a float between 0 and 1' do
132
+ expect(telescope.clarity).to be_between(0.0, 1.0)
133
+ end
134
+
135
+ it 'is higher with larger aperture' do
136
+ t1 = described_class.new(lens_type: :reflector, aperture: 0.2)
137
+ t2 = described_class.new(lens_type: :reflector, aperture: 0.9)
138
+ expect(t2.clarity).to be > t1.clarity
139
+ end
140
+ end
141
+
142
+ describe '#focus!' do
143
+ it 'sets focal_distance' do
144
+ telescope.focus!(0.7)
145
+ expect(telescope.focal_distance).to eq(0.7)
146
+ end
147
+
148
+ it 'clamps focal_distance to 0..1' do
149
+ telescope.focus!(5.0)
150
+ expect(telescope.focal_distance).to eq(1.0)
151
+ end
152
+
153
+ it 'returns self for chaining' do
154
+ expect(telescope.focus!(0.5)).to eq(telescope)
155
+ end
156
+ end
157
+
158
+ describe '#enable_tracking! / #disable_tracking!' do
159
+ it 'enables tracking' do
160
+ telescope.enable_tracking!
161
+ expect(telescope.tracking).to be true
162
+ end
163
+
164
+ it 'disables tracking' do
165
+ telescope.enable_tracking!
166
+ telescope.disable_tracking!
167
+ expect(telescope.tracking).to be false
168
+ end
169
+
170
+ it 'enable_tracking! returns self' do
171
+ expect(telescope.enable_tracking!).to eq(telescope)
172
+ end
173
+
174
+ it 'disable_tracking! returns self' do
175
+ expect(telescope.disable_tracking!).to eq(telescope)
176
+ end
177
+ end
178
+
179
+ describe '#deep_field?' do
180
+ it 'returns false at low magnification' do
181
+ expect(telescope.deep_field?).to be false
182
+ end
183
+
184
+ it 'returns true when magnification >= 50' do
185
+ telescope.zoom_in!(50.0)
186
+ expect(telescope.deep_field?).to be true
187
+ end
188
+ end
189
+
190
+ describe '#wide_field?' do
191
+ it 'returns true at base magnification' do
192
+ expect(telescope.wide_field?).to be true
193
+ end
194
+
195
+ it 'returns false at high magnification' do
196
+ telescope.zoom_in!(10.0)
197
+ expect(telescope.wide_field?).to be false
198
+ end
199
+ end
200
+
201
+ describe '#sharp?' do
202
+ it 'returns true for large aperture telescope' do
203
+ t = described_class.new(lens_type: :adaptive, aperture: 1.0)
204
+ expect(t.sharp?).to be true
205
+ end
206
+
207
+ it 'returns false for small aperture telescope' do
208
+ t = described_class.new(lens_type: :radio, aperture: 0.1)
209
+ expect(t.sharp?).to be false
210
+ end
211
+ end
212
+
213
+ describe '#blurry?' do
214
+ it 'returns false for normal aperture' do
215
+ expect(telescope.blurry?).to be false
216
+ end
217
+
218
+ it 'returns true for very small aperture' do
219
+ t = described_class.new(lens_type: :refractor, aperture: 0.1)
220
+ expect(t.blurry?).to be true
221
+ end
222
+ end
223
+
224
+ describe '#clarity_label' do
225
+ it 'returns a symbol' do
226
+ expect(telescope.clarity_label).to be_a(Symbol)
227
+ end
228
+
229
+ it 'returns :crystal for a large-aperture telescope' do
230
+ t = described_class.new(lens_type: :adaptive, aperture: 1.0)
231
+ expect(t.clarity_label).to eq(:crystal)
232
+ end
233
+ end
234
+
235
+ describe '#to_h' do
236
+ it 'returns a hash with all expected keys' do
237
+ h = telescope.to_h
238
+ %i[id lens_type aperture magnification tracking focal_distance
239
+ field_of_view clarity clarity_label deep_field wide_field
240
+ sharp blurry created_at].each do |key|
241
+ expect(h).to have_key(key)
242
+ end
243
+ end
244
+
245
+ it 'reflects current state' do
246
+ telescope.zoom_in!(10.0)
247
+ h = telescope.to_h
248
+ expect(h[:magnification]).to eq(10.0)
249
+ end
250
+ end
251
+ end