lex-agentic-social 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 (235) 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-social.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/social/apprenticeship/client.rb +28 -0
  8. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship.rb +90 -0
  9. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine.rb +109 -0
  10. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model.rb +93 -0
  11. data/lib/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship.rb +112 -0
  12. data/lib/legion/extensions/agentic/social/apprenticeship/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/social/apprenticeship.rb +19 -0
  14. data/lib/legion/extensions/agentic/social/conflict/actors/stale_check.rb +45 -0
  15. data/lib/legion/extensions/agentic/social/conflict/client.rb +27 -0
  16. data/lib/legion/extensions/agentic/social/conflict/helpers/conflict_log.rb +70 -0
  17. data/lib/legion/extensions/agentic/social/conflict/helpers/llm_enhancer.rb +130 -0
  18. data/lib/legion/extensions/agentic/social/conflict/helpers/severity.rb +47 -0
  19. data/lib/legion/extensions/agentic/social/conflict/runners/conflict.rb +112 -0
  20. data/lib/legion/extensions/agentic/social/conflict/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/social/conflict.rb +19 -0
  22. data/lib/legion/extensions/agentic/social/conscience/client.rb +26 -0
  23. data/lib/legion/extensions/agentic/social/conscience/helpers/constants.rb +53 -0
  24. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_evaluator.rb +178 -0
  25. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_store.rb +116 -0
  26. data/lib/legion/extensions/agentic/social/conscience/runners/conscience.rb +117 -0
  27. data/lib/legion/extensions/agentic/social/conscience/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/social/conscience.rb +19 -0
  29. data/lib/legion/extensions/agentic/social/consent/actors/tier_evaluation.rb +45 -0
  30. data/lib/legion/extensions/agentic/social/consent/client.rb +27 -0
  31. data/lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb +199 -0
  32. data/lib/legion/extensions/agentic/social/consent/helpers/tiers.rb +54 -0
  33. data/lib/legion/extensions/agentic/social/consent/local_migrations/20260316000010_create_consent_domains.rb +16 -0
  34. data/lib/legion/extensions/agentic/social/consent/runners/consent.rb +228 -0
  35. data/lib/legion/extensions/agentic/social/consent/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/social/consent.rb +25 -0
  37. data/lib/legion/extensions/agentic/social/entrainment/client.rb +19 -0
  38. data/lib/legion/extensions/agentic/social/entrainment/helpers/constants.rb +40 -0
  39. data/lib/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine.rb +120 -0
  40. data/lib/legion/extensions/agentic/social/entrainment/helpers/pairing.rb +86 -0
  41. data/lib/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment.rb +89 -0
  42. data/lib/legion/extensions/agentic/social/entrainment/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/social/entrainment.rb +19 -0
  44. data/lib/legion/extensions/agentic/social/governance/actors/shadow_ai_scan.rb +19 -0
  45. data/lib/legion/extensions/agentic/social/governance/actors/vote_timeout.rb +45 -0
  46. data/lib/legion/extensions/agentic/social/governance/client.rb +27 -0
  47. data/lib/legion/extensions/agentic/social/governance/helpers/layers.rb +40 -0
  48. data/lib/legion/extensions/agentic/social/governance/helpers/proposal.rb +94 -0
  49. data/lib/legion/extensions/agentic/social/governance/runners/governance.rb +87 -0
  50. data/lib/legion/extensions/agentic/social/governance/runners/shadow_ai.rb +93 -0
  51. data/lib/legion/extensions/agentic/social/governance/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/social/governance.rb +20 -0
  53. data/lib/legion/extensions/agentic/social/joint_attention/actors/decay.rb +45 -0
  54. data/lib/legion/extensions/agentic/social/joint_attention/client.rb +28 -0
  55. data/lib/legion/extensions/agentic/social/joint_attention/helpers/attention_target.rb +124 -0
  56. data/lib/legion/extensions/agentic/social/joint_attention/helpers/constants.rb +34 -0
  57. data/lib/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager.rb +157 -0
  58. data/lib/legion/extensions/agentic/social/joint_attention/runners/joint_attention.rb +88 -0
  59. data/lib/legion/extensions/agentic/social/joint_attention/version.rb +13 -0
  60. data/lib/legion/extensions/agentic/social/joint_attention.rb +20 -0
  61. data/lib/legion/extensions/agentic/social/mentalizing/actors/decay.rb +45 -0
  62. data/lib/legion/extensions/agentic/social/mentalizing/client.rb +28 -0
  63. data/lib/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution.rb +58 -0
  64. data/lib/legion/extensions/agentic/social/mentalizing/helpers/constants.rb +33 -0
  65. data/lib/legion/extensions/agentic/social/mentalizing/helpers/mental_model.rb +137 -0
  66. data/lib/legion/extensions/agentic/social/mentalizing/runners/mentalizing.rb +93 -0
  67. data/lib/legion/extensions/agentic/social/mentalizing/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/social/mentalizing.rb +20 -0
  69. data/lib/legion/extensions/agentic/social/mirror/client.rb +33 -0
  70. data/lib/legion/extensions/agentic/social/mirror/helpers/constants.rb +65 -0
  71. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_engine.rb +132 -0
  72. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_event.rb +46 -0
  73. data/lib/legion/extensions/agentic/social/mirror/helpers/simulation.rb +43 -0
  74. data/lib/legion/extensions/agentic/social/mirror/runners/observe.rb +52 -0
  75. data/lib/legion/extensions/agentic/social/mirror/runners/resonance.rb +79 -0
  76. data/lib/legion/extensions/agentic/social/mirror/runners/simulate.rb +63 -0
  77. data/lib/legion/extensions/agentic/social/mirror/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/social/mirror.rb +22 -0
  79. data/lib/legion/extensions/agentic/social/mirror_system/actors/decay.rb +45 -0
  80. data/lib/legion/extensions/agentic/social/mirror_system/client.rb +28 -0
  81. data/lib/legion/extensions/agentic/social/mirror_system/helpers/constants.rb +62 -0
  82. data/lib/legion/extensions/agentic/social/mirror_system/helpers/mirror_system.rb +162 -0
  83. data/lib/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior.rb +67 -0
  84. data/lib/legion/extensions/agentic/social/mirror_system/runners/mirror.rb +99 -0
  85. data/lib/legion/extensions/agentic/social/mirror_system/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/social/mirror_system.rb +20 -0
  87. data/lib/legion/extensions/agentic/social/moral_reasoning/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/constants.rb +49 -0
  89. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma.rb +68 -0
  90. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer.rb +140 -0
  91. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine.rb +239 -0
  92. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation.rb +45 -0
  93. data/lib/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning.rb +121 -0
  94. data/lib/legion/extensions/agentic/social/moral_reasoning/version.rb +13 -0
  95. data/lib/legion/extensions/agentic/social/moral_reasoning.rb +21 -0
  96. data/lib/legion/extensions/agentic/social/perspective_shifting/client.rb +29 -0
  97. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/constants.rb +67 -0
  98. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective.rb +45 -0
  99. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view.rb +57 -0
  100. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb +166 -0
  101. data/lib/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting.rb +167 -0
  102. data/lib/legion/extensions/agentic/social/perspective_shifting/version.rb +13 -0
  103. data/lib/legion/extensions/agentic/social/perspective_shifting.rb +20 -0
  104. data/lib/legion/extensions/agentic/social/social/client.rb +25 -0
  105. data/lib/legion/extensions/agentic/social/social/helpers/constants.rb +84 -0
  106. data/lib/legion/extensions/agentic/social/social/helpers/social_graph.rb +172 -0
  107. data/lib/legion/extensions/agentic/social/social/runners/social.rb +146 -0
  108. data/lib/legion/extensions/agentic/social/social/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/social/social.rb +18 -0
  110. data/lib/legion/extensions/agentic/social/social_learning/client.rb +25 -0
  111. data/lib/legion/extensions/agentic/social/social_learning/helpers/constants.rb +42 -0
  112. data/lib/legion/extensions/agentic/social/social_learning/helpers/model_agent.rb +82 -0
  113. data/lib/legion/extensions/agentic/social/social_learning/helpers/observed_behavior.rb +61 -0
  114. data/lib/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine.rb +134 -0
  115. data/lib/legion/extensions/agentic/social/social_learning/runners/social_learning.rb +105 -0
  116. data/lib/legion/extensions/agentic/social/social_learning/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/social/social_learning.rb +20 -0
  118. data/lib/legion/extensions/agentic/social/symbiosis/client.rb +23 -0
  119. data/lib/legion/extensions/agentic/social/symbiosis/helpers/constants.rb +50 -0
  120. data/lib/legion/extensions/agentic/social/symbiosis/helpers/ecosystem.rb +113 -0
  121. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine.rb +104 -0
  122. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond.rb +112 -0
  123. data/lib/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis.rb +101 -0
  124. data/lib/legion/extensions/agentic/social/symbiosis/version.rb +13 -0
  125. data/lib/legion/extensions/agentic/social/symbiosis.rb +22 -0
  126. data/lib/legion/extensions/agentic/social/theory_of_mind/client.rb +26 -0
  127. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model.rb +173 -0
  128. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/constants.rb +70 -0
  129. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker.rb +169 -0
  130. data/lib/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind.rb +159 -0
  131. data/lib/legion/extensions/agentic/social/theory_of_mind/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/social/theory_of_mind.rb +19 -0
  133. data/lib/legion/extensions/agentic/social/trust/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/social/trust/client.rb +27 -0
  135. data/lib/legion/extensions/agentic/social/trust/helpers/trust_map.rb +160 -0
  136. data/lib/legion/extensions/agentic/social/trust/helpers/trust_model.rb +52 -0
  137. data/lib/legion/extensions/agentic/social/trust/local_migrations/20260316000020_create_trust_entries.rb +23 -0
  138. data/lib/legion/extensions/agentic/social/trust/runners/trust.rb +80 -0
  139. data/lib/legion/extensions/agentic/social/trust/version.rb +13 -0
  140. data/lib/legion/extensions/agentic/social/trust.rb +25 -0
  141. data/lib/legion/extensions/agentic/social/version.rb +11 -0
  142. data/lib/legion/extensions/agentic/social.rb +34 -0
  143. data/spec/legion/extensions/agentic/social/apprenticeship/client_spec.rb +20 -0
  144. data/spec/legion/extensions/agentic/social/apprenticeship/cognitive_apprenticeship_spec.rb +11 -0
  145. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine_spec.rb +146 -0
  146. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model_spec.rb +124 -0
  147. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_spec.rb +136 -0
  148. data/spec/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship_spec.rb +154 -0
  149. data/spec/legion/extensions/agentic/social/conflict/actors/stale_check_spec.rb +45 -0
  150. data/spec/legion/extensions/agentic/social/conflict/client_spec.rb +15 -0
  151. data/spec/legion/extensions/agentic/social/conflict/helpers/conflict_log_spec.rb +232 -0
  152. data/spec/legion/extensions/agentic/social/conflict/helpers/llm_enhancer_spec.rb +189 -0
  153. data/spec/legion/extensions/agentic/social/conflict/helpers/severity_spec.rb +215 -0
  154. data/spec/legion/extensions/agentic/social/conflict/runners/conflict_spec.rb +151 -0
  155. data/spec/legion/extensions/agentic/social/conscience/client_spec.rb +58 -0
  156. data/spec/legion/extensions/agentic/social/conscience/helpers/constants_spec.rb +124 -0
  157. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_evaluator_spec.rb +253 -0
  158. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_store_spec.rb +230 -0
  159. data/spec/legion/extensions/agentic/social/conscience/runners/conscience_spec.rb +239 -0
  160. data/spec/legion/extensions/agentic/social/consent/actors/tier_evaluation_spec.rb +46 -0
  161. data/spec/legion/extensions/agentic/social/consent/client_spec.rb +33 -0
  162. data/spec/legion/extensions/agentic/social/consent/helpers/tiers_spec.rb +49 -0
  163. data/spec/legion/extensions/agentic/social/consent/local_persistence_spec.rb +234 -0
  164. data/spec/legion/extensions/agentic/social/consent/runners/consent_spec.rb +224 -0
  165. data/spec/legion/extensions/agentic/social/entrainment/client_spec.rb +21 -0
  166. data/spec/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine_spec.rb +116 -0
  167. data/spec/legion/extensions/agentic/social/entrainment/helpers/pairing_spec.rb +103 -0
  168. data/spec/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment_spec.rb +87 -0
  169. data/spec/legion/extensions/agentic/social/governance/actors/vote_timeout_spec.rb +45 -0
  170. data/spec/legion/extensions/agentic/social/governance/client_spec.rb +14 -0
  171. data/spec/legion/extensions/agentic/social/governance/helpers/layers_spec.rb +190 -0
  172. data/spec/legion/extensions/agentic/social/governance/helpers/proposal_spec.rb +188 -0
  173. data/spec/legion/extensions/agentic/social/governance/runners/governance_spec.rb +101 -0
  174. data/spec/legion/extensions/agentic/social/governance/runners/shadow_ai_spec.rb +65 -0
  175. data/spec/legion/extensions/agentic/social/joint_attention/client_spec.rb +36 -0
  176. data/spec/legion/extensions/agentic/social/joint_attention/helpers/attention_target_spec.rb +258 -0
  177. data/spec/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager_spec.rb +238 -0
  178. data/spec/legion/extensions/agentic/social/joint_attention/runners/joint_attention_spec.rb +228 -0
  179. data/spec/legion/extensions/agentic/social/mentalizing/client_spec.rb +19 -0
  180. data/spec/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution_spec.rb +108 -0
  181. data/spec/legion/extensions/agentic/social/mentalizing/helpers/mental_model_spec.rb +179 -0
  182. data/spec/legion/extensions/agentic/social/mentalizing/runners/mentalizing_spec.rb +162 -0
  183. data/spec/legion/extensions/agentic/social/mirror/client_spec.rb +92 -0
  184. data/spec/legion/extensions/agentic/social/mirror/helpers/constants_spec.rb +123 -0
  185. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_engine_spec.rb +217 -0
  186. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_event_spec.rb +102 -0
  187. data/spec/legion/extensions/agentic/social/mirror/helpers/simulation_spec.rb +100 -0
  188. data/spec/legion/extensions/agentic/social/mirror/runners/observe_spec.rb +77 -0
  189. data/spec/legion/extensions/agentic/social/mirror/runners/resonance_spec.rb +123 -0
  190. data/spec/legion/extensions/agentic/social/mirror/runners/simulate_spec.rb +103 -0
  191. data/spec/legion/extensions/agentic/social/mirror_system/client_spec.rb +40 -0
  192. data/spec/legion/extensions/agentic/social/mirror_system/helpers/mirror_system_spec.rb +144 -0
  193. data/spec/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior_spec.rb +98 -0
  194. data/spec/legion/extensions/agentic/social/mirror_system/runners/mirror_spec.rb +122 -0
  195. data/spec/legion/extensions/agentic/social/moral_reasoning/client_spec.rb +34 -0
  196. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma_spec.rb +108 -0
  197. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer_spec.rb +232 -0
  198. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine_spec.rb +266 -0
  199. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation_spec.rb +70 -0
  200. data/spec/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning_spec.rb +275 -0
  201. data/spec/legion/extensions/agentic/social/perspective_shifting/client_spec.rb +30 -0
  202. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/constants_spec.rb +99 -0
  203. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_spec.rb +77 -0
  204. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view_spec.rb +105 -0
  205. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine_spec.rb +246 -0
  206. data/spec/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting_spec.rb +277 -0
  207. data/spec/legion/extensions/agentic/social/social/client_spec.rb +72 -0
  208. data/spec/legion/extensions/agentic/social/social/helpers/constants_spec.rb +99 -0
  209. data/spec/legion/extensions/agentic/social/social/helpers/social_graph_spec.rb +322 -0
  210. data/spec/legion/extensions/agentic/social/social/runners/social_spec.rb +220 -0
  211. data/spec/legion/extensions/agentic/social/social_learning/client_spec.rb +25 -0
  212. data/spec/legion/extensions/agentic/social/social_learning/helpers/constants_spec.rb +44 -0
  213. data/spec/legion/extensions/agentic/social/social_learning/helpers/model_agent_spec.rb +120 -0
  214. data/spec/legion/extensions/agentic/social/social_learning/helpers/observed_behavior_spec.rb +81 -0
  215. data/spec/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine_spec.rb +196 -0
  216. data/spec/legion/extensions/agentic/social/social_learning/runners/social_learning_spec.rb +150 -0
  217. data/spec/legion/extensions/agentic/social/symbiosis/client_spec.rb +45 -0
  218. data/spec/legion/extensions/agentic/social/symbiosis/helpers/constants_spec.rb +73 -0
  219. data/spec/legion/extensions/agentic/social/symbiosis/helpers/ecosystem_spec.rb +185 -0
  220. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine_spec.rb +182 -0
  221. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond_spec.rb +209 -0
  222. data/spec/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis_spec.rb +182 -0
  223. data/spec/legion/extensions/agentic/social/theory_of_mind/client_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model_spec.rb +244 -0
  225. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/constants_spec.rb +71 -0
  226. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker_spec.rb +228 -0
  227. data/spec/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind_spec.rb +221 -0
  228. data/spec/legion/extensions/agentic/social/trust/actors/decay_spec.rb +62 -0
  229. data/spec/legion/extensions/agentic/social/trust/client_spec.rb +17 -0
  230. data/spec/legion/extensions/agentic/social/trust/helpers/trust_map_spec.rb +299 -0
  231. data/spec/legion/extensions/agentic/social/trust/helpers/trust_model_spec.rb +179 -0
  232. data/spec/legion/extensions/agentic/social/trust/local_persistence_spec.rb +359 -0
  233. data/spec/legion/extensions/agentic/social/trust/runners/trust_spec.rb +84 -0
  234. data/spec/spec_helper.rb +54 -0
  235. metadata +319 -0
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::Constants do
4
+ describe 'constants' do
5
+ it 'defines MAX_PERSPECTIVES' do
6
+ expect(described_class::MAX_PERSPECTIVES).to eq(50)
7
+ end
8
+
9
+ it 'defines MAX_SITUATIONS' do
10
+ expect(described_class::MAX_SITUATIONS).to eq(200)
11
+ end
12
+
13
+ it 'defines MAX_VIEWS_PER_SITUATION' do
14
+ expect(described_class::MAX_VIEWS_PER_SITUATION).to eq(20)
15
+ end
16
+
17
+ it 'defines DEFAULT_EMPATHY' do
18
+ expect(described_class::DEFAULT_EMPATHY).to eq(0.5)
19
+ end
20
+
21
+ it 'defines MIN_PERSPECTIVES_FOR_SYNTHESIS' do
22
+ expect(described_class::MIN_PERSPECTIVES_FOR_SYNTHESIS).to eq(2)
23
+ end
24
+
25
+ it 'defines all perspective types' do
26
+ expect(described_class::PERSPECTIVE_TYPES).to include(:stakeholder, :ethical, :temporal, :cultural)
27
+ end
28
+
29
+ it 'defines all priority types' do
30
+ expect(described_class::PRIORITY_TYPES).to include(:safety, :efficiency, :fairness, :innovation)
31
+ end
32
+ end
33
+
34
+ describe '.empathy_label' do
35
+ it 'returns :deeply_empathic for 0.9' do
36
+ expect(described_class.empathy_label(0.9)).to eq(:deeply_empathic)
37
+ end
38
+
39
+ it 'returns :empathic for 0.7' do
40
+ expect(described_class.empathy_label(0.7)).to eq(:empathic)
41
+ end
42
+
43
+ it 'returns :moderate for 0.5' do
44
+ expect(described_class.empathy_label(0.5)).to eq(:moderate)
45
+ end
46
+
47
+ it 'returns :limited for 0.3' do
48
+ expect(described_class.empathy_label(0.3)).to eq(:limited)
49
+ end
50
+
51
+ it 'returns :detached for 0.1' do
52
+ expect(described_class.empathy_label(0.1)).to eq(:detached)
53
+ end
54
+ end
55
+
56
+ describe '.coverage_label' do
57
+ it 'returns :comprehensive for 0.9' do
58
+ expect(described_class.coverage_label(0.9)).to eq(:comprehensive)
59
+ end
60
+
61
+ it 'returns :thorough for 0.7' do
62
+ expect(described_class.coverage_label(0.7)).to eq(:thorough)
63
+ end
64
+
65
+ it 'returns :partial for 0.5' do
66
+ expect(described_class.coverage_label(0.5)).to eq(:partial)
67
+ end
68
+
69
+ it 'returns :narrow for 0.3' do
70
+ expect(described_class.coverage_label(0.3)).to eq(:narrow)
71
+ end
72
+
73
+ it 'returns :blind for 0.1' do
74
+ expect(described_class.coverage_label(0.1)).to eq(:blind)
75
+ end
76
+ end
77
+
78
+ describe '.agreement_label' do
79
+ it 'returns :consensus for 0.9' do
80
+ expect(described_class.agreement_label(0.9)).to eq(:consensus)
81
+ end
82
+
83
+ it 'returns :agreement for 0.7' do
84
+ expect(described_class.agreement_label(0.7)).to eq(:agreement)
85
+ end
86
+
87
+ it 'returns :mixed for 0.5' do
88
+ expect(described_class.agreement_label(0.5)).to eq(:mixed)
89
+ end
90
+
91
+ it 'returns :disagreement for 0.3' do
92
+ expect(described_class.agreement_label(0.3)).to eq(:disagreement)
93
+ end
94
+
95
+ it 'returns :conflict for 0.1' do
96
+ expect(described_class.agreement_label(0.1)).to eq(:conflict)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::Perspective do
4
+ let(:perspective) do
5
+ described_class.new(
6
+ name: 'CEO',
7
+ perspective_type: :stakeholder,
8
+ priorities: %i[growth stability],
9
+ expertise_domains: %w[business strategy],
10
+ empathy_level: 0.7,
11
+ bias_toward: :profit
12
+ )
13
+ end
14
+
15
+ describe '#initialize' do
16
+ it 'assigns a UUID id' do
17
+ expect(perspective.id).to match(/\A[0-9a-f-]{36}\z/)
18
+ end
19
+
20
+ it 'assigns name' do
21
+ expect(perspective.name).to eq('CEO')
22
+ end
23
+
24
+ it 'assigns perspective_type' do
25
+ expect(perspective.perspective_type).to eq(:stakeholder)
26
+ end
27
+
28
+ it 'assigns priorities' do
29
+ expect(perspective.priorities).to eq(%i[growth stability])
30
+ end
31
+
32
+ it 'assigns expertise_domains' do
33
+ expect(perspective.expertise_domains).to eq(%w[business strategy])
34
+ end
35
+
36
+ it 'clamps empathy_level between 0 and 1' do
37
+ p = described_class.new(name: 'x', perspective_type: :ethical, empathy_level: 1.5)
38
+ expect(p.empathy_level).to eq(1.0)
39
+ end
40
+
41
+ it 'clamps empathy_level at 0' do
42
+ p = described_class.new(name: 'x', perspective_type: :ethical, empathy_level: -0.5)
43
+ expect(p.empathy_level).to eq(0.0)
44
+ end
45
+
46
+ it 'assigns bias_toward' do
47
+ expect(perspective.bias_toward).to eq(:profit)
48
+ end
49
+
50
+ it 'assigns created_at as UTC time' do
51
+ expect(perspective.created_at).to be_a(Time)
52
+ end
53
+
54
+ it 'defaults priorities to empty array' do
55
+ p = described_class.new(name: 'x', perspective_type: :ethical)
56
+ expect(p.priorities).to eq([])
57
+ end
58
+
59
+ it 'defaults expertise_domains to empty array' do
60
+ p = described_class.new(name: 'x', perspective_type: :ethical)
61
+ expect(p.expertise_domains).to eq([])
62
+ end
63
+ end
64
+
65
+ describe '#to_h' do
66
+ subject(:hash) { perspective.to_h }
67
+
68
+ it 'includes all keys' do
69
+ expect(hash.keys).to include(:id, :name, :perspective_type, :priorities,
70
+ :expertise_domains, :empathy_level, :bias_toward, :created_at)
71
+ end
72
+
73
+ it 'rounds empathy_level to 10 decimal places' do
74
+ expect(hash[:empathy_level]).to eq(0.7.round(10))
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::PerspectiveView do
4
+ let(:view) do
5
+ described_class.new(
6
+ situation_id: 'sit-123',
7
+ perspective_id: 'persp-456',
8
+ valence: 0.6,
9
+ concerns: ['risk of failure'],
10
+ opportunities: ['cost savings'],
11
+ confidence: 0.8
12
+ )
13
+ end
14
+
15
+ describe '#initialize' do
16
+ it 'assigns a UUID id' do
17
+ expect(view.id).to match(/\A[0-9a-f-]{36}\z/)
18
+ end
19
+
20
+ it 'assigns situation_id' do
21
+ expect(view.situation_id).to eq('sit-123')
22
+ end
23
+
24
+ it 'assigns perspective_id' do
25
+ expect(view.perspective_id).to eq('persp-456')
26
+ end
27
+
28
+ it 'clamps valence to -1..1' do
29
+ v = described_class.new(situation_id: 's', perspective_id: 'p', valence: 2.0)
30
+ expect(v.valence).to eq(1.0)
31
+ end
32
+
33
+ it 'clamps valence at -1' do
34
+ v = described_class.new(situation_id: 's', perspective_id: 'p', valence: -3.0)
35
+ expect(v.valence).to eq(-1.0)
36
+ end
37
+
38
+ it 'clamps confidence to 0..1' do
39
+ v = described_class.new(situation_id: 's', perspective_id: 'p', confidence: 1.5)
40
+ expect(v.confidence).to eq(1.0)
41
+ end
42
+
43
+ it 'assigns concerns array' do
44
+ expect(view.concerns).to eq(['risk of failure'])
45
+ end
46
+
47
+ it 'assigns opportunities array' do
48
+ expect(view.opportunities).to eq(['cost savings'])
49
+ end
50
+
51
+ it 'assigns created_at' do
52
+ expect(view.created_at).to be_a(Time)
53
+ end
54
+ end
55
+
56
+ describe '#positive?' do
57
+ it 'returns true when valence > 0.1' do
58
+ expect(view.positive?).to be true
59
+ end
60
+
61
+ it 'returns false for neutral valence' do
62
+ v = described_class.new(situation_id: 's', perspective_id: 'p', valence: 0.0)
63
+ expect(v.positive?).to be false
64
+ end
65
+ end
66
+
67
+ describe '#negative?' do
68
+ it 'returns true when valence < -0.1' do
69
+ v = described_class.new(situation_id: 's', perspective_id: 'p', valence: -0.5)
70
+ expect(v.negative?).to be true
71
+ end
72
+
73
+ it 'returns false for positive valence' do
74
+ expect(view.negative?).to be false
75
+ end
76
+ end
77
+
78
+ describe '#neutral?' do
79
+ it 'returns true when valence is near 0' do
80
+ v = described_class.new(situation_id: 's', perspective_id: 'p', valence: 0.05)
81
+ expect(v.neutral?).to be true
82
+ end
83
+
84
+ it 'returns false when clearly positive' do
85
+ expect(view.neutral?).to be false
86
+ end
87
+ end
88
+
89
+ describe '#to_h' do
90
+ subject(:hash) { view.to_h }
91
+
92
+ it 'includes all keys' do
93
+ expect(hash.keys).to include(:id, :situation_id, :perspective_id, :valence,
94
+ :concerns, :opportunities, :confidence, :created_at)
95
+ end
96
+
97
+ it 'rounds valence to 10 decimal places' do
98
+ expect(hash[:valence]).to eq(0.6.round(10))
99
+ end
100
+
101
+ it 'rounds confidence to 10 decimal places' do
102
+ expect(hash[:confidence]).to eq(0.8.round(10))
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::ShiftingEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ let(:ceo_id) do
7
+ engine.add_perspective(name: 'CEO', type: :stakeholder, priorities: %i[growth], empathy: 0.6).id
8
+ end
9
+
10
+ let(:engineer_id) do
11
+ engine.add_perspective(name: 'Engineer', type: :pragmatic, priorities: %i[stability], empathy: 0.7).id
12
+ end
13
+
14
+ let(:ethicist_id) do
15
+ engine.add_perspective(name: 'Ethicist', type: :ethical, priorities: %i[fairness], empathy: 0.9).id
16
+ end
17
+
18
+ let(:situation_id) do
19
+ engine.add_situation(content: 'Deploy AI hiring tool')[:id]
20
+ end
21
+
22
+ describe '#add_perspective' do
23
+ it 'creates a perspective and stores it' do
24
+ p = engine.add_perspective(name: 'Manager', type: :stakeholder)
25
+ expect(engine.perspectives[p.id]).to eq(p)
26
+ end
27
+
28
+ it 'returns error for invalid type' do
29
+ result = engine.add_perspective(name: 'x', type: :unknown)
30
+ expect(result[:error]).to eq(:invalid_type)
31
+ end
32
+
33
+ it 'returns error when at capacity' do
34
+ 50.times { |i| engine.add_perspective(name: "p#{i}", type: :stakeholder) }
35
+ result = engine.add_perspective(name: 'overflow', type: :stakeholder)
36
+ expect(result[:error]).to eq(:too_many_perspectives)
37
+ end
38
+ end
39
+
40
+ describe '#add_situation' do
41
+ it 'stores a situation and returns id' do
42
+ sit = engine.add_situation(content: 'test situation')
43
+ expect(sit[:id]).to match(/\A[0-9a-f-]{36}\z/)
44
+ expect(engine.situations[sit[:id]][:content]).to eq('test situation')
45
+ end
46
+ end
47
+
48
+ describe '#generate_view' do
49
+ it 'creates and stores a view for a situation' do
50
+ view = engine.generate_view(
51
+ situation_id: situation_id,
52
+ perspective_id: ceo_id,
53
+ valence: 0.7,
54
+ concerns: ['regulatory risk'],
55
+ opportunities: ['efficiency gains'],
56
+ confidence: 0.85
57
+ )
58
+ expect(engine.situations[situation_id][:views]).to include(view)
59
+ end
60
+
61
+ it 'returns error for unknown situation' do
62
+ result = engine.generate_view(situation_id: 'nope', perspective_id: ceo_id)
63
+ expect(result[:error]).to eq(:situation_not_found)
64
+ end
65
+
66
+ it 'returns error for unknown perspective' do
67
+ result = engine.generate_view(situation_id: situation_id, perspective_id: 'nope')
68
+ expect(result[:error]).to eq(:perspective_not_found)
69
+ end
70
+
71
+ it 'returns error when view limit reached' do
72
+ 20.times do |i|
73
+ pid = engine.add_perspective(name: "p#{i}", type: :stakeholder).id
74
+ engine.generate_view(situation_id: situation_id, perspective_id: pid)
75
+ end
76
+ extra_id = engine.add_perspective(name: 'extra', type: :ethical).id
77
+ result = engine.generate_view(situation_id: situation_id, perspective_id: extra_id)
78
+ expect(result[:error]).to eq(:too_many_views)
79
+ end
80
+ end
81
+
82
+ describe '#views_for_situation' do
83
+ it 'returns all views for a situation' do
84
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
85
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, valence: -0.3)
86
+ views = engine.views_for_situation(situation_id: situation_id)
87
+ expect(views.size).to eq(2)
88
+ end
89
+
90
+ it 'returns empty array for unknown situation' do
91
+ expect(engine.views_for_situation(situation_id: 'nope')).to eq([])
92
+ end
93
+ end
94
+
95
+ describe '#perspective_agreement' do
96
+ it 'returns 0.0 when fewer than 2 views' do
97
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
98
+ expect(engine.perspective_agreement(situation_id: situation_id)).to eq(0.0)
99
+ end
100
+
101
+ it 'returns high agreement when valences are similar' do
102
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
103
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, valence: 0.5)
104
+ score = engine.perspective_agreement(situation_id: situation_id)
105
+ expect(score).to be > 0.9
106
+ end
107
+
108
+ it 'returns low agreement when valences diverge' do
109
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 1.0)
110
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, valence: -1.0)
111
+ score = engine.perspective_agreement(situation_id: situation_id)
112
+ expect(score).to be < 0.1
113
+ end
114
+ end
115
+
116
+ describe '#blind_spots' do
117
+ it 'returns perspective types not yet applied' do
118
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
119
+ spots = engine.blind_spots(situation_id: situation_id)
120
+ expect(spots).to include(:ethical, :temporal, :cultural)
121
+ expect(spots).not_to include(:stakeholder)
122
+ end
123
+
124
+ it 'returns all types when no views exist' do
125
+ spots = engine.blind_spots(situation_id: situation_id)
126
+ expect(spots).to eq(Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::Constants::PERSPECTIVE_TYPES)
127
+ end
128
+ end
129
+
130
+ describe '#coverage_score' do
131
+ it 'returns 0.0 when no perspectives exist' do
132
+ e = described_class.new
133
+ sit = e.add_situation(content: 'x')[:id]
134
+ expect(e.coverage_score(situation_id: sit)).to eq(0.0)
135
+ end
136
+
137
+ it 'returns fraction of perspectives covered' do
138
+ # Force all 3 perspectives to exist before generating any views
139
+ ceo_id
140
+ engineer_id
141
+ ethicist_id
142
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
143
+ score = engine.coverage_score(situation_id: situation_id)
144
+ # 1 of 3 perspectives applied
145
+ expect(score).to be_within(0.001).of(1.0 / 3.0)
146
+ end
147
+
148
+ it 'returns 1.0 when all perspectives covered' do
149
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
150
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, valence: -0.2)
151
+ engine.generate_view(situation_id: situation_id, perspective_id: ethicist_id, valence: -0.6)
152
+ expect(engine.coverage_score(situation_id: situation_id)).to eq(1.0)
153
+ end
154
+ end
155
+
156
+ describe '#dominant_view' do
157
+ it 'returns the view with the highest confidence' do
158
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, confidence: 0.5)
159
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, confidence: 0.9)
160
+ dominant = engine.dominant_view(situation_id: situation_id)
161
+ expect(dominant.confidence).to eq(0.9)
162
+ end
163
+
164
+ it 'returns nil when no views' do
165
+ expect(engine.dominant_view(situation_id: situation_id)).to be_nil
166
+ end
167
+ end
168
+
169
+ describe '#synthesize' do
170
+ it 'returns error when fewer than 2 views' do
171
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
172
+ result = engine.synthesize(situation_id: situation_id)
173
+ expect(result[:error]).to eq(:insufficient_views)
174
+ end
175
+
176
+ it 'returns weighted_valence combining all views' do
177
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id,
178
+ valence: 1.0, confidence: 1.0)
179
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id,
180
+ valence: -1.0, confidence: 1.0)
181
+ result = engine.synthesize(situation_id: situation_id)
182
+ expect(result[:weighted_valence]).to be_within(0.001).of(0.0)
183
+ end
184
+
185
+ it 'includes unique concerns from all views' do
186
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id,
187
+ concerns: ['risk A'], confidence: 0.8)
188
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id,
189
+ concerns: ['risk B'], confidence: 0.7)
190
+ result = engine.synthesize(situation_id: situation_id)
191
+ expect(result[:concerns]).to include('risk A', 'risk B')
192
+ end
193
+
194
+ it 'includes unique opportunities from all views' do
195
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id,
196
+ opportunities: ['gain A'], confidence: 0.8)
197
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id,
198
+ opportunities: ['gain B'], confidence: 0.7)
199
+ result = engine.synthesize(situation_id: situation_id)
200
+ expect(result[:opportunities]).to include('gain A', 'gain B')
201
+ end
202
+
203
+ it 'includes view_count, coverage_score, coverage_label, agreement, agreement_label' do
204
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, confidence: 0.8)
205
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, confidence: 0.7)
206
+ result = engine.synthesize(situation_id: situation_id)
207
+ expect(result.keys).to include(:view_count, :coverage_score, :coverage_label,
208
+ :agreement, :agreement_label)
209
+ end
210
+ end
211
+
212
+ describe '#most_divergent_pair' do
213
+ it 'returns nil when fewer than 2 views' do
214
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.5)
215
+ expect(engine.most_divergent_pair(situation_id: situation_id)).to be_nil
216
+ end
217
+
218
+ it 'returns the pair with the largest valence difference' do
219
+ engine.generate_view(situation_id: situation_id, perspective_id: ceo_id, valence: 0.9)
220
+ engine.generate_view(situation_id: situation_id, perspective_id: engineer_id, valence: -0.8)
221
+ engine.generate_view(situation_id: situation_id, perspective_id: ethicist_id, valence: 0.1)
222
+ pair = engine.most_divergent_pair(situation_id: situation_id)
223
+ expect(pair).not_to be_nil
224
+ divergence = (pair[0].valence - pair[1].valence).abs
225
+ expect(divergence).to be_within(0.001).of(1.7)
226
+ end
227
+ end
228
+
229
+ describe '#to_h' do
230
+ it 'includes perspective_count and situation_count' do
231
+ ceo_id
232
+ situation_id
233
+ h = engine.to_h
234
+ expect(h[:perspective_count]).to eq(1)
235
+ expect(h[:situation_count]).to eq(1)
236
+ end
237
+
238
+ it 'serializes perspectives and situations' do
239
+ ceo_id
240
+ situation_id
241
+ h = engine.to_h
242
+ expect(h[:perspectives].first[:name]).to eq('CEO')
243
+ expect(h[:situations].first[:content]).to eq('Deploy AI hiring tool')
244
+ end
245
+ end
246
+ end