lex-fatigue 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 845130eba06fa9a364b06a89d3c775770aaf6936c5db41e9ac946c179c23f690
4
+ data.tar.gz: d5b1744ba68da9b80fc2647279a471426b87aed72832a7268feea28afff31fa8
5
+ SHA512:
6
+ metadata.gz: c60dc6d59bc80ef1323a0522a4dfa7eaf316fc4054d3d0aa04796ebf243bc505962923c6f89736312247b27ec5e4fef93c397bba0efc7e449b77e5be9ffa9bbf
7
+ data.tar.gz: 72c2555f2378411df4e05680a54592f2d4602475113404061ae197fb6d55ab38bb199c90f01efe1e70eae829d997c420188fe5bfe5804b78f54f082dad5a0020
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop', require: false
11
+ gem 'rubocop-rspec', require: false
12
+ gem 'simplecov'
13
+ end
14
+
15
+ gem 'legion-gaia', path: '../../legion-gaia'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright 2026 Matthew Iverson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # lex-fatigue
2
+
3
+ Cognitive fatigue modeling for the LegionIO brain-modeled cognitive architecture.
4
+
5
+ ## What It Does
6
+
7
+ Models how sustained cognitive effort degrades processing capacity over time and how rest restores it. Tracks energy level as a continuous float (0–1), computes a performance degradation factor that scales with fatigue, detects burnout risk, and generates rest recommendations. Energy decays faster under high cognitive load and emotional arousal, and recovers during rest periods.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ client = Legion::Extensions::Fatigue::Client.new
13
+
14
+ # Update per tick with the tick's load metrics
15
+ client.update_fatigue(tick_results: { cognitive_load: 0.7, emotional_arousal: 0.4 })
16
+ # => { energy: 0.96, fatigue_level: :fresh, performance_factor: 1.0,
17
+ # recommendation: :continue, burnout_risk: false }
18
+
19
+ # Check current status
20
+ client.energy_status
21
+ # => { energy: 0.72, fatigue_level: :alert, performance_factor: 1.0, needs_rest: false }
22
+
23
+ # Enter recovery mode
24
+ client.enter_rest(mode: :full_rest)
25
+ # => { mode: :full_rest, energy_before: 0.35, energy_after: 0.5, recovered: 0.15 }
26
+
27
+ # Project future energy
28
+ client.energy_forecast(ticks: 50)
29
+ # => { forecast: [{ tick: 10, projected_energy: 0.62, fatigue_level: :alert }, ...] }
30
+
31
+ # Burnout risk check
32
+ client.burnout_risk_assessment
33
+ # => { at_risk: false, energy: 0.72, ticks_until_burnout: 180 }
34
+
35
+ # Session statistics
36
+ client.fatigue_stats
37
+ ```
38
+
39
+ ## Fatigue Levels
40
+
41
+ | Energy | Level |
42
+ |---|---|
43
+ | 0.8 – 1.0 | `:fresh` |
44
+ | 0.6 – 0.8 | `:alert` |
45
+ | 0.4 – 0.6 | `:tired` |
46
+ | 0.2 – 0.4 | `:exhausted` |
47
+ | 0.0 – 0.2 | `:burnout` |
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ bundle install
53
+ bundle exec rspec
54
+ bundle exec rubocop
55
+ ```
56
+
57
+ ## License
58
+
59
+ MIT
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/fatigue/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-fatigue'
7
+ spec.version = Legion::Extensions::Fatigue::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Fatigue'
12
+ spec.description = 'Cognitive fatigue modeling for brain-modeled agentic AI'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-fatigue'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-fatigue'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-fatigue'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-fatigue'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-fatigue/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ Dir.glob('{lib,spec}/**/*') + %w[lex-fatigue.gemspec Gemfile LICENSE README.md]
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/fatigue/helpers/constants'
4
+ require 'legion/extensions/fatigue/helpers/energy_model'
5
+ require 'legion/extensions/fatigue/helpers/fatigue_store'
6
+ require 'legion/extensions/fatigue/runners/fatigue'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Fatigue
11
+ class Client
12
+ include Runners::Fatigue
13
+
14
+ attr_reader :fatigue_store
15
+
16
+ def initialize(fatigue_store: nil, **)
17
+ @fatigue_store = fatigue_store || Helpers::FatigueStore.new
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Fatigue
6
+ module Helpers
7
+ module Constants
8
+ MAX_ENERGY = 1.0
9
+ MIN_ENERGY = 0.0
10
+
11
+ RESTING_RECOVERY_RATE = 0.02
12
+ ACTIVE_DRAIN_RATE = 0.01
13
+ COGNITIVE_DRAIN_MULTIPLIER = 1.5
14
+ EMOTIONAL_DRAIN_MULTIPLIER = 1.3
15
+
16
+ FATIGUE_LEVELS = {
17
+ fresh: 0.8,
18
+ alert: 0.6,
19
+ tired: 0.4,
20
+ exhausted: 0.2,
21
+ depleted: 0.0
22
+ }.freeze
23
+
24
+ PERFORMANCE_DEGRADATION = {
25
+ fresh: 1.0,
26
+ alert: 0.95,
27
+ tired: 0.8,
28
+ exhausted: 0.6,
29
+ depleted: 0.3
30
+ }.freeze
31
+
32
+ RECOVERY_MODES = %i[active_rest light_duty full_rest sleep].freeze
33
+
34
+ RECOVERY_RATES = {
35
+ active_rest: 0.005,
36
+ light_duty: 0.01,
37
+ full_rest: 0.02,
38
+ sleep: 0.05
39
+ }.freeze
40
+
41
+ REST_THRESHOLD = 0.3
42
+ CRITICAL_THRESHOLD = 0.15
43
+ SECOND_WIND_CHANCE = 0.05
44
+ BURNOUT_THRESHOLD = 50
45
+ MAX_HISTORY = 100
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Fatigue
6
+ module Helpers
7
+ class EnergyModel
8
+ include Constants
9
+
10
+ attr_reader :energy, :fatigue_level, :ticks_active, :ticks_resting,
11
+ :consecutive_low_ticks, :burnout, :recovery_mode,
12
+ :peak_energy, :history
13
+
14
+ def initialize
15
+ @energy = Constants::MAX_ENERGY
16
+ @fatigue_level = :fresh
17
+ @ticks_active = 0
18
+ @ticks_resting = 0
19
+ @consecutive_low_ticks = 0
20
+ @burnout = false
21
+ @recovery_mode = nil
22
+ @peak_energy = Constants::MAX_ENERGY
23
+ @history = []
24
+ end
25
+
26
+ def tick(cognitive_load: 0.5, emotional_arousal: 0.5, is_resting: false)
27
+ if is_resting
28
+ recover
29
+ @ticks_resting += 1
30
+ @consecutive_low_ticks = 0
31
+ else
32
+ drain(cognitive_load, emotional_arousal)
33
+ @ticks_active += 1
34
+ check_second_wind
35
+ end
36
+
37
+ @energy = @energy.clamp(Constants::MIN_ENERGY, Constants::MAX_ENERGY)
38
+ @peak_energy = @energy if @energy > @peak_energy
39
+ classify_fatigue
40
+ track_low_ticks
41
+ check_burnout
42
+ record_snapshot
43
+
44
+ to_h
45
+ end
46
+
47
+ def performance_factor
48
+ Constants::PERFORMANCE_DEGRADATION[@fatigue_level]
49
+ end
50
+
51
+ def needs_rest?
52
+ @energy < Constants::REST_THRESHOLD
53
+ end
54
+
55
+ def critically_fatigued?
56
+ @energy < Constants::CRITICAL_THRESHOLD
57
+ end
58
+
59
+ def burnout?
60
+ @burnout
61
+ end
62
+
63
+ def enter_recovery(mode)
64
+ return unless Constants::RECOVERY_MODES.include?(mode)
65
+
66
+ @recovery_mode = mode
67
+ end
68
+
69
+ def exit_recovery
70
+ @recovery_mode = nil
71
+ end
72
+
73
+ def time_to_rest_threshold
74
+ return 0 if needs_rest?
75
+
76
+ current_drain = effective_drain(0.5, 0.5)
77
+ return Float::INFINITY if current_drain <= 0.0
78
+
79
+ ((@energy - Constants::REST_THRESHOLD) / current_drain).ceil
80
+ end
81
+
82
+ def time_to_full_recovery
83
+ rate = @recovery_mode ? Constants::RECOVERY_RATES[@recovery_mode] : Constants::RESTING_RECOVERY_RATE
84
+ return 0 if @energy >= Constants::MAX_ENERGY
85
+ return Float::INFINITY if rate <= 0.0
86
+
87
+ ((Constants::MAX_ENERGY - @energy) / rate).ceil
88
+ end
89
+
90
+ def trend
91
+ return :stable if @history.size < 5
92
+
93
+ recent = @history.last(5).map { |s| s[:energy] }
94
+ delta = recent.last - recent.first
95
+ if delta > 0.01
96
+ :recovering
97
+ elsif delta < -0.01
98
+ :draining
99
+ else
100
+ :stable
101
+ end
102
+ end
103
+
104
+ def to_h
105
+ {
106
+ energy: @energy.round(4),
107
+ fatigue_level: @fatigue_level,
108
+ performance_factor: performance_factor,
109
+ needs_rest: needs_rest?,
110
+ critically_fatigued: critically_fatigued?,
111
+ burnout: @burnout,
112
+ recovery_mode: @recovery_mode,
113
+ peak_energy: @peak_energy.round(4),
114
+ ticks_active: @ticks_active,
115
+ ticks_resting: @ticks_resting,
116
+ consecutive_low_ticks: @consecutive_low_ticks,
117
+ trend: trend,
118
+ history_size: @history.size
119
+ }
120
+ end
121
+
122
+ private
123
+
124
+ def effective_drain(cognitive_load, emotional_arousal)
125
+ cognitive_factor = 1.0 + ((cognitive_load - 0.5) * Constants::COGNITIVE_DRAIN_MULTIPLIER)
126
+ emotional_factor = 1.0 + ([0.0, emotional_arousal - 0.5].max * Constants::EMOTIONAL_DRAIN_MULTIPLIER)
127
+ Constants::ACTIVE_DRAIN_RATE * cognitive_factor * emotional_factor
128
+ end
129
+
130
+ def drain(cognitive_load, emotional_arousal)
131
+ @energy -= effective_drain(cognitive_load, emotional_arousal)
132
+ end
133
+
134
+ def recover
135
+ rate = @recovery_mode ? Constants::RECOVERY_RATES[@recovery_mode] : Constants::RESTING_RECOVERY_RATE
136
+ @energy += rate
137
+ end
138
+
139
+ def classify_fatigue
140
+ @fatigue_level = Constants::FATIGUE_LEVELS.each_key.find do |level|
141
+ @energy >= Constants::FATIGUE_LEVELS[level]
142
+ end || :depleted
143
+ end
144
+
145
+ def track_low_ticks
146
+ if needs_rest?
147
+ @consecutive_low_ticks += 1
148
+ else
149
+ @consecutive_low_ticks = 0
150
+ end
151
+ end
152
+
153
+ def check_second_wind
154
+ return unless @fatigue_level == :tired
155
+ return unless rand < Constants::SECOND_WIND_CHANCE
156
+
157
+ @energy = [@energy + 0.1, Constants::MAX_ENERGY].min
158
+ end
159
+
160
+ def check_burnout
161
+ @burnout = true if @consecutive_low_ticks > Constants::BURNOUT_THRESHOLD
162
+ end
163
+
164
+ def record_snapshot
165
+ @history << {
166
+ energy: @energy.round(4),
167
+ fatigue_level: @fatigue_level,
168
+ performance: performance_factor,
169
+ at: Time.now.utc
170
+ }
171
+ @history.shift if @history.size > Constants::MAX_HISTORY
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Fatigue
6
+ module Helpers
7
+ class FatigueStore
8
+ include Constants
9
+
10
+ attr_reader :model, :session_start, :peak_performance_streak,
11
+ :total_rest_ticks, :total_active_ticks
12
+
13
+ def initialize(model: nil)
14
+ @model = model || EnergyModel.new
15
+ @session_start = Time.now.utc
16
+ @peak_performance_streak = 0
17
+ @total_rest_ticks = 0
18
+ @total_active_ticks = 0
19
+ @current_streak = 0
20
+ end
21
+
22
+ def update(tick_results: {})
23
+ cognitive_load = tick_results[:cognitive_load] || 0.5
24
+ emotional_arousal = tick_results[:emotional_arousal] || 0.5
25
+ is_resting = tick_results[:mode] == :resting || !@model.recovery_mode.nil?
26
+
27
+ result = @model.tick(
28
+ cognitive_load: cognitive_load,
29
+ emotional_arousal: emotional_arousal,
30
+ is_resting: is_resting
31
+ )
32
+
33
+ if is_resting
34
+ @total_rest_ticks += 1
35
+ else
36
+ @total_active_ticks += 1
37
+ end
38
+
39
+ update_streak(result[:performance_factor])
40
+ result
41
+ end
42
+
43
+ def recommend_action
44
+ energy = @model.energy
45
+
46
+ if @model.burnout?
47
+ :emergency_shutdown
48
+ elsif energy < Constants::CRITICAL_THRESHOLD
49
+ :enter_rest
50
+ elsif @model.needs_rest?
51
+ :take_break
52
+ elsif @model.fatigue_level == :tired
53
+ :reduce_load
54
+ else
55
+ :continue
56
+ end
57
+ end
58
+
59
+ def session_stats
60
+ total_ticks = @total_active_ticks + @total_rest_ticks
61
+ duration = Time.now.utc - @session_start
62
+
63
+ {
64
+ duration_seconds: duration.round(2),
65
+ total_ticks: total_ticks,
66
+ active_ticks: @total_active_ticks,
67
+ rest_ticks: @total_rest_ticks,
68
+ active_ratio: total_ticks.positive? ? (@total_active_ticks.to_f / total_ticks).round(4) : 0.0,
69
+ current_energy: @model.energy.round(4),
70
+ fatigue_level: @model.fatigue_level,
71
+ burnout: @model.burnout?,
72
+ peak_performance_streak: @peak_performance_streak
73
+ }
74
+ end
75
+
76
+ def energy_forecast(ticks:)
77
+ current = @model.energy
78
+ drain_rate = Constants::ACTIVE_DRAIN_RATE
79
+ projected = []
80
+
81
+ ticks.times do |i|
82
+ projected_energy = (current - (drain_rate * (i + 1))).clamp(Constants::MIN_ENERGY, Constants::MAX_ENERGY)
83
+ level = classify_level(projected_energy)
84
+ projected << {
85
+ tick: i + 1,
86
+ energy: projected_energy.round(4),
87
+ fatigue_level: level
88
+ }
89
+ end
90
+
91
+ {
92
+ current_energy: current.round(4),
93
+ forecast: projected,
94
+ ticks_to_rest: @model.time_to_rest_threshold
95
+ }
96
+ end
97
+
98
+ def optimal_rest_schedule
99
+ ticks_until_rest = @model.time_to_rest_threshold
100
+ ticks_to_recover = @model.time_to_full_recovery
101
+
102
+ {
103
+ recommend_rest_in: ticks_until_rest,
104
+ full_recovery_ticks: ticks_to_recover,
105
+ current_energy: @model.energy.round(4),
106
+ recommended_mode: suggest_recovery_mode,
107
+ trend: @model.trend
108
+ }
109
+ end
110
+
111
+ private
112
+
113
+ def classify_level(energy)
114
+ Constants::FATIGUE_LEVELS.each_key.find do |level|
115
+ energy >= Constants::FATIGUE_LEVELS[level]
116
+ end || :depleted
117
+ end
118
+
119
+ def update_streak(performance_factor)
120
+ if performance_factor >= Constants::PERFORMANCE_DEGRADATION[:fresh]
121
+ @current_streak += 1
122
+ @peak_performance_streak = @current_streak if @current_streak > @peak_performance_streak
123
+ else
124
+ @current_streak = 0
125
+ end
126
+ end
127
+
128
+ def suggest_recovery_mode
129
+ energy = @model.energy
130
+ if energy < Constants::CRITICAL_THRESHOLD
131
+ :sleep
132
+ elsif energy < Constants::REST_THRESHOLD
133
+ :full_rest
134
+ else
135
+ :active_rest
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Fatigue
6
+ module Runners
7
+ module Fatigue
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def update_fatigue(tick_results: {}, **)
12
+ result = fatigue_store.update(tick_results: tick_results)
13
+ recommendation = fatigue_store.recommend_action
14
+
15
+ Legion::Logging.debug "[fatigue] tick: energy=#{result[:energy]} level=#{result[:fatigue_level]} " \
16
+ "perf=#{result[:performance_factor]} rec=#{recommendation}"
17
+
18
+ {
19
+ energy: result[:energy],
20
+ fatigue_level: result[:fatigue_level],
21
+ performance_factor: result[:performance_factor],
22
+ recommendation: recommendation,
23
+ needs_rest: result[:needs_rest],
24
+ burnout: result[:burnout]
25
+ }
26
+ end
27
+
28
+ def energy_status(**)
29
+ model = fatigue_store.model
30
+ Legion::Logging.debug "[fatigue] status: energy=#{model.energy.round(3)} level=#{model.fatigue_level}"
31
+
32
+ {
33
+ energy: model.energy.round(4),
34
+ fatigue_level: model.fatigue_level,
35
+ performance_factor: model.performance_factor,
36
+ needs_rest: model.needs_rest?,
37
+ critically_fatigued: model.critically_fatigued?,
38
+ burnout: model.burnout?,
39
+ recovery_mode: model.recovery_mode,
40
+ trend: model.trend
41
+ }
42
+ end
43
+
44
+ def enter_rest(mode: :full_rest, **)
45
+ return { success: false, error: "unknown recovery mode: #{mode}" } unless Helpers::Constants::RECOVERY_MODES.include?(mode)
46
+
47
+ fatigue_store.model.enter_recovery(mode)
48
+ Legion::Logging.info "[fatigue] entered recovery mode=#{mode}"
49
+ { success: true, mode: mode, energy: fatigue_store.model.energy.round(4) }
50
+ end
51
+
52
+ def exit_rest(**)
53
+ fatigue_store.model.exit_recovery
54
+ Legion::Logging.info '[fatigue] exited recovery mode'
55
+ { success: true, energy: fatigue_store.model.energy.round(4), fatigue_level: fatigue_store.model.fatigue_level }
56
+ end
57
+
58
+ def energy_forecast(ticks: 50, **)
59
+ Legion::Logging.debug "[fatigue] forecasting #{ticks} ticks"
60
+ fatigue_store.energy_forecast(ticks: ticks)
61
+ end
62
+
63
+ def fatigue_stats(**)
64
+ stats = fatigue_store.session_stats
65
+ model = fatigue_store.model
66
+ Legion::Logging.debug "[fatigue] stats: ticks=#{stats[:total_ticks]} burnout=#{stats[:burnout]}"
67
+
68
+ {
69
+ session: stats,
70
+ history: model.history.last(10),
71
+ trend: model.trend,
72
+ schedule: fatigue_store.optimal_rest_schedule
73
+ }
74
+ end
75
+
76
+ private
77
+
78
+ def fatigue_store
79
+ @fatigue_store ||= Helpers::FatigueStore.new
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Fatigue
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/fatigue/version'
4
+ require 'legion/extensions/fatigue/helpers/constants'
5
+ require 'legion/extensions/fatigue/helpers/energy_model'
6
+ require 'legion/extensions/fatigue/helpers/fatigue_store'
7
+ require 'legion/extensions/fatigue/runners/fatigue'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Fatigue
12
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
13
+ end
14
+ end
15
+ end