lex-agentic-affect 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 (218) 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-affect.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/affect/appraisal/client.rb +20 -0
  8. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal.rb +112 -0
  9. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine.rb +129 -0
  10. data/lib/legion/extensions/agentic/affect/appraisal/helpers/constants.rb +43 -0
  11. data/lib/legion/extensions/agentic/affect/appraisal/runners/appraisal.rb +105 -0
  12. data/lib/legion/extensions/agentic/affect/appraisal/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/affect/appraisal.rb +19 -0
  14. data/lib/legion/extensions/agentic/affect/cognitive_empathy/client.rb +19 -0
  15. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/constants.rb +37 -0
  16. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine.rb +151 -0
  17. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective.rb +92 -0
  18. data/lib/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy.rb +93 -0
  19. data/lib/legion/extensions/agentic/affect/cognitive_empathy/version.rb +13 -0
  20. data/lib/legion/extensions/agentic/affect/cognitive_empathy.rb +20 -0
  21. data/lib/legion/extensions/agentic/affect/contagion/client.rb +28 -0
  22. data/lib/legion/extensions/agentic/affect/contagion/helpers/constants.rb +34 -0
  23. data/lib/legion/extensions/agentic/affect/contagion/helpers/contagion_engine.rb +184 -0
  24. data/lib/legion/extensions/agentic/affect/contagion/helpers/meme.rb +97 -0
  25. data/lib/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion.rb +125 -0
  26. data/lib/legion/extensions/agentic/affect/contagion/version.rb +13 -0
  27. data/lib/legion/extensions/agentic/affect/contagion.rb +19 -0
  28. data/lib/legion/extensions/agentic/affect/defusion/client.rb +28 -0
  29. data/lib/legion/extensions/agentic/affect/defusion/helpers/constants.rb +64 -0
  30. data/lib/legion/extensions/agentic/affect/defusion/helpers/defusion_engine.rb +167 -0
  31. data/lib/legion/extensions/agentic/affect/defusion/helpers/thought.rb +92 -0
  32. data/lib/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion.rb +127 -0
  33. data/lib/legion/extensions/agentic/affect/defusion/version.rb +13 -0
  34. data/lib/legion/extensions/agentic/affect/defusion.rb +19 -0
  35. data/lib/legion/extensions/agentic/affect/emotion/actors/momentum_decay.rb +45 -0
  36. data/lib/legion/extensions/agentic/affect/emotion/client.rb +36 -0
  37. data/lib/legion/extensions/agentic/affect/emotion/helpers/baseline.rb +52 -0
  38. data/lib/legion/extensions/agentic/affect/emotion/helpers/momentum.rb +52 -0
  39. data/lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb +92 -0
  40. data/lib/legion/extensions/agentic/affect/emotion/runners/gut.rb +102 -0
  41. data/lib/legion/extensions/agentic/affect/emotion/runners/valence.rb +120 -0
  42. data/lib/legion/extensions/agentic/affect/emotion/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/affect/emotion.rb +20 -0
  44. data/lib/legion/extensions/agentic/affect/empathy/client.rb +21 -0
  45. data/lib/legion/extensions/agentic/affect/empathy/helpers/constants.rb +54 -0
  46. data/lib/legion/extensions/agentic/affect/empathy/helpers/mental_model.rb +185 -0
  47. data/lib/legion/extensions/agentic/affect/empathy/helpers/model_store.rb +88 -0
  48. data/lib/legion/extensions/agentic/affect/empathy/runners/empathy.rb +173 -0
  49. data/lib/legion/extensions/agentic/affect/empathy/version.rb +13 -0
  50. data/lib/legion/extensions/agentic/affect/empathy.rb +20 -0
  51. data/lib/legion/extensions/agentic/affect/fatigue/client.rb +26 -0
  52. data/lib/legion/extensions/agentic/affect/fatigue/helpers/constants.rb +54 -0
  53. data/lib/legion/extensions/agentic/affect/fatigue/helpers/energy_model.rb +181 -0
  54. data/lib/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store.rb +146 -0
  55. data/lib/legion/extensions/agentic/affect/fatigue/runners/fatigue.rb +89 -0
  56. data/lib/legion/extensions/agentic/affect/fatigue/version.rb +13 -0
  57. data/lib/legion/extensions/agentic/affect/fatigue.rb +19 -0
  58. data/lib/legion/extensions/agentic/affect/flow/client.rb +25 -0
  59. data/lib/legion/extensions/agentic/affect/flow/helpers/constants.rb +84 -0
  60. data/lib/legion/extensions/agentic/affect/flow/helpers/flow_detector.rb +166 -0
  61. data/lib/legion/extensions/agentic/affect/flow/runners/flow.rb +129 -0
  62. data/lib/legion/extensions/agentic/affect/flow/version.rb +13 -0
  63. data/lib/legion/extensions/agentic/affect/flow.rb +18 -0
  64. data/lib/legion/extensions/agentic/affect/interoception/actors/decay.rb +45 -0
  65. data/lib/legion/extensions/agentic/affect/interoception/client.rb +28 -0
  66. data/lib/legion/extensions/agentic/affect/interoception/helpers/body_budget.rb +152 -0
  67. data/lib/legion/extensions/agentic/affect/interoception/helpers/constants.rb +68 -0
  68. data/lib/legion/extensions/agentic/affect/interoception/helpers/somatic_marker.rb +75 -0
  69. data/lib/legion/extensions/agentic/affect/interoception/runners/interoception.rb +101 -0
  70. data/lib/legion/extensions/agentic/affect/interoception/version.rb +13 -0
  71. data/lib/legion/extensions/agentic/affect/interoception.rb +20 -0
  72. data/lib/legion/extensions/agentic/affect/mood/client.rb +21 -0
  73. data/lib/legion/extensions/agentic/affect/mood/helpers/constants.rb +78 -0
  74. data/lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb +154 -0
  75. data/lib/legion/extensions/agentic/affect/mood/runners/mood.rb +122 -0
  76. data/lib/legion/extensions/agentic/affect/mood/version.rb +13 -0
  77. data/lib/legion/extensions/agentic/affect/mood.rb +18 -0
  78. data/lib/legion/extensions/agentic/affect/motivation/client.rb +26 -0
  79. data/lib/legion/extensions/agentic/affect/motivation/helpers/constants.rb +48 -0
  80. data/lib/legion/extensions/agentic/affect/motivation/helpers/drive_state.rb +98 -0
  81. data/lib/legion/extensions/agentic/affect/motivation/helpers/motivation_store.rb +106 -0
  82. data/lib/legion/extensions/agentic/affect/motivation/runners/motivation.rb +165 -0
  83. data/lib/legion/extensions/agentic/affect/motivation/version.rb +13 -0
  84. data/lib/legion/extensions/agentic/affect/motivation.rb +19 -0
  85. data/lib/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate.rb +45 -0
  86. data/lib/legion/extensions/agentic/affect/reappraisal/client.rb +28 -0
  87. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/constants.rb +82 -0
  88. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event.rb +98 -0
  89. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer.rb +88 -0
  90. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine.rb +153 -0
  91. data/lib/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal.rb +164 -0
  92. data/lib/legion/extensions/agentic/affect/reappraisal/version.rb +13 -0
  93. data/lib/legion/extensions/agentic/affect/reappraisal.rb +20 -0
  94. data/lib/legion/extensions/agentic/affect/regulation/client.rb +25 -0
  95. data/lib/legion/extensions/agentic/affect/regulation/helpers/constants.rb +71 -0
  96. data/lib/legion/extensions/agentic/affect/regulation/helpers/regulation_model.rb +175 -0
  97. data/lib/legion/extensions/agentic/affect/regulation/runners/emotional_regulation.rb +127 -0
  98. data/lib/legion/extensions/agentic/affect/regulation/version.rb +13 -0
  99. data/lib/legion/extensions/agentic/affect/regulation.rb +18 -0
  100. data/lib/legion/extensions/agentic/affect/resilience/client.rb +27 -0
  101. data/lib/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker.rb +130 -0
  102. data/lib/legion/extensions/agentic/affect/resilience/helpers/constants.rb +79 -0
  103. data/lib/legion/extensions/agentic/affect/resilience/helpers/resilience_model.rb +165 -0
  104. data/lib/legion/extensions/agentic/affect/resilience/runners/resilience.rb +150 -0
  105. data/lib/legion/extensions/agentic/affect/resilience/version.rb +13 -0
  106. data/lib/legion/extensions/agentic/affect/resilience.rb +19 -0
  107. data/lib/legion/extensions/agentic/affect/resonance/client.rb +24 -0
  108. data/lib/legion/extensions/agentic/affect/resonance/helpers/category.rb +75 -0
  109. data/lib/legion/extensions/agentic/affect/resonance/helpers/constants.rb +47 -0
  110. data/lib/legion/extensions/agentic/affect/resonance/helpers/resonance_engine.rb +115 -0
  111. data/lib/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance.rb +94 -0
  112. data/lib/legion/extensions/agentic/affect/resonance/version.rb +13 -0
  113. data/lib/legion/extensions/agentic/affect/resonance.rb +19 -0
  114. data/lib/legion/extensions/agentic/affect/reward/client.rb +26 -0
  115. data/lib/legion/extensions/agentic/affect/reward/helpers/constants.rb +67 -0
  116. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_signal.rb +178 -0
  117. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_store.rb +142 -0
  118. data/lib/legion/extensions/agentic/affect/reward/runners/reward.rb +92 -0
  119. data/lib/legion/extensions/agentic/affect/reward/version.rb +13 -0
  120. data/lib/legion/extensions/agentic/affect/reward.rb +19 -0
  121. data/lib/legion/extensions/agentic/affect/somatic_marker/actors/decay.rb +45 -0
  122. data/lib/legion/extensions/agentic/affect/somatic_marker/client.rb +29 -0
  123. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/body_state.rb +69 -0
  124. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/constants.rb +43 -0
  125. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store.rb +160 -0
  126. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker.rb +74 -0
  127. data/lib/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker.rb +132 -0
  128. data/lib/legion/extensions/agentic/affect/somatic_marker/version.rb +13 -0
  129. data/lib/legion/extensions/agentic/affect/somatic_marker.rb +20 -0
  130. data/lib/legion/extensions/agentic/affect/version.rb +11 -0
  131. data/lib/legion/extensions/agentic/affect.rb +34 -0
  132. data/spec/legion/extensions/agentic/affect/appraisal/client_spec.rb +52 -0
  133. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine_spec.rb +161 -0
  134. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_spec.rb +175 -0
  135. data/spec/legion/extensions/agentic/affect/appraisal/helpers/constants_spec.rb +49 -0
  136. data/spec/legion/extensions/agentic/affect/appraisal/runners/appraisal_spec.rb +116 -0
  137. data/spec/legion/extensions/agentic/affect/cognitive_empathy/client_spec.rb +62 -0
  138. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine_spec.rb +316 -0
  139. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective_spec.rb +132 -0
  140. data/spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_spec.rb +200 -0
  141. data/spec/legion/extensions/agentic/affect/contagion/client_spec.rb +63 -0
  142. data/spec/legion/extensions/agentic/affect/contagion/helpers/constants_spec.rb +86 -0
  143. data/spec/legion/extensions/agentic/affect/contagion/helpers/contagion_engine_spec.rb +241 -0
  144. data/spec/legion/extensions/agentic/affect/contagion/helpers/meme_spec.rb +160 -0
  145. data/spec/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion_spec.rb +211 -0
  146. data/spec/legion/extensions/agentic/affect/defusion/client_spec.rb +80 -0
  147. data/spec/legion/extensions/agentic/affect/defusion/helpers/constants_spec.rb +84 -0
  148. data/spec/legion/extensions/agentic/affect/defusion/helpers/defusion_engine_spec.rb +250 -0
  149. data/spec/legion/extensions/agentic/affect/defusion/helpers/thought_spec.rb +178 -0
  150. data/spec/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion_spec.rb +185 -0
  151. data/spec/legion/extensions/agentic/affect/emotion/actors/momentum_decay_spec.rb +46 -0
  152. data/spec/legion/extensions/agentic/affect/emotion/client_spec.rb +46 -0
  153. data/spec/legion/extensions/agentic/affect/emotion/helpers/baseline_spec.rb +48 -0
  154. data/spec/legion/extensions/agentic/affect/emotion/helpers/momentum_spec.rb +45 -0
  155. data/spec/legion/extensions/agentic/affect/emotion/helpers/valence_spec.rb +91 -0
  156. data/spec/legion/extensions/agentic/affect/emotion/runners/gut_spec.rb +73 -0
  157. data/spec/legion/extensions/agentic/affect/emotion/runners/valence_spec.rb +67 -0
  158. data/spec/legion/extensions/agentic/affect/empathy/client_spec.rb +20 -0
  159. data/spec/legion/extensions/agentic/affect/empathy/helpers/constants_spec.rb +23 -0
  160. data/spec/legion/extensions/agentic/affect/empathy/helpers/mental_model_spec.rb +150 -0
  161. data/spec/legion/extensions/agentic/affect/empathy/helpers/model_store_spec.rb +94 -0
  162. data/spec/legion/extensions/agentic/affect/empathy/runners/empathy_spec.rb +127 -0
  163. data/spec/legion/extensions/agentic/affect/fatigue/client_spec.rb +66 -0
  164. data/spec/legion/extensions/agentic/affect/fatigue/helpers/constants_spec.rb +130 -0
  165. data/spec/legion/extensions/agentic/affect/fatigue/helpers/energy_model_spec.rb +281 -0
  166. data/spec/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store_spec.rb +157 -0
  167. data/spec/legion/extensions/agentic/affect/fatigue/runners/fatigue_spec.rb +127 -0
  168. data/spec/legion/extensions/agentic/affect/flow/client_spec.rb +58 -0
  169. data/spec/legion/extensions/agentic/affect/flow/helpers/constants_spec.rb +112 -0
  170. data/spec/legion/extensions/agentic/affect/flow/helpers/flow_detector_spec.rb +268 -0
  171. data/spec/legion/extensions/agentic/affect/flow/runners/flow_spec.rb +222 -0
  172. data/spec/legion/extensions/agentic/affect/interoception/client_spec.rb +52 -0
  173. data/spec/legion/extensions/agentic/affect/interoception/helpers/body_budget_spec.rb +178 -0
  174. data/spec/legion/extensions/agentic/affect/interoception/helpers/somatic_marker_spec.rb +120 -0
  175. data/spec/legion/extensions/agentic/affect/interoception/runners/interoception_spec.rb +108 -0
  176. data/spec/legion/extensions/agentic/affect/mood/client_spec.rb +20 -0
  177. data/spec/legion/extensions/agentic/affect/mood/helpers/constants_spec.rb +29 -0
  178. data/spec/legion/extensions/agentic/affect/mood/helpers/mood_state_spec.rb +94 -0
  179. data/spec/legion/extensions/agentic/affect/mood/runners/mood_spec.rb +71 -0
  180. data/spec/legion/extensions/agentic/affect/motivation/client_spec.rb +35 -0
  181. data/spec/legion/extensions/agentic/affect/motivation/helpers/constants_spec.rb +111 -0
  182. data/spec/legion/extensions/agentic/affect/motivation/helpers/drive_state_spec.rb +183 -0
  183. data/spec/legion/extensions/agentic/affect/motivation/helpers/motivation_store_spec.rb +185 -0
  184. data/spec/legion/extensions/agentic/affect/motivation/runners/motivation_spec.rb +248 -0
  185. data/spec/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate_spec.rb +46 -0
  186. data/spec/legion/extensions/agentic/affect/reappraisal/client_spec.rb +64 -0
  187. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/constants_spec.rb +102 -0
  188. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event_spec.rb +177 -0
  189. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer_spec.rb +161 -0
  190. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine_spec.rb +211 -0
  191. data/spec/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal_spec.rb +312 -0
  192. data/spec/legion/extensions/agentic/affect/regulation/client_spec.rb +61 -0
  193. data/spec/legion/extensions/agentic/affect/regulation/helpers/constants_spec.rb +108 -0
  194. data/spec/legion/extensions/agentic/affect/regulation/helpers/regulation_model_spec.rb +200 -0
  195. data/spec/legion/extensions/agentic/affect/regulation/runners/emotional_regulation_spec.rb +190 -0
  196. data/spec/legion/extensions/agentic/affect/resilience/client_spec.rb +36 -0
  197. data/spec/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker_spec.rb +164 -0
  198. data/spec/legion/extensions/agentic/affect/resilience/helpers/constants_spec.rb +78 -0
  199. data/spec/legion/extensions/agentic/affect/resilience/helpers/resilience_model_spec.rb +133 -0
  200. data/spec/legion/extensions/agentic/affect/resilience/runners/resilience_spec.rb +150 -0
  201. data/spec/legion/extensions/agentic/affect/resonance/client_spec.rb +66 -0
  202. data/spec/legion/extensions/agentic/affect/resonance/cognitive_resonance_spec.rb +27 -0
  203. data/spec/legion/extensions/agentic/affect/resonance/helpers/category_spec.rb +146 -0
  204. data/spec/legion/extensions/agentic/affect/resonance/helpers/constants_spec.rb +104 -0
  205. data/spec/legion/extensions/agentic/affect/resonance/helpers/resonance_engine_spec.rb +189 -0
  206. data/spec/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance_spec.rb +197 -0
  207. data/spec/legion/extensions/agentic/affect/reward/client_spec.rb +42 -0
  208. data/spec/legion/extensions/agentic/affect/reward/helpers/constants_spec.rb +91 -0
  209. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_signal_spec.rb +296 -0
  210. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_store_spec.rb +167 -0
  211. data/spec/legion/extensions/agentic/affect/reward/runners/reward_spec.rb +149 -0
  212. data/spec/legion/extensions/agentic/affect/somatic_marker/client_spec.rb +83 -0
  213. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/body_state_spec.rb +155 -0
  214. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store_spec.rb +233 -0
  215. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker_spec.rb +172 -0
  216. data/spec/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker_spec.rb +181 -0
  217. data/spec/spec_helper.rb +46 -0
  218. metadata +302 -0
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resilience::Helpers::AdversityTracker do
6
+ subject(:tracker) { described_class.new }
7
+
8
+ describe '#initialize' do
9
+ it 'starts with no active adversities' do
10
+ expect(tracker.active_adversities).to be_empty
11
+ end
12
+
13
+ it 'starts with no resolved adversities' do
14
+ expect(tracker.resolved_adversities).to be_empty
15
+ end
16
+
17
+ it 'starts with zero consecutive recoveries' do
18
+ expect(tracker.consecutive_recoveries).to eq(0)
19
+ end
20
+ end
21
+
22
+ describe '#register' do
23
+ it 'registers a valid adversity' do
24
+ adversity = tracker.register(type: :prediction_failure, severity: :moderate)
25
+ expect(adversity).to include(:id, :type, :severity, :impact, :phase, :current_health)
26
+ end
27
+
28
+ it 'adds to active adversities' do
29
+ tracker.register(type: :trust_violation, severity: :major)
30
+ expect(tracker.active_adversities.size).to eq(1)
31
+ end
32
+
33
+ it 'rejects unknown adversity type' do
34
+ result = tracker.register(type: :unknown, severity: :minor)
35
+ expect(result).to be_nil
36
+ end
37
+
38
+ it 'rejects unknown severity' do
39
+ result = tracker.register(type: :prediction_failure, severity: :unknown)
40
+ expect(result).to be_nil
41
+ end
42
+
43
+ it 'sets initial health based on impact' do
44
+ adversity = tracker.register(type: :prediction_failure, severity: :major)
45
+ expect(adversity[:current_health]).to eq(0.5)
46
+ end
47
+
48
+ it 'starts in absorbing phase' do
49
+ adversity = tracker.register(type: :prediction_failure, severity: :minor)
50
+ expect(adversity[:phase]).to eq(:absorbing)
51
+ end
52
+
53
+ it 'assigns sequential IDs' do
54
+ a1 = tracker.register(type: :prediction_failure, severity: :minor)
55
+ a2 = tracker.register(type: :trust_violation, severity: :moderate)
56
+ expect(a2[:id]).to eq(a1[:id] + 1)
57
+ end
58
+
59
+ it 'accepts optional context' do
60
+ adversity = tracker.register(type: :system_error, severity: :minor, context: { detail: 'timeout' })
61
+ expect(adversity[:context][:detail]).to eq('timeout')
62
+ end
63
+
64
+ it 'caps active adversities' do
65
+ max = Legion::Extensions::Agentic::Affect::Resilience::Helpers::Constants::MAX_ACTIVE_ADVERSITIES
66
+ (max + 5).times { tracker.register(type: :prediction_failure, severity: :minor) }
67
+ expect(tracker.active_adversities.size).to eq(max)
68
+ end
69
+ end
70
+
71
+ describe '#tick_recovery' do
72
+ before do
73
+ tracker.register(type: :prediction_failure, severity: :moderate)
74
+ end
75
+
76
+ it 'returns recovery status' do
77
+ result = tracker.tick_recovery
78
+ expect(result).to include(:active_count, :resolved_count, :worst_health)
79
+ end
80
+
81
+ it 'increments ticks elapsed' do
82
+ tracker.tick_recovery
83
+ expect(tracker.active_adversities.first[:ticks_elapsed]).to eq(1)
84
+ end
85
+
86
+ it 'improves health over time' do
87
+ initial = tracker.active_adversities.first[:current_health]
88
+ tracker.tick_recovery
89
+ expect(tracker.active_adversities.first[:current_health]).to be > initial
90
+ end
91
+
92
+ it 'resolves adversity when health reaches threshold' do
93
+ recovery_ticks = Legion::Extensions::Agentic::Affect::Resilience::Helpers::Constants::SEVERITY_LEVELS[:moderate][:recovery_ticks]
94
+ (recovery_ticks + 5).times { tracker.tick_recovery }
95
+ expect(tracker.active_adversities).to be_empty
96
+ expect(tracker.resolved_adversities.size).to eq(1)
97
+ end
98
+
99
+ it 'increments consecutive recoveries on resolution' do
100
+ 30.times { tracker.tick_recovery }
101
+ expect(tracker.consecutive_recoveries).to be >= 1
102
+ end
103
+
104
+ it 'advances phases during recovery' do
105
+ 3.times { tracker.tick_recovery }
106
+ phase = tracker.active_adversities.first&.fetch(:phase, nil)
107
+ expect(phase).not_to be_nil
108
+ end
109
+ end
110
+
111
+ describe '#worst_health' do
112
+ it 'returns 1.0 with no adversities' do
113
+ expect(tracker.worst_health).to eq(1.0)
114
+ end
115
+
116
+ it 'returns lowest health among active' do
117
+ tracker.register(type: :prediction_failure, severity: :minor)
118
+ tracker.register(type: :trust_violation, severity: :critical)
119
+ expect(tracker.worst_health).to be < 0.1
120
+ end
121
+ end
122
+
123
+ describe '#active_by_type' do
124
+ it 'groups active adversities by type' do
125
+ tracker.register(type: :prediction_failure, severity: :minor)
126
+ tracker.register(type: :prediction_failure, severity: :moderate)
127
+ tracker.register(type: :trust_violation, severity: :minor)
128
+ result = tracker.active_by_type
129
+ expect(result[:prediction_failure]).to eq(2)
130
+ expect(result[:trust_violation]).to eq(1)
131
+ end
132
+ end
133
+
134
+ describe '#recovery_rate' do
135
+ it 'returns 0.0 with no resolved adversities' do
136
+ expect(tracker.recovery_rate).to eq(0.0)
137
+ end
138
+
139
+ it 'returns rate of on-time recoveries' do
140
+ tracker.register(type: :prediction_failure, severity: :minor)
141
+ 20.times { tracker.tick_recovery }
142
+ expect(tracker.recovery_rate).to be > 0.0
143
+ end
144
+ end
145
+
146
+ describe '#average_recovery_speed' do
147
+ it 'returns 0.0 with no resolved adversities' do
148
+ expect(tracker.average_recovery_speed).to eq(0.0)
149
+ end
150
+
151
+ it 'returns ratio of actual to expected ticks' do
152
+ tracker.register(type: :prediction_failure, severity: :minor)
153
+ 20.times { tracker.tick_recovery }
154
+ expect(tracker.average_recovery_speed).to be > 0.0
155
+ end
156
+ end
157
+
158
+ describe '#total_adversities' do
159
+ it 'counts active plus resolved' do
160
+ tracker.register(type: :prediction_failure, severity: :minor)
161
+ expect(tracker.total_adversities).to eq(1)
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resilience::Helpers::Constants do
6
+ describe 'ADVERSITY_TYPES' do
7
+ it 'defines 8 types' do
8
+ expect(described_class::ADVERSITY_TYPES.size).to eq(8)
9
+ end
10
+
11
+ it 'includes prediction failure' do
12
+ expect(described_class::ADVERSITY_TYPES).to include(:prediction_failure)
13
+ end
14
+
15
+ it 'includes trust violation' do
16
+ expect(described_class::ADVERSITY_TYPES).to include(:trust_violation)
17
+ end
18
+
19
+ it 'is frozen' do
20
+ expect(described_class::ADVERSITY_TYPES).to be_frozen
21
+ end
22
+ end
23
+
24
+ describe 'RECOVERY_PHASES' do
25
+ it 'defines 4 phases' do
26
+ expect(described_class::RECOVERY_PHASES).to eq(%i[absorbing adapting recovering thriving])
27
+ end
28
+ end
29
+
30
+ describe 'DIMENSIONS' do
31
+ it 'defines 4 dimensions' do
32
+ expect(described_class::DIMENSIONS.size).to eq(4)
33
+ end
34
+
35
+ it 'has weights summing to 1.0' do
36
+ total = described_class::DIMENSIONS.values.sum { |v| v[:weight] }
37
+ expect(total).to be_within(0.001).of(1.0)
38
+ end
39
+
40
+ it 'includes elasticity, robustness, adaptability, growth' do
41
+ expect(described_class::DIMENSIONS.keys).to contain_exactly(:elasticity, :robustness, :adaptability, :growth)
42
+ end
43
+ end
44
+
45
+ describe 'SEVERITY_LEVELS' do
46
+ it 'defines 5 levels' do
47
+ expect(described_class::SEVERITY_LEVELS.size).to eq(5)
48
+ end
49
+
50
+ it 'has increasing impact' do
51
+ impacts = described_class::SEVERITY_LEVELS.values.map { |v| v[:impact] }
52
+ expect(impacts).to eq(impacts.sort)
53
+ end
54
+
55
+ it 'has increasing recovery ticks' do
56
+ ticks = described_class::SEVERITY_LEVELS.values.map { |v| v[:recovery_ticks] }
57
+ expect(ticks).to eq(ticks.sort)
58
+ end
59
+ end
60
+
61
+ describe 'thresholds' do
62
+ it 'defines fragility threshold' do
63
+ expect(described_class::FRAGILITY_THRESHOLD).to eq(0.3)
64
+ end
65
+
66
+ it 'defines antifragility threshold' do
67
+ expect(described_class::ANTIFRAGILITY_THRESHOLD).to eq(0.7)
68
+ end
69
+
70
+ it 'defines recovery threshold' do
71
+ expect(described_class::RECOVERY_THRESHOLD).to eq(0.9)
72
+ end
73
+
74
+ it 'defines growth trigger' do
75
+ expect(described_class::GROWTH_TRIGGER).to eq(3)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resilience::Helpers::ResilienceModel do
6
+ subject(:model) { described_class.new }
7
+
8
+ let(:tracker) { Legion::Extensions::Agentic::Affect::Resilience::Helpers::AdversityTracker.new }
9
+
10
+ describe '#initialize' do
11
+ it 'starts all dimensions at 0.5' do
12
+ model.dimensions.each_value do |val|
13
+ expect(val).to eq(0.5)
14
+ end
15
+ end
16
+
17
+ it 'starts with zero growth bonus' do
18
+ expect(model.growth_bonus).to eq(0.0)
19
+ end
20
+
21
+ it 'starts with empty history' do
22
+ expect(model.history).to be_empty
23
+ end
24
+ end
25
+
26
+ describe '#update_from_tracker' do
27
+ it 'records a snapshot' do
28
+ model.update_from_tracker(tracker)
29
+ expect(model.history.size).to eq(1)
30
+ end
31
+
32
+ it 'updates dimensions' do
33
+ tracker.register(type: :prediction_failure, severity: :moderate)
34
+ 5.times { tracker.tick_recovery }
35
+ model.update_from_tracker(tracker)
36
+ changed = model.dimensions.values.any? { |v| (v - 0.5).abs > 0.001 }
37
+ expect(changed).to be true
38
+ end
39
+ end
40
+
41
+ describe '#composite_score' do
42
+ it 'starts at approximately 0.5' do
43
+ expect(model.composite_score).to be_within(0.01).of(0.5)
44
+ end
45
+
46
+ it 'is between 0.0 and 1.0' do
47
+ expect(model.composite_score).to be_between(0.0, 1.0)
48
+ end
49
+ end
50
+
51
+ describe '#classification' do
52
+ it 'starts as resilient (score ~0.5)' do
53
+ expect(model.classification).to eq(:resilient)
54
+ end
55
+
56
+ it 'classifies antifragile for high scores' do
57
+ model.dimensions.each_key { |k| model.dimensions[k] = 0.85 }
58
+ expect(model.classification).to eq(:antifragile)
59
+ end
60
+
61
+ it 'classifies fragile for low scores' do
62
+ model.dimensions.each_key { |k| model.dimensions[k] = 0.35 }
63
+ expect(model.classification).to eq(:fragile)
64
+ end
65
+
66
+ it 'classifies brittle for very low scores' do
67
+ model.dimensions.each_key { |k| model.dimensions[k] = 0.1 }
68
+ expect(model.classification).to eq(:brittle)
69
+ end
70
+ end
71
+
72
+ describe '#dimension_detail' do
73
+ it 'returns detail for valid dimension' do
74
+ detail = model.dimension_detail(:elasticity)
75
+ expect(detail).to include(:name, :value, :config, :trend, :healthy)
76
+ end
77
+
78
+ it 'returns nil for unknown dimension' do
79
+ expect(model.dimension_detail(:unknown)).to be_nil
80
+ end
81
+ end
82
+
83
+ describe '#trend' do
84
+ it 'returns insufficient_data with few entries' do
85
+ expect(model.trend).to eq(:insufficient_data)
86
+ end
87
+
88
+ it 'returns a trend with enough data' do
89
+ 10.times { model.update_from_tracker(tracker) }
90
+ expect(%i[strengthening weakening stable]).to include(model.trend)
91
+ end
92
+ end
93
+
94
+ describe '#to_h' do
95
+ it 'returns complete state hash' do
96
+ h = model.to_h
97
+ expect(h).to include(:dimensions, :growth_bonus, :composite, :class, :trend)
98
+ end
99
+ end
100
+
101
+ describe 'growth bonus' do
102
+ it 'increases after consecutive recoveries' do
103
+ tracker.register(type: :prediction_failure, severity: :minor)
104
+ 20.times { tracker.tick_recovery }
105
+ tracker.register(type: :trust_violation, severity: :minor)
106
+ 20.times { tracker.tick_recovery }
107
+ tracker.register(type: :system_error, severity: :minor)
108
+ 20.times { tracker.tick_recovery }
109
+
110
+ model.update_from_tracker(tracker)
111
+ expect(model.growth_bonus).to be > 0.0
112
+ end
113
+
114
+ it 'caps at MAX_GROWTH_BONUS' do
115
+ max = Legion::Extensions::Agentic::Affect::Resilience::Helpers::Constants::MAX_GROWTH_BONUS
116
+ # Force many consecutive recoveries
117
+ 20.times do
118
+ tracker.register(type: :prediction_failure, severity: :minor)
119
+ 20.times { tracker.tick_recovery }
120
+ model.update_from_tracker(tracker)
121
+ end
122
+ expect(model.growth_bonus).to be <= max
123
+ end
124
+ end
125
+
126
+ describe 'history cap' do
127
+ it 'caps at MAX_RESILIENCE_HISTORY' do
128
+ max = Legion::Extensions::Agentic::Affect::Resilience::Helpers::Constants::MAX_RESILIENCE_HISTORY
129
+ (max + 10).times { model.update_from_tracker(tracker) }
130
+ expect(model.history.size).to eq(max)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resilience::Runners::Resilience do
6
+ let(:client) { Legion::Extensions::Agentic::Affect::Resilience::Client.new }
7
+
8
+ let(:normal_tick) do
9
+ {
10
+ prediction_engine: { error_rate: 0.2 },
11
+ trust: {},
12
+ conflict: {},
13
+ fatigue: { energy: 0.7 },
14
+ emotional_evaluation: { arousal: 0.5 }
15
+ }
16
+ end
17
+
18
+ let(:adverse_tick) do
19
+ {
20
+ prediction_engine: { error_rate: 0.8 },
21
+ trust: { violation: true },
22
+ conflict: { severity: 4 },
23
+ fatigue: { energy: 0.1 },
24
+ emotional_evaluation: { arousal: 0.95 }
25
+ }
26
+ end
27
+
28
+ describe '#update_resilience' do
29
+ it 'returns resilience status' do
30
+ result = client.update_resilience(tick_results: normal_tick)
31
+ expect(result).to include(:active_adversities, :resolved_this_tick, :worst_health,
32
+ :composite_score, :classification, :growth_bonus)
33
+ end
34
+
35
+ it 'detects no adversities in normal conditions' do
36
+ result = client.update_resilience(tick_results: normal_tick)
37
+ expect(result[:active_adversities]).to eq(0)
38
+ end
39
+
40
+ it 'detects multiple adversities in adverse conditions' do
41
+ result = client.update_resilience(tick_results: adverse_tick)
42
+ expect(result[:active_adversities]).to be > 0
43
+ end
44
+
45
+ it 'handles empty tick results' do
46
+ result = client.update_resilience(tick_results: {})
47
+ expect(result[:composite_score]).to be_a(Float)
48
+ end
49
+ end
50
+
51
+ describe '#register_adversity' do
52
+ it 'registers valid adversity' do
53
+ result = client.register_adversity(type: :prediction_failure, severity: :moderate)
54
+ expect(result[:success]).to be true
55
+ expect(result[:adversity]).to include(:id, :type, :severity)
56
+ end
57
+
58
+ it 'rejects invalid adversity' do
59
+ result = client.register_adversity(type: :unknown, severity: :minor)
60
+ expect(result[:success]).to be false
61
+ end
62
+ end
63
+
64
+ describe '#resilience_status' do
65
+ it 'returns full status' do
66
+ status = client.resilience_status
67
+ expect(status).to include(:dimensions, :composite, :class, :trend,
68
+ :active_adversities, :total_adversities,
69
+ :consecutive_recoveries, :recovery_rate)
70
+ end
71
+ end
72
+
73
+ describe '#adversity_report' do
74
+ it 'returns adversity details' do
75
+ report = client.adversity_report
76
+ expect(report).to include(:active, :by_type, :total, :worst, :avg_speed)
77
+ end
78
+
79
+ it 'reports active adversities' do
80
+ client.register_adversity(type: :prediction_failure, severity: :minor)
81
+ report = client.adversity_report
82
+ expect(report[:active].size).to eq(1)
83
+ end
84
+ end
85
+
86
+ describe '#dimension_detail' do
87
+ it 'returns detail for known dimension' do
88
+ detail = client.dimension_detail(dimension: :elasticity)
89
+ expect(detail).to include(:name, :value, :config, :trend, :healthy)
90
+ end
91
+
92
+ it 'returns error for unknown dimension' do
93
+ detail = client.dimension_detail(dimension: :unknown)
94
+ expect(detail).to have_key(:error)
95
+ end
96
+ end
97
+
98
+ describe '#resilience_stats' do
99
+ it 'returns comprehensive stats' do
100
+ stats = client.resilience_stats
101
+ expect(stats).to include(:composite, :classification, :dimensions,
102
+ :growth_bonus, :trend, :total_adversities,
103
+ :active_adversities, :recovery_rate,
104
+ :consecutive_recoveries, :history_size)
105
+ end
106
+ end
107
+
108
+ describe 'adversity detection from tick results' do
109
+ it 'detects high prediction error' do
110
+ client.update_resilience(tick_results: { prediction_engine: { error_rate: 0.8 } })
111
+ expect(client.adversity_tracker.active_adversities.size).to eq(1)
112
+ end
113
+
114
+ it 'detects trust violation' do
115
+ client.update_resilience(tick_results: { trust: { violation: true } })
116
+ expect(client.adversity_tracker.active_adversities.any? { |a| a[:type] == :trust_violation }).to be true
117
+ end
118
+
119
+ it 'detects conflict escalation' do
120
+ client.update_resilience(tick_results: { conflict: { severity: 4 } })
121
+ expect(client.adversity_tracker.active_adversities.any? { |a| a[:type] == :conflict_escalation }).to be true
122
+ end
123
+
124
+ it 'detects resource depletion' do
125
+ client.update_resilience(tick_results: { fatigue: { energy: 0.05 } })
126
+ expect(client.adversity_tracker.active_adversities.any? { |a| a[:type] == :resource_depletion }).to be true
127
+ end
128
+
129
+ it 'detects emotional shock' do
130
+ client.update_resilience(tick_results: { emotional_evaluation: { arousal: 0.95 } })
131
+ expect(client.adversity_tracker.active_adversities.any? { |a| a[:type] == :emotional_shock }).to be true
132
+ end
133
+ end
134
+
135
+ describe 'recovery over time' do
136
+ it 'resolves adversities after enough ticks' do
137
+ client.register_adversity(type: :prediction_failure, severity: :minor)
138
+ 20.times { client.update_resilience(tick_results: normal_tick) }
139
+ expect(client.adversity_tracker.active_adversities).to be_empty
140
+ end
141
+
142
+ it 'builds growth bonus from consecutive recoveries' do
143
+ 3.times do
144
+ client.register_adversity(type: :prediction_failure, severity: :minor)
145
+ 20.times { client.update_resilience(tick_results: normal_tick) }
146
+ end
147
+ expect(client.resilience_model.growth_bonus).to be > 0.0
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resonance::Client do
4
+ let(:client) { described_class.new }
5
+
6
+ it 'responds to present_input' do
7
+ expect(client).to respond_to(:present_input)
8
+ end
9
+
10
+ it 'responds to classify' do
11
+ expect(client).to respond_to(:classify)
12
+ end
13
+
14
+ it 'responds to adjust_vigilance' do
15
+ expect(client).to respond_to(:adjust_vigilance)
16
+ end
17
+
18
+ it 'responds to resonance_report' do
19
+ expect(client).to respond_to(:resonance_report)
20
+ end
21
+
22
+ it 'responds to category_count' do
23
+ expect(client).to respond_to(:category_count)
24
+ end
25
+
26
+ it 'responds to reset_engine' do
27
+ expect(client).to respond_to(:reset_engine)
28
+ end
29
+
30
+ it 'round-trips a full resonance cycle' do
31
+ # Present a pattern and create a category
32
+ r1 = client.present_input(input: [0.9, 0.8, 0.7])
33
+ expect(r1[:outcome]).to eq(:new_category)
34
+
35
+ # Present same pattern — should resonate
36
+ r2 = client.present_input(input: [0.9, 0.8, 0.7])
37
+ expect(r2[:outcome]).to eq(:resonance)
38
+
39
+ # Classify the same pattern
40
+ c = client.classify(input: [0.9, 0.8, 0.7])
41
+ expect(c[:found]).to be(true)
42
+ expect(c[:quality]).to be > 0.9
43
+
44
+ # Get a report
45
+ report = client.resonance_report
46
+ expect(report[:category_count]).to be >= 1
47
+
48
+ # Adjust vigilance
49
+ v = client.adjust_vigilance(amount: 0.05)
50
+ expect(v[:success]).to be(true)
51
+ end
52
+
53
+ it 'each client instance has its own engine' do
54
+ c1 = described_class.new
55
+ c2 = described_class.new
56
+ c1.present_input(input: [0.5, 0.5])
57
+ expect(c1.category_count[:count]).to eq(1)
58
+ expect(c2.category_count[:count]).to eq(0)
59
+ end
60
+
61
+ it 'reset_engine clears categories' do
62
+ client.present_input(input: [0.5, 0.5])
63
+ client.reset_engine
64
+ expect(client.category_count[:count]).to eq(0)
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Resonance do
4
+ it 'defines VERSION' do
5
+ expect(described_class::VERSION).to eq('0.1.0')
6
+ end
7
+
8
+ it 'provides the Client class' do
9
+ expect(described_class::Client).to be_a(Class)
10
+ end
11
+
12
+ it 'provides Helpers::Constants module' do
13
+ expect(described_class::Helpers::Constants).to be_a(Module)
14
+ end
15
+
16
+ it 'provides Helpers::Category class' do
17
+ expect(described_class::Helpers::Category).to be_a(Class)
18
+ end
19
+
20
+ it 'provides Helpers::ResonanceEngine class' do
21
+ expect(described_class::Helpers::ResonanceEngine).to be_a(Class)
22
+ end
23
+
24
+ it 'provides Runners::CognitiveResonance module' do
25
+ expect(described_class::Runners::CognitiveResonance).to be_a(Module)
26
+ end
27
+ end