lex-cognitive-compass 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/.github/workflows/ci.yml +16 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.rubocop.yml +37 -0
- data/CLAUDE.md +75 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +78 -0
- data/README.md +39 -0
- data/lex-cognitive-compass.gemspec +33 -0
- data/lib/legion/extensions/cognitive_compass/client.rb +11 -0
- data/lib/legion/extensions/cognitive_compass/helpers/bearing.rb +75 -0
- data/lib/legion/extensions/cognitive_compass/helpers/compass_engine.rb +104 -0
- data/lib/legion/extensions/cognitive_compass/helpers/constants.rb +61 -0
- data/lib/legion/extensions/cognitive_compass/helpers/magnetic_bias.rb +77 -0
- data/lib/legion/extensions/cognitive_compass/runners/cognitive_compass.rb +72 -0
- data/lib/legion/extensions/cognitive_compass/version.rb +9 -0
- data/lib/legion/extensions/cognitive_compass.rb +19 -0
- metadata +79 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: aabc5df25b6e0b4abd5a92508175ad6da5d036b5ce8f245540558f3f1702ab02
|
|
4
|
+
data.tar.gz: 41b0628f4ddfba105e7deda09d369b5e7f1bd9dba30a695f0b0cb427d42c1c07
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4b97868fcac953522701fe87cfef7523d0f03fd37541656d77f3f983ba7a390099e29c9aea2fb762df115c61d442de55d209cf4a303fe7f6be8c3e254ef7e437
|
|
7
|
+
data.tar.gz: 3986bc6d68d324dc34d8b3ef032db800ecb233c9984bc254ee798cc7677bd2bf20b60c2435c5c5f8a79519fb076dbbb1ce6572268a52af12b2f127ec8acf9741
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.4
|
|
4
|
+
|
|
5
|
+
Style/Documentation:
|
|
6
|
+
Enabled: false
|
|
7
|
+
|
|
8
|
+
Naming/PredicateMethod:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
11
|
+
Naming/PredicatePrefix:
|
|
12
|
+
Enabled: false
|
|
13
|
+
|
|
14
|
+
Metrics/ClassLength:
|
|
15
|
+
Max: 150
|
|
16
|
+
|
|
17
|
+
Metrics/MethodLength:
|
|
18
|
+
Max: 25
|
|
19
|
+
|
|
20
|
+
Metrics/AbcSize:
|
|
21
|
+
Max: 25
|
|
22
|
+
|
|
23
|
+
Metrics/ParameterLists:
|
|
24
|
+
Max: 8
|
|
25
|
+
MaxOptionalParameters: 8
|
|
26
|
+
|
|
27
|
+
Layout/HashAlignment:
|
|
28
|
+
EnforcedColonStyle: table
|
|
29
|
+
EnforcedHashRocketStyle: table
|
|
30
|
+
|
|
31
|
+
Metrics/BlockLength:
|
|
32
|
+
Exclude:
|
|
33
|
+
- 'spec/**/*'
|
|
34
|
+
|
|
35
|
+
Style/OneClassPerFile:
|
|
36
|
+
Exclude:
|
|
37
|
+
- 'spec/spec_helper.rb'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# lex-cognitive-compass
|
|
2
|
+
|
|
3
|
+
**Level 3 Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
- **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Maps cardinal directions to value axes (truth/utility/beauty/safety), tracks magnetic declination from cognitive biases, and provides true north calibration for aligned decision-making. Bearings represent directional assessments toward value axes; biases cause declination that skews bearings away from true north.
|
|
10
|
+
|
|
11
|
+
## Gem Info
|
|
12
|
+
|
|
13
|
+
- **Gem name**: `lex-cognitive-compass`
|
|
14
|
+
- **Version**: `0.1.0`
|
|
15
|
+
- **Module**: `Legion::Extensions::CognitiveCompass`
|
|
16
|
+
- **Ruby**: `>= 3.4`
|
|
17
|
+
- **License**: MIT
|
|
18
|
+
|
|
19
|
+
## File Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/legion/extensions/cognitive_compass/
|
|
23
|
+
cognitive_compass.rb
|
|
24
|
+
version.rb
|
|
25
|
+
client.rb
|
|
26
|
+
helpers/
|
|
27
|
+
constants.rb
|
|
28
|
+
compass_engine.rb
|
|
29
|
+
bearing.rb
|
|
30
|
+
magnetic_bias.rb
|
|
31
|
+
runners/
|
|
32
|
+
cognitive_compass.rb
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Key Constants
|
|
36
|
+
|
|
37
|
+
From `helpers/constants.rb`:
|
|
38
|
+
|
|
39
|
+
- `CARDINAL_DIRECTIONS` — `{ north: :truth, east: :utility, south: :beauty, west: :safety }`
|
|
40
|
+
- `INTERCARDINAL_DIRECTIONS` — `{ northeast: [:truth, :utility], southeast: [:utility, :beauty], southwest: [:beauty, :safety], northwest: [:safety, :truth] }`
|
|
41
|
+
- `BIAS_TYPES` — `%i[confirmation anchoring availability recency authority framing sunk_cost optimism]`
|
|
42
|
+
- `MAX_COMPASSES` = `50`, `MAX_BIASES` = `20`
|
|
43
|
+
- `DECLINATION_DECAY` = `0.02`, `CALIBRATION_BOOST` = `0.15`
|
|
44
|
+
- `ACCURACY_LABELS` — `0.8+` = `:precise`, `0.6` = `:reliable`, `0.4` = `:approximate`, `0.2` = `:drifting`, below = `:lost`
|
|
45
|
+
- `DECLINATION_LABELS` — `0.8+` = `:severe`, `0.6` = `:significant`, `0.4` = `:moderate`, `0.2` = `:mild`, below = `:negligible`
|
|
46
|
+
|
|
47
|
+
## Runners
|
|
48
|
+
|
|
49
|
+
All methods in `Runners::CognitiveCompass` (`extend self`):
|
|
50
|
+
|
|
51
|
+
- `take_bearing(direction:, context:, confidence: nil)` — records a directional assessment toward a value axis; returns `Bearing` with accuracy derived from confidence
|
|
52
|
+
- `register_bias(bias_type:, domain:, declination: nil, strength: nil)` — registers a cognitive bias that causes magnetic declination; validates against `BIAS_TYPES`
|
|
53
|
+
- `calibrate(bias_id:, amount: nil)` — reduces declination on a bias by `CALIBRATION_BOOST` (or custom amount); represents active bias correction
|
|
54
|
+
- `list_bearings(direction: nil)` — all bearings, optionally filtered by direction
|
|
55
|
+
- `list_biases` — all registered biases with current declination
|
|
56
|
+
- `compass_status` — full compass report: bearing distribution, total declination, true north alignment
|
|
57
|
+
|
|
58
|
+
## Helpers
|
|
59
|
+
|
|
60
|
+
- `CompassEngine` — manages bearings and biases. `compass_report` computes aggregate: directional distribution, average declination, calibration state.
|
|
61
|
+
- `Bearing` — directional assessment with `direction`, `context`, `confidence`, `accuracy`. `direction` must be a valid cardinal or intercardinal value.
|
|
62
|
+
- `MagneticBias` — cognitive bias with `bias_type`, `domain`, `declination`, `strength`. `calibrate!(amount)` reduces declination. `declination_label` classifies severity.
|
|
63
|
+
|
|
64
|
+
## Integration Points
|
|
65
|
+
|
|
66
|
+
- `lex-cognitive-blindspot` tracks unknown-unknowns; compass tracks known value misalignments (biases that are registered and measurable).
|
|
67
|
+
- `lex-tick` action selection phase can check compass bearing toward `:safety` and `:truth` before executing high-stakes decisions — bearings with low accuracy or high declination should trigger deliberate mode.
|
|
68
|
+
- `calibrate` can be called after a `lex-conflict` resolution event, representing bias reduction through adversarial challenge.
|
|
69
|
+
|
|
70
|
+
## Development Notes
|
|
71
|
+
|
|
72
|
+
- `CARDINAL_DIRECTIONS` maps direction symbols to value names — north=truth is the primary alignment axis. "True north" metaphor: an unbiased agent points north (toward truth) reliably.
|
|
73
|
+
- Intercardinal directions blend two value axes; callers use these for nuanced assessments (e.g., `northeast` = truth + utility balance).
|
|
74
|
+
- `MAX_BIASES = 20` is intentionally small — too many registered biases suggests the calibration model is incomplete; address biases, don't just log them.
|
|
75
|
+
- `DECLINATION_DECAY = 0.02` models natural bias reduction over time without active calibration (slow passive correction).
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
lex-cognitive-compass (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
addressable (2.8.9)
|
|
10
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
11
|
+
ast (2.4.3)
|
|
12
|
+
bigdecimal (4.0.1)
|
|
13
|
+
diff-lcs (1.6.2)
|
|
14
|
+
json (2.19.1)
|
|
15
|
+
json-schema (6.2.0)
|
|
16
|
+
addressable (~> 2.8)
|
|
17
|
+
bigdecimal (>= 3.1, < 5)
|
|
18
|
+
language_server-protocol (3.17.0.5)
|
|
19
|
+
lint_roller (1.1.0)
|
|
20
|
+
mcp (0.8.0)
|
|
21
|
+
json-schema (>= 4.1)
|
|
22
|
+
parallel (1.27.0)
|
|
23
|
+
parser (3.3.10.2)
|
|
24
|
+
ast (~> 2.4.1)
|
|
25
|
+
racc
|
|
26
|
+
prism (1.9.0)
|
|
27
|
+
public_suffix (7.0.5)
|
|
28
|
+
racc (1.8.1)
|
|
29
|
+
rainbow (3.1.1)
|
|
30
|
+
regexp_parser (2.11.3)
|
|
31
|
+
rspec (3.13.2)
|
|
32
|
+
rspec-core (~> 3.13.0)
|
|
33
|
+
rspec-expectations (~> 3.13.0)
|
|
34
|
+
rspec-mocks (~> 3.13.0)
|
|
35
|
+
rspec-core (3.13.6)
|
|
36
|
+
rspec-support (~> 3.13.0)
|
|
37
|
+
rspec-expectations (3.13.5)
|
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
39
|
+
rspec-support (~> 3.13.0)
|
|
40
|
+
rspec-mocks (3.13.8)
|
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
42
|
+
rspec-support (~> 3.13.0)
|
|
43
|
+
rspec-support (3.13.7)
|
|
44
|
+
rubocop (1.85.1)
|
|
45
|
+
json (~> 2.3)
|
|
46
|
+
language_server-protocol (~> 3.17.0.2)
|
|
47
|
+
lint_roller (~> 1.1.0)
|
|
48
|
+
mcp (~> 0.6)
|
|
49
|
+
parallel (~> 1.10)
|
|
50
|
+
parser (>= 3.3.0.2)
|
|
51
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
52
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
53
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
54
|
+
ruby-progressbar (~> 1.7)
|
|
55
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
56
|
+
rubocop-ast (1.49.1)
|
|
57
|
+
parser (>= 3.3.7.2)
|
|
58
|
+
prism (~> 1.7)
|
|
59
|
+
rubocop-rspec (3.9.0)
|
|
60
|
+
lint_roller (~> 1.1)
|
|
61
|
+
rubocop (~> 1.81)
|
|
62
|
+
ruby-progressbar (1.13.0)
|
|
63
|
+
unicode-display_width (3.2.0)
|
|
64
|
+
unicode-emoji (~> 4.1)
|
|
65
|
+
unicode-emoji (4.2.0)
|
|
66
|
+
|
|
67
|
+
PLATFORMS
|
|
68
|
+
arm64-darwin-25
|
|
69
|
+
ruby
|
|
70
|
+
|
|
71
|
+
DEPENDENCIES
|
|
72
|
+
lex-cognitive-compass!
|
|
73
|
+
rspec (~> 3.13)
|
|
74
|
+
rubocop (~> 1.75)
|
|
75
|
+
rubocop-rspec
|
|
76
|
+
|
|
77
|
+
BUNDLED WITH
|
|
78
|
+
2.6.9
|
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# lex-cognitive-compass
|
|
2
|
+
|
|
3
|
+
Cognitive compass LEX — internal directional sense for decision-making. Maps cardinal directions to value axes (truth/utility/beauty/safety), tracks magnetic declination from cognitive biases, and provides true north calibration for aligned decision-making.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Decision-making has a direction — toward truth, utility, beauty, or safety. This extension models an internal compass that takes bearings (directional assessments in context), registers cognitive biases that cause magnetic declination (pulling bearings away from true north), and supports active calibration (reducing bias-driven drift).
|
|
8
|
+
|
|
9
|
+
Eight bias types are modeled: confirmation, anchoring, availability, recency, authority, framing, sunk cost, and optimism. Intercardinal directions blend two value axes for nuanced assessments.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
client = Legion::Extensions::CognitiveCompass::Client.new
|
|
15
|
+
|
|
16
|
+
client.take_bearing(direction: :north, context: 'fact-checking response accuracy', confidence: 0.8)
|
|
17
|
+
client.take_bearing(direction: :west, context: 'evaluating action safety', confidence: 0.7)
|
|
18
|
+
|
|
19
|
+
bias = client.register_bias(
|
|
20
|
+
bias_type: :confirmation,
|
|
21
|
+
domain: :reasoning,
|
|
22
|
+
declination: 0.6
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
client.calibrate(bias_id: bias[:bias][:id]) # reduce declination
|
|
26
|
+
client.compass_status
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Development
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bundle install
|
|
33
|
+
bundle exec rspec
|
|
34
|
+
bundle exec rubocop
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_compass/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-compass'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveCompass::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
spec.license = 'MIT'
|
|
11
|
+
|
|
12
|
+
spec.summary = 'Cognitive compass LEX — internal directional sense for decision-making'
|
|
13
|
+
spec.description = 'Maps cardinal directions to value axes (truth/utility/beauty/safety), ' \
|
|
14
|
+
'tracks magnetic declination from cognitive biases, and provides true ' \
|
|
15
|
+
'north calibration for aligned decision-making.'
|
|
16
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-compass'
|
|
17
|
+
|
|
18
|
+
spec.required_ruby_version = '>= 3.4'
|
|
19
|
+
|
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
21
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
22
|
+
spec.metadata['documentation_uri'] = "#{spec.homepage}#readme"
|
|
23
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
24
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
25
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
26
|
+
|
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
|
28
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(test|spec|features)/}) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
spec.require_paths = ['lib']
|
|
32
|
+
spec.add_development_dependency 'legion-gaia'
|
|
33
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveCompass
|
|
6
|
+
module Helpers
|
|
7
|
+
class Bearing
|
|
8
|
+
attr_reader :id, :direction, :value_axis, :context,
|
|
9
|
+
:taken_at
|
|
10
|
+
attr_accessor :confidence
|
|
11
|
+
|
|
12
|
+
def initialize(direction:, context:, confidence: nil)
|
|
13
|
+
validate_direction!(direction)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@direction = direction.to_sym
|
|
16
|
+
@value_axis = resolve_axis(direction.to_sym)
|
|
17
|
+
@context = context.to_s
|
|
18
|
+
@confidence = (confidence || 0.7).to_f.clamp(0.0, 1.0).round(10)
|
|
19
|
+
@taken_at = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def reliable?
|
|
23
|
+
@confidence >= 0.6
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def lost?
|
|
27
|
+
@confidence < 0.2
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cardinal?
|
|
31
|
+
Constants::CARDINAL_DIRECTIONS.key?(@direction)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def accuracy_label
|
|
35
|
+
Constants.label_for(Constants::ACCURACY_LABELS, @confidence)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_h
|
|
39
|
+
{
|
|
40
|
+
id: @id,
|
|
41
|
+
direction: @direction,
|
|
42
|
+
value_axis: @value_axis,
|
|
43
|
+
context: @context,
|
|
44
|
+
confidence: @confidence,
|
|
45
|
+
accuracy_label: accuracy_label,
|
|
46
|
+
cardinal: cardinal?,
|
|
47
|
+
reliable: reliable?,
|
|
48
|
+
lost: lost?,
|
|
49
|
+
taken_at: @taken_at
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def resolve_axis(dir)
|
|
56
|
+
Constants::CARDINAL_DIRECTIONS.fetch(dir) do
|
|
57
|
+
Constants::INTERCARDINAL_DIRECTIONS.fetch(dir) do
|
|
58
|
+
raise ArgumentError, "unknown direction: #{dir}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def validate_direction!(val)
|
|
64
|
+
sym = val.to_sym
|
|
65
|
+
return if Constants::CARDINAL_DIRECTIONS.key?(sym) ||
|
|
66
|
+
Constants::INTERCARDINAL_DIRECTIONS.key?(sym)
|
|
67
|
+
|
|
68
|
+
raise ArgumentError,
|
|
69
|
+
"unknown direction: #{val.inspect}; must be cardinal or intercardinal"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveCompass
|
|
6
|
+
module Helpers
|
|
7
|
+
class CompassEngine
|
|
8
|
+
def initialize
|
|
9
|
+
@bearings = {}
|
|
10
|
+
@biases = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def take_bearing(direction:, context:, confidence: nil)
|
|
14
|
+
bearing = Bearing.new(direction: direction, context: context,
|
|
15
|
+
confidence: confidence)
|
|
16
|
+
@bearings[bearing.id] = bearing
|
|
17
|
+
bearing
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def register_bias(bias_type:, domain:, declination: nil, strength: nil)
|
|
21
|
+
raise ArgumentError, 'too many biases' if @biases.size >= Constants::MAX_BIASES
|
|
22
|
+
|
|
23
|
+
bias = MagneticBias.new(bias_type: bias_type, domain: domain,
|
|
24
|
+
declination: declination, strength: strength)
|
|
25
|
+
@biases[bias.id] = bias
|
|
26
|
+
bias
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def calibrate(bias_id:, amount: Constants::CALIBRATION_BOOST)
|
|
30
|
+
fetch_bias(bias_id).correct!(amount: amount)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def true_north_offset
|
|
34
|
+
return 0.0 if @biases.empty?
|
|
35
|
+
|
|
36
|
+
total = @biases.values.sum(&:declination)
|
|
37
|
+
(total / @biases.size).round(10)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def accuracy
|
|
41
|
+
offset = true_north_offset
|
|
42
|
+
(1.0 - offset).clamp(0.0, 1.0).round(10)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def accuracy_label
|
|
46
|
+
Constants.label_for(Constants::ACCURACY_LABELS, accuracy)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def decay_biases!(rate: Constants::DECLINATION_DECAY)
|
|
50
|
+
@biases.each_value { |b| b.decay!(rate: rate) }
|
|
51
|
+
pruned = @biases.select { |_, b| b.negligible? && b.strength < 0.1 }.keys
|
|
52
|
+
pruned.each { |id| @biases.delete(id) }
|
|
53
|
+
{ remaining: @biases.size, pruned: pruned.size }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def dominant_direction
|
|
57
|
+
return nil if @bearings.empty?
|
|
58
|
+
|
|
59
|
+
counts = Hash.new(0)
|
|
60
|
+
@bearings.each_value { |b| counts[b.direction] += b.confidence }
|
|
61
|
+
counts.max_by { |_, v| v }&.first
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def bearings_by_direction
|
|
65
|
+
result = Hash.new(0)
|
|
66
|
+
@bearings.each_value { |b| result[b.direction] += 1 }
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def strongest_biases(limit: 5)
|
|
71
|
+
@biases.values.sort_by { |b| -b.strength }.first(limit)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def compass_report
|
|
75
|
+
{
|
|
76
|
+
total_bearings: @bearings.size,
|
|
77
|
+
total_biases: @biases.size,
|
|
78
|
+
accuracy: accuracy,
|
|
79
|
+
accuracy_label: accuracy_label,
|
|
80
|
+
true_north_offset: true_north_offset,
|
|
81
|
+
dominant_direction: dominant_direction,
|
|
82
|
+
by_direction: bearings_by_direction,
|
|
83
|
+
severe_biases: @biases.count { |_, b| b.severe? }
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def all_bearings
|
|
88
|
+
@bearings.values
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def all_biases
|
|
92
|
+
@biases.values
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def fetch_bias(id)
|
|
98
|
+
@biases.fetch(id) { raise ArgumentError, "bias not found: #{id}" }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveCompass
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
# Cardinal directions mapped to value axes
|
|
9
|
+
CARDINAL_DIRECTIONS = {
|
|
10
|
+
north: :truth,
|
|
11
|
+
east: :utility,
|
|
12
|
+
south: :beauty,
|
|
13
|
+
west: :safety
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
# Intercardinal directions (blended values)
|
|
17
|
+
INTERCARDINAL_DIRECTIONS = {
|
|
18
|
+
northeast: %i[truth utility],
|
|
19
|
+
southeast: %i[utility beauty],
|
|
20
|
+
southwest: %i[beauty safety],
|
|
21
|
+
northwest: %i[safety truth]
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
# Bias types that cause magnetic declination
|
|
25
|
+
BIAS_TYPES = %i[
|
|
26
|
+
confirmation anchoring availability recency
|
|
27
|
+
authority framing sunk_cost optimism
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
MAX_COMPASSES = 50
|
|
31
|
+
MAX_BIASES = 20
|
|
32
|
+
DECLINATION_DECAY = 0.02
|
|
33
|
+
CALIBRATION_BOOST = 0.15
|
|
34
|
+
|
|
35
|
+
# Bearing accuracy labels
|
|
36
|
+
ACCURACY_LABELS = [
|
|
37
|
+
[(0.8..), :precise],
|
|
38
|
+
[(0.6...0.8), :reliable],
|
|
39
|
+
[(0.4...0.6), :approximate],
|
|
40
|
+
[(0.2...0.4), :drifting],
|
|
41
|
+
[(..0.2), :lost]
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
44
|
+
# Declination severity labels
|
|
45
|
+
DECLINATION_LABELS = [
|
|
46
|
+
[(0.8..), :severe],
|
|
47
|
+
[(0.6...0.8), :significant],
|
|
48
|
+
[(0.4...0.6), :moderate],
|
|
49
|
+
[(0.2...0.4), :mild],
|
|
50
|
+
[(..0.2), :negligible]
|
|
51
|
+
].freeze
|
|
52
|
+
|
|
53
|
+
def self.label_for(table, value)
|
|
54
|
+
table.each { |range, label| return label if range.cover?(value) }
|
|
55
|
+
table.last.last
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveCompass
|
|
6
|
+
module Helpers
|
|
7
|
+
class MagneticBias
|
|
8
|
+
attr_reader :id, :bias_type, :domain, :detected_at
|
|
9
|
+
attr_accessor :declination, :strength
|
|
10
|
+
|
|
11
|
+
def initialize(bias_type:, domain:, declination: nil, strength: nil)
|
|
12
|
+
validate_bias!(bias_type)
|
|
13
|
+
@id = SecureRandom.uuid
|
|
14
|
+
@bias_type = bias_type.to_sym
|
|
15
|
+
@domain = domain.to_sym
|
|
16
|
+
@declination = (declination || 0.3).to_f.clamp(0.0, 1.0).round(10)
|
|
17
|
+
@strength = (strength || 0.5).to_f.clamp(0.0, 1.0).round(10)
|
|
18
|
+
@detected_at = Time.now.utc
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def correct!(amount: Constants::CALIBRATION_BOOST)
|
|
22
|
+
@declination = (@declination - amount.abs).clamp(0.0, 1.0).round(10)
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def intensify!(amount: 0.1)
|
|
27
|
+
@strength = (@strength + amount.abs).clamp(0.0, 1.0).round(10)
|
|
28
|
+
@declination = (@declination + (amount.abs * 0.5)).clamp(0.0, 1.0).round(10)
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def decay!(rate: Constants::DECLINATION_DECAY)
|
|
33
|
+
@strength = (@strength - rate.abs).clamp(0.0, 1.0).round(10)
|
|
34
|
+
@declination = (@declination - (rate.abs * 0.5)).clamp(0.0, 1.0).round(10)
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def severe?
|
|
39
|
+
@declination >= 0.8
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def negligible?
|
|
43
|
+
@declination < 0.2
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def declination_label
|
|
47
|
+
Constants.label_for(Constants::DECLINATION_LABELS, @declination)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_h
|
|
51
|
+
{
|
|
52
|
+
id: @id,
|
|
53
|
+
bias_type: @bias_type,
|
|
54
|
+
domain: @domain,
|
|
55
|
+
declination: @declination,
|
|
56
|
+
declination_label: declination_label,
|
|
57
|
+
strength: @strength,
|
|
58
|
+
severe: severe?,
|
|
59
|
+
negligible: negligible?,
|
|
60
|
+
detected_at: @detected_at
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def validate_bias!(val)
|
|
67
|
+
return if Constants::BIAS_TYPES.include?(val.to_sym)
|
|
68
|
+
|
|
69
|
+
raise ArgumentError,
|
|
70
|
+
"unknown bias type: #{val.inspect}; " \
|
|
71
|
+
"must be one of #{Constants::BIAS_TYPES.inspect}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveCompass
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveCompass
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def take_bearing(direction:, context:, confidence: nil, engine: nil, **)
|
|
11
|
+
eng = resolve_engine(engine)
|
|
12
|
+
bearing = eng.take_bearing(direction: direction, context: context,
|
|
13
|
+
confidence: confidence)
|
|
14
|
+
{ success: true, bearing: bearing.to_h }
|
|
15
|
+
rescue ArgumentError => e
|
|
16
|
+
{ success: false, error: e.message }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def register_bias(bias_type:, domain:, declination: nil,
|
|
20
|
+
strength: nil, engine: nil, **)
|
|
21
|
+
eng = resolve_engine(engine)
|
|
22
|
+
bias = eng.register_bias(bias_type: bias_type, domain: domain,
|
|
23
|
+
declination: declination, strength: strength)
|
|
24
|
+
{ success: true, bias: bias.to_h }
|
|
25
|
+
rescue ArgumentError => e
|
|
26
|
+
{ success: false, error: e.message }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def calibrate(bias_id:, amount: nil, engine: nil, **)
|
|
30
|
+
eng = resolve_engine(engine)
|
|
31
|
+
opts = { bias_id: bias_id }
|
|
32
|
+
opts[:amount] = amount if amount
|
|
33
|
+
bias = eng.calibrate(**opts)
|
|
34
|
+
{ success: true, bias: bias.to_h }
|
|
35
|
+
rescue ArgumentError => e
|
|
36
|
+
{ success: false, error: e.message }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def list_bearings(engine: nil, direction: nil, **)
|
|
40
|
+
eng = resolve_engine(engine)
|
|
41
|
+
results = eng.all_bearings
|
|
42
|
+
results = results.select { |b| b.direction == direction.to_sym } if direction
|
|
43
|
+
{ success: true, bearings: results.map(&:to_h), count: results.size }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def list_biases(engine: nil, **)
|
|
47
|
+
eng = resolve_engine(engine)
|
|
48
|
+
{ success: true, biases: eng.all_biases.map(&:to_h),
|
|
49
|
+
count: eng.all_biases.size }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def compass_status(engine: nil, **)
|
|
53
|
+
eng = resolve_engine(engine)
|
|
54
|
+
{ success: true, report: eng.compass_report }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def resolve_engine(engine)
|
|
62
|
+
engine || default_engine
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def default_engine
|
|
66
|
+
@default_engine ||= Helpers::CompassEngine.new
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
require_relative 'cognitive_compass/version'
|
|
6
|
+
require_relative 'cognitive_compass/helpers/constants'
|
|
7
|
+
require_relative 'cognitive_compass/helpers/bearing'
|
|
8
|
+
require_relative 'cognitive_compass/helpers/magnetic_bias'
|
|
9
|
+
require_relative 'cognitive_compass/helpers/compass_engine'
|
|
10
|
+
require_relative 'cognitive_compass/runners/cognitive_compass'
|
|
11
|
+
require_relative 'cognitive_compass/client'
|
|
12
|
+
|
|
13
|
+
module Legion
|
|
14
|
+
module Extensions
|
|
15
|
+
module CognitiveCompass
|
|
16
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-compass
|
|
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: Maps cardinal directions to value axes (truth/utility/beauty/safety),
|
|
27
|
+
tracks magnetic declination from cognitive biases, and provides true north calibration
|
|
28
|
+
for aligned decision-making.
|
|
29
|
+
email:
|
|
30
|
+
- matthewdiverson@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".github/workflows/ci.yml"
|
|
36
|
+
- ".gitignore"
|
|
37
|
+
- ".rspec"
|
|
38
|
+
- ".rubocop.yml"
|
|
39
|
+
- CLAUDE.md
|
|
40
|
+
- Gemfile
|
|
41
|
+
- Gemfile.lock
|
|
42
|
+
- README.md
|
|
43
|
+
- lex-cognitive-compass.gemspec
|
|
44
|
+
- lib/legion/extensions/cognitive_compass.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_compass/client.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_compass/helpers/bearing.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_compass/helpers/compass_engine.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_compass/helpers/constants.rb
|
|
49
|
+
- lib/legion/extensions/cognitive_compass/helpers/magnetic_bias.rb
|
|
50
|
+
- lib/legion/extensions/cognitive_compass/runners/cognitive_compass.rb
|
|
51
|
+
- lib/legion/extensions/cognitive_compass/version.rb
|
|
52
|
+
homepage: https://github.com/LegionIO/lex-cognitive-compass
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata:
|
|
56
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-compass
|
|
57
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-compass
|
|
58
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-compass#readme
|
|
59
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-compass/blob/main/CHANGELOG.md
|
|
60
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-compass/issues
|
|
61
|
+
rubygems_mfa_required: 'true'
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '3.4'
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
requirements: []
|
|
76
|
+
rubygems_version: 3.6.9
|
|
77
|
+
specification_version: 4
|
|
78
|
+
summary: Cognitive compass LEX — internal directional sense for decision-making
|
|
79
|
+
test_files: []
|