lex-cognitive-echo-chamber 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 +61 -0
- data/CLAUDE.md +82 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +78 -0
- data/README.md +57 -0
- data/lex-cognitive-echo-chamber.gemspec +31 -0
- data/lib/legion/extensions/cognitive_echo_chamber/client.rb +15 -0
- data/lib/legion/extensions/cognitive_echo_chamber/helpers/chamber.rb +130 -0
- data/lib/legion/extensions/cognitive_echo_chamber/helpers/chamber_engine.rb +152 -0
- data/lib/legion/extensions/cognitive_echo_chamber/helpers/constants.rb +56 -0
- data/lib/legion/extensions/cognitive_echo_chamber/helpers/echo.rb +92 -0
- data/lib/legion/extensions/cognitive_echo_chamber/runners/cognitive_echo_chamber.rb +80 -0
- data/lib/legion/extensions/cognitive_echo_chamber/version.rb +9 -0
- data/lib/legion/extensions/cognitive_echo_chamber.rb +18 -0
- metadata +79 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9b14c32b92c7a3820386d857c0c8f447fc85ac2d0b4054d7a7c1caad9438eae0
|
|
4
|
+
data.tar.gz: 6fa1fb07bfa0c474f3e60d0ed47dc4dac10c2dbaa0f244ea251a8ce3f83af24e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 37da8e8f476ddb16589aa2ae0fb8adfe81795c16ac1e692ff72e5efb1e04c8d7de1d707b7161f5d959aa26dc094cd4d74446db682e1bb8260dfb47692c8c8944
|
|
7
|
+
data.tar.gz: '0841afec55c9c980ded78f596e9ade8f64340264ee4759e2a9b5c20c271680f04510919e2c2b8ef7ce7aca273134c9410dece066e58cc8412a985cecd7ef62a3'
|
|
@@ -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,61 @@
|
|
|
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/MethodLength:
|
|
17
|
+
Max: 25
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 150
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 150
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/AbcSize:
|
|
31
|
+
Max: 25
|
|
32
|
+
|
|
33
|
+
Metrics/ParameterLists:
|
|
34
|
+
Max: 8
|
|
35
|
+
MaxOptionalParameters: 8
|
|
36
|
+
|
|
37
|
+
Metrics/CyclomaticComplexity:
|
|
38
|
+
Max: 15
|
|
39
|
+
|
|
40
|
+
Metrics/PerceivedComplexity:
|
|
41
|
+
Max: 17
|
|
42
|
+
|
|
43
|
+
Style/Documentation:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/FrozenStringLiteralComment:
|
|
47
|
+
Enabled: true
|
|
48
|
+
EnforcedStyle: always
|
|
49
|
+
|
|
50
|
+
Naming/FileName:
|
|
51
|
+
Enabled: false
|
|
52
|
+
|
|
53
|
+
Naming/PredicateMethod:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
Naming/PredicatePrefix:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
Style/OneClassPerFile:
|
|
60
|
+
Exclude:
|
|
61
|
+
- 'spec/spec_helper.rb'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# lex-cognitive-echo-chamber
|
|
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
|
+
Models self-reinforcing belief loops — the cognitive echo chamber phenomenon where ideas amplify themselves without external correction. Unlike `lex-cognitive-echo` (which tracks individual transient residual activations), the echo chamber models a structured enclosure: echoes of type belief/assumption/bias/hypothesis/conviction circulate inside a Chamber with defined wall thickness. Disruption requires force exceeding the wall. Sealed chambers (wall_thickness >= 0.8) are nearly impervious to external correction.
|
|
10
|
+
|
|
11
|
+
## Gem Info
|
|
12
|
+
|
|
13
|
+
- **Gem name**: `lex-cognitive-echo-chamber`
|
|
14
|
+
- **Version**: `0.1.0`
|
|
15
|
+
- **Module**: `Legion::Extensions::CognitiveEchoChamber`
|
|
16
|
+
- **Ruby**: `>= 3.4`
|
|
17
|
+
- **License**: MIT
|
|
18
|
+
|
|
19
|
+
## File Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/legion/extensions/cognitive_echo_chamber/
|
|
23
|
+
cognitive_echo_chamber.rb
|
|
24
|
+
version.rb
|
|
25
|
+
client.rb
|
|
26
|
+
helpers/
|
|
27
|
+
constants.rb
|
|
28
|
+
chamber_engine.rb
|
|
29
|
+
echo.rb
|
|
30
|
+
chamber.rb
|
|
31
|
+
runners/
|
|
32
|
+
cognitive_echo_chamber.rb
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Key Constants
|
|
36
|
+
|
|
37
|
+
From `helpers/constants.rb`:
|
|
38
|
+
|
|
39
|
+
- `ECHO_TYPES` — `%i[belief assumption bias hypothesis conviction]`
|
|
40
|
+
- `CHAMBER_STATES` — `%i[forming resonating saturated disrupted collapsed]`
|
|
41
|
+
- `MAX_ECHOES` = `500`, `MAX_CHAMBERS` = `50`
|
|
42
|
+
- `AMPLIFICATION_RATE` = `0.1`, `DECAY_RATE` = `0.02`
|
|
43
|
+
- `DISRUPTION_THRESHOLD` = `0.7` (resonance level at which a chamber is considered saturated; also the echo amplitude threshold for `resonate?`)
|
|
44
|
+
- `SEALED_THRESHOLD` = `0.8` (wall_thickness >= this = sealed, nearly impossible to disrupt)
|
|
45
|
+
- `POROUS_THRESHOLD` = `0.3` (wall_thickness <= this = porous, easily disrupted)
|
|
46
|
+
- `BREAKTHROUGH_BONUS` = `0.15` (fraction of breakthrough force subtracted from wall_thickness on successful disruption)
|
|
47
|
+
- `SILENT_THRESHOLD` = `0.05`, `DEFAULT_AMPLITUDE` = `0.5`, `DEFAULT_WALL_THICKNESS` = `0.5`
|
|
48
|
+
- `RESONANCE_LABELS` — range hash: `0.8+` = `:thunderous`, `0.6` = `:resonant`, `0.4` = `:humming`, `0.2` = `:fading`, below = `:silent`
|
|
49
|
+
- `AMPLIFICATION_LABELS` — `0.8+` = `:deafening`, `0.6` = `:loud`, `0.4` = `:moderate`, `0.2` = `:quiet`, below = `:muted`
|
|
50
|
+
- `CHAMBER_STATE_LABELS` — human-readable descriptions of each chamber state
|
|
51
|
+
|
|
52
|
+
## Runners
|
|
53
|
+
|
|
54
|
+
All methods in `Runners::CognitiveEchoChamber`:
|
|
55
|
+
|
|
56
|
+
- `create_echo(content:, echo_type: :belief, domain: :general, source_agent: nil, amplitude: 0.5, engine: nil)` — creates a new echo in the pool; validates content non-empty; invalid echo_type defaults to `:belief`
|
|
57
|
+
- `create_chamber(label:, domain: :general, wall_thickness: 0.5, engine: nil)` — creates a new named chamber enclosure
|
|
58
|
+
- `amplify(echo_id:, rate: 0.1, engine: nil)` — increases echo amplitude; also increments echo frequency counter
|
|
59
|
+
- `disrupt(chamber_id:, force:, engine: nil)` — attempts to break through a chamber's walls; fails if force <= wall_thickness; success reduces wall_thickness by `breakthrough * BREAKTHROUGH_BONUS` and dampens all enclosed echoes by the breakthrough amount
|
|
60
|
+
- `list_echoes(echo_type: nil, domain: nil, engine: nil)` — returns active echoes; optionally filtered by type and/or domain
|
|
61
|
+
- `chamber_status(engine: nil)` — full report: total echoes, active echoes, resonating echoes, total chambers, sealed/porous counts, disruption count, loudest 3 echoes
|
|
62
|
+
|
|
63
|
+
## Helpers
|
|
64
|
+
|
|
65
|
+
- `ChamberEngine` — manages `@echoes` hash and `@chambers` hash. `decay_all!` applies `DECAY_RATE` to all echoes and prunes silent ones. `add_echo_to_chamber(echo_id:, chamber_id:)` links an echo into a chamber. `disruption_history` returns a copy of the disruption log. Pruning: on overflow, silent echoes removed first; if still over limit, the faintest echo removed.
|
|
66
|
+
- `Echo` — amplitude-tracked belief unit. `amplify!(rate)` increments both amplitude and frequency. `dampen!(rate)` reduces amplitude. Predicates: `resonate?` (amplitude >= 0.7), `fading?` (amplitude <= 0.3), `silent?` (amplitude <= 0.05). `frequency_label` uses `RESONANCE_LABELS` against a normalized frequency score (frequency / 20, capped at 1.0).
|
|
67
|
+
- `Chamber` — enclosure with `wall_thickness`, `resonance_frequency`, `state`. `add_echo(echo)` adds to internal echo pool and recalculates resonance. `amplify_all!(rate)` amplifies every enclosed echo. `disrupt!(force)` returns `{ success: false }` if force <= wall_thickness, otherwise reduces wall and dampens echoes. `resonance_frequency` = ratio of resonating echoes to total echoes. State transitions: `:forming` -> `:resonating` (resonance >= 0.3) -> `:saturated` (resonance >= 0.7); disruption locks state to `:disrupted`.
|
|
68
|
+
|
|
69
|
+
## Integration Points
|
|
70
|
+
|
|
71
|
+
- `lex-cognitive-echo` models individual transient residual activations; `lex-cognitive-echo-chamber` models the structural enclosure effect where similar echoes reinforce each other and resist correction.
|
|
72
|
+
- `disrupt` is the mechanism for injecting counter-evidence. Callers from `lex-tick` can call `disrupt` with external input force to model perspective-challenging interactions.
|
|
73
|
+
- `source_agent` on echoes links chamber beliefs back to the originating agent — useful for `lex-mesh` multi-agent scenarios where one agent's beliefs propagate into another's echo chamber.
|
|
74
|
+
- `chamber_status` resonating/sealed metrics can be surfaced during `lex-tick`'s `post_tick_reflection` phase to flag cognitive rigidity.
|
|
75
|
+
|
|
76
|
+
## Development Notes
|
|
77
|
+
|
|
78
|
+
- Chambers and echoes are separate pools — an echo must be explicitly added to a chamber via `add_echo_to_chamber`. Creating an echo does not auto-enroll it in any chamber.
|
|
79
|
+
- `Chamber#disrupt!` reduces `wall_thickness` by `breakthrough * BREAKTHROUGH_BONUS`, not by the full breakthrough amount — chambers become easier to disrupt over time but never instantly destroyed.
|
|
80
|
+
- Chamber state transitions are blocked once state is `:disrupted` or `:collapsed` — the engine must explicitly manage transition out of these states.
|
|
81
|
+
- `resonance_frequency` on a Chamber is the ratio of `resonate?` echoes to total echoes — distinct from the global engine's `resonating_echoes` count.
|
|
82
|
+
- `Echo#frequency` counts amplify calls, not time — it is a repetition counter, not a temporal frequency.
|
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', require: false
|
|
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-echo-chamber (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-echo-chamber!
|
|
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,57 @@
|
|
|
1
|
+
# lex-cognitive-echo-chamber
|
|
2
|
+
|
|
3
|
+
Self-reinforcing belief loop modeling for the LegionIO cognitive architecture. Tracks how beliefs, assumptions, and biases amplify inside an enclosed cognitive space — and models how external disruption can break through chamber walls.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
An echo chamber is a named enclosure with a wall thickness that resists external input. Echoes (of type belief, assumption, bias, hypothesis, or conviction) are created in a shared pool and can be added to a chamber. Echoes inside a chamber amplify each other — the more a belief echoes, the stronger its amplitude grows. Disruption requires applying a force that exceeds the wall thickness; a successful disruption reduces the wall (making future disruptions easier) and dampens enclosed echoes.
|
|
8
|
+
|
|
9
|
+
Chambers progress through states: `forming` (sparse) -> `resonating` (active reinforcement) -> `saturated` (maximum self-reinforcement). A sealed chamber (wall_thickness >= 0.8) is nearly impervious to correction. A porous chamber (wall_thickness <= 0.3) absorbs external input readily.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
client = Legion::Extensions::CognitiveEchoChamber::Client.new
|
|
15
|
+
|
|
16
|
+
# Create an echo in the pool
|
|
17
|
+
result = client.create_echo(
|
|
18
|
+
content: 'all uncertainty should be resolved before acting',
|
|
19
|
+
echo_type: :belief,
|
|
20
|
+
domain: :decision_making
|
|
21
|
+
)
|
|
22
|
+
echo_id = result[:echo][:id]
|
|
23
|
+
|
|
24
|
+
# Create a chamber enclosure
|
|
25
|
+
chamber_result = client.create_chamber(
|
|
26
|
+
label: 'conservative_bias',
|
|
27
|
+
domain: :decision_making,
|
|
28
|
+
wall_thickness: 0.6
|
|
29
|
+
)
|
|
30
|
+
chamber_id = chamber_result[:chamber][:id]
|
|
31
|
+
|
|
32
|
+
# Amplify the echo — each call increases amplitude and frequency count
|
|
33
|
+
client.amplify(echo_id: echo_id)
|
|
34
|
+
|
|
35
|
+
# Attempt disruption with external counter-evidence
|
|
36
|
+
client.disrupt(chamber_id: chamber_id, force: 0.8)
|
|
37
|
+
# Returns: { success: true, breakthrough: 0.2, wall_remaining: 0.57 }
|
|
38
|
+
# Or: { success: false, reason: 'insufficient_force' }
|
|
39
|
+
|
|
40
|
+
# Query echoes
|
|
41
|
+
client.list_echoes(echo_type: :belief, domain: :decision_making)
|
|
42
|
+
|
|
43
|
+
# Status report
|
|
44
|
+
client.chamber_status
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bundle install
|
|
51
|
+
bundle exec rspec
|
|
52
|
+
bundle exec rubocop
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_echo_chamber/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-echo-chamber'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveEchoChamber::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Cognitive echo chamber dynamics for LegionIO agentic architecture'
|
|
12
|
+
spec.description = 'Models cognitive echo chambers — when thoughts reinforce themselves without ' \
|
|
13
|
+
'external challenge, creating self-reinforcing belief loops. Tracks amplification, ' \
|
|
14
|
+
'resonance, and breakthrough events when external input disrupts the echo.'
|
|
15
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-echo-chamber'
|
|
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-echo-chamber'
|
|
21
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-echo-chamber/blob/master/README.md'
|
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-echo-chamber/blob/master/CHANGELOG.md'
|
|
23
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-echo-chamber/issues'
|
|
24
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
25
|
+
|
|
26
|
+
spec.files = Dir.chdir(__dir__) do
|
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
28
|
+
end
|
|
29
|
+
spec.require_paths = ['lib']
|
|
30
|
+
spec.add_development_dependency 'legion-gaia'
|
|
31
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveEchoChamber
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::CognitiveEchoChamber
|
|
8
|
+
|
|
9
|
+
def initialize(engine: nil)
|
|
10
|
+
@default_engine = engine || Helpers::ChamberEngine.new
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveEchoChamber
|
|
8
|
+
module Helpers
|
|
9
|
+
class Chamber
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :label, :domain, :state, :wall_thickness,
|
|
13
|
+
:resonance_frequency, :created_at, :disruption_count
|
|
14
|
+
|
|
15
|
+
def initialize(label:, domain: :general, wall_thickness: DEFAULT_WALL_THICKNESS)
|
|
16
|
+
@id = SecureRandom.uuid
|
|
17
|
+
@label = label
|
|
18
|
+
@domain = domain.to_sym
|
|
19
|
+
@state = :forming
|
|
20
|
+
@wall_thickness = wall_thickness.to_f.clamp(0.0, 1.0).round(10)
|
|
21
|
+
@resonance_frequency = 0.0
|
|
22
|
+
@echoes = {}
|
|
23
|
+
@disruption_count = 0
|
|
24
|
+
@created_at = Time.now.utc
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_echo(echo)
|
|
28
|
+
return false if @echoes.size >= MAX_ECHOES
|
|
29
|
+
|
|
30
|
+
@echoes[echo.id] = echo
|
|
31
|
+
recalculate_resonance
|
|
32
|
+
update_state
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def remove_echo(echo_id)
|
|
37
|
+
removed = @echoes.delete(echo_id)
|
|
38
|
+
recalculate_resonance if removed
|
|
39
|
+
update_state
|
|
40
|
+
!removed.nil?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def amplify_all!(rate = AMPLIFICATION_RATE)
|
|
44
|
+
@echoes.each_value { |e| e.amplify!(rate) }
|
|
45
|
+
recalculate_resonance
|
|
46
|
+
update_state
|
|
47
|
+
{ amplified: @echoes.size, resonance_frequency: @resonance_frequency }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def disrupt!(force)
|
|
51
|
+
force = force.to_f.clamp(0.0, 1.0)
|
|
52
|
+
return { success: false, reason: 'insufficient_force', force: force, wall: @wall_thickness } if force <= @wall_thickness
|
|
53
|
+
|
|
54
|
+
@disruption_count += 1
|
|
55
|
+
breakthrough = force - @wall_thickness
|
|
56
|
+
@wall_thickness = (@wall_thickness - (breakthrough * BREAKTHROUGH_BONUS)).clamp(0.0, 1.0).round(10)
|
|
57
|
+
@echoes.each_value { |e| e.dampen!(breakthrough.round(10)) }
|
|
58
|
+
@state = :disrupted
|
|
59
|
+
recalculate_resonance
|
|
60
|
+
{ success: true, force: force, breakthrough: breakthrough.round(10), wall_remaining: @wall_thickness }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def sealed?
|
|
64
|
+
@wall_thickness >= SEALED_THRESHOLD
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def porous?
|
|
68
|
+
@wall_thickness <= POROUS_THRESHOLD
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def resonance_level
|
|
72
|
+
Constants.label_for(RESONANCE_LABELS, @resonance_frequency)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def echo_count
|
|
76
|
+
@echoes.size
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def active_echoes
|
|
80
|
+
@echoes.values.reject(&:silent?)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resonating_echoes
|
|
84
|
+
@echoes.values.select(&:resonate?)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_h
|
|
88
|
+
{
|
|
89
|
+
id: @id,
|
|
90
|
+
label: @label,
|
|
91
|
+
domain: @domain,
|
|
92
|
+
state: @state,
|
|
93
|
+
wall_thickness: @wall_thickness,
|
|
94
|
+
resonance_frequency: @resonance_frequency,
|
|
95
|
+
resonance_level: resonance_level,
|
|
96
|
+
sealed: sealed?,
|
|
97
|
+
porous: porous?,
|
|
98
|
+
echo_count: echo_count,
|
|
99
|
+
active_echo_count: active_echoes.size,
|
|
100
|
+
disruption_count: @disruption_count,
|
|
101
|
+
created_at: @created_at
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def recalculate_resonance
|
|
108
|
+
return @resonance_frequency = 0.0 if @echoes.empty?
|
|
109
|
+
|
|
110
|
+
resonating = resonating_echoes
|
|
111
|
+
@resonance_frequency = (resonating.size.to_f / @echoes.size).round(10)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def update_state
|
|
115
|
+
return if @state == :disrupted || @state == :collapsed
|
|
116
|
+
return @state = :forming if @echoes.empty?
|
|
117
|
+
|
|
118
|
+
@state = if @resonance_frequency >= DISRUPTION_THRESHOLD
|
|
119
|
+
:saturated
|
|
120
|
+
elsif @resonance_frequency >= POROUS_THRESHOLD
|
|
121
|
+
:resonating
|
|
122
|
+
else
|
|
123
|
+
:forming
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveEchoChamber
|
|
6
|
+
module Helpers
|
|
7
|
+
class ChamberEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@echoes = {}
|
|
12
|
+
@chambers = {}
|
|
13
|
+
@disruption_history = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_echo(content:, echo_type: :belief, domain: :general,
|
|
17
|
+
source_agent: nil, amplitude: DEFAULT_AMPLITUDE)
|
|
18
|
+
prune_echoes
|
|
19
|
+
echo = Echo.new(
|
|
20
|
+
content: content,
|
|
21
|
+
echo_type: echo_type,
|
|
22
|
+
domain: domain,
|
|
23
|
+
source_agent: source_agent,
|
|
24
|
+
amplitude: amplitude
|
|
25
|
+
)
|
|
26
|
+
@echoes[echo.id] = echo
|
|
27
|
+
echo
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_chamber(label:, domain: :general, wall_thickness: DEFAULT_WALL_THICKNESS)
|
|
31
|
+
prune_chambers
|
|
32
|
+
chamber = Chamber.new(label: label, domain: domain, wall_thickness: wall_thickness)
|
|
33
|
+
@chambers[chamber.id] = chamber
|
|
34
|
+
chamber
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def amplify_echo(echo_id:, rate: AMPLIFICATION_RATE)
|
|
38
|
+
echo = @echoes[echo_id]
|
|
39
|
+
return nil unless echo
|
|
40
|
+
|
|
41
|
+
echo.amplify!(rate)
|
|
42
|
+
echo
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def disrupt_chamber(chamber_id:, force:)
|
|
46
|
+
chamber = @chambers[chamber_id]
|
|
47
|
+
return { success: false, error: 'chamber not found' } unless chamber
|
|
48
|
+
|
|
49
|
+
result = chamber.disrupt!(force)
|
|
50
|
+
record_disruption(chamber_id: chamber_id, force: force, result: result) if result[:success]
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def decay_all!
|
|
55
|
+
echo_count = @echoes.size
|
|
56
|
+
@echoes.each_value { |e| e.dampen!(DECAY_RATE) }
|
|
57
|
+
prune_silent_echoes
|
|
58
|
+
{ decayed: echo_count, remaining: @echoes.size, pruned: echo_count - @echoes.size }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def echoes_by_type(echo_type:)
|
|
62
|
+
@echoes.values.select { |e| e.echo_type == echo_type.to_sym }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def loudest_echoes(limit: 5)
|
|
66
|
+
@echoes.values.sort_by { |e| -e.amplitude }.first(limit)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def most_sealed_chambers(limit: 5)
|
|
70
|
+
@chambers.values.sort_by { |c| -c.wall_thickness }.first(limit)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def disruption_history
|
|
74
|
+
@disruption_history.dup
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add_echo_to_chamber(echo_id:, chamber_id:)
|
|
78
|
+
echo = @echoes[echo_id]
|
|
79
|
+
chamber = @chambers[chamber_id]
|
|
80
|
+
return { success: false, error: 'echo not found' } unless echo
|
|
81
|
+
return { success: false, error: 'chamber not found' } unless chamber
|
|
82
|
+
|
|
83
|
+
added = chamber.add_echo(echo)
|
|
84
|
+
{ success: added, echo_id: echo_id, chamber_id: chamber_id }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def echo_report
|
|
88
|
+
{
|
|
89
|
+
total_echoes: @echoes.size,
|
|
90
|
+
active_echoes: active_echoes.size,
|
|
91
|
+
resonating_echoes: resonating_echoes.size,
|
|
92
|
+
total_chambers: @chambers.size,
|
|
93
|
+
sealed_chambers: @chambers.values.count(&:sealed?),
|
|
94
|
+
porous_chambers: @chambers.values.count(&:porous?),
|
|
95
|
+
disruption_count: @disruption_history.size,
|
|
96
|
+
loudest: loudest_echoes(limit: 3).map(&:to_h)
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def active_echoes
|
|
101
|
+
@echoes.values.reject(&:silent?)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def resonating_echoes
|
|
105
|
+
@echoes.values.select(&:resonate?)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def chamber_by_id(chamber_id)
|
|
109
|
+
@chambers[chamber_id]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def echo_by_id(echo_id)
|
|
113
|
+
@echoes[echo_id]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def prune_echoes
|
|
119
|
+
return if @echoes.size < MAX_ECHOES
|
|
120
|
+
|
|
121
|
+
silent = @echoes.values.select(&:silent?)
|
|
122
|
+
silent.each { |e| @echoes.delete(e.id) }
|
|
123
|
+
return unless @echoes.size >= MAX_ECHOES
|
|
124
|
+
|
|
125
|
+
faintest = @echoes.values.min_by(&:amplitude)
|
|
126
|
+
@echoes.delete(faintest.id) if faintest
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def prune_chambers
|
|
130
|
+
return if @chambers.size < MAX_CHAMBERS
|
|
131
|
+
|
|
132
|
+
collapsed = @chambers.values.select { |c| c.state == :collapsed }
|
|
133
|
+
collapsed.each { |c| @chambers.delete(c.id) }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def prune_silent_echoes
|
|
137
|
+
@echoes.select { |_, e| e.silent? }.each_key { |id| @echoes.delete(id) }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def record_disruption(chamber_id:, force:, result:)
|
|
141
|
+
@disruption_history << {
|
|
142
|
+
chamber_id: chamber_id,
|
|
143
|
+
force: force,
|
|
144
|
+
breakthrough: result[:breakthrough],
|
|
145
|
+
occurred_at: Time.now.utc
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveEchoChamber
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_ECHOES = 500
|
|
9
|
+
MAX_CHAMBERS = 50
|
|
10
|
+
AMPLIFICATION_RATE = 0.1
|
|
11
|
+
DECAY_RATE = 0.02
|
|
12
|
+
DISRUPTION_THRESHOLD = 0.7
|
|
13
|
+
SEALED_THRESHOLD = 0.8
|
|
14
|
+
POROUS_THRESHOLD = 0.3
|
|
15
|
+
BREAKTHROUGH_BONUS = 0.15
|
|
16
|
+
SILENT_THRESHOLD = 0.05
|
|
17
|
+
DEFAULT_AMPLITUDE = 0.5
|
|
18
|
+
DEFAULT_WALL_THICKNESS = 0.5
|
|
19
|
+
|
|
20
|
+
ECHO_TYPES = %i[belief assumption bias hypothesis conviction].freeze
|
|
21
|
+
|
|
22
|
+
CHAMBER_STATES = %i[forming resonating saturated disrupted collapsed].freeze
|
|
23
|
+
|
|
24
|
+
RESONANCE_LABELS = {
|
|
25
|
+
(0.8..) => :thunderous,
|
|
26
|
+
(0.6...0.8) => :resonant,
|
|
27
|
+
(0.4...0.6) => :humming,
|
|
28
|
+
(0.2...0.4) => :fading,
|
|
29
|
+
(..0.2) => :silent
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
AMPLIFICATION_LABELS = {
|
|
33
|
+
(0.8..) => :deafening,
|
|
34
|
+
(0.6...0.8) => :loud,
|
|
35
|
+
(0.4...0.6) => :moderate,
|
|
36
|
+
(0.2...0.4) => :quiet,
|
|
37
|
+
(..0.2) => :muted
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
CHAMBER_STATE_LABELS = {
|
|
41
|
+
forming: 'Chamber is newly formed, echoes still accumulating',
|
|
42
|
+
resonating: 'Chamber is actively reinforcing beliefs',
|
|
43
|
+
saturated: 'Chamber has reached maximum self-reinforcement',
|
|
44
|
+
disrupted: 'External input has broken through the chamber walls',
|
|
45
|
+
collapsed: 'Chamber has lost coherence and dissolved'
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
def self.label_for(table, value)
|
|
49
|
+
match = table.find { |range, _| range.cover?(value) }
|
|
50
|
+
match&.last
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveEchoChamber
|
|
8
|
+
module Helpers
|
|
9
|
+
class Echo
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :content, :echo_type, :domain, :source_agent,
|
|
13
|
+
:amplitude, :frequency, :original_amplitude, :created_at
|
|
14
|
+
|
|
15
|
+
def initialize(content:, echo_type: :belief, domain: :general,
|
|
16
|
+
source_agent: nil, amplitude: DEFAULT_AMPLITUDE)
|
|
17
|
+
@id = SecureRandom.uuid
|
|
18
|
+
@content = content
|
|
19
|
+
@echo_type = validate_type(echo_type)
|
|
20
|
+
@domain = domain.to_sym
|
|
21
|
+
@source_agent = source_agent
|
|
22
|
+
@amplitude = amplitude.to_f.clamp(0.0, 1.0).round(10)
|
|
23
|
+
@original_amplitude = @amplitude
|
|
24
|
+
@frequency = 1
|
|
25
|
+
@created_at = Time.now.utc
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def amplify!(rate = AMPLIFICATION_RATE)
|
|
29
|
+
@frequency += 1
|
|
30
|
+
@amplitude = (@amplitude + rate).clamp(0.0, 1.0).round(10)
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dampen!(rate = DECAY_RATE)
|
|
35
|
+
@amplitude = (@amplitude - rate).clamp(0.0, 1.0).round(10)
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def resonate?
|
|
40
|
+
@amplitude >= DISRUPTION_THRESHOLD
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fading?
|
|
44
|
+
@amplitude <= POROUS_THRESHOLD
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def silent?
|
|
48
|
+
@amplitude <= SILENT_THRESHOLD
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def frequency_label
|
|
52
|
+
Constants.label_for(RESONANCE_LABELS, frequency_score)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def amplitude_label
|
|
56
|
+
Constants.label_for(AMPLIFICATION_LABELS, @amplitude)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_h
|
|
60
|
+
{
|
|
61
|
+
id: @id,
|
|
62
|
+
content: @content,
|
|
63
|
+
echo_type: @echo_type,
|
|
64
|
+
domain: @domain,
|
|
65
|
+
source_agent: @source_agent,
|
|
66
|
+
amplitude: @amplitude,
|
|
67
|
+
original_amplitude: @original_amplitude,
|
|
68
|
+
frequency: @frequency,
|
|
69
|
+
frequency_label: frequency_label,
|
|
70
|
+
amplitude_label: amplitude_label,
|
|
71
|
+
resonate: resonate?,
|
|
72
|
+
fading: fading?,
|
|
73
|
+
silent: silent?,
|
|
74
|
+
created_at: @created_at
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def validate_type(type)
|
|
81
|
+
sym = type.to_sym
|
|
82
|
+
ECHO_TYPES.include?(sym) ? sym : :belief
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def frequency_score
|
|
86
|
+
[@frequency.to_f / 20.0, 1.0].min.round(10)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveEchoChamber
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveEchoChamber
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def create_echo(content:, echo_type: :belief, domain: :general,
|
|
11
|
+
source_agent: nil, amplitude: 0.5, engine: nil, **)
|
|
12
|
+
raise ArgumentError, 'content cannot be empty' if content.to_s.strip.empty?
|
|
13
|
+
|
|
14
|
+
eng = engine || default_engine
|
|
15
|
+
echo = eng.create_echo(
|
|
16
|
+
content: content,
|
|
17
|
+
echo_type: echo_type,
|
|
18
|
+
domain: domain,
|
|
19
|
+
source_agent: source_agent,
|
|
20
|
+
amplitude: amplitude
|
|
21
|
+
)
|
|
22
|
+
Legion::Logging.debug "[cognitive_echo_chamber] echo created id=#{echo.id} type=#{echo_type} domain=#{domain}"
|
|
23
|
+
{ success: true, echo: echo.to_h }
|
|
24
|
+
rescue ArgumentError => e
|
|
25
|
+
{ success: false, error: e.message }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_chamber(label:, domain: :general, wall_thickness: 0.5, engine: nil, **)
|
|
29
|
+
raise ArgumentError, 'label cannot be empty' if label.to_s.strip.empty?
|
|
30
|
+
|
|
31
|
+
eng = engine || default_engine
|
|
32
|
+
chamber = eng.create_chamber(label: label, domain: domain, wall_thickness: wall_thickness)
|
|
33
|
+
Legion::Logging.debug "[cognitive_echo_chamber] chamber created id=#{chamber.id} label=#{label}"
|
|
34
|
+
{ success: true, chamber: chamber.to_h }
|
|
35
|
+
rescue ArgumentError => e
|
|
36
|
+
{ success: false, error: e.message }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def amplify(echo_id:, rate: 0.1, engine: nil, **)
|
|
40
|
+
eng = engine || default_engine
|
|
41
|
+
echo = eng.amplify_echo(echo_id: echo_id, rate: rate)
|
|
42
|
+
return { success: false, error: 'echo not found' } unless echo
|
|
43
|
+
|
|
44
|
+
Legion::Logging.debug "[cognitive_echo_chamber] amplified echo=#{echo_id} amplitude=#{echo.amplitude}"
|
|
45
|
+
{ success: true, echo: echo.to_h }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def disrupt(chamber_id:, force:, engine: nil, **)
|
|
49
|
+
eng = engine || default_engine
|
|
50
|
+
result = eng.disrupt_chamber(chamber_id: chamber_id, force: force)
|
|
51
|
+
Legion::Logging.debug "[cognitive_echo_chamber] disrupt chamber=#{chamber_id} success=#{result[:success]}"
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def list_echoes(echo_type: nil, domain: nil, engine: nil, **)
|
|
56
|
+
eng = engine || default_engine
|
|
57
|
+
echoes = if echo_type
|
|
58
|
+
eng.echoes_by_type(echo_type: echo_type)
|
|
59
|
+
else
|
|
60
|
+
eng.active_echoes
|
|
61
|
+
end
|
|
62
|
+
echoes = echoes.select { |e| e.domain == domain.to_sym } if domain
|
|
63
|
+
{ success: true, echoes: echoes.map(&:to_h), count: echoes.size }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def chamber_status(engine: nil, **)
|
|
67
|
+
eng = engine || default_engine
|
|
68
|
+
{ success: true, **eng.echo_report }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def default_engine
|
|
74
|
+
@default_engine ||= Helpers::ChamberEngine.new
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require_relative 'cognitive_echo_chamber/version'
|
|
5
|
+
require_relative 'cognitive_echo_chamber/helpers/constants'
|
|
6
|
+
require_relative 'cognitive_echo_chamber/helpers/echo'
|
|
7
|
+
require_relative 'cognitive_echo_chamber/helpers/chamber'
|
|
8
|
+
require_relative 'cognitive_echo_chamber/helpers/chamber_engine'
|
|
9
|
+
require_relative 'cognitive_echo_chamber/runners/cognitive_echo_chamber'
|
|
10
|
+
require_relative 'cognitive_echo_chamber/client'
|
|
11
|
+
|
|
12
|
+
module Legion
|
|
13
|
+
module Extensions
|
|
14
|
+
module CognitiveEchoChamber
|
|
15
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-echo-chamber
|
|
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: Models cognitive echo chambers — when thoughts reinforce themselves without
|
|
27
|
+
external challenge, creating self-reinforcing belief loops. Tracks amplification,
|
|
28
|
+
resonance, and breakthrough events when external input disrupts the echo.
|
|
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-echo-chamber.gemspec
|
|
44
|
+
- lib/legion/extensions/cognitive_echo_chamber.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_echo_chamber/client.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_echo_chamber/helpers/chamber.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_echo_chamber/helpers/chamber_engine.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_echo_chamber/helpers/constants.rb
|
|
49
|
+
- lib/legion/extensions/cognitive_echo_chamber/helpers/echo.rb
|
|
50
|
+
- lib/legion/extensions/cognitive_echo_chamber/runners/cognitive_echo_chamber.rb
|
|
51
|
+
- lib/legion/extensions/cognitive_echo_chamber/version.rb
|
|
52
|
+
homepage: https://github.com/LegionIO/lex-cognitive-echo-chamber
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata:
|
|
56
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-echo-chamber
|
|
57
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-echo-chamber
|
|
58
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-echo-chamber/blob/master/README.md
|
|
59
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-echo-chamber/blob/master/CHANGELOG.md
|
|
60
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-echo-chamber/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 echo chamber dynamics for LegionIO agentic architecture
|
|
79
|
+
test_files: []
|