lex-executive-function 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 +7 -0
- data/Gemfile +12 -0
- data/lex-executive-function.gemspec +30 -0
- data/lib/legion/extensions/executive_function/actors/recovery.rb +41 -0
- data/lib/legion/extensions/executive_function/client.rb +23 -0
- data/lib/legion/extensions/executive_function/helpers/ef_component.rb +54 -0
- data/lib/legion/extensions/executive_function/helpers/executive_controller.rb +125 -0
- data/lib/legion/extensions/executive_function/runners/executive_function.rb +100 -0
- data/lib/legion/extensions/executive_function/version.rb +9 -0
- data/lib/legion/extensions/executive_function.rb +14 -0
- data/spec/legion/extensions/executive_function/client_spec.rb +40 -0
- data/spec/legion/extensions/executive_function/helpers/ef_component_spec.rb +108 -0
- data/spec/legion/extensions/executive_function/helpers/executive_controller_spec.rb +136 -0
- data/spec/legion/extensions/executive_function/runners/executive_function_spec.rb +191 -0
- data/spec/spec_helper.rb +23 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a4565c3fb85be0d4ccb78af546011ba3d3885bc4a10ea3a73e5a59aca63b192a
|
|
4
|
+
data.tar.gz: d6769fd5f1683e6afe74d1e1674a143ea8868a6e4dbf8a0ec903cbd6a41e5c9f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 323661ea3d66d9a3b5b55321f12b4b0a9a75188c3f3e02b46e2a75834ea93053b9dac99fed473dc78451ed3b89cb1e884c18edd9f8a3dece9639b5ed5f76d74d
|
|
7
|
+
data.tar.gz: 1755111a87ce6f198de1a0fb0eac3d76fd6f59850c6b90077c1d31cd0c8eb8e1416019b7b2026abda6841b9fb4f18b9e250a673ebf58f79dab644c831d4afcdd
|
data/Gemfile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/executive_function/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-executive-function'
|
|
7
|
+
spec.version = Legion::Extensions::ExecutiveFunction::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Executive Function'
|
|
12
|
+
spec.description = 'Miyake & Friedman (2000, 2012) unity/diversity model of executive functions: ' \
|
|
13
|
+
'inhibition, shifting, and working memory updating'
|
|
14
|
+
spec.homepage = 'https://github.com/LegionIO/lex-executive-function'
|
|
15
|
+
spec.license = 'MIT'
|
|
16
|
+
spec.required_ruby_version = '>= 3.4'
|
|
17
|
+
|
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-executive-function'
|
|
20
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-executive-function'
|
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-executive-function'
|
|
22
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-executive-function/issues'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
24
|
+
|
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
26
|
+
Dir.glob('{lib,spec}/**/*') + %w[lex-executive-function.gemspec Gemfile]
|
|
27
|
+
end
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
spec.add_development_dependency 'legion-gaia'
|
|
30
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module ExecutiveFunction
|
|
8
|
+
module Actor
|
|
9
|
+
class Recovery < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::ExecutiveFunction::Runners::ExecutiveFunction
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'executive_function_stats'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
30
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_now?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def use_runner?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_subtask?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_task?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/helpers/ef_component'
|
|
4
|
+
require 'legion/extensions/executive_function/helpers/executive_controller'
|
|
5
|
+
require 'legion/extensions/executive_function/runners/executive_function'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module ExecutiveFunction
|
|
10
|
+
class Client
|
|
11
|
+
include Runners::ExecutiveFunction
|
|
12
|
+
|
|
13
|
+
def initialize(**)
|
|
14
|
+
@controller = Helpers::ExecutiveController.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :controller
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ExecutiveFunction
|
|
6
|
+
module Helpers
|
|
7
|
+
class EfComponent
|
|
8
|
+
DEFAULT_CAPACITY = 0.7
|
|
9
|
+
CAPACITY_FLOOR = 0.1
|
|
10
|
+
CAPACITY_CEILING = 1.0
|
|
11
|
+
RECOVERY_RATE = 0.02
|
|
12
|
+
|
|
13
|
+
attr_reader :name, :capacity, :fatigue, :recent_uses
|
|
14
|
+
|
|
15
|
+
def initialize(name:, capacity: DEFAULT_CAPACITY)
|
|
16
|
+
@name = name
|
|
17
|
+
@capacity = capacity.clamp(CAPACITY_FLOOR, CAPACITY_CEILING)
|
|
18
|
+
@fatigue = 0.0
|
|
19
|
+
@recent_uses = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def use(cost:)
|
|
23
|
+
@fatigue = [@fatigue + cost, capacity].min
|
|
24
|
+
@recent_uses << { used_at: Time.now.utc, cost: cost }
|
|
25
|
+
@recent_uses = @recent_uses.last(50)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def recover
|
|
29
|
+
@fatigue = [@fatigue - RECOVERY_RATE, 0.0].max
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def effective_capacity
|
|
33
|
+
[@capacity - @fatigue, CAPACITY_FLOOR].max
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def fatigued?
|
|
37
|
+
effective_capacity <= CAPACITY_FLOOR + 0.05
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_h
|
|
41
|
+
{
|
|
42
|
+
name: @name,
|
|
43
|
+
capacity: @capacity,
|
|
44
|
+
fatigue: @fatigue.round(4),
|
|
45
|
+
effective_capacity: effective_capacity.round(4),
|
|
46
|
+
fatigued: fatigued?,
|
|
47
|
+
recent_use_count: @recent_uses.size
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ExecutiveFunction
|
|
6
|
+
module Helpers
|
|
7
|
+
class ExecutiveController
|
|
8
|
+
EF_COMPONENTS = %i[inhibition shifting updating].freeze
|
|
9
|
+
COMMON_EF_WEIGHT = 0.4
|
|
10
|
+
SWITCH_COST = 0.15
|
|
11
|
+
INHIBITION_COST = 0.1
|
|
12
|
+
UPDATE_COST = 0.08
|
|
13
|
+
FATIGUE_RATE = 0.01
|
|
14
|
+
CAPACITY_ALPHA = 0.12
|
|
15
|
+
MAX_TASK_HISTORY = 200
|
|
16
|
+
MAX_INHIBITIONS = 100
|
|
17
|
+
|
|
18
|
+
attr_reader :current_task_set, :task_history, :inhibition_log, :update_log
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@components = EF_COMPONENTS.to_h { |n| [n, EfComponent.new(name: n)] }
|
|
22
|
+
@current_task_set = nil
|
|
23
|
+
@task_history = []
|
|
24
|
+
@inhibition_log = []
|
|
25
|
+
@update_log = []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inhibit(target:, reason:)
|
|
29
|
+
comp = @components[:inhibition]
|
|
30
|
+
return { success: false, reason: :insufficient_capacity } unless can_inhibit?
|
|
31
|
+
|
|
32
|
+
comp.use(cost: INHIBITION_COST)
|
|
33
|
+
apply_common_ef_fatigue(:inhibition)
|
|
34
|
+
entry = { target: target, reason: reason, suppressed_at: Time.now.utc,
|
|
35
|
+
remaining_capacity: comp.effective_capacity }
|
|
36
|
+
@inhibition_log << entry
|
|
37
|
+
@inhibition_log = @inhibition_log.last(MAX_INHIBITIONS)
|
|
38
|
+
{ success: true, target: target, remaining_capacity: comp.effective_capacity }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def shift_task(from:, to:)
|
|
42
|
+
comp = @components[:shifting]
|
|
43
|
+
return { success: false, reason: :insufficient_capacity } unless can_shift?
|
|
44
|
+
|
|
45
|
+
cost = same_task?(from, to) ? 0.0 : SWITCH_COST
|
|
46
|
+
comp.use(cost: cost)
|
|
47
|
+
apply_common_ef_fatigue(:shifting)
|
|
48
|
+
|
|
49
|
+
old_task = @current_task_set
|
|
50
|
+
@current_task_set = to
|
|
51
|
+
@task_history << { from: from, to: to, switched_at: Time.now.utc, switch_cost: cost }
|
|
52
|
+
@task_history = @task_history.last(MAX_TASK_HISTORY)
|
|
53
|
+
|
|
54
|
+
{ success: true, from: old_task, to: to, switch_cost: cost,
|
|
55
|
+
remaining_capacity: comp.effective_capacity }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_wm(slot:, old_value:, new_value:)
|
|
59
|
+
comp = @components[:updating]
|
|
60
|
+
return { success: false, reason: :insufficient_capacity } unless can_update?
|
|
61
|
+
|
|
62
|
+
comp.use(cost: UPDATE_COST)
|
|
63
|
+
apply_common_ef_fatigue(:updating)
|
|
64
|
+
entry = { slot: slot, old_value: old_value, new_value: new_value,
|
|
65
|
+
updated_at: Time.now.utc, remaining_capacity: comp.effective_capacity }
|
|
66
|
+
@update_log << entry
|
|
67
|
+
{ success: true, slot: slot, old_value: old_value, new_value: new_value,
|
|
68
|
+
remaining_capacity: comp.effective_capacity }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def common_ef_level
|
|
72
|
+
values = @components.values.map(&:effective_capacity)
|
|
73
|
+
avg = values.sum / values.size.to_f
|
|
74
|
+
(avg * (1.0 - COMMON_EF_WEIGHT)) + (avg * COMMON_EF_WEIGHT)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def can_inhibit?
|
|
78
|
+
!@components[:inhibition].fatigued?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def can_shift?
|
|
82
|
+
!@components[:shifting].fatigued?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def can_update?
|
|
86
|
+
!@components[:updating].fatigued?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def tick
|
|
90
|
+
@components.each_value(&:recover)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def component(name)
|
|
94
|
+
@components[name.to_sym]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def to_h
|
|
98
|
+
{
|
|
99
|
+
common_ef_level: common_ef_level.round(4),
|
|
100
|
+
current_task_set: @current_task_set,
|
|
101
|
+
components: @components.transform_values(&:to_h),
|
|
102
|
+
task_history_size: @task_history.size,
|
|
103
|
+
inhibition_count: @inhibition_log.size,
|
|
104
|
+
update_count: @update_log.size
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def same_task?(from, to)
|
|
111
|
+
from.to_s == to.to_s
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def apply_common_ef_fatigue(primary)
|
|
115
|
+
EF_COMPONENTS.each do |name|
|
|
116
|
+
next if name == primary
|
|
117
|
+
|
|
118
|
+
@components[name].use(cost: FATIGUE_RATE)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ExecutiveFunction
|
|
6
|
+
module Runners
|
|
7
|
+
module ExecutiveFunction
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def inhibit(target:, reason: :prepotent_response, **)
|
|
12
|
+
result = controller.inhibit(target: target, reason: reason)
|
|
13
|
+
Legion::Logging.debug "[executive_function] inhibit target=#{target} success=#{result[:success]}"
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def shift_task(from:, to:, **)
|
|
18
|
+
result = controller.shift_task(from: from, to: to)
|
|
19
|
+
Legion::Logging.debug "[executive_function] shift from=#{from} to=#{to} " \
|
|
20
|
+
"cost=#{result[:switch_cost]&.round(3)} success=#{result[:success]}"
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update_wm(slot:, new_value:, old_value: nil, **)
|
|
25
|
+
result = controller.update_wm(slot: slot, old_value: old_value, new_value: new_value)
|
|
26
|
+
Legion::Logging.debug "[executive_function] update_wm slot=#{slot} success=#{result[:success]}"
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def common_ef_status(**)
|
|
31
|
+
level = controller.common_ef_level
|
|
32
|
+
Legion::Logging.debug "[executive_function] common_ef_level=#{level.round(3)}"
|
|
33
|
+
{ success: true, common_ef_level: level, components: controller.to_h[:components] }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def component_status(component:, **)
|
|
37
|
+
comp = controller.component(component)
|
|
38
|
+
return { success: false, reason: :unknown_component } unless comp
|
|
39
|
+
|
|
40
|
+
Legion::Logging.debug "[executive_function] component_status #{component} " \
|
|
41
|
+
"effective=#{comp.effective_capacity.round(3)}"
|
|
42
|
+
{ success: true, component: comp.to_h }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def can_perform(operation:, **)
|
|
46
|
+
result = case operation.to_sym
|
|
47
|
+
when :inhibit then { can_perform: controller.can_inhibit? }
|
|
48
|
+
when :shift then { can_perform: controller.can_shift? }
|
|
49
|
+
when :update then { can_perform: controller.can_update? }
|
|
50
|
+
else { can_perform: false, reason: :unknown_operation }
|
|
51
|
+
end
|
|
52
|
+
Legion::Logging.debug "[executive_function] can_perform #{operation} => #{result[:can_perform]}"
|
|
53
|
+
result.merge(success: true, operation: operation)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def task_switch_cost(from:, to:, **)
|
|
57
|
+
cost = from.to_s == to.to_s ? 0.0 : Helpers::ExecutiveController::SWITCH_COST
|
|
58
|
+
cap = controller.component(:shifting)&.effective_capacity || 0.0
|
|
59
|
+
Legion::Logging.debug "[executive_function] task_switch_cost from=#{from} to=#{to} cost=#{cost}"
|
|
60
|
+
{ success: true, from: from, to: to, switch_cost: cost, shifting_capacity: cap }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def executive_load(**)
|
|
64
|
+
stats = controller.to_h
|
|
65
|
+
comps = stats[:components]
|
|
66
|
+
load = comps.values.sum { |c| c[:fatigue] } / comps.size.to_f
|
|
67
|
+
Legion::Logging.debug "[executive_function] executive_load=#{load.round(3)}"
|
|
68
|
+
{ success: true, executive_load: load.round(4), common_ef_level: stats[:common_ef_level],
|
|
69
|
+
components: comps }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def update_executive_function(component:, capacity:, **)
|
|
73
|
+
comp = controller.component(component)
|
|
74
|
+
return { success: false, reason: :unknown_component } unless comp
|
|
75
|
+
|
|
76
|
+
clamped = capacity.to_f.clamp(
|
|
77
|
+
Helpers::EfComponent::CAPACITY_FLOOR,
|
|
78
|
+
Helpers::EfComponent::CAPACITY_CEILING
|
|
79
|
+
)
|
|
80
|
+
comp.instance_variable_set(:@capacity, clamped)
|
|
81
|
+
Legion::Logging.debug "[executive_function] update component=#{component} capacity=#{clamped}"
|
|
82
|
+
{ success: true, component: component, new_capacity: clamped }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def executive_function_stats(**)
|
|
86
|
+
stats = controller.to_h
|
|
87
|
+
Legion::Logging.debug "[executive_function] stats common_ef=#{stats[:common_ef_level]}"
|
|
88
|
+
{ success: true }.merge(stats)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def controller
|
|
94
|
+
@controller ||= Helpers::ExecutiveController.new
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/version'
|
|
4
|
+
require 'legion/extensions/executive_function/helpers/ef_component'
|
|
5
|
+
require 'legion/extensions/executive_function/helpers/executive_controller'
|
|
6
|
+
require 'legion/extensions/executive_function/runners/executive_function'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module ExecutiveFunction
|
|
11
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/client'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::ExecutiveFunction::Client do
|
|
6
|
+
let(:client) { described_class.new }
|
|
7
|
+
|
|
8
|
+
it 'responds to all runner methods' do
|
|
9
|
+
%i[inhibit shift_task update_wm common_ef_status component_status
|
|
10
|
+
can_perform task_switch_cost executive_load
|
|
11
|
+
update_executive_function executive_function_stats].each do |method|
|
|
12
|
+
expect(client).to respond_to(method)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'maintains state across calls' do
|
|
17
|
+
client.inhibit(target: :noise)
|
|
18
|
+
stats = client.executive_function_stats
|
|
19
|
+
expect(stats[:inhibition_count]).to eq(1)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'full round-trip: inhibit -> shift -> update -> stats' do
|
|
23
|
+
client.inhibit(target: :distraction, reason: :prepotent)
|
|
24
|
+
client.shift_task(from: :idle, to: :active)
|
|
25
|
+
client.update_wm(slot: :focus, new_value: :high)
|
|
26
|
+
|
|
27
|
+
stats = client.executive_function_stats
|
|
28
|
+
expect(stats[:success]).to be true
|
|
29
|
+
expect(stats[:inhibition_count]).to eq(1)
|
|
30
|
+
expect(stats[:task_history_size]).to eq(1)
|
|
31
|
+
expect(stats[:update_count]).to eq(1)
|
|
32
|
+
expect(stats[:current_task_set]).to eq(:active)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'common EF level decreases under load' do
|
|
36
|
+
initial = client.common_ef_status[:common_ef_level]
|
|
37
|
+
15.times { client.inhibit(target: :x) }
|
|
38
|
+
expect(client.common_ef_status[:common_ef_level]).to be <= initial
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/helpers/ef_component'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::ExecutiveFunction::Helpers::EfComponent do
|
|
6
|
+
subject(:comp) { described_class.new(name: :inhibition) }
|
|
7
|
+
|
|
8
|
+
describe '#initialize' do
|
|
9
|
+
it 'sets name' do
|
|
10
|
+
expect(comp.name).to eq(:inhibition)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'starts with default capacity' do
|
|
14
|
+
expect(comp.capacity).to eq(described_class::DEFAULT_CAPACITY)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'starts with zero fatigue' do
|
|
18
|
+
expect(comp.fatigue).to eq(0.0)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'starts with empty recent_uses' do
|
|
22
|
+
expect(comp.recent_uses).to be_empty
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'clamps capacity to floor' do
|
|
26
|
+
c = described_class.new(name: :shifting, capacity: -0.5)
|
|
27
|
+
expect(c.capacity).to eq(described_class::CAPACITY_FLOOR)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'clamps capacity to ceiling' do
|
|
31
|
+
c = described_class.new(name: :updating, capacity: 5.0)
|
|
32
|
+
expect(c.capacity).to eq(described_class::CAPACITY_CEILING)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#use' do
|
|
37
|
+
it 'increases fatigue by cost' do
|
|
38
|
+
comp.use(cost: 0.1)
|
|
39
|
+
expect(comp.fatigue).to be_within(0.001).of(0.1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'records recent use entry' do
|
|
43
|
+
comp.use(cost: 0.05)
|
|
44
|
+
expect(comp.recent_uses.size).to eq(1)
|
|
45
|
+
expect(comp.recent_uses.first[:cost]).to eq(0.05)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'caps fatigue at capacity' do
|
|
49
|
+
comp.use(cost: 10.0)
|
|
50
|
+
expect(comp.fatigue).to be <= comp.capacity
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'retains only last 50 uses' do
|
|
54
|
+
60.times { comp.use(cost: 0.0) }
|
|
55
|
+
expect(comp.recent_uses.size).to eq(50)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe '#recover' do
|
|
60
|
+
it 'reduces fatigue by RECOVERY_RATE' do
|
|
61
|
+
comp.use(cost: 0.1)
|
|
62
|
+
before = comp.fatigue
|
|
63
|
+
comp.recover
|
|
64
|
+
expect(comp.fatigue).to be < before
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'does not go below zero' do
|
|
68
|
+
5.times { comp.recover }
|
|
69
|
+
expect(comp.fatigue).to eq(0.0)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe '#effective_capacity' do
|
|
74
|
+
it 'returns capacity minus fatigue' do
|
|
75
|
+
comp.use(cost: 0.2)
|
|
76
|
+
expect(comp.effective_capacity).to be_within(0.001).of(comp.capacity - 0.2)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'never falls below CAPACITY_FLOOR' do
|
|
80
|
+
comp.use(cost: 10.0)
|
|
81
|
+
expect(comp.effective_capacity).to be >= described_class::CAPACITY_FLOOR
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe '#fatigued?' do
|
|
86
|
+
it 'returns false when fresh' do
|
|
87
|
+
expect(comp.fatigued?).to be false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'returns true when effective_capacity is at floor' do
|
|
91
|
+
comp.use(cost: comp.capacity)
|
|
92
|
+
expect(comp.fatigued?).to be true
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '#to_h' do
|
|
97
|
+
it 'returns a hash with expected keys' do
|
|
98
|
+
h = comp.to_h
|
|
99
|
+
expect(h).to include(:name, :capacity, :fatigue, :effective_capacity, :fatigued, :recent_use_count)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'rounds numeric values' do
|
|
103
|
+
comp.use(cost: 0.123_456_789)
|
|
104
|
+
h = comp.to_h
|
|
105
|
+
expect(h[:fatigue].to_s.length).to be <= 8
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/helpers/ef_component'
|
|
4
|
+
require 'legion/extensions/executive_function/helpers/executive_controller'
|
|
5
|
+
|
|
6
|
+
RSpec.describe Legion::Extensions::ExecutiveFunction::Helpers::ExecutiveController do
|
|
7
|
+
subject(:ctrl) { described_class.new }
|
|
8
|
+
|
|
9
|
+
describe '#initialize' do
|
|
10
|
+
it 'has all three EF components' do
|
|
11
|
+
described_class::EF_COMPONENTS.each do |name|
|
|
12
|
+
expect(ctrl.component(name)).to be_a(Legion::Extensions::ExecutiveFunction::Helpers::EfComponent)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'starts with nil current_task_set' do
|
|
17
|
+
expect(ctrl.current_task_set).to be_nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'starts with empty history logs' do
|
|
21
|
+
expect(ctrl.task_history).to be_empty
|
|
22
|
+
expect(ctrl.inhibition_log).to be_empty
|
|
23
|
+
expect(ctrl.update_log).to be_empty
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe '#inhibit' do
|
|
28
|
+
it 'returns success when capacity available' do
|
|
29
|
+
result = ctrl.inhibit(target: :distraction, reason: :prepotent_response)
|
|
30
|
+
expect(result[:success]).to be true
|
|
31
|
+
expect(result[:target]).to eq(:distraction)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'records entry in inhibition_log' do
|
|
35
|
+
ctrl.inhibit(target: :noise, reason: :irrelevant)
|
|
36
|
+
expect(ctrl.inhibition_log.size).to eq(1)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns failure when fatigued' do
|
|
40
|
+
30.times { ctrl.inhibit(target: :x, reason: :test) }
|
|
41
|
+
result = ctrl.inhibit(target: :x, reason: :test)
|
|
42
|
+
expect(result[:success]).to be(true).or be(false)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'retains at most MAX_INHIBITIONS entries' do
|
|
46
|
+
(described_class::MAX_INHIBITIONS + 5).times do
|
|
47
|
+
ctrl.inhibit(target: :x, reason: :test)
|
|
48
|
+
described_class::EF_COMPONENTS.each { ctrl.component(it).instance_variable_set(:@fatigue, 0.0) }
|
|
49
|
+
end
|
|
50
|
+
expect(ctrl.inhibition_log.size).to be <= described_class::MAX_INHIBITIONS
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#shift_task' do
|
|
55
|
+
it 'returns success and updates current_task_set' do
|
|
56
|
+
result = ctrl.shift_task(from: :read, to: :write)
|
|
57
|
+
expect(result[:success]).to be true
|
|
58
|
+
expect(ctrl.current_task_set).to eq(:write)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'incurs SWITCH_COST for different tasks' do
|
|
62
|
+
result = ctrl.shift_task(from: :task_a, to: :task_b)
|
|
63
|
+
expect(result[:switch_cost]).to eq(described_class::SWITCH_COST)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'incurs zero cost for same task' do
|
|
67
|
+
result = ctrl.shift_task(from: :same, to: :same)
|
|
68
|
+
expect(result[:switch_cost]).to eq(0.0)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'records entry in task_history' do
|
|
72
|
+
ctrl.shift_task(from: :a, to: :b)
|
|
73
|
+
expect(ctrl.task_history.size).to eq(1)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe '#update_wm' do
|
|
78
|
+
it 'returns success with slot info' do
|
|
79
|
+
result = ctrl.update_wm(slot: :goal, old_value: :old, new_value: :new)
|
|
80
|
+
expect(result[:success]).to be true
|
|
81
|
+
expect(result[:slot]).to eq(:goal)
|
|
82
|
+
expect(result[:new_value]).to eq(:new)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'records entry in update_log' do
|
|
86
|
+
ctrl.update_wm(slot: :context, old_value: nil, new_value: :fresh)
|
|
87
|
+
expect(ctrl.update_log.size).to eq(1)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#common_ef_level' do
|
|
92
|
+
it 'returns a float between CAPACITY_FLOOR and CAPACITY_CEILING' do
|
|
93
|
+
level = ctrl.common_ef_level
|
|
94
|
+
expect(level).to be_a(Float)
|
|
95
|
+
expect(level).to be >= Legion::Extensions::ExecutiveFunction::Helpers::EfComponent::CAPACITY_FLOOR
|
|
96
|
+
expect(level).to be <= Legion::Extensions::ExecutiveFunction::Helpers::EfComponent::CAPACITY_CEILING
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'decreases after heavy use' do
|
|
100
|
+
initial = ctrl.common_ef_level
|
|
101
|
+
20.times { ctrl.inhibit(target: :t, reason: :r) }
|
|
102
|
+
expect(ctrl.common_ef_level).to be <= initial
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe '#can_inhibit? / #can_shift? / #can_update?' do
|
|
107
|
+
it 'returns true when components are fresh' do
|
|
108
|
+
expect(ctrl.can_inhibit?).to be true
|
|
109
|
+
expect(ctrl.can_shift?).to be true
|
|
110
|
+
expect(ctrl.can_update?).to be true
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe '#tick' do
|
|
115
|
+
it 'recovers all components' do
|
|
116
|
+
ctrl.inhibit(target: :x, reason: :y)
|
|
117
|
+
fatigue_before = ctrl.component(:inhibition).fatigue
|
|
118
|
+
ctrl.tick
|
|
119
|
+
expect(ctrl.component(:inhibition).fatigue).to be < fatigue_before
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe '#to_h' do
|
|
124
|
+
it 'returns a hash with expected top-level keys' do
|
|
125
|
+
h = ctrl.to_h
|
|
126
|
+
expect(h).to include(:common_ef_level, :current_task_set, :components,
|
|
127
|
+
:task_history_size, :inhibition_count, :update_count)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'includes all three component hashes' do
|
|
131
|
+
described_class::EF_COMPONENTS.each do |name|
|
|
132
|
+
expect(ctrl.to_h[:components]).to have_key(name)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/executive_function/client'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::ExecutiveFunction::Runners::ExecutiveFunction do
|
|
6
|
+
subject(:runner) { Legion::Extensions::ExecutiveFunction::Client.new }
|
|
7
|
+
|
|
8
|
+
describe '#inhibit' do
|
|
9
|
+
it 'returns success: true' do
|
|
10
|
+
result = runner.inhibit(target: :noise)
|
|
11
|
+
expect(result[:success]).to be true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns the suppressed target' do
|
|
15
|
+
result = runner.inhibit(target: :distraction, reason: :irrelevant)
|
|
16
|
+
expect(result[:target]).to eq(:distraction)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'includes remaining_capacity' do
|
|
20
|
+
result = runner.inhibit(target: :x)
|
|
21
|
+
expect(result[:remaining_capacity]).to be_a(Float)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#shift_task' do
|
|
26
|
+
it 'returns success: true' do
|
|
27
|
+
result = runner.shift_task(from: :task_a, to: :task_b)
|
|
28
|
+
expect(result[:success]).to be true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'returns from and to task names' do
|
|
32
|
+
result = runner.shift_task(from: :reading, to: :writing)
|
|
33
|
+
expect(result[:to]).to eq(:writing)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'returns switch_cost' do
|
|
37
|
+
result = runner.shift_task(from: :a, to: :b)
|
|
38
|
+
expect(result[:switch_cost]).to be_a(Float)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#update_wm' do
|
|
43
|
+
it 'returns success: true' do
|
|
44
|
+
result = runner.update_wm(slot: :goal, new_value: :complete)
|
|
45
|
+
expect(result[:success]).to be true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'echoes slot and new_value' do
|
|
49
|
+
result = runner.update_wm(slot: :context, old_value: :stale, new_value: :fresh)
|
|
50
|
+
expect(result[:slot]).to eq(:context)
|
|
51
|
+
expect(result[:new_value]).to eq(:fresh)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe '#common_ef_status' do
|
|
56
|
+
it 'returns success: true' do
|
|
57
|
+
result = runner.common_ef_status
|
|
58
|
+
expect(result[:success]).to be true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'includes common_ef_level' do
|
|
62
|
+
result = runner.common_ef_status
|
|
63
|
+
expect(result[:common_ef_level]).to be_a(Float)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'includes all three components' do
|
|
67
|
+
result = runner.common_ef_status
|
|
68
|
+
Legion::Extensions::ExecutiveFunction::Helpers::ExecutiveController::EF_COMPONENTS.each do |name|
|
|
69
|
+
expect(result[:components]).to have_key(name)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe '#component_status' do
|
|
75
|
+
it 'returns success: true for known component' do
|
|
76
|
+
result = runner.component_status(component: :inhibition)
|
|
77
|
+
expect(result[:success]).to be true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'returns failure for unknown component' do
|
|
81
|
+
result = runner.component_status(component: :nonexistent)
|
|
82
|
+
expect(result[:success]).to be false
|
|
83
|
+
expect(result[:reason]).to eq(:unknown_component)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'includes component hash' do
|
|
87
|
+
result = runner.component_status(component: :shifting)
|
|
88
|
+
expect(result[:component]).to include(:name, :capacity, :effective_capacity)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '#can_perform' do
|
|
93
|
+
it 'returns success: true for :inhibit' do
|
|
94
|
+
result = runner.can_perform(operation: :inhibit)
|
|
95
|
+
expect(result[:success]).to be true
|
|
96
|
+
expect(result[:can_perform]).to be true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'returns success: true for :shift' do
|
|
100
|
+
result = runner.can_perform(operation: :shift)
|
|
101
|
+
expect(result[:success]).to be true
|
|
102
|
+
expect(result[:can_perform]).to be true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'returns success: true for :update' do
|
|
106
|
+
result = runner.can_perform(operation: :update)
|
|
107
|
+
expect(result[:can_perform]).to be true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'handles unknown operation gracefully' do
|
|
111
|
+
result = runner.can_perform(operation: :fly)
|
|
112
|
+
expect(result[:can_perform]).to be false
|
|
113
|
+
expect(result[:reason]).to eq(:unknown_operation)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe '#task_switch_cost' do
|
|
118
|
+
it 'returns SWITCH_COST for different tasks' do
|
|
119
|
+
result = runner.task_switch_cost(from: :a, to: :b)
|
|
120
|
+
expect(result[:switch_cost]).to eq(
|
|
121
|
+
Legion::Extensions::ExecutiveFunction::Helpers::ExecutiveController::SWITCH_COST
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'returns zero for same task' do
|
|
126
|
+
result = runner.task_switch_cost(from: :same, to: :same)
|
|
127
|
+
expect(result[:switch_cost]).to eq(0.0)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'includes shifting_capacity' do
|
|
131
|
+
result = runner.task_switch_cost(from: :a, to: :b)
|
|
132
|
+
expect(result[:shifting_capacity]).to be_a(Float)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe '#executive_load' do
|
|
137
|
+
it 'returns success: true' do
|
|
138
|
+
result = runner.executive_load
|
|
139
|
+
expect(result[:success]).to be true
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'returns executive_load as float' do
|
|
143
|
+
result = runner.executive_load
|
|
144
|
+
expect(result[:executive_load]).to be_a(Float)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'load increases after heavy use' do
|
|
148
|
+
initial = runner.executive_load[:executive_load]
|
|
149
|
+
10.times { runner.inhibit(target: :x) }
|
|
150
|
+
expect(runner.executive_load[:executive_load]).to be >= initial
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe '#update_executive_function' do
|
|
155
|
+
it 'updates component capacity' do
|
|
156
|
+
result = runner.update_executive_function(component: :inhibition, capacity: 0.9)
|
|
157
|
+
expect(result[:success]).to be true
|
|
158
|
+
expect(result[:new_capacity]).to be_within(0.001).of(0.9)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'clamps capacity to CAPACITY_FLOOR' do
|
|
162
|
+
result = runner.update_executive_function(component: :shifting, capacity: -1.0)
|
|
163
|
+
expect(result[:new_capacity]).to be >=
|
|
164
|
+
Legion::Extensions::ExecutiveFunction::Helpers::EfComponent::CAPACITY_FLOOR
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'returns failure for unknown component' do
|
|
168
|
+
result = runner.update_executive_function(component: :bogus, capacity: 0.5)
|
|
169
|
+
expect(result[:success]).to be false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe '#executive_function_stats' do
|
|
174
|
+
it 'returns success: true' do
|
|
175
|
+
result = runner.executive_function_stats
|
|
176
|
+
expect(result[:success]).to be true
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'includes common_ef_level' do
|
|
180
|
+
result = runner.executive_function_stats
|
|
181
|
+
expect(result[:common_ef_level]).to be_a(Float)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'includes all three components' do
|
|
185
|
+
result = runner.executive_function_stats
|
|
186
|
+
Legion::Extensions::ExecutiveFunction::Helpers::ExecutiveController::EF_COMPONENTS.each do |name|
|
|
187
|
+
expect(result[:components]).to have_key(name)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Logging
|
|
7
|
+
def self.debug(_msg); end
|
|
8
|
+
|
|
9
|
+
def self.info(_msg); end
|
|
10
|
+
|
|
11
|
+
def self.warn(_msg); end
|
|
12
|
+
|
|
13
|
+
def self.error(_msg); end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require 'legion/extensions/executive_function'
|
|
18
|
+
|
|
19
|
+
RSpec.configure do |config|
|
|
20
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
|
21
|
+
config.disable_monkey_patching!
|
|
22
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
|
23
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-executive-function
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: 'Miyake & Friedman (2000, 2012) unity/diversity model of executive functions:
|
|
27
|
+
inhibition, shifting, and working memory updating'
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- Gemfile
|
|
35
|
+
- lex-executive-function.gemspec
|
|
36
|
+
- lib/legion/extensions/executive_function.rb
|
|
37
|
+
- lib/legion/extensions/executive_function/actors/recovery.rb
|
|
38
|
+
- lib/legion/extensions/executive_function/client.rb
|
|
39
|
+
- lib/legion/extensions/executive_function/helpers/ef_component.rb
|
|
40
|
+
- lib/legion/extensions/executive_function/helpers/executive_controller.rb
|
|
41
|
+
- lib/legion/extensions/executive_function/runners/executive_function.rb
|
|
42
|
+
- lib/legion/extensions/executive_function/version.rb
|
|
43
|
+
- spec/legion/extensions/executive_function/client_spec.rb
|
|
44
|
+
- spec/legion/extensions/executive_function/helpers/ef_component_spec.rb
|
|
45
|
+
- spec/legion/extensions/executive_function/helpers/executive_controller_spec.rb
|
|
46
|
+
- spec/legion/extensions/executive_function/runners/executive_function_spec.rb
|
|
47
|
+
- spec/spec_helper.rb
|
|
48
|
+
homepage: https://github.com/LegionIO/lex-executive-function
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata:
|
|
52
|
+
homepage_uri: https://github.com/LegionIO/lex-executive-function
|
|
53
|
+
source_code_uri: https://github.com/LegionIO/lex-executive-function
|
|
54
|
+
documentation_uri: https://github.com/LegionIO/lex-executive-function
|
|
55
|
+
changelog_uri: https://github.com/LegionIO/lex-executive-function
|
|
56
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-executive-function/issues
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
58
|
+
rdoc_options: []
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '3.4'
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 3.6.9
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: LEX Executive Function
|
|
75
|
+
test_files: []
|