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,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Motivation::Runners::Motivation do
6
+ let(:client) { Legion::Extensions::Agentic::Affect::Motivation::Client.new }
7
+
8
+ let(:normal_tick) do
9
+ {
10
+ consent: { tier: :collaborate },
11
+ prediction_engine: { accuracy: 0.8 },
12
+ trust: { overall_level: 0.7 },
13
+ memory_retrieval: { novel_traces: 3 },
14
+ scheduler: { pending_tasks: 5 },
15
+ extinction: { level: 0 }
16
+ }
17
+ end
18
+
19
+ let(:adverse_tick) do
20
+ {
21
+ consent: { tier: :supervised },
22
+ prediction_engine: { accuracy: 0.2 },
23
+ trust: { overall_level: 0.1 },
24
+ memory_retrieval: { novel_traces: 0 },
25
+ scheduler: { pending_tasks: 0 },
26
+ extinction: { level: 3 }
27
+ }
28
+ end
29
+
30
+ describe '#update_motivation' do
31
+ it 'returns a hash with expected keys' do
32
+ result = client.update_motivation(tick_results: normal_tick)
33
+ expect(result).to include(:mode, :overall_level, :intrinsic_average,
34
+ :extrinsic_average, :amotivated, :burnout)
35
+ end
36
+
37
+ it 'returns a valid mode symbol' do
38
+ result = client.update_motivation(tick_results: normal_tick)
39
+ expect(Legion::Extensions::Agentic::Affect::Motivation::Helpers::Constants::MOTIVATION_MODES).to include(result[:mode])
40
+ end
41
+
42
+ it 'handles empty tick results' do
43
+ result = client.update_motivation(tick_results: {})
44
+ expect(result[:overall_level]).to be_a(Float)
45
+ end
46
+
47
+ it 'handles missing tick keys gracefully' do
48
+ result = client.update_motivation(tick_results: { trust: {} })
49
+ expect(result).to include(:mode)
50
+ end
51
+
52
+ it 'returns amotivated as false initially' do
53
+ result = client.update_motivation(tick_results: {})
54
+ expect(result[:amotivated]).to be false
55
+ end
56
+
57
+ it 'decays drives on each tick' do
58
+ initial = client.motivation_store.drive_state.overall_level
59
+ client.update_motivation(tick_results: {})
60
+ after = client.motivation_store.drive_state.overall_level
61
+ expect(after).to be < initial
62
+ end
63
+ end
64
+
65
+ describe '#signal_drive' do
66
+ it 'updates a valid drive' do
67
+ result = client.signal_drive(drive: :autonomy, signal: 0.9)
68
+ expect(result[:success]).to be true
69
+ expect(result[:drive]).to eq(:autonomy)
70
+ expect(result[:level]).to be_a(Float)
71
+ end
72
+
73
+ it 'accepts string drive names' do
74
+ result = client.signal_drive(drive: 'competence', signal: 0.7)
75
+ expect(result[:success]).to be true
76
+ end
77
+
78
+ it 'rejects unknown drive' do
79
+ result = client.signal_drive(drive: :nonexistent, signal: 0.5)
80
+ expect(result[:success]).to be false
81
+ expect(result[:error]).to include('unknown drive')
82
+ end
83
+
84
+ it 'reflects updated level in drive_status' do
85
+ client.signal_drive(drive: :novelty, signal: 1.0)
86
+ status = client.drive_status
87
+ expect(status[:drives][:novelty][:level]).to be > 0.5
88
+ end
89
+ end
90
+
91
+ describe '#commit_to_goal' do
92
+ it 'commits a goal with valid drives' do
93
+ result = client.commit_to_goal(goal_id: 'goal_a', drives: %i[autonomy competence])
94
+ expect(result[:success]).to be true
95
+ expect(result[:goal_id]).to eq('goal_a')
96
+ expect(result[:energy]).to be_a(Float)
97
+ end
98
+
99
+ it 'returns failure for entirely invalid drives' do
100
+ result = client.commit_to_goal(goal_id: 'bad', drives: [:nonexistent])
101
+ expect(result[:success]).to be false
102
+ expect(result[:error]).to be_a(String)
103
+ end
104
+ end
105
+
106
+ describe '#release_goal' do
107
+ it 'releases a committed goal' do
108
+ client.commit_to_goal(goal_id: 'temp_goal', drives: [:autonomy])
109
+ result = client.release_goal(goal_id: 'temp_goal')
110
+ expect(result[:success]).to be true
111
+ expect(client.motivation_store.goal_motivations).not_to have_key('temp_goal')
112
+ end
113
+
114
+ it 'succeeds for unknown goal id' do
115
+ result = client.release_goal(goal_id: 'no_such_goal')
116
+ expect(result[:success]).to be true
117
+ end
118
+ end
119
+
120
+ describe '#motivation_for' do
121
+ it 'returns 0.0 energy for uncommitted goal' do
122
+ result = client.motivation_for(goal_id: 'unknown_goal')
123
+ expect(result[:energy]).to eq(0.0)
124
+ end
125
+
126
+ it 'returns positive energy for committed goal' do
127
+ client.commit_to_goal(goal_id: 'tracked', drives: %i[autonomy competence])
128
+ result = client.motivation_for(goal_id: 'tracked')
129
+ expect(result[:energy]).to be >= 0.0
130
+ end
131
+
132
+ it 'includes goal_id in result' do
133
+ result = client.motivation_for(goal_id: 'some_goal')
134
+ expect(result[:goal_id]).to eq('some_goal')
135
+ end
136
+ end
137
+
138
+ describe '#most_motivated_goal' do
139
+ it 'returns nil goal_id when no goals committed' do
140
+ result = client.most_motivated_goal
141
+ expect(result[:goal_id]).to be_nil
142
+ end
143
+
144
+ it 'returns the most energized goal' do
145
+ client.commit_to_goal(goal_id: 'low', drives: [:obligation])
146
+ client.commit_to_goal(goal_id: 'high', drives: %i[autonomy competence relatedness novelty])
147
+ %i[autonomy competence relatedness novelty].each do |d|
148
+ 5.times { client.signal_drive(drive: d, signal: 1.0) }
149
+ end
150
+ result = client.most_motivated_goal
151
+ expect(result[:goal_id]).to eq('high')
152
+ end
153
+
154
+ it 'includes energy and drives in result' do
155
+ client.commit_to_goal(goal_id: 'g1', drives: [:autonomy])
156
+ result = client.most_motivated_goal
157
+ expect(result).to include(:goal_id, :energy, :drives)
158
+ end
159
+ end
160
+
161
+ describe '#drive_status' do
162
+ it 'returns drives hash' do
163
+ status = client.drive_status
164
+ expect(status).to have_key(:drives)
165
+ end
166
+
167
+ it 'returns mode' do
168
+ status = client.drive_status
169
+ expect(status).to have_key(:mode)
170
+ end
171
+
172
+ it 'returns overall level' do
173
+ status = client.drive_status
174
+ expect(status).to have_key(:overall)
175
+ end
176
+
177
+ it 'includes level and satisfied for each drive type' do
178
+ status = client.drive_status
179
+ Legion::Extensions::Agentic::Affect::Motivation::Helpers::Constants::DRIVE_TYPES.each do |type|
180
+ expect(status[:drives][type]).to include(:level, :satisfied)
181
+ end
182
+ end
183
+ end
184
+
185
+ describe '#motivation_stats' do
186
+ it 'returns overall_level' do
187
+ expect(client.motivation_stats).to have_key(:overall_level)
188
+ end
189
+
190
+ it 'returns current_mode' do
191
+ expect(client.motivation_stats).to have_key(:current_mode)
192
+ end
193
+
194
+ it 'returns intrinsic_average' do
195
+ expect(client.motivation_stats).to have_key(:intrinsic_average)
196
+ end
197
+
198
+ it 'returns extrinsic_average' do
199
+ expect(client.motivation_stats).to have_key(:extrinsic_average)
200
+ end
201
+
202
+ it 'returns amotivated flag' do
203
+ expect(client.motivation_stats).to have_key(:amotivated)
204
+ end
205
+
206
+ it 'returns goal_count' do
207
+ expect(client.motivation_stats).to have_key(:goal_count)
208
+ end
209
+ end
210
+
211
+ describe 'drive signal extraction from tick results' do
212
+ it 'extracts autonomy from consent tier :autonomous' do
213
+ client.update_motivation(tick_results: { consent: { tier: :autonomous } })
214
+ expect(client.motivation_store.drive_state.drive_level(:autonomy)).to be > 0.5
215
+ end
216
+
217
+ it 'extracts competence from prediction accuracy' do
218
+ client.update_motivation(tick_results: { prediction_engine: { accuracy: 0.95 } })
219
+ expect(client.motivation_store.drive_state.drive_level(:competence)).to be > 0.5
220
+ end
221
+
222
+ it 'extracts relatedness from trust level' do
223
+ client.update_motivation(tick_results: { trust: { overall_level: 0.9 } })
224
+ expect(client.motivation_store.drive_state.drive_level(:relatedness)).to be > 0.5
225
+ end
226
+
227
+ it 'extracts novelty from novel traces count' do
228
+ client.update_motivation(tick_results: { memory_retrieval: { novel_traces: 8 } })
229
+ expect(client.motivation_store.drive_state.drive_level(:novelty)).to be >= 0.0
230
+ end
231
+
232
+ it 'extracts obligation from pending tasks' do
233
+ client.update_motivation(tick_results: { scheduler: { pending_tasks: 10 } })
234
+ expect(client.motivation_store.drive_state.drive_level(:obligation)).to be > 0.0
235
+ end
236
+
237
+ it 'extracts survival from extinction level' do
238
+ client.update_motivation(tick_results: { extinction: { level: 4 } })
239
+ expect(client.motivation_store.drive_state.drive_level(:survival)).to be > 0.5
240
+ end
241
+
242
+ it 'lowers autonomy from constrained consent tier' do
243
+ initial = client.motivation_store.drive_state.drive_level(:autonomy)
244
+ client.update_motivation(tick_results: { consent: { tier: :supervised } })
245
+ expect(client.motivation_store.drive_state.drive_level(:autonomy)).to be < initial
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Actors
6
+ class Every # rubocop:disable Lint/EmptyClass
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ $LOADED_FEATURES << 'legion/extensions/actors/every'
13
+
14
+ require_relative '../../../../../../../lib/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate'
15
+
16
+ RSpec.describe Legion::Extensions::Agentic::Affect::Reappraisal::Actor::AutoRegulate do
17
+ subject(:actor) { described_class.new }
18
+
19
+ describe '#runner_class' do
20
+ it { expect(actor.runner_class).to eq Legion::Extensions::Agentic::Affect::Reappraisal::Runners::CognitiveReappraisal }
21
+ end
22
+
23
+ describe '#runner_function' do
24
+ it { expect(actor.runner_function).to eq 'regulate_pending_events' }
25
+ end
26
+
27
+ describe '#time' do
28
+ it { expect(actor.time).to eq 300 }
29
+ end
30
+
31
+ describe '#run_now?' do
32
+ it { expect(actor.run_now?).to be false }
33
+ end
34
+
35
+ describe '#use_runner?' do
36
+ it { expect(actor.use_runner?).to be false }
37
+ end
38
+
39
+ describe '#check_subtask?' do
40
+ it { expect(actor.check_subtask?).to be false }
41
+ end
42
+
43
+ describe '#generate_task?' do
44
+ it { expect(actor.generate_task?).to be false }
45
+ end
46
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/reappraisal/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Reappraisal::Client do
6
+ it 'responds to all runner methods' do
7
+ client = described_class.new
8
+ expect(client).to respond_to(:register_event)
9
+ expect(client).to respond_to(:reappraise_event)
10
+ expect(client).to respond_to(:auto_reappraise_event)
11
+ expect(client).to respond_to(:negative_events)
12
+ expect(client).to respond_to(:intense_events)
13
+ expect(client).to respond_to(:most_regulated_events)
14
+ expect(client).to respond_to(:reappraisal_status)
15
+ expect(client).to respond_to(:reappraisal_report)
16
+ end
17
+
18
+ it 'accepts an injected engine' do
19
+ engine = Legion::Extensions::Agentic::Affect::Reappraisal::Helpers::ReappraisalEngine.new
20
+ client = described_class.new(engine: engine)
21
+ result = client.register_event(
22
+ content: 'injected test',
23
+ valence: -0.5,
24
+ intensity: 0.4,
25
+ appraisal: 'difficult',
26
+ engine: engine
27
+ )
28
+ expect(result[:success]).to be true
29
+ expect(engine.events.size).to eq(1)
30
+ end
31
+
32
+ it 'creates its own engine when none injected' do
33
+ client = described_class.new
34
+ result = client.register_event(
35
+ content: 'standalone test',
36
+ valence: 0.3,
37
+ intensity: 0.2,
38
+ appraisal: 'fine'
39
+ )
40
+ expect(result[:success]).to be true
41
+ end
42
+
43
+ it 'executes a full register -> reappraise -> report cycle' do
44
+ client = described_class.new
45
+ reg = client.register_event(
46
+ content: 'system alert',
47
+ valence: -0.6,
48
+ intensity: 0.6,
49
+ appraisal: 'unrecoverable failure'
50
+ )
51
+ expect(reg[:success]).to be true
52
+
53
+ reap = client.reappraise_event(
54
+ event_id: reg[:event_id],
55
+ strategy: :temporal_distancing,
56
+ new_appraisal: 'this will be a minor footnote in six months'
57
+ )
58
+ expect(reap[:success]).to be true
59
+
60
+ report = client.reappraisal_report
61
+ expect(report[:report][:total_events]).to eq(1)
62
+ expect(report[:report][:total_reappraisals]).to eq(1)
63
+ end
64
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Reappraisal::Helpers::Constants do
4
+ describe 'STRATEGIES' do
5
+ it 'includes all six strategies' do
6
+ expect(described_class::STRATEGIES).to include(
7
+ :reinterpretation, :distancing, :benefit_finding,
8
+ :normalizing, :perspective_taking, :temporal_distancing
9
+ )
10
+ end
11
+
12
+ it 'is frozen' do
13
+ expect(described_class::STRATEGIES).to be_frozen
14
+ end
15
+ end
16
+
17
+ describe 'STRATEGY_EFFECTIVENESS' do
18
+ it 'has an entry for every strategy' do
19
+ described_class::STRATEGIES.each do |s|
20
+ expect(described_class::STRATEGY_EFFECTIVENESS).to have_key(s)
21
+ end
22
+ end
23
+
24
+ it 'rates reinterpretation highest' do
25
+ expect(described_class::STRATEGY_EFFECTIVENESS[:reinterpretation]).to be >
26
+ described_class::STRATEGY_EFFECTIVENESS[:temporal_distancing]
27
+ end
28
+
29
+ it 'all effectiveness values are between 0 and 1' do
30
+ described_class::STRATEGY_EFFECTIVENESS.each_value do |v|
31
+ expect(v).to be_between(0.0, 1.0)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '.valid_strategy?' do
37
+ it 'returns true for valid strategies' do
38
+ expect(described_class.valid_strategy?(:reinterpretation)).to be true
39
+ expect(described_class.valid_strategy?(:distancing)).to be true
40
+ end
41
+
42
+ it 'returns false for unknown strategy' do
43
+ expect(described_class.valid_strategy?(:nonexistent)).to be false
44
+ end
45
+ end
46
+
47
+ describe '.label_for' do
48
+ it 'returns :positive for valence 0.8' do
49
+ expect(described_class.label_for(0.8, described_class::VALENCE_LABELS)).to eq(:positive)
50
+ end
51
+
52
+ it 'returns :negative for valence -0.8' do
53
+ expect(described_class.label_for(-0.8, described_class::VALENCE_LABELS)).to eq(:negative)
54
+ end
55
+
56
+ it 'returns :neutral for valence 0.0' do
57
+ expect(described_class.label_for(0.0, described_class::VALENCE_LABELS)).to eq(:neutral)
58
+ end
59
+
60
+ it 'returns :overwhelming for intensity 0.9' do
61
+ expect(described_class.label_for(0.9, described_class::INTENSITY_LABELS)).to eq(:overwhelming)
62
+ end
63
+
64
+ it 'returns :faint for intensity 0.1' do
65
+ expect(described_class.label_for(0.1, described_class::INTENSITY_LABELS)).to eq(:faint)
66
+ end
67
+
68
+ it 'returns :excellent for regulation 0.9' do
69
+ expect(described_class.label_for(0.9, described_class::REGULATION_LABELS)).to eq(:excellent)
70
+ end
71
+ end
72
+
73
+ describe '.clamp' do
74
+ it 'clamps to -1..1 by default' do
75
+ expect(described_class.clamp(2.0)).to eq(1.0)
76
+ expect(described_class.clamp(-2.0)).to eq(-1.0)
77
+ expect(described_class.clamp(0.5)).to eq(0.5)
78
+ end
79
+ end
80
+
81
+ describe '.clamp_intensity' do
82
+ it 'clamps to 0..1' do
83
+ expect(described_class.clamp_intensity(1.5)).to eq(1.0)
84
+ expect(described_class.clamp_intensity(-0.1)).to eq(0.0)
85
+ expect(described_class.clamp_intensity(0.7)).to eq(0.7)
86
+ end
87
+ end
88
+
89
+ describe 'thresholds' do
90
+ it 'defines NEGATIVE_VALENCE_THRESHOLD' do
91
+ expect(described_class::NEGATIVE_VALENCE_THRESHOLD).to eq(-0.3)
92
+ end
93
+
94
+ it 'defines HIGH_INTENSITY_THRESHOLD' do
95
+ expect(described_class::HIGH_INTENSITY_THRESHOLD).to eq(0.7)
96
+ end
97
+
98
+ it 'defines REAPPRAISAL_DIFFICULTY_MULTIPLIER' do
99
+ expect(described_class::REAPPRAISAL_DIFFICULTY_MULTIPLIER).to eq(0.5)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Affect::Reappraisal::Helpers::EmotionalEvent do
4
+ subject(:event) do
5
+ described_class.new(
6
+ content: 'test event',
7
+ valence: -0.6,
8
+ intensity: 0.5,
9
+ appraisal: 'threatening situation'
10
+ )
11
+ end
12
+
13
+ describe '#initialize' do
14
+ it 'assigns a UUID id' do
15
+ expect(event.id).to match(/\A[0-9a-f-]{36}\z/)
16
+ end
17
+
18
+ it 'stores content' do
19
+ expect(event.content).to eq('test event')
20
+ end
21
+
22
+ it 'stores initial_valence clamped to -1..1' do
23
+ e = described_class.new(content: 'x', valence: -2.0, intensity: 0.5, appraisal: 'a')
24
+ expect(e.initial_valence).to eq(-1.0)
25
+ end
26
+
27
+ it 'stores initial_intensity clamped to 0..1' do
28
+ e = described_class.new(content: 'x', valence: 0.0, intensity: 1.5, appraisal: 'a')
29
+ expect(e.initial_intensity).to eq(1.0)
30
+ end
31
+
32
+ it 'sets current_valence equal to initial_valence' do
33
+ expect(event.current_valence).to eq(event.initial_valence)
34
+ end
35
+
36
+ it 'sets current_intensity equal to initial_intensity' do
37
+ expect(event.current_intensity).to eq(event.initial_intensity)
38
+ end
39
+
40
+ it 'starts with reappraisal_count 0' do
41
+ expect(event.reappraisal_count).to eq(0)
42
+ end
43
+
44
+ it 'sets created_at to a Time' do
45
+ expect(event.created_at).to be_a(Time)
46
+ end
47
+ end
48
+
49
+ describe '#reappraise!' do
50
+ it 'increases valence toward positive' do
51
+ before = event.current_valence
52
+ event.reappraise!(strategy: :reinterpretation, new_appraisal: 'challenge')
53
+ expect(event.current_valence).to be > before
54
+ end
55
+
56
+ it 'reduces intensity' do
57
+ before = event.current_intensity
58
+ event.reappraise!(strategy: :reinterpretation, new_appraisal: 'challenge')
59
+ expect(event.current_intensity).to be < before
60
+ end
61
+
62
+ it 'increments reappraisal_count' do
63
+ expect { event.reappraise!(strategy: :distancing, new_appraisal: 'distant') }
64
+ .to change(event, :reappraisal_count).by(1)
65
+ end
66
+
67
+ it 'updates appraisal text' do
68
+ event.reappraise!(strategy: :benefit_finding, new_appraisal: 'growth opportunity')
69
+ expect(event.appraisal).to eq('growth opportunity')
70
+ end
71
+
72
+ it 'returns amount of change as a Float' do
73
+ change = event.reappraise!(strategy: :reinterpretation, new_appraisal: 'new view')
74
+ expect(change).to be_a(Float)
75
+ expect(change).to be >= 0.0
76
+ end
77
+
78
+ it 'returns 0.0 for invalid strategy' do
79
+ change = event.reappraise!(strategy: :nonexistent, new_appraisal: 'irrelevant')
80
+ expect(change).to eq(0.0)
81
+ end
82
+
83
+ it 'applies difficulty multiplier for high-intensity events' do
84
+ high = described_class.new(content: 'x', valence: -0.8, intensity: 0.9, appraisal: 'bad')
85
+ low = described_class.new(content: 'x', valence: -0.8, intensity: 0.3, appraisal: 'bad')
86
+
87
+ high_change = high.reappraise!(strategy: :reinterpretation, new_appraisal: 'reframed')
88
+ low_change = low.reappraise!(strategy: :reinterpretation, new_appraisal: 'reframed')
89
+
90
+ expect(high_change).to be < low_change
91
+ end
92
+
93
+ it 'does not allow valence to exceed 1.0' do
94
+ e = described_class.new(content: 'x', valence: 0.95, intensity: 0.1, appraisal: 'ok')
95
+ 10.times { e.reappraise!(strategy: :reinterpretation, new_appraisal: 'great') }
96
+ expect(e.current_valence).to be <= 1.0
97
+ end
98
+
99
+ it 'does not allow intensity to go below 0.0' do
100
+ e = described_class.new(content: 'x', valence: -0.5, intensity: 0.05, appraisal: 'bad')
101
+ 10.times { e.reappraise!(strategy: :reinterpretation, new_appraisal: 'better') }
102
+ expect(e.current_intensity).to be >= 0.0
103
+ end
104
+ end
105
+
106
+ describe '#negative?' do
107
+ it 'returns true when current_valence is below threshold' do
108
+ expect(event.negative?).to be true
109
+ end
110
+
111
+ it 'returns false for positive valence' do
112
+ e = described_class.new(content: 'x', valence: 0.5, intensity: 0.3, appraisal: 'good')
113
+ expect(e.negative?).to be false
114
+ end
115
+ end
116
+
117
+ describe '#intense?' do
118
+ it 'returns false for moderate intensity' do
119
+ expect(event.intense?).to be false
120
+ end
121
+
122
+ it 'returns true for high intensity' do
123
+ e = described_class.new(content: 'x', valence: -0.5, intensity: 0.8, appraisal: 'bad')
124
+ expect(e.intense?).to be true
125
+ end
126
+ end
127
+
128
+ describe '#regulation_amount' do
129
+ it 'returns 0.0 for unmodified event' do
130
+ expect(event.regulation_amount).to eq(0.0)
131
+ end
132
+
133
+ it 'returns positive value after reappraisal' do
134
+ event.reappraise!(strategy: :reinterpretation, new_appraisal: 'changed')
135
+ expect(event.regulation_amount).to be > 0.0
136
+ end
137
+
138
+ it 'returns value between 0 and 1' do
139
+ event.reappraise!(strategy: :reinterpretation, new_appraisal: 'changed')
140
+ expect(event.regulation_amount).to be_between(0.0, 1.0)
141
+ end
142
+ end
143
+
144
+ describe '#valence_label' do
145
+ it 'returns :negative for valence -0.6' do
146
+ expect(event.valence_label).to eq(:negative)
147
+ end
148
+
149
+ it 'returns :positive after positive reappraisals' do
150
+ e = described_class.new(content: 'x', valence: 0.7, intensity: 0.3, appraisal: 'good')
151
+ expect(e.valence_label).to eq(:positive)
152
+ end
153
+ end
154
+
155
+ describe '#intensity_label' do
156
+ it 'returns :moderate for intensity 0.5' do
157
+ expect(event.intensity_label).to eq(:moderate)
158
+ end
159
+ end
160
+
161
+ describe '#to_h' do
162
+ it 'returns a hash with all expected keys' do
163
+ h = event.to_h
164
+ expect(h).to include(:id, :content, :initial_valence, :current_valence,
165
+ :initial_intensity, :current_intensity, :appraisal,
166
+ :reappraisal_count, :negative, :intense,
167
+ :regulation_amount, :valence_label, :intensity_label, :created_at)
168
+ end
169
+
170
+ it 'reflects updated state after reappraisal' do
171
+ event.reappraise!(strategy: :benefit_finding, new_appraisal: 'growth')
172
+ h = event.to_h
173
+ expect(h[:reappraisal_count]).to eq(1)
174
+ expect(h[:appraisal]).to eq('growth')
175
+ end
176
+ end
177
+ end