lex-agentic-learning 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 (192) 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-learning.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/learning/anchoring/client.rb +26 -0
  8. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor.rb +65 -0
  9. data/lib/legion/extensions/agentic/learning/anchoring/helpers/anchor_store.rb +132 -0
  10. data/lib/legion/extensions/agentic/learning/anchoring/helpers/constants.rb +31 -0
  11. data/lib/legion/extensions/agentic/learning/anchoring/runners/anchoring.rb +100 -0
  12. data/lib/legion/extensions/agentic/learning/anchoring/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/learning/anchoring.rb +19 -0
  14. data/lib/legion/extensions/agentic/learning/catalyst/client.rb +15 -0
  15. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst.rb +87 -0
  16. data/lib/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine.rb +153 -0
  17. data/lib/legion/extensions/agentic/learning/catalyst/helpers/constants.rb +55 -0
  18. data/lib/legion/extensions/agentic/learning/catalyst/helpers/reaction.rb +87 -0
  19. data/lib/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst.rb +103 -0
  20. data/lib/legion/extensions/agentic/learning/catalyst/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/learning/catalyst.rb +22 -0
  22. data/lib/legion/extensions/agentic/learning/chrysalis/client.rb +22 -0
  23. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis.rb +137 -0
  24. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/cocoon.rb +89 -0
  25. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/constants.rb +49 -0
  26. data/lib/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine.rb +157 -0
  27. data/lib/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis.rb +129 -0
  28. data/lib/legion/extensions/agentic/learning/chrysalis/version.rb +13 -0
  29. data/lib/legion/extensions/agentic/learning/chrysalis.rb +21 -0
  30. data/lib/legion/extensions/agentic/learning/curiosity/client.rb +28 -0
  31. data/lib/legion/extensions/agentic/learning/curiosity/helpers/constants.rb +30 -0
  32. data/lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb +167 -0
  33. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder.rb +73 -0
  34. data/lib/legion/extensions/agentic/learning/curiosity/helpers/wonder_store.rb +149 -0
  35. data/lib/legion/extensions/agentic/learning/curiosity/runners/curiosity.rb +163 -0
  36. data/lib/legion/extensions/agentic/learning/curiosity/version.rb +13 -0
  37. data/lib/legion/extensions/agentic/learning/curiosity.rb +21 -0
  38. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/client.rb +28 -0
  39. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants.rb +31 -0
  40. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine.rb +122 -0
  41. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap.rb +70 -0
  42. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity.rb +106 -0
  43. data/lib/legion/extensions/agentic/learning/epistemic_curiosity/version.rb +13 -0
  44. data/lib/legion/extensions/agentic/learning/epistemic_curiosity.rb +19 -0
  45. data/lib/legion/extensions/agentic/learning/fermentation/client.rb +19 -0
  46. data/lib/legion/extensions/agentic/learning/fermentation/helpers/batch.rb +75 -0
  47. data/lib/legion/extensions/agentic/learning/fermentation/helpers/constants.rb +78 -0
  48. data/lib/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine.rb +147 -0
  49. data/lib/legion/extensions/agentic/learning/fermentation/helpers/substrate.rb +108 -0
  50. data/lib/legion/extensions/agentic/learning/fermentation/runners/cognitive_fermentation.rb +60 -0
  51. data/lib/legion/extensions/agentic/learning/fermentation/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/learning/fermentation.rb +22 -0
  53. data/lib/legion/extensions/agentic/learning/habit/client.rb +26 -0
  54. data/lib/legion/extensions/agentic/learning/habit/helpers/action_sequence.rb +120 -0
  55. data/lib/legion/extensions/agentic/learning/habit/helpers/constants.rb +44 -0
  56. data/lib/legion/extensions/agentic/learning/habit/helpers/habit_store.rb +148 -0
  57. data/lib/legion/extensions/agentic/learning/habit/runners/habit.rb +86 -0
  58. data/lib/legion/extensions/agentic/learning/habit/version.rb +13 -0
  59. data/lib/legion/extensions/agentic/learning/habit.rb +19 -0
  60. data/lib/legion/extensions/agentic/learning/hebbian/actors/decay.rb +45 -0
  61. data/lib/legion/extensions/agentic/learning/hebbian/client.rb +29 -0
  62. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly.rb +82 -0
  63. data/lib/legion/extensions/agentic/learning/hebbian/helpers/assembly_network.rb +190 -0
  64. data/lib/legion/extensions/agentic/learning/hebbian/helpers/constants.rb +50 -0
  65. data/lib/legion/extensions/agentic/learning/hebbian/helpers/unit.rb +94 -0
  66. data/lib/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly.rb +94 -0
  67. data/lib/legion/extensions/agentic/learning/hebbian/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/learning/hebbian.rb +20 -0
  69. data/lib/legion/extensions/agentic/learning/learning_rate/client.rb +25 -0
  70. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/constants.rb +35 -0
  71. data/lib/legion/extensions/agentic/learning/learning_rate/helpers/rate_model.rb +133 -0
  72. data/lib/legion/extensions/agentic/learning/learning_rate/runners/learning_rate.rb +85 -0
  73. data/lib/legion/extensions/agentic/learning/learning_rate/version.rb +13 -0
  74. data/lib/legion/extensions/agentic/learning/learning_rate.rb +18 -0
  75. data/lib/legion/extensions/agentic/learning/meta_learning/client.rb +27 -0
  76. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/constants.rb +46 -0
  77. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain.rb +85 -0
  78. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine.rb +202 -0
  79. data/lib/legion/extensions/agentic/learning/meta_learning/helpers/strategy.rb +62 -0
  80. data/lib/legion/extensions/agentic/learning/meta_learning/runners/meta_learning.rb +118 -0
  81. data/lib/legion/extensions/agentic/learning/meta_learning/version.rb +13 -0
  82. data/lib/legion/extensions/agentic/learning/meta_learning.rb +20 -0
  83. data/lib/legion/extensions/agentic/learning/plasticity/client.rb +15 -0
  84. data/lib/legion/extensions/agentic/learning/plasticity/helpers/constants.rb +45 -0
  85. data/lib/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway.rb +85 -0
  86. data/lib/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine.rb +130 -0
  87. data/lib/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity.rb +85 -0
  88. data/lib/legion/extensions/agentic/learning/plasticity/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/learning/plasticity.rb +19 -0
  90. data/lib/legion/extensions/agentic/learning/preference_learning/actors/decay.rb +45 -0
  91. data/lib/legion/extensions/agentic/learning/preference_learning/client.rb +28 -0
  92. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/constants.rb +35 -0
  93. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/option.rb +78 -0
  94. data/lib/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine.rb +121 -0
  95. data/lib/legion/extensions/agentic/learning/preference_learning/runners/preference_learning.rb +84 -0
  96. data/lib/legion/extensions/agentic/learning/preference_learning/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/learning/preference_learning.rb +19 -0
  98. data/lib/legion/extensions/agentic/learning/procedural/client.rb +19 -0
  99. data/lib/legion/extensions/agentic/learning/procedural/helpers/constants.rb +46 -0
  100. data/lib/legion/extensions/agentic/learning/procedural/helpers/learning_engine.rb +160 -0
  101. data/lib/legion/extensions/agentic/learning/procedural/helpers/production.rb +66 -0
  102. data/lib/legion/extensions/agentic/learning/procedural/helpers/skill.rb +101 -0
  103. data/lib/legion/extensions/agentic/learning/procedural/runners/procedural_learning.rb +96 -0
  104. data/lib/legion/extensions/agentic/learning/procedural/version.rb +13 -0
  105. data/lib/legion/extensions/agentic/learning/procedural.rb +20 -0
  106. data/lib/legion/extensions/agentic/learning/scaffolding/client.rb +26 -0
  107. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/constants.rb +42 -0
  108. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffold.rb +136 -0
  109. data/lib/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine.rb +112 -0
  110. data/lib/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding.rb +107 -0
  111. data/lib/legion/extensions/agentic/learning/scaffolding/version.rb +13 -0
  112. data/lib/legion/extensions/agentic/learning/scaffolding.rb +19 -0
  113. data/lib/legion/extensions/agentic/learning/version.rb +11 -0
  114. data/lib/legion/extensions/agentic/learning.rb +31 -0
  115. data/spec/legion/extensions/agentic/learning/anchoring/client_spec.rb +32 -0
  116. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_spec.rb +130 -0
  117. data/spec/legion/extensions/agentic/learning/anchoring/helpers/anchor_store_spec.rb +201 -0
  118. data/spec/legion/extensions/agentic/learning/anchoring/helpers/constants_spec.rb +63 -0
  119. data/spec/legion/extensions/agentic/learning/anchoring/runners/anchoring_spec.rb +199 -0
  120. data/spec/legion/extensions/agentic/learning/catalyst/client_spec.rb +58 -0
  121. data/spec/legion/extensions/agentic/learning/catalyst/cognitive_catalyst_spec.rb +49 -0
  122. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_engine_spec.rb +263 -0
  123. data/spec/legion/extensions/agentic/learning/catalyst/helpers/catalyst_spec.rb +214 -0
  124. data/spec/legion/extensions/agentic/learning/catalyst/helpers/reaction_spec.rb +223 -0
  125. data/spec/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst_spec.rb +217 -0
  126. data/spec/legion/extensions/agentic/learning/chrysalis/client_spec.rb +83 -0
  127. data/spec/legion/extensions/agentic/learning/chrysalis/cognitive_chrysalis_spec.rb +15 -0
  128. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_engine_spec.rb +57 -0
  129. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/chrysalis_spec.rb +305 -0
  130. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/cocoon_spec.rb +206 -0
  131. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/constants_spec.rb +109 -0
  132. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphic_cycle_spec.rb +76 -0
  133. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/metamorphosis_engine_spec.rb +247 -0
  134. data/spec/legion/extensions/agentic/learning/chrysalis/helpers/transformation_phase_spec.rb +98 -0
  135. data/spec/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis_spec.rb +180 -0
  136. data/spec/legion/extensions/agentic/learning/chrysalis/runners/reporting_spec.rb +81 -0
  137. data/spec/legion/extensions/agentic/learning/chrysalis/runners/transformation_spec.rb +74 -0
  138. data/spec/legion/extensions/agentic/learning/curiosity/client_spec.rb +27 -0
  139. data/spec/legion/extensions/agentic/learning/curiosity/helpers/gap_detector_spec.rb +118 -0
  140. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_spec.rb +130 -0
  141. data/spec/legion/extensions/agentic/learning/curiosity/helpers/wonder_store_spec.rb +136 -0
  142. data/spec/legion/extensions/agentic/learning/curiosity/runners/curiosity_spec.rb +159 -0
  143. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/client_spec.rb +47 -0
  144. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/constants_spec.rb +45 -0
  145. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/curiosity_engine_spec.rb +229 -0
  146. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/helpers/knowledge_gap_spec.rb +188 -0
  147. data/spec/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity_spec.rb +175 -0
  148. data/spec/legion/extensions/agentic/learning/fermentation/client_spec.rb +36 -0
  149. data/spec/legion/extensions/agentic/learning/fermentation/helpers/batch_spec.rb +72 -0
  150. data/spec/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine_spec.rb +138 -0
  151. data/spec/legion/extensions/agentic/learning/fermentation/helpers/substrate_spec.rb +146 -0
  152. data/spec/legion/extensions/agentic/learning/habit/client_spec.rb +50 -0
  153. data/spec/legion/extensions/agentic/learning/habit/helpers/action_sequence_spec.rb +276 -0
  154. data/spec/legion/extensions/agentic/learning/habit/helpers/constants_spec.rb +115 -0
  155. data/spec/legion/extensions/agentic/learning/habit/helpers/habit_store_spec.rb +274 -0
  156. data/spec/legion/extensions/agentic/learning/habit/runners/habit_spec.rb +228 -0
  157. data/spec/legion/extensions/agentic/learning/hebbian/client_spec.rb +38 -0
  158. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_network_spec.rb +142 -0
  159. data/spec/legion/extensions/agentic/learning/hebbian/helpers/assembly_spec.rb +89 -0
  160. data/spec/legion/extensions/agentic/learning/hebbian/helpers/unit_spec.rb +119 -0
  161. data/spec/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly_spec.rb +109 -0
  162. data/spec/legion/extensions/agentic/learning/learning_rate/client_spec.rb +51 -0
  163. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/constants_spec.rb +29 -0
  164. data/spec/legion/extensions/agentic/learning/learning_rate/helpers/rate_model_spec.rb +151 -0
  165. data/spec/legion/extensions/agentic/learning/learning_rate/runners/learning_rate_spec.rb +92 -0
  166. data/spec/legion/extensions/agentic/learning/meta_learning/client_spec.rb +27 -0
  167. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/constants_spec.rb +43 -0
  168. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/learning_domain_spec.rb +146 -0
  169. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/meta_learning_engine_spec.rb +309 -0
  170. data/spec/legion/extensions/agentic/learning/meta_learning/helpers/strategy_spec.rb +82 -0
  171. data/spec/legion/extensions/agentic/learning/meta_learning/runners/meta_learning_spec.rb +185 -0
  172. data/spec/legion/extensions/agentic/learning/plasticity/helpers/constants_spec.rb +54 -0
  173. data/spec/legion/extensions/agentic/learning/plasticity/helpers/neural_pathway_spec.rb +136 -0
  174. data/spec/legion/extensions/agentic/learning/plasticity/helpers/plasticity_engine_spec.rb +157 -0
  175. data/spec/legion/extensions/agentic/learning/plasticity/runners/cognitive_plasticity_spec.rb +83 -0
  176. data/spec/legion/extensions/agentic/learning/preference_learning/client_spec.rb +17 -0
  177. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/constants_spec.rb +67 -0
  178. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/option_spec.rb +104 -0
  179. data/spec/legion/extensions/agentic/learning/preference_learning/helpers/preference_engine_spec.rb +151 -0
  180. data/spec/legion/extensions/agentic/learning/preference_learning/runners/preference_learning_spec.rb +86 -0
  181. data/spec/legion/extensions/agentic/learning/procedural/client_spec.rb +22 -0
  182. data/spec/legion/extensions/agentic/learning/procedural/helpers/learning_engine_spec.rb +135 -0
  183. data/spec/legion/extensions/agentic/learning/procedural/helpers/production_spec.rb +66 -0
  184. data/spec/legion/extensions/agentic/learning/procedural/helpers/skill_spec.rb +102 -0
  185. data/spec/legion/extensions/agentic/learning/procedural/runners/procedural_learning_spec.rb +94 -0
  186. data/spec/legion/extensions/agentic/learning/scaffolding/client_spec.rb +20 -0
  187. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/constants_spec.rb +36 -0
  188. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffold_spec.rb +187 -0
  189. data/spec/legion/extensions/agentic/learning/scaffolding/helpers/scaffolding_engine_spec.rb +159 -0
  190. data/spec/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding_spec.rb +163 -0
  191. data/spec/spec_helper.rb +46 -0
  192. metadata +277 -0
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Procedural::Runners::ProceduralLearning do
4
+ let(:runner_host) do
5
+ obj = Object.new
6
+ obj.extend(described_class)
7
+ obj
8
+ end
9
+
10
+ describe '#create_skill' do
11
+ it 'creates a skill' do
12
+ result = runner_host.create_skill(name: 'api_retry', domain: :http)
13
+ expect(result[:success]).to be true
14
+ expect(result[:skill_id]).to be_a(String)
15
+ end
16
+ end
17
+
18
+ describe '#add_skill_production' do
19
+ it 'adds a production to a skill' do
20
+ created = runner_host.create_skill(name: 'test', domain: :test)
21
+ result = runner_host.add_skill_production(
22
+ skill_id: created[:skill_id], condition: 'if_error',
23
+ action: 'retry', domain: :test
24
+ )
25
+ expect(result[:success]).to be true
26
+ end
27
+ end
28
+
29
+ describe '#practice_skill' do
30
+ it 'increases proficiency' do
31
+ created = runner_host.create_skill(name: 'test', domain: :test)
32
+ result = runner_host.practice_skill(skill_id: created[:skill_id], success: true)
33
+ expect(result[:success]).to be true
34
+ expect(result[:proficiency]).to be > 0.1
35
+ end
36
+ end
37
+
38
+ describe '#execute_production' do
39
+ it 'executes a production' do
40
+ created = runner_host.create_skill(name: 'test', domain: :test)
41
+ prod = runner_host.add_skill_production(
42
+ skill_id: created[:skill_id], condition: 'test',
43
+ action: 'act', domain: :test
44
+ )
45
+ result = runner_host.execute_production(production_id: prod[:production_id], success: true)
46
+ expect(result[:success]).to be true
47
+ end
48
+ end
49
+
50
+ describe '#skill_assessment' do
51
+ it 'returns skill assessment' do
52
+ created = runner_host.create_skill(name: 'test', domain: :test)
53
+ result = runner_host.skill_assessment(skill_id: created[:skill_id])
54
+ expect(result[:success]).to be true
55
+ end
56
+ end
57
+
58
+ describe '#compiled_skills' do
59
+ it 'returns compiled skills' do
60
+ result = runner_host.compiled_skills
61
+ expect(result[:success]).to be true
62
+ end
63
+ end
64
+
65
+ describe '#autonomous_skills' do
66
+ it 'returns autonomous skills' do
67
+ result = runner_host.autonomous_skills
68
+ expect(result[:success]).to be true
69
+ end
70
+ end
71
+
72
+ describe '#most_practiced_skills' do
73
+ it 'returns most practiced skills' do
74
+ result = runner_host.most_practiced_skills(limit: 3)
75
+ expect(result[:success]).to be true
76
+ end
77
+ end
78
+
79
+ describe '#update_procedural_learning' do
80
+ it 'runs decay and prune cycle' do
81
+ result = runner_host.update_procedural_learning
82
+ expect(result[:success]).to be true
83
+ expect(result).to include(:pruned)
84
+ end
85
+ end
86
+
87
+ describe '#procedural_learning_stats' do
88
+ it 'returns stats' do
89
+ result = runner_host.procedural_learning_stats
90
+ expect(result[:success]).to be true
91
+ expect(result).to include(:total_skills, :total_productions)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/learning/scaffolding/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Learning::Scaffolding::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ expect(client).to respond_to(:create_scaffold)
10
+ expect(client).to respond_to(:attempt_scaffolded_task)
11
+ expect(client).to respond_to(:recommend_scaffolded_task)
12
+ expect(client).to respond_to(:mastered_scaffolded_skills)
13
+ expect(client).to respond_to(:zpd_skills)
14
+ expect(client).to respond_to(:domain_scaffolds)
15
+ expect(client).to respond_to(:adjust_scaffold_support)
16
+ expect(client).to respond_to(:overall_scaffolded_competence)
17
+ expect(client).to respond_to(:update_cognitive_scaffolding)
18
+ expect(client).to respond_to(:cognitive_scaffolding_stats)
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Constants do
4
+ let(:constants) { described_class }
5
+
6
+ it 'defines SUPPORT_LEVELS as frozen array of symbols' do
7
+ expect(constants::SUPPORT_LEVELS).to eq(%i[full guided prompted independent])
8
+ expect(constants::SUPPORT_LEVELS).to be_frozen
9
+ end
10
+
11
+ it 'defines ZONE_LABELS covering 0..1 range' do
12
+ all_covered = [0.0, 0.1, 0.3, 0.5, 0.85, 1.0].all? do |val|
13
+ constants::ZONE_LABELS.any? { |range, _| range.cover?(val) }
14
+ end
15
+ expect(all_covered).to be true
16
+ end
17
+
18
+ it 'defines competence bounds' do
19
+ expect(constants::COMPETENCE_FLOOR).to eq(0.0)
20
+ expect(constants::COMPETENCE_CEILING).to eq(1.0)
21
+ end
22
+
23
+ it 'defines ZPD bounds within 0..1' do
24
+ expect(constants::ZPD_LOWER).to be >= 0.0
25
+ expect(constants::ZPD_UPPER).to be <= 1.0
26
+ expect(constants::ZPD_LOWER).to be < constants::ZPD_UPPER
27
+ end
28
+
29
+ it 'LEARNING_GAIN > FAILURE_SETBACK' do
30
+ expect(constants::LEARNING_GAIN).to be > constants::FAILURE_SETBACK
31
+ end
32
+
33
+ it 'MASTERY_THRESHOLD is above ZPD_UPPER' do
34
+ expect(constants::MASTERY_THRESHOLD).to be >= constants::ZPD_UPPER
35
+ end
36
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Scaffold do
4
+ let(:constants) { Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Constants }
5
+
6
+ subject(:scaffold) { described_class.new(skill_name: 'algebra', domain: :math) }
7
+
8
+ describe '#initialize' do
9
+ it 'assigns an id' do
10
+ expect(scaffold.id).to be_a(String)
11
+ expect(scaffold.id).not_to be_empty
12
+ end
13
+
14
+ it 'sets default competence' do
15
+ expect(scaffold.competence).to eq(constants::DEFAULT_COMPETENCE)
16
+ end
17
+
18
+ it 'sets skill_name and domain' do
19
+ expect(scaffold.skill_name).to eq('algebra')
20
+ expect(scaffold.domain).to eq(:math)
21
+ end
22
+
23
+ it 'starts with no practice' do
24
+ expect(scaffold.practice_count).to eq(0)
25
+ expect(scaffold.task_history).to be_empty
26
+ end
27
+
28
+ it 'clamps competence to valid range' do
29
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 2.5)
30
+ expect(s.competence).to eq(1.0)
31
+ end
32
+ end
33
+
34
+ describe '#current_zone' do
35
+ it 'returns :zpd for default competence' do
36
+ expect(scaffold.current_zone).to eq(:zpd)
37
+ end
38
+
39
+ it 'returns :mastered when competence >= MASTERY_THRESHOLD' do
40
+ s = described_class.new(skill_name: 'x', domain: :test, competence: constants::MASTERY_THRESHOLD)
41
+ expect(s.current_zone).to eq(:mastered)
42
+ end
43
+
44
+ it 'returns :beyond_reach for low competence' do
45
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 0.1)
46
+ expect(s.current_zone).to eq(:beyond_reach)
47
+ end
48
+ end
49
+
50
+ describe '#mastered?' do
51
+ it 'returns false at default competence' do
52
+ expect(scaffold.mastered?).to be false
53
+ end
54
+
55
+ it 'returns true at mastery threshold' do
56
+ s = described_class.new(skill_name: 'x', domain: :test, competence: constants::MASTERY_THRESHOLD)
57
+ expect(s.mastered?).to be true
58
+ end
59
+ end
60
+
61
+ describe '#in_zpd?' do
62
+ it 'returns true at default competence' do
63
+ expect(scaffold.in_zpd?).to be true
64
+ end
65
+
66
+ it 'returns false below ZPD_LOWER' do
67
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 0.1)
68
+ expect(s.in_zpd?).to be false
69
+ end
70
+
71
+ it 'returns false at or above ZPD_UPPER' do
72
+ s = described_class.new(skill_name: 'x', domain: :test, competence: constants::ZPD_UPPER)
73
+ expect(s.in_zpd?).to be false
74
+ end
75
+ end
76
+
77
+ describe '#recommended_difficulty' do
78
+ it 'returns a value above current competence' do
79
+ expect(scaffold.recommended_difficulty).to be >= scaffold.competence
80
+ end
81
+
82
+ it 'is within valid competence bounds' do
83
+ expect(scaffold.recommended_difficulty).to be_between(constants::COMPETENCE_FLOOR, constants::COMPETENCE_CEILING)
84
+ end
85
+ end
86
+
87
+ describe '#attempt_task' do
88
+ context 'success in ZPD' do
89
+ it 'increases competence by LEARNING_GAIN' do
90
+ before = scaffold.competence
91
+ difficulty = 0.5
92
+ scaffold.attempt_task(difficulty: difficulty, success: true)
93
+ expect(scaffold.competence).to be > before
94
+ end
95
+ end
96
+
97
+ context 'success below ZPD' do
98
+ it 'increases competence by half LEARNING_GAIN' do
99
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 0.15)
100
+ before = s.competence
101
+ s.attempt_task(difficulty: 0.1, success: true)
102
+ expect(s.competence - before).to be_within(0.001).of(constants::LEARNING_GAIN / 2.0)
103
+ end
104
+ end
105
+
106
+ context 'failure' do
107
+ it 'decreases competence by FAILURE_SETBACK' do
108
+ before = scaffold.competence
109
+ scaffold.attempt_task(difficulty: 0.5, success: false)
110
+ expect(scaffold.competence).to eq((before - constants::FAILURE_SETBACK).clamp(0.0, 1.0))
111
+ end
112
+ end
113
+
114
+ it 'increments practice_count' do
115
+ scaffold.attempt_task(difficulty: 0.5, success: true)
116
+ expect(scaffold.practice_count).to eq(1)
117
+ end
118
+
119
+ it 'records task in history' do
120
+ scaffold.attempt_task(difficulty: 0.5, success: true)
121
+ expect(scaffold.task_history.size).to eq(1)
122
+ expect(scaffold.task_history.last[:success]).to be true
123
+ end
124
+
125
+ it 'clamps competence at COMPETENCE_CEILING' do
126
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 0.99)
127
+ 10.times { s.attempt_task(difficulty: 0.5, success: true) }
128
+ expect(s.competence).to be <= constants::COMPETENCE_CEILING
129
+ end
130
+
131
+ it 'clamps competence at COMPETENCE_FLOOR' do
132
+ s = described_class.new(skill_name: 'x', domain: :test, competence: 0.01)
133
+ 10.times { s.attempt_task(difficulty: 0.5, success: false) }
134
+ expect(s.competence).to be >= constants::COMPETENCE_FLOOR
135
+ end
136
+ end
137
+
138
+ describe '#fade_support!' do
139
+ it 'moves toward independent' do
140
+ scaffold.instance_variable_set(:@support_level, :full)
141
+ scaffold.fade_support!
142
+ expect(scaffold.support_level).to eq(:guided)
143
+ end
144
+
145
+ it 'does not go beyond independent' do
146
+ scaffold.instance_variable_set(:@support_level, :independent)
147
+ scaffold.fade_support!
148
+ expect(scaffold.support_level).to eq(:independent)
149
+ end
150
+ end
151
+
152
+ describe '#increase_support!' do
153
+ it 'moves toward full' do
154
+ scaffold.instance_variable_set(:@support_level, :independent)
155
+ scaffold.increase_support!
156
+ expect(scaffold.support_level).to eq(:prompted)
157
+ end
158
+
159
+ it 'does not go beyond full' do
160
+ scaffold.instance_variable_set(:@support_level, :full)
161
+ scaffold.increase_support!
162
+ expect(scaffold.support_level).to eq(:full)
163
+ end
164
+ end
165
+
166
+ describe '#to_h' do
167
+ it 'contains expected keys' do
168
+ h = scaffold.to_h
169
+ expect(h).to have_key(:id)
170
+ expect(h).to have_key(:skill_name)
171
+ expect(h).to have_key(:domain)
172
+ expect(h).to have_key(:competence)
173
+ expect(h).to have_key(:support_level)
174
+ expect(h).to have_key(:current_zone)
175
+ expect(h).to have_key(:practice_count)
176
+ expect(h).to have_key(:mastered)
177
+ expect(h).to have_key(:in_zpd)
178
+ end
179
+ end
180
+
181
+ describe 'task history cap' do
182
+ it 'does not exceed MAX_HISTORY' do
183
+ (constants::MAX_HISTORY + 5).times { scaffold.attempt_task(difficulty: 0.5, success: true) }
184
+ expect(scaffold.task_history.size).to eq(constants::MAX_HISTORY)
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::ScaffoldingEngine do
4
+ let(:engine) { described_class.new }
5
+ let(:constants) { Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Constants }
6
+
7
+ def make_scaffold(skill: 'reading', domain: :language, competence: nil)
8
+ engine.create_scaffold(skill_name: skill, domain: domain, competence: competence)
9
+ end
10
+
11
+ describe '#create_scaffold' do
12
+ it 'returns a Scaffold' do
13
+ scaffold = make_scaffold
14
+ expect(scaffold).to be_a(Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Scaffold)
15
+ end
16
+
17
+ it 'stores the scaffold' do
18
+ scaffold = make_scaffold
19
+ expect(engine.scaffolds[scaffold.id]).to eq(scaffold)
20
+ end
21
+
22
+ it 'uses DEFAULT_COMPETENCE when not provided' do
23
+ scaffold = make_scaffold
24
+ expect(scaffold.competence).to eq(constants::DEFAULT_COMPETENCE)
25
+ end
26
+
27
+ it 'accepts custom competence' do
28
+ scaffold = make_scaffold(competence: 0.7)
29
+ expect(scaffold.competence).to eq(0.7)
30
+ end
31
+ end
32
+
33
+ describe '#attempt_scaffolded_task' do
34
+ it 'returns the scaffold after attempt' do
35
+ scaffold = make_scaffold
36
+ result = engine.attempt_scaffolded_task(scaffold_id: scaffold.id, difficulty: 0.5, success: true)
37
+ expect(result).to be_a(Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Scaffold)
38
+ end
39
+
40
+ it 'returns nil for unknown scaffold_id' do
41
+ result = engine.attempt_scaffolded_task(scaffold_id: 'nonexistent', difficulty: 0.5, success: true)
42
+ expect(result).to be_nil
43
+ end
44
+
45
+ it 'updates competence on attempt' do
46
+ scaffold = make_scaffold
47
+ before = scaffold.competence
48
+ engine.attempt_scaffolded_task(scaffold_id: scaffold.id, difficulty: 0.5, success: true)
49
+ expect(scaffold.competence).not_to eq(before)
50
+ end
51
+ end
52
+
53
+ describe '#recommend_task' do
54
+ it 'returns difficulty, support_level, and zone' do
55
+ scaffold = make_scaffold
56
+ rec = engine.recommend_task(scaffold_id: scaffold.id)
57
+ expect(rec).to include(:difficulty, :support_level, :zone)
58
+ end
59
+
60
+ it 'returns nil for unknown id' do
61
+ expect(engine.recommend_task(scaffold_id: 'unknown')).to be_nil
62
+ end
63
+ end
64
+
65
+ describe '#mastered_skills' do
66
+ it 'returns empty when nothing mastered' do
67
+ make_scaffold
68
+ expect(engine.mastered_skills).to be_empty
69
+ end
70
+
71
+ it 'returns mastered scaffolds' do
72
+ make_scaffold(competence: constants::MASTERY_THRESHOLD)
73
+ expect(engine.mastered_skills.size).to eq(1)
74
+ end
75
+ end
76
+
77
+ describe '#zpd_skills' do
78
+ it 'returns scaffolds in ZPD' do
79
+ make_scaffold
80
+ expect(engine.zpd_skills.size).to eq(1)
81
+ end
82
+
83
+ it 'does not include mastered scaffolds' do
84
+ make_scaffold(competence: constants::MASTERY_THRESHOLD)
85
+ expect(engine.zpd_skills).to be_empty
86
+ end
87
+ end
88
+
89
+ describe '#by_domain' do
90
+ it 'filters by domain' do
91
+ make_scaffold(domain: :math)
92
+ make_scaffold(domain: :language)
93
+ expect(engine.by_domain(domain: :math).size).to eq(1)
94
+ end
95
+ end
96
+
97
+ describe '#adjust_support' do
98
+ it 'increases support level' do
99
+ scaffold = make_scaffold
100
+ scaffold.instance_variable_set(:@support_level, :independent)
101
+ engine.adjust_support(scaffold_id: scaffold.id, direction: :increase)
102
+ expect(scaffold.support_level).to eq(:prompted)
103
+ end
104
+
105
+ it 'decreases (fades) support level' do
106
+ scaffold = make_scaffold
107
+ scaffold.instance_variable_set(:@support_level, :full)
108
+ engine.adjust_support(scaffold_id: scaffold.id, direction: :decrease)
109
+ expect(scaffold.support_level).to eq(:guided)
110
+ end
111
+
112
+ it 'returns nil for unknown id' do
113
+ expect(engine.adjust_support(scaffold_id: 'x', direction: :increase)).to be_nil
114
+ end
115
+ end
116
+
117
+ describe '#overall_competence' do
118
+ it 'returns DEFAULT_COMPETENCE with no scaffolds' do
119
+ expect(engine.overall_competence).to eq(constants::DEFAULT_COMPETENCE)
120
+ end
121
+
122
+ it 'averages competences' do
123
+ engine.create_scaffold(skill_name: 'a', domain: :test, competence: 0.4)
124
+ engine.create_scaffold(skill_name: 'b', domain: :test, competence: 0.6)
125
+ expect(engine.overall_competence).to be_within(0.001).of(0.5)
126
+ end
127
+ end
128
+
129
+ describe '#decay_all' do
130
+ it 'returns the scaffold count' do
131
+ 2.times { |i| make_scaffold(skill: "s#{i}") }
132
+ expect(engine.decay_all).to eq(2)
133
+ end
134
+
135
+ it 'reduces competence on non-mastered scaffolds' do
136
+ scaffold = make_scaffold
137
+ before = scaffold.competence
138
+ engine.decay_all
139
+ expect(scaffold.competence).to be < before
140
+ end
141
+
142
+ it 'does not decay mastered scaffolds' do
143
+ scaffold = make_scaffold(competence: constants::MASTERY_THRESHOLD)
144
+ before = scaffold.competence
145
+ engine.decay_all
146
+ expect(scaffold.competence).to eq(before)
147
+ end
148
+ end
149
+
150
+ describe '#to_h' do
151
+ it 'contains expected stat keys' do
152
+ h = engine.to_h
153
+ expect(h).to have_key(:total_scaffolds)
154
+ expect(h).to have_key(:mastered_count)
155
+ expect(h).to have_key(:zpd_count)
156
+ expect(h).to have_key(:overall_competence)
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/learning/scaffolding/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Learning::Scaffolding::Runners::CognitiveScaffolding do
6
+ let(:client) { Legion::Extensions::Agentic::Learning::Scaffolding::Client.new }
7
+ let(:constants) { Legion::Extensions::Agentic::Learning::Scaffolding::Helpers::Constants }
8
+
9
+ def create_scaffold(skill: 'algebra', domain: :math, competence: nil)
10
+ result = if competence
11
+ client.create_scaffold(skill_name: skill, domain: domain, competence: competence)
12
+ else
13
+ client.create_scaffold(skill_name: skill, domain: domain)
14
+ end
15
+ result[:scaffold][:id]
16
+ end
17
+
18
+ describe '#create_scaffold' do
19
+ it 'returns success with scaffold data' do
20
+ result = client.create_scaffold(skill_name: 'reading', domain: :language)
21
+ expect(result[:success]).to be true
22
+ expect(result[:scaffold]).to include(:id, :skill_name, :domain, :competence)
23
+ end
24
+
25
+ it 'uses DEFAULT_COMPETENCE by default' do
26
+ result = client.create_scaffold(skill_name: 'reading', domain: :language)
27
+ expect(result[:scaffold][:competence]).to eq(constants::DEFAULT_COMPETENCE)
28
+ end
29
+
30
+ it 'accepts custom competence' do
31
+ result = client.create_scaffold(skill_name: 'x', domain: :test, competence: 0.6)
32
+ expect(result[:scaffold][:competence]).to eq(0.6)
33
+ end
34
+ end
35
+
36
+ describe '#attempt_scaffolded_task' do
37
+ it 'returns success for valid scaffold' do
38
+ sid = create_scaffold
39
+ result = client.attempt_scaffolded_task(scaffold_id: sid, difficulty: 0.5, success: true)
40
+ expect(result[:success]).to be true
41
+ expect(result[:scaffold]).to include(:competence)
42
+ end
43
+
44
+ it 'returns failure for unknown scaffold_id' do
45
+ result = client.attempt_scaffolded_task(scaffold_id: 'nonexistent', difficulty: 0.5, success: true)
46
+ expect(result[:success]).to be false
47
+ expect(result[:reason]).to eq(:not_found)
48
+ end
49
+
50
+ it 'increases competence on success' do
51
+ result_create = client.create_scaffold(skill_name: 'y', domain: :test)
52
+ sid = result_create[:scaffold][:id]
53
+ before = result_create[:scaffold][:competence]
54
+ attempt = client.attempt_scaffolded_task(scaffold_id: sid, difficulty: 0.5, success: true)
55
+ expect(attempt[:scaffold][:competence]).to be > before
56
+ end
57
+
58
+ it 'decreases competence on failure' do
59
+ result_create = client.create_scaffold(skill_name: 'z', domain: :test)
60
+ sid = result_create[:scaffold][:id]
61
+ before = result_create[:scaffold][:competence]
62
+ attempt = client.attempt_scaffolded_task(scaffold_id: sid, difficulty: 0.5, success: false)
63
+ expect(attempt[:scaffold][:competence]).to be < before
64
+ end
65
+ end
66
+
67
+ describe '#recommend_scaffolded_task' do
68
+ it 'returns recommendation for valid scaffold' do
69
+ sid = create_scaffold
70
+ result = client.recommend_scaffolded_task(scaffold_id: sid)
71
+ expect(result[:success]).to be true
72
+ expect(result[:recommendation]).to include(:difficulty, :support_level, :zone)
73
+ end
74
+
75
+ it 'returns failure for unknown id' do
76
+ result = client.recommend_scaffolded_task(scaffold_id: 'bad')
77
+ expect(result[:success]).to be false
78
+ expect(result[:reason]).to eq(:not_found)
79
+ end
80
+ end
81
+
82
+ describe '#mastered_scaffolded_skills' do
83
+ it 'returns empty when none mastered' do
84
+ create_scaffold
85
+ result = client.mastered_scaffolded_skills
86
+ expect(result[:success]).to be true
87
+ expect(result[:count]).to eq(0)
88
+ end
89
+
90
+ it 'returns mastered scaffolds' do
91
+ create_scaffold(competence: constants::MASTERY_THRESHOLD)
92
+ result = client.mastered_scaffolded_skills
93
+ expect(result[:count]).to eq(1)
94
+ end
95
+ end
96
+
97
+ describe '#zpd_skills' do
98
+ it 'returns scaffolds in ZPD' do
99
+ create_scaffold
100
+ result = client.zpd_skills
101
+ expect(result[:success]).to be true
102
+ expect(result[:count]).to eq(1)
103
+ end
104
+ end
105
+
106
+ describe '#domain_scaffolds' do
107
+ it 'filters scaffolds by domain' do
108
+ create_scaffold(domain: :math)
109
+ create_scaffold(domain: :language)
110
+ result = client.domain_scaffolds(domain: :math)
111
+ expect(result[:success]).to be true
112
+ expect(result[:count]).to eq(1)
113
+ expect(result[:domain]).to eq(:math)
114
+ end
115
+ end
116
+
117
+ describe '#adjust_scaffold_support' do
118
+ it 'adjusts support direction increase' do
119
+ sid = create_scaffold
120
+ result = client.adjust_scaffold_support(scaffold_id: sid, direction: :increase)
121
+ expect(result[:success]).to be true
122
+ end
123
+
124
+ it 'returns failure for unknown id' do
125
+ result = client.adjust_scaffold_support(scaffold_id: 'bad', direction: :increase)
126
+ expect(result[:success]).to be false
127
+ expect(result[:reason]).to eq(:not_found)
128
+ end
129
+ end
130
+
131
+ describe '#overall_scaffolded_competence' do
132
+ it 'returns success with a float' do
133
+ result = client.overall_scaffolded_competence
134
+ expect(result[:success]).to be true
135
+ expect(result[:overall_competence]).to be_a(Float)
136
+ end
137
+
138
+ it 'reflects created scaffolds' do
139
+ client.create_scaffold(skill_name: 'a', domain: :test, competence: 0.8)
140
+ result = client.overall_scaffolded_competence
141
+ expect(result[:overall_competence]).to be > constants::DEFAULT_COMPETENCE
142
+ end
143
+ end
144
+
145
+ describe '#update_cognitive_scaffolding' do
146
+ it 'runs decay and returns stats' do
147
+ create_scaffold
148
+ result = client.update_cognitive_scaffolding
149
+ expect(result[:success]).to be true
150
+ expect(result[:action]).to eq(:decay)
151
+ expect(result[:scaffold_count]).to eq(1)
152
+ end
153
+ end
154
+
155
+ describe '#cognitive_scaffolding_stats' do
156
+ it 'returns stats hash' do
157
+ create_scaffold
158
+ result = client.cognitive_scaffolding_stats
159
+ expect(result[:success]).to be true
160
+ expect(result[:stats]).to include(:total_scaffolds, :mastered_count, :zpd_count, :overall_competence)
161
+ end
162
+ end
163
+ end