lex-cognitive-prism 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: 2866c399e77ba7a627177c1260044518ba6605ddb64b0e22b7372b82739733f2
4
+ data.tar.gz: 55b48aeea65122c92cd15f280aee3b9fe4a02875a51496cce44f17fe78238a56
5
+ SHA512:
6
+ metadata.gz: 2d6a93e60168661c0e8c62ed3e671c2afd8356d1c87e63e8294ef71066ba29c0da92b4f11f50b77b58fb76c9b538a7488d51c6fbe1b777ed526d8df37f82915d
7
+ data.tar.gz: 57e618e9139a6701313b3baf52df1a6faa88d53fc7a77fedb02204b3c44d84cb87b551d8cd97e26b4f63df41e9ac7e8b2f74efdc04121151deb1d758c641ea5d
@@ -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
@@ -0,0 +1,2 @@
1
+ .rspec_status
2
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,51 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 160
8
+
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+
12
+ Layout/HashAlignment:
13
+ EnforcedHashRocketStyle: table
14
+ EnforcedColonStyle: table
15
+
16
+ Metrics/ClassLength:
17
+ Max: 150
18
+
19
+ Metrics/MethodLength:
20
+ Max: 25
21
+
22
+ Metrics/AbcSize:
23
+ Max: 25
24
+
25
+ Metrics/ParameterLists:
26
+ Max: 8
27
+ MaxOptionalParameters: 8
28
+
29
+ Metrics/BlockLength:
30
+ Exclude:
31
+ - 'spec/**/*'
32
+
33
+ Style/Documentation:
34
+ Enabled: false
35
+
36
+ Style/OneClassPerFile:
37
+ Exclude:
38
+ - 'spec/spec_helper.rb'
39
+
40
+ Naming/PredicateMethod:
41
+ Enabled: false
42
+
43
+ Naming/PredicatePrefix:
44
+ Enabled: false
45
+
46
+ Style/FrozenStringLiteralComment:
47
+ Enabled: true
48
+ EnforcedStyle: always
49
+
50
+ Naming/FileName:
51
+ Enabled: false
data/CLAUDE.md ADDED
@@ -0,0 +1,111 @@
1
+ # lex-cognitive-prism
2
+
3
+ **Level 3 Leaf Documentation**
4
+ - **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
5
+
6
+ ## Purpose
7
+
8
+ Light prism metaphor for cognitive decomposition and synthesis. A beam of cognitive content (idea, concept, problem) is passed through a prism to decompose it into spectral components across eight bands: infrared (meta_contextual), red (concrete), orange (applied), yellow (structural), green (relational), blue (conceptual), violet (abstract), ultraviolet (transcendent). Each band maps to an abstraction level. Components can be attenuated (faded) or amplified (boosted). Decomposed beams can be recomposed from selected active components into a synthesized view.
9
+
10
+ ## Gem Info
11
+
12
+ - **Gem name**: `lex-cognitive-prism`
13
+ - **Module**: `Legion::Extensions::CognitivePrism`
14
+ - **Version**: `0.1.0`
15
+ - **Ruby**: `>= 3.4`
16
+ - **License**: MIT
17
+
18
+ ## File Structure
19
+
20
+ ```
21
+ lib/legion/extensions/cognitive_prism/
22
+ version.rb
23
+ client.rb
24
+ helpers/
25
+ constants.rb
26
+ spectral_component.rb
27
+ beam.rb
28
+ prism_engine.rb
29
+ runners/
30
+ cognitive_prism.rb
31
+ ```
32
+
33
+ ## Key Constants
34
+
35
+ | Constant | Value | Purpose |
36
+ |---|---|---|
37
+ | `SPECTRAL_BANDS` | `%i[infrared red orange yellow green blue violet ultraviolet]` | Eight cognitive abstraction levels |
38
+ | `WAVELENGTH_RANGES` | hash | Wavelength integer ranges per band (700–1000 for infrared down to 10–379 for ultraviolet) |
39
+ | `MAX_BEAMS` | `200` | Per-engine beam capacity (raises `ArgumentError` at limit) |
40
+ | `INTENSITY_LABELS` | array of range/label hashes | From `:trace` to `:brilliant` |
41
+ | `PURITY_LABELS` | array of range/label hashes | From `:turbid` to `:prismatic` |
42
+
43
+ ## Helpers
44
+
45
+ ### `Helpers::SpectralComponent`
46
+ One spectral band of a decomposed beam. Has `band`, `wavelength`, `intensity` (0.0–1.0), and `content` (chunk of original content for that band).
47
+
48
+ Constants on the class: `ATTENUATION_RATE_DEFAULT = 0.05`, `AMPLIFY_BOOST_DEFAULT = 0.1`, `DOMINANT_THRESHOLD = 0.7`, `FADED_THRESHOLD = 0.1`.
49
+
50
+ - `attenuate!(rate:)` — reduces intensity
51
+ - `amplify!(boost:)` — increases intensity
52
+ - `dominant?` — intensity >= 0.7
53
+ - `faded?` — intensity <= 0.1
54
+ - `intensity_label`
55
+ - `to_h`
56
+
57
+ ### `Helpers::Beam`
58
+ Cognitive content beam awaiting or having undergone decomposition. Has `beam_id`, `domain`, `content`, `components` (array), and `purity`.
59
+
60
+ - `decompose!` — chunks content by band using `assign_bands`; assigns abstraction levels; computes intensity per band via `compute_intensity` (base from chunk length × abstraction weight); computes purity
61
+ - `recompose` → string synthesizing active non-faded components sorted by intensity
62
+ - `dominant_band` → band with highest intensity (or first dominant band)
63
+ - `spectral_balance` → hash of `band => fraction_of_total`
64
+ - `purity` — uniformity metric: 1.0 = perfectly even distribution; 0.0 = single dominant band. Formula normalizes dominance ratio against ideal uniform ratio.
65
+ - `to_h`
66
+
67
+ **Abstraction weights** (applied in `compute_intensity`): concrete = 1.0, applied = 0.9, structural = 0.85, relational = 0.8, conceptual = 0.75, abstract = 0.7, meta_contextual = 0.6, transcendent = 0.5. Concrete bands produce higher intensity from same content length.
68
+
69
+ ### `Helpers::PrismEngine`
70
+ Top-level store.
71
+
72
+ - `create_beam(domain:, content:, beam_id:)` → creation result or raises `ArgumentError` at `MAX_BEAMS`
73
+ - `decompose(beam_id)` → decomposition result with `component_count`, `dominant_band`, `purity`
74
+ - `recompose(component_ids)` → synthesis result from selected component IDs
75
+ - `attenuate_all!(rate:)` → count of attenuated components
76
+ - `dominant_bands` → frequency hash of which bands dominate across all beams
77
+ - `most_intense(limit:)` → top N components across all beams
78
+ - `spectral_report` → aggregate stats
79
+ - `get_beam(beam_id)` → beam object
80
+ - `clear!` → empties all beams and components
81
+
82
+ ## Runners
83
+
84
+ Module: `Runners::CognitivePrism`
85
+
86
+ | Runner Method | Description |
87
+ |---|---|
88
+ | `create_beam(domain:, content:, beam_id:)` | Register a new beam |
89
+ | `decompose(beam_id:)` | Decompose beam into spectral components |
90
+ | `recompose(component_ids:)` | Synthesize from selected components |
91
+ | `attenuate_all(rate:)` | Attenuate all components (decay) |
92
+ | `dominant_bands` | Frequency of dominant bands across all beams |
93
+ | `most_intense(limit:)` | Top N most intense components |
94
+ | `spectral_report` | Aggregate stats |
95
+
96
+ All runners return `{success: true/false, ...}` hashes. Error responses on `ArgumentError` (e.g., capacity exceeded, unknown band).
97
+
98
+ ## Integration Points
99
+
100
+ - `lex-tick` `action_selection` phase: decompose the current situation → dominant band determines reasoning style (concrete band dominant → ground action in facts; abstract band dominant → conceptual reasoning)
101
+ - `lex-memory`: decomposed beams can be stored per-band as semantic traces with band as domain tag
102
+ - `lex-emotion`: ultraviolet (transcendent) band intensity reflects metacognitive depth; can influence arousal
103
+ - Purity score: high purity = balanced multi-perspective analysis; low purity = narrow single-mode thinking
104
+
105
+ ## Development Notes
106
+
107
+ - `Client` instantiates `@default_engine = Helpers::PrismEngine.new` via runner memoization
108
+ - `decompose!` splits content by equal chunks across 8 bands: `chunk_size = content.length / 8` (ceiling). Short content means small chunks per band.
109
+ - Purity formula: `1.0 - |(dominance_ratio - 1/n) * n / (n-1)|` — measures deviation from perfect uniformity; high purity ≠ high clarity, it means balanced spectral distribution
110
+ - `recompose(component_ids)` looks up IDs in `@components` hash keyed by `"#{beam_id}:#{band}"` format
111
+ - `MAX_BEAMS = 200` raises `ArgumentError` (not returns error hash) — rescue is in the runner layer
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rubocop', '~> 1.75'
10
+ gem 'rubocop-rspec', require: false
11
+ end
12
+
13
+ gem 'legion-gaia', path: '../../legion-gaia'
data/Gemfile.lock ADDED
@@ -0,0 +1,78 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lex-cognitive-prism (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-prism!
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,61 @@
1
+ # lex-cognitive-prism
2
+
3
+ Light prism decomposition model for LegionIO cognitive agents. Cognitive beams (ideas, problems, concepts) are decomposed into eight spectral bands across abstraction levels from concrete to transcendent. Bands can be attenuated or amplified; active components can be recomposed into a synthesized view.
4
+
5
+ ## What It Does
6
+
7
+ - Eight spectral bands mapping to abstraction levels:
8
+ - `infrared` → meta_contextual
9
+ - `red` → concrete
10
+ - `orange` → applied
11
+ - `yellow` → structural
12
+ - `green` → relational
13
+ - `blue` → conceptual
14
+ - `violet` → abstract
15
+ - `ultraviolet` → transcendent
16
+ - Decompose: split content across bands with intensity based on chunk size × abstraction weight
17
+ - Purity score: uniformity of spectral distribution (high = balanced multi-perspective)
18
+ - Dominant band: highest-intensity spectral component
19
+ - Recompose: synthesize a view from selected active (non-faded) component IDs
20
+ - Attenuation: fade all components (global decay cycle)
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ # Create a beam
26
+ result = runner.create_beam(domain: :architecture,
27
+ content: 'decompose the service by bounded context applying DDD')
28
+
29
+ # Decompose into spectral components
30
+ runner.decompose(beam_id: result[:beam_id])
31
+ # => { success: true, component_count: 8, dominant_band: :red, purity: 0.72 }
32
+
33
+ # Get all beams dominant band distribution
34
+ runner.dominant_bands
35
+ # => { success: true, bands: { red: 3, blue: 1, violet: 1 } }
36
+
37
+ # Most intense components across all beams
38
+ runner.most_intense(limit: 3)
39
+
40
+ # Recompose from specific component IDs
41
+ runner.recompose(component_ids: ['beam-id:red', 'beam-id:blue', 'beam-id:violet'])
42
+ # => { success: true, synthesis: 'red(...): ... | blue(...): ... | violet(...): ...' }
43
+
44
+ # Attenuate all (periodic fade)
45
+ runner.attenuate_all(rate: 0.05)
46
+
47
+ # Status
48
+ runner.spectral_report
49
+ ```
50
+
51
+ ## Development
52
+
53
+ ```bash
54
+ bundle install
55
+ bundle exec rspec
56
+ bundle exec rubocop
57
+ ```
58
+
59
+ ## License
60
+
61
+ MIT
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/cognitive_prism/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-cognitive-prism'
7
+ spec.version = Legion::Extensions::CognitivePrism::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Cognitive Prism'
12
+ spec.description = 'Spectral decomposition of complex ideas into band-specific components — ' \
13
+ 'white light in, rainbow out. Decompose ideas across abstraction wavelengths, ' \
14
+ 'attenuate and amplify components, then recompose synthesized understanding.'
15
+ spec.homepage = 'https://github.com/LegionIO/lex-cognitive-prism'
16
+ spec.license = 'MIT'
17
+ spec.required_ruby_version = '>= 3.4'
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-prism'
21
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-prism'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-prism'
23
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-prism/issues'
24
+ spec.metadata['rubygems_mfa_required'] = 'true'
25
+
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.require_paths = ['lib']
30
+ spec.add_development_dependency 'legion-gaia'
31
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ class Client
7
+ include Runners::CognitivePrism
8
+
9
+ attr_reader :engine
10
+
11
+ def initialize(engine: nil, **)
12
+ @engine = engine || Helpers::PrismEngine.new
13
+ @default_engine = @engine
14
+ end
15
+
16
+ private
17
+
18
+ attr_writer :default_engine
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ module Helpers
7
+ class Beam
8
+ BAND_MIDPOINTS = Constants::WAVELENGTH_RANGES.transform_values do |range|
9
+ ((range.min + range.max) / 2.0).round
10
+ end.freeze
11
+
12
+ attr_reader :beam_id, :domain, :content, :components
13
+
14
+ def initialize(beam_id:, domain:, content:)
15
+ @beam_id = beam_id
16
+ @domain = domain
17
+ @content = content
18
+ @components = []
19
+ @purity = 0.0
20
+ end
21
+
22
+ def purity
23
+ @purity.round(10)
24
+ end
25
+
26
+ def decompose!
27
+ @components = []
28
+ bands = assign_bands(content)
29
+
30
+ bands.each do |band, band_content|
31
+ wavelength = BAND_MIDPOINTS.fetch(band)
32
+ intensity = compute_intensity(band, band_content)
33
+ @components << SpectralComponent.new(
34
+ band: band,
35
+ wavelength: wavelength,
36
+ intensity: intensity,
37
+ content: band_content
38
+ )
39
+ end
40
+
41
+ @purity = compute_purity
42
+ self
43
+ end
44
+
45
+ def recompose
46
+ return '' if @components.empty?
47
+
48
+ active = @components.reject(&:faded?)
49
+ sorted = active.sort_by { |c| -c.intensity }
50
+ parts = sorted.map { |c| "#{c.band}(#{c.intensity.round(3)}): #{summarize(c.content)}" }
51
+ parts.join(' | ')
52
+ end
53
+
54
+ def dominant_band
55
+ return nil if @components.empty?
56
+
57
+ dominant = @components.select(&:dominant?)
58
+ candidates = dominant.any? ? dominant : @components
59
+ candidates.max_by(&:intensity)&.band
60
+ end
61
+
62
+ def spectral_balance
63
+ return {} if @components.empty?
64
+
65
+ total = @components.sum(&:intensity)
66
+ return {} if total.zero?
67
+
68
+ @components.to_h do |c|
69
+ [c.band, (c.intensity / total).round(10)]
70
+ end
71
+ end
72
+
73
+ def to_h
74
+ {
75
+ beam_id: @beam_id,
76
+ domain: @domain,
77
+ content: @content,
78
+ purity: purity,
79
+ purity_label: purity_label,
80
+ dominant_band: dominant_band,
81
+ spectral_balance: spectral_balance,
82
+ component_count: @components.size,
83
+ components: @components.map(&:to_h)
84
+ }
85
+ end
86
+
87
+ private
88
+
89
+ def assign_bands(raw_content)
90
+ content_str = raw_content.is_a?(String) ? raw_content : raw_content.to_s
91
+ total_length = content_str.length
92
+ chunk_size = total_length.positive? ? (total_length.to_f / Constants::SPECTRAL_BANDS.size).ceil : 1
93
+
94
+ Constants::SPECTRAL_BANDS.each_with_index.with_object({}) do |(band, idx), acc|
95
+ start_pos = idx * chunk_size
96
+ chunk = total_length.positive? ? content_str[start_pos, chunk_size] || '' : ''
97
+ acc[band] = { raw: chunk, abstraction_level: abstraction_level_for(band) }
98
+ end
99
+ end
100
+
101
+ def abstraction_level_for(band)
102
+ abstraction_map = {
103
+ infrared: :meta_contextual,
104
+ red: :concrete,
105
+ orange: :applied,
106
+ yellow: :structural,
107
+ green: :relational,
108
+ blue: :conceptual,
109
+ violet: :abstract,
110
+ ultraviolet: :transcendent
111
+ }
112
+ abstraction_map.fetch(band, :unknown)
113
+ end
114
+
115
+ def compute_intensity(_band, band_content)
116
+ raw = band_content[:raw].to_s
117
+ level = band_content[:abstraction_level]
118
+ base = raw.empty? ? 0.1 : (raw.length.to_f / 20).clamp(0.1, 1.0)
119
+ weight = abstraction_weight(level)
120
+ (base * weight).clamp(0.0, 1.0).round(10)
121
+ end
122
+
123
+ def abstraction_weight(level)
124
+ weights = {
125
+ meta_contextual: 0.6,
126
+ concrete: 1.0,
127
+ applied: 0.9,
128
+ structural: 0.85,
129
+ relational: 0.8,
130
+ conceptual: 0.75,
131
+ abstract: 0.7,
132
+ transcendent: 0.5
133
+ }
134
+ weights.fetch(level, 0.7)
135
+ end
136
+
137
+ def compute_purity
138
+ return 0.0 if @components.empty?
139
+
140
+ intensities = @components.map(&:intensity)
141
+ total = intensities.sum
142
+ return 0.0 if total.zero?
143
+
144
+ max = intensities.max
145
+ dominance_ratio = max / total
146
+ n = Constants::SPECTRAL_BANDS.size.to_f
147
+ uniformity = 1.0 - ((dominance_ratio - (1.0 / n)) * n / (n - 1)).abs
148
+ uniformity.clamp(0.0, 1.0).round(10)
149
+ end
150
+
151
+ def purity_label
152
+ Constants::PURITY_LABELS.find { |entry| entry[:range].cover?(@purity) }&.fetch(:label, :unknown)
153
+ end
154
+
155
+ def summarize(band_content)
156
+ return '' unless band_content.is_a?(Hash)
157
+
158
+ raw = band_content[:raw].to_s
159
+ raw.length > 30 ? "#{raw[0, 30]}..." : raw
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ module Helpers
7
+ module Constants
8
+ SPECTRAL_BANDS = %i[infrared red orange yellow green blue violet ultraviolet].freeze
9
+
10
+ WAVELENGTH_RANGES = {
11
+ infrared: 700..1000,
12
+ red: 620..699,
13
+ orange: 590..619,
14
+ yellow: 560..589,
15
+ green: 490..559,
16
+ blue: 450..489,
17
+ violet: 380..449,
18
+ ultraviolet: 10..379
19
+ }.freeze
20
+
21
+ MAX_BEAMS = 200
22
+
23
+ INTENSITY_LABELS = [
24
+ { range: 0.0..0.19, label: :trace },
25
+ { range: 0.2..0.39, label: :faint },
26
+ { range: 0.4..0.59, label: :moderate },
27
+ { range: 0.6..0.79, label: :strong },
28
+ { range: 0.8..1.0, label: :brilliant }
29
+ ].freeze
30
+
31
+ PURITY_LABELS = [
32
+ { range: 0.0..0.24, label: :turbid },
33
+ { range: 0.25..0.49, label: :murky },
34
+ { range: 0.5..0.74, label: :clear },
35
+ { range: 0.75..0.89, label: :pure },
36
+ { range: 0.9..1.0, label: :prismatic }
37
+ ].freeze
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ module Helpers
7
+ class PrismEngine
8
+ def initialize
9
+ @beams = {}
10
+ @components = {}
11
+ end
12
+
13
+ def create_beam(domain:, content:, beam_id: nil)
14
+ raise ArgumentError, "MAX_BEAMS (#{Constants::MAX_BEAMS}) reached" if @beams.size >= Constants::MAX_BEAMS
15
+
16
+ id = beam_id || SecureRandom.uuid
17
+ beam = Beam.new(beam_id: id, domain: domain, content: content)
18
+ @beams[id] = beam
19
+ { success: true, beam_id: id, domain: domain }
20
+ end
21
+
22
+ def decompose(beam_id)
23
+ beam = @beams.fetch(beam_id) { return { success: false, error: "Beam #{beam_id} not found" } }
24
+
25
+ beam.decompose!
26
+ beam.components.each { |c| @components["#{beam_id}:#{c.band}"] = c }
27
+
28
+ Legion::Logging.debug "[cognitive_prism] decompose: beam=#{beam_id[0..7]} components=#{beam.components.size} purity=#{beam.purity.round(4)}"
29
+ {
30
+ success: true,
31
+ beam_id: beam_id,
32
+ component_count: beam.components.size,
33
+ dominant_band: beam.dominant_band,
34
+ purity: beam.purity
35
+ }
36
+ end
37
+
38
+ def recompose(component_ids)
39
+ selected = component_ids.filter_map { |cid| resolve_component(cid) }
40
+ active = selected.reject(&:faded?)
41
+ return { success: true, synthesis: '', active_count: 0, total_count: 0 } if active.empty?
42
+
43
+ synthesis = build_synthesis(active)
44
+ { success: true, synthesis: synthesis, active_count: active.size, total_count: selected.size }
45
+ end
46
+
47
+ def attenuate_all!(rate: SpectralComponent::ATTENUATION_RATE_DEFAULT)
48
+ count = 0
49
+ @beams.each_value do |beam|
50
+ beam.components.each do |c|
51
+ c.attenuate!(rate: rate)
52
+ count += 1
53
+ end
54
+ end
55
+ { success: true, attenuated: count, rate: rate }
56
+ end
57
+
58
+ def dominant_bands
59
+ result = Hash.new(0)
60
+ @beams.each_value do |beam|
61
+ band = beam.dominant_band
62
+ result[band] += 1 if band
63
+ end
64
+ result.sort_by { |_, v| -v }.to_h
65
+ end
66
+
67
+ def most_intense(limit: 5)
68
+ all_components = @beams.values.flat_map(&:components)
69
+ all_components
70
+ .sort_by { |c| -c.intensity }
71
+ .first(limit)
72
+ .map(&:to_h)
73
+ end
74
+
75
+ def spectral_report
76
+ total_beams = @beams.size
77
+ total_components = @beams.values.sum { |b| b.components.size }
78
+ decomposed_beams = @beams.count { |_, b| b.components.any? }
79
+
80
+ avg_purity = if decomposed_beams.positive?
81
+ purity_sum = @beams.values.select { |b| b.components.any? }.sum(&:purity)
82
+ (purity_sum / decomposed_beams).round(10)
83
+ else
84
+ 0.0
85
+ end
86
+
87
+ {
88
+ total_beams: total_beams,
89
+ decomposed_beams: decomposed_beams,
90
+ total_components: total_components,
91
+ dominant_bands: dominant_bands,
92
+ avg_purity: avg_purity,
93
+ most_intense: most_intense(limit: 3)
94
+ }
95
+ end
96
+
97
+ def get_beam(beam_id)
98
+ @beams[beam_id]
99
+ end
100
+
101
+ def beam_count
102
+ @beams.size
103
+ end
104
+
105
+ def clear!
106
+ @beams.clear
107
+ @components.clear
108
+ self
109
+ end
110
+
111
+ private
112
+
113
+ def resolve_component(cid)
114
+ return @components[cid] if @components.key?(cid)
115
+
116
+ @components.values.find { |c| component_key(c.band) == cid }
117
+ end
118
+
119
+ def build_synthesis(active)
120
+ active.sort_by { |c| -c.intensity }
121
+ .map { |c| "[#{c.band}/#{c.wavelength}nm #{c.intensity.round(3)}]: #{c.content}" }
122
+ .join(' | ')
123
+ end
124
+
125
+ def component_key(band)
126
+ band.to_s
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ module Helpers
7
+ class SpectralComponent
8
+ ATTENUATION_RATE_DEFAULT = 0.05
9
+ AMPLIFY_BOOST_DEFAULT = 0.1
10
+ DOMINANT_THRESHOLD = 0.7
11
+ FADED_THRESHOLD = 0.1
12
+
13
+ attr_reader :band, :wavelength, :content
14
+
15
+ def initialize(band:, wavelength:, intensity:, content:)
16
+ raise ArgumentError, "Unknown band: #{band}" unless Constants::SPECTRAL_BANDS.include?(band)
17
+
18
+ range = Constants::WAVELENGTH_RANGES.fetch(band)
19
+ raise ArgumentError, "Wavelength #{wavelength} out of range for #{band}" unless range.cover?(wavelength)
20
+
21
+ @band = band
22
+ @wavelength = wavelength
23
+ @intensity = intensity.clamp(0.0, 1.0)
24
+ @content = content
25
+ end
26
+
27
+ def intensity
28
+ @intensity.round(10)
29
+ end
30
+
31
+ def attenuate!(rate: ATTENUATION_RATE_DEFAULT)
32
+ @intensity = (@intensity - rate.to_f.abs).clamp(0.0, 1.0)
33
+ self
34
+ end
35
+
36
+ def amplify!(boost: AMPLIFY_BOOST_DEFAULT)
37
+ @intensity = (@intensity + boost.to_f.abs).clamp(0.0, 1.0)
38
+ self
39
+ end
40
+
41
+ def dominant?
42
+ @intensity >= DOMINANT_THRESHOLD
43
+ end
44
+
45
+ def faded?
46
+ @intensity <= FADED_THRESHOLD
47
+ end
48
+
49
+ def intensity_label
50
+ Constants::INTENSITY_LABELS.find { |entry| entry[:range].cover?(@intensity) }&.fetch(:label, :unknown)
51
+ end
52
+
53
+ def to_h
54
+ {
55
+ band: @band,
56
+ wavelength: @wavelength,
57
+ intensity: intensity,
58
+ intensity_label: intensity_label,
59
+ content: @content,
60
+ dominant: dominant?,
61
+ faded: faded?
62
+ }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ module Runners
7
+ module CognitivePrism
8
+ extend self
9
+
10
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
11
+
12
+ def create_beam(domain:, content:, beam_id: nil, engine: nil, **)
13
+ prism = engine || default_engine
14
+ prism.create_beam(domain: domain, content: content, beam_id: beam_id)
15
+ rescue ArgumentError => e
16
+ Legion::Logging.warn "[cognitive_prism] create_beam failed: #{e.message}"
17
+ { success: false, error: e.message }
18
+ end
19
+
20
+ def decompose(beam_id:, engine: nil, **)
21
+ prism = engine || default_engine
22
+ prism.decompose(beam_id)
23
+ rescue ArgumentError => e
24
+ Legion::Logging.warn "[cognitive_prism] decompose failed: #{e.message}"
25
+ { success: false, error: e.message }
26
+ end
27
+
28
+ def recompose(component_ids:, engine: nil, **)
29
+ prism = engine || default_engine
30
+ prism.recompose(component_ids)
31
+ rescue ArgumentError => e
32
+ Legion::Logging.warn "[cognitive_prism] recompose failed: #{e.message}"
33
+ { success: false, error: e.message }
34
+ end
35
+
36
+ def attenuate_all(rate: Helpers::SpectralComponent::ATTENUATION_RATE_DEFAULT, engine: nil, **)
37
+ prism = engine || default_engine
38
+ prism.attenuate_all!(rate: rate)
39
+ rescue ArgumentError => e
40
+ Legion::Logging.warn "[cognitive_prism] attenuate_all failed: #{e.message}"
41
+ { success: false, error: e.message }
42
+ end
43
+
44
+ def dominant_bands(engine: nil, **)
45
+ prism = engine || default_engine
46
+ { success: true, bands: prism.dominant_bands }
47
+ rescue ArgumentError => e
48
+ { success: false, error: e.message }
49
+ end
50
+
51
+ def most_intense(limit: 5, engine: nil, **)
52
+ prism = engine || default_engine
53
+ { success: true, components: prism.most_intense(limit: limit) }
54
+ rescue ArgumentError => e
55
+ { success: false, error: e.message }
56
+ end
57
+
58
+ def spectral_report(engine: nil, **)
59
+ prism = engine || default_engine
60
+ report = prism.spectral_report
61
+ { success: true }.merge(report)
62
+ rescue ArgumentError => e
63
+ { success: false, error: e.message }
64
+ end
65
+
66
+ private
67
+
68
+ def default_engine
69
+ @default_engine ||= Helpers::PrismEngine.new
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitivePrism
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative 'cognitive_prism/version'
6
+ require_relative 'cognitive_prism/helpers/constants'
7
+ require_relative 'cognitive_prism/helpers/spectral_component'
8
+ require_relative 'cognitive_prism/helpers/beam'
9
+ require_relative 'cognitive_prism/helpers/prism_engine'
10
+ require_relative 'cognitive_prism/runners/cognitive_prism'
11
+ require_relative 'cognitive_prism/client'
12
+
13
+ module Legion
14
+ module Extensions
15
+ module CognitivePrism
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-prism
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: Spectral decomposition of complex ideas into band-specific components
27
+ — white light in, rainbow out. Decompose ideas across abstraction wavelengths, attenuate
28
+ and amplify components, then recompose synthesized understanding.
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-prism.gemspec
44
+ - lib/legion/extensions/cognitive_prism.rb
45
+ - lib/legion/extensions/cognitive_prism/client.rb
46
+ - lib/legion/extensions/cognitive_prism/helpers/beam.rb
47
+ - lib/legion/extensions/cognitive_prism/helpers/constants.rb
48
+ - lib/legion/extensions/cognitive_prism/helpers/prism_engine.rb
49
+ - lib/legion/extensions/cognitive_prism/helpers/spectral_component.rb
50
+ - lib/legion/extensions/cognitive_prism/runners/cognitive_prism.rb
51
+ - lib/legion/extensions/cognitive_prism/version.rb
52
+ homepage: https://github.com/LegionIO/lex-cognitive-prism
53
+ licenses:
54
+ - MIT
55
+ metadata:
56
+ homepage_uri: https://github.com/LegionIO/lex-cognitive-prism
57
+ source_code_uri: https://github.com/LegionIO/lex-cognitive-prism
58
+ documentation_uri: https://github.com/LegionIO/lex-cognitive-prism
59
+ changelog_uri: https://github.com/LegionIO/lex-cognitive-prism
60
+ bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-prism/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: LEX Cognitive Prism
79
+ test_files: []