lex-cognitive-liminal 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 +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +37 -0
- data/CLAUDE.md +116 -0
- data/Gemfile +11 -0
- data/README.md +52 -0
- data/lex-cognitive-liminal.gemspec +29 -0
- data/lib/legion/extensions/cognitive_liminal/client.rb +15 -0
- data/lib/legion/extensions/cognitive_liminal/helpers/constants.rb +75 -0
- data/lib/legion/extensions/cognitive_liminal/helpers/liminal_engine.rb +111 -0
- data/lib/legion/extensions/cognitive_liminal/helpers/threshold_crossing.rb +132 -0
- data/lib/legion/extensions/cognitive_liminal/runners/cognitive_liminal.rb +68 -0
- data/lib/legion/extensions/cognitive_liminal/version.rb +9 -0
- data/lib/legion/extensions/cognitive_liminal.rb +15 -0
- data/spec/legion/extensions/cognitive_liminal/client_spec.rb +21 -0
- data/spec/legion/extensions/cognitive_liminal/helpers/liminal_engine_spec.rb +130 -0
- data/spec/legion/extensions/cognitive_liminal/helpers/threshold_crossing_spec.rb +155 -0
- data/spec/legion/extensions/cognitive_liminal/runners_spec.rb +81 -0
- data/spec/legion/extensions/cognitive_liminal_spec.rb +7 -0
- data/spec/spec_helper.rb +28 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '0871023ea17e12821fa4bc8c1734a6206e13ab67c3c6edeceedb749a36fd1e74'
|
|
4
|
+
data.tar.gz: 8e2b1dfce63884782db8293b0605fadd40e3edecfaef92cf92d4afd4e7cf923a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1bf9764fe3e730c9faf8f1e316f3d47561ecfa255be63dda7f891cd6dd940ec814fc1029f700798a513032fd7485b449e4ca2ebee6d47da1e4afbf391d097681
|
|
7
|
+
data.tar.gz: 0f9950a0098906cf1623cfad679f92ba211c3de35e13f20a48efd7d10161eaffb7a639d5abceefd91da3d25ed07cbe5b51e9fb43a2be7b4ecb3d9831a7dc5bb6
|
|
@@ -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,116 @@
|
|
|
1
|
+
# lex-cognitive-liminal
|
|
2
|
+
|
|
3
|
+
**Level 3 Leaf Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Threshold crossing model based on anthropological liminality theory (van Gennep / Turner). Tracks cognitive transitions through three phases: `separation` (leaving the old state), `margin` (the liminal in-between), and `incorporation` (arriving at the new state). Each crossing tracks ambiguity (peaks at the liminal threshold), creative potential (highest in fertile liminal space), and progress (0.0 → 1.0).
|
|
9
|
+
|
|
10
|
+
## Gem Info
|
|
11
|
+
|
|
12
|
+
- **Gem name**: `lex-cognitive-liminal`
|
|
13
|
+
- **Module**: `Legion::Extensions::CognitiveLiminal`
|
|
14
|
+
- **Version**: `0.1.0`
|
|
15
|
+
- **Ruby**: `>= 3.4`
|
|
16
|
+
- **License**: MIT
|
|
17
|
+
|
|
18
|
+
## File Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
lib/legion/extensions/cognitive_liminal/
|
|
22
|
+
version.rb
|
|
23
|
+
client.rb
|
|
24
|
+
helpers/
|
|
25
|
+
constants.rb
|
|
26
|
+
threshold_crossing.rb
|
|
27
|
+
liminal_engine.rb
|
|
28
|
+
runners/
|
|
29
|
+
cognitive_liminal.rb
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Key Constants
|
|
33
|
+
|
|
34
|
+
| Constant | Value | Purpose |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `MAX_TRANSITIONS` | `200` | Per-engine crossing capacity |
|
|
37
|
+
| `DEFAULT_AMBIGUITY` | `0.5` | Starting ambiguity for new crossings |
|
|
38
|
+
| `AMBIGUITY_GROWTH` | `0.08` | Ambiguity increase per `dissolve!` call |
|
|
39
|
+
| `AMBIGUITY_RESOLUTION` | `0.12` | Ambiguity decrease per `crystallize!` call |
|
|
40
|
+
| `CREATIVE_POTENTIAL_PEAK` | `0.7` | Creative potential at peak liminality |
|
|
41
|
+
| `DISSOLUTION_RATE` | `0.05` | Default progress regression rate |
|
|
42
|
+
| `CRYSTALLIZATION_RATE` | `0.07` | Default progress advancement rate |
|
|
43
|
+
| `SEPARATION_THRESHOLD` | `0.3` | Progress cutoff for separation phase |
|
|
44
|
+
| `LIMINAL_PEAK` | `0.7` | Progress value for peak liminality |
|
|
45
|
+
| `INCORPORATION_THRESHOLD` | `0.9` | Progress cutoff for incorporation phase |
|
|
46
|
+
| `TRANSITION_PHASES` | `%i[separation margin incorporation complete]` | Valid phases |
|
|
47
|
+
| `ORIGIN_STATES` / `DESTINATION_STATES` | symbol arrays | Valid state labels |
|
|
48
|
+
| `LIMINAL_QUALITIES` | symbol array | Tags for the in-between quality |
|
|
49
|
+
|
|
50
|
+
## Helpers
|
|
51
|
+
|
|
52
|
+
### `Helpers::ThresholdCrossing`
|
|
53
|
+
Single crossing instance. Has `id`, `origin_state`, `destination_state`, `domain`, `progress`, `ambiguity`, `creative_potential`, `phase`, and `completed_at`.
|
|
54
|
+
|
|
55
|
+
- `advance!(rate)` — increases progress via `CRYSTALLIZATION_RATE`, advances phase
|
|
56
|
+
- `dissolve!(rate)` — regresses progress, increases ambiguity
|
|
57
|
+
- `crystallize!(rate)` — advances progress, reduces ambiguity
|
|
58
|
+
- `complete!` — marks crossing done, sets `completed_at`
|
|
59
|
+
- `liminal?` — phase is `:margin`
|
|
60
|
+
- `separated?` — phase is `:separation`
|
|
61
|
+
- `incorporated?` — phase is `:incorporation` or `:complete`
|
|
62
|
+
- `peak_liminality?` — progress near `LIMINAL_PEAK`
|
|
63
|
+
- `fertile?` — creative potential above a threshold
|
|
64
|
+
- Label methods for ambiguity, creative potential, and phase
|
|
65
|
+
|
|
66
|
+
### `Helpers::LiminalEngine`
|
|
67
|
+
Manages all crossings. Enforces `MAX_TRANSITIONS`.
|
|
68
|
+
|
|
69
|
+
- `begin_crossing(origin:, destination:, domain:)` → crossing
|
|
70
|
+
- `advance_crossing(crossing_id:)` → crossing state
|
|
71
|
+
- `dissolve_crossing(crossing_id:)` → crossing state
|
|
72
|
+
- `crystallize_crossing(crossing_id:)` → crossing state
|
|
73
|
+
- `active_crossings` → all incomplete crossings
|
|
74
|
+
- `completed_crossings` → all complete crossings
|
|
75
|
+
- `liminal_crossings` → crossings currently in margin phase
|
|
76
|
+
- `fertile_crossings` → crossings with high creative potential
|
|
77
|
+
- `peak_crossings` → crossings at peak liminality
|
|
78
|
+
- `crossings_by_domain(domain:)` → filtered list
|
|
79
|
+
- `average_ambiguity` → float
|
|
80
|
+
- `average_creative_potential` → float
|
|
81
|
+
- `liminal_density` → ratio of liminal to total active crossings
|
|
82
|
+
- `most_liminal` → crossing with highest ambiguity
|
|
83
|
+
- `liminal_report` → aggregate stats hash
|
|
84
|
+
|
|
85
|
+
## Runners
|
|
86
|
+
|
|
87
|
+
Module: `Runners::CognitiveLiminal`
|
|
88
|
+
|
|
89
|
+
| Runner Method | Description |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `begin_crossing(origin:, destination:, domain:)` | Start a new transition |
|
|
92
|
+
| `advance_crossing(crossing_id:)` | Push crossing forward |
|
|
93
|
+
| `dissolve_crossing(crossing_id:)` | Regress and increase ambiguity |
|
|
94
|
+
| `crystallize_crossing(crossing_id:)` | Advance and reduce ambiguity |
|
|
95
|
+
| `active_crossings` | All in-progress crossings |
|
|
96
|
+
| `liminal_crossings` | Crossings in margin phase |
|
|
97
|
+
| `fertile_crossings` | Crossings with high creative potential |
|
|
98
|
+
| `liminal_status` | Full aggregate report |
|
|
99
|
+
|
|
100
|
+
All runners return `{success: true/false, ...}` hashes.
|
|
101
|
+
|
|
102
|
+
## Integration Points
|
|
103
|
+
|
|
104
|
+
- No direct dependencies on other agentic LEX gems
|
|
105
|
+
- Fits `lex-tick` `action_selection` phase: high ambiguity crossings can prompt consultation behavior
|
|
106
|
+
- Creative potential peaks can feed into `lex-emotion` as a positive arousal signal
|
|
107
|
+
- Peak liminality states represent prime conditions for insight — can trigger `lex-dream` association walks
|
|
108
|
+
- `lex-identity` entropy checks may coincide with high-ambiguity transitions
|
|
109
|
+
|
|
110
|
+
## Development Notes
|
|
111
|
+
|
|
112
|
+
- `Client` instantiates `@liminal_engine = Helpers::LiminalEngine.new`
|
|
113
|
+
- Phase transitions are implicit: `advance!` checks progress thresholds against `SEPARATION_THRESHOLD`, `LIMINAL_PEAK`, and `INCORPORATION_THRESHOLD`
|
|
114
|
+
- A crossing can complete only by reaching `INCORPORATION_THRESHOLD` via `advance!` or `crystallize!`
|
|
115
|
+
- `fertile?` is true when creative potential >= `CREATIVE_POTENTIAL_PEAK * 0.9` (near peak)
|
|
116
|
+
- Ambiguity and creative potential are independent dimensions: high ambiguity + high creative potential = the fertile liminal state
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# lex-cognitive-liminal
|
|
2
|
+
|
|
3
|
+
Threshold crossing model for LegionIO cognitive agents. Based on anthropological liminality theory, tracks transitions through separation, margin, and incorporation phases while measuring ambiguity and creative potential in the in-between space.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
- Begin crossings from an origin state to a destination state within a domain
|
|
8
|
+
- Three-phase progression: separation → margin (liminal) → incorporation
|
|
9
|
+
- Track ambiguity (peaks mid-transition) and creative potential (highest in fertile liminal space)
|
|
10
|
+
- `advance` / `crystallize` push crossings forward; `dissolve` regresses and increases ambiguity
|
|
11
|
+
- Identify fertile crossings (high creative potential), peak-liminality crossings, and domain clusters
|
|
12
|
+
- Supports up to 200 simultaneous transitions
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
# Start a crossing
|
|
18
|
+
result = runner.begin_crossing(
|
|
19
|
+
origin: :certainty, destination: :new_framework, domain: :architecture
|
|
20
|
+
)
|
|
21
|
+
crossing_id = result[:crossing][:id]
|
|
22
|
+
|
|
23
|
+
# Advance through phases
|
|
24
|
+
runner.advance_crossing(crossing_id: crossing_id)
|
|
25
|
+
# => { success: true, crossing: { phase: :separation, progress: 0.07, ambiguity: 0.5, ... } }
|
|
26
|
+
|
|
27
|
+
# In the margin — dissolve for more ambiguity (creative space)
|
|
28
|
+
runner.dissolve_crossing(crossing_id: crossing_id)
|
|
29
|
+
|
|
30
|
+
# Crystallize to resolve and advance
|
|
31
|
+
runner.crystallize_crossing(crossing_id: crossing_id)
|
|
32
|
+
|
|
33
|
+
# Find all crossings in the fertile liminal state
|
|
34
|
+
runner.fertile_crossings
|
|
35
|
+
# => { success: true, crossings: [...], count: N }
|
|
36
|
+
|
|
37
|
+
# Overall status
|
|
38
|
+
runner.liminal_status
|
|
39
|
+
# => { success: true, total_active: N, liminal_count: N, avg_ambiguity: 0.XX, avg_creative_potential: 0.XX, ... }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bundle install
|
|
46
|
+
bundle exec rspec
|
|
47
|
+
bundle exec rubocop
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_liminal/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-liminal'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveLiminal::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
spec.summary = 'Liminal threshold states between cognitive modes for LegionIO agents'
|
|
11
|
+
spec.description = 'Models liminality — the creative in-between states during cognitive ' \
|
|
12
|
+
'mode transitions with ambiguity, dissolution, and crystallization'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-liminal'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata = {
|
|
18
|
+
'homepage_uri' => spec.homepage,
|
|
19
|
+
'source_code_uri' => spec.homepage,
|
|
20
|
+
'documentation_uri' => "#{spec.homepage}/blob/origin/README.md",
|
|
21
|
+
'changelog_uri' => "#{spec.homepage}/blob/origin/CHANGELOG.md",
|
|
22
|
+
'bug_tracker_uri' => "#{spec.homepage}/issues",
|
|
23
|
+
'rubygems_mfa_required' => 'true'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
spec.files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
spec.add_development_dependency 'legion-gaia'
|
|
29
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveLiminal
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_TRANSITIONS = 200
|
|
9
|
+
MAX_THRESHOLDS = 100
|
|
10
|
+
|
|
11
|
+
# Liminal dynamics
|
|
12
|
+
DEFAULT_AMBIGUITY = 0.5
|
|
13
|
+
AMBIGUITY_GROWTH = 0.08
|
|
14
|
+
AMBIGUITY_RESOLUTION = 0.12
|
|
15
|
+
CREATIVE_POTENTIAL_PEAK = 0.7
|
|
16
|
+
DISSOLUTION_RATE = 0.05
|
|
17
|
+
CRYSTALLIZATION_RATE = 0.07
|
|
18
|
+
|
|
19
|
+
# Phase thresholds
|
|
20
|
+
SEPARATION_THRESHOLD = 0.3
|
|
21
|
+
LIMINAL_PEAK = 0.7
|
|
22
|
+
INCORPORATION_THRESHOLD = 0.9
|
|
23
|
+
|
|
24
|
+
TRANSITION_PHASES = %i[
|
|
25
|
+
separation margin incorporation
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
ORIGIN_STATES = %i[
|
|
29
|
+
analytical creative intuitive deliberate
|
|
30
|
+
receptive focused diffuse dormant
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
DESTINATION_STATES = %i[
|
|
34
|
+
analytical creative intuitive deliberate
|
|
35
|
+
receptive focused diffuse dormant
|
|
36
|
+
].freeze
|
|
37
|
+
|
|
38
|
+
LIMINAL_QUALITIES = %i[
|
|
39
|
+
ambiguous fertile dissolving crystallizing
|
|
40
|
+
betwixt chaotic generative suspended
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
AMBIGUITY_LABELS = {
|
|
44
|
+
(0.8..) => :maximally_liminal,
|
|
45
|
+
(0.6...0.8) => :deeply_liminal,
|
|
46
|
+
(0.4...0.6) => :liminal,
|
|
47
|
+
(0.2...0.4) => :transitioning,
|
|
48
|
+
(..0.2) => :settled
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
POTENTIAL_LABELS = {
|
|
52
|
+
(0.8..) => :explosive,
|
|
53
|
+
(0.6...0.8) => :high,
|
|
54
|
+
(0.4...0.6) => :moderate,
|
|
55
|
+
(0.2...0.4) => :low,
|
|
56
|
+
(..0.2) => :depleted
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
PROGRESS_LABELS = {
|
|
60
|
+
(0.8..) => :nearly_incorporated,
|
|
61
|
+
(0.6...0.8) => :deep_margin,
|
|
62
|
+
(0.4...0.6) => :peak_liminality,
|
|
63
|
+
(0.2...0.4) => :early_separation,
|
|
64
|
+
(..0.2) => :pre_liminal
|
|
65
|
+
}.freeze
|
|
66
|
+
|
|
67
|
+
def self.label_for(labels, value)
|
|
68
|
+
labels.each { |range, label| return label if range.cover?(value) }
|
|
69
|
+
:unknown
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveLiminal
|
|
6
|
+
module Helpers
|
|
7
|
+
class LiminalEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@crossings = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def begin_crossing(origin:, destination:, domain: :general, ambiguity: DEFAULT_AMBIGUITY)
|
|
15
|
+
prune_completed
|
|
16
|
+
crossing = ThresholdCrossing.new(origin: origin, destination: destination,
|
|
17
|
+
domain: domain, ambiguity: ambiguity)
|
|
18
|
+
@crossings[crossing.id] = crossing
|
|
19
|
+
crossing
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def advance_crossing(crossing_id:)
|
|
23
|
+
crossing = @crossings[crossing_id]
|
|
24
|
+
return nil unless crossing
|
|
25
|
+
|
|
26
|
+
crossing.advance!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def dissolve_crossing(crossing_id:)
|
|
30
|
+
crossing = @crossings[crossing_id]
|
|
31
|
+
return nil unless crossing
|
|
32
|
+
|
|
33
|
+
crossing.dissolve!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def crystallize_crossing(crossing_id:)
|
|
37
|
+
crossing = @crossings[crossing_id]
|
|
38
|
+
return nil unless crossing
|
|
39
|
+
|
|
40
|
+
crossing.crystallize!
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def active_crossings = @crossings.values.select { |c| c.status == :active }
|
|
44
|
+
def completed_crossings = @crossings.values.select(&:incorporated?)
|
|
45
|
+
def liminal_crossings = @crossings.values.select(&:liminal?)
|
|
46
|
+
def fertile_crossings = @crossings.values.select(&:fertile?)
|
|
47
|
+
def peak_crossings = @crossings.values.select(&:peak_liminality?)
|
|
48
|
+
|
|
49
|
+
def crossings_by_domain(domain:)
|
|
50
|
+
@crossings.values.select { |c| c.domain == domain.to_sym }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def average_ambiguity
|
|
54
|
+
active = active_crossings
|
|
55
|
+
return 0.0 if active.empty?
|
|
56
|
+
|
|
57
|
+
(active.sum(&:ambiguity) / active.size).round(10)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def average_creative_potential
|
|
61
|
+
active = active_crossings
|
|
62
|
+
return 0.0 if active.empty?
|
|
63
|
+
|
|
64
|
+
(active.sum(&:creative_potential) / active.size).round(10)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def liminal_density
|
|
68
|
+
return 0.0 if @crossings.empty?
|
|
69
|
+
|
|
70
|
+
(liminal_crossings.size.to_f / [@crossings.size, 1].max).clamp(0.0, 1.0).round(10)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def most_liminal(limit: 5) = @crossings.values.sort_by { |c| -c.ambiguity }.first(limit)
|
|
74
|
+
|
|
75
|
+
def liminal_report
|
|
76
|
+
{
|
|
77
|
+
total_crossings: @crossings.size,
|
|
78
|
+
active_count: active_crossings.size,
|
|
79
|
+
liminal_count: liminal_crossings.size,
|
|
80
|
+
completed_count: completed_crossings.size,
|
|
81
|
+
fertile_count: fertile_crossings.size,
|
|
82
|
+
peak_count: peak_crossings.size,
|
|
83
|
+
average_ambiguity: average_ambiguity,
|
|
84
|
+
average_potential: average_creative_potential,
|
|
85
|
+
liminal_density: liminal_density,
|
|
86
|
+
most_liminal: most_liminal(limit: 3).map(&:to_h)
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_h
|
|
91
|
+
{
|
|
92
|
+
total_crossings: @crossings.size,
|
|
93
|
+
active: active_crossings.size,
|
|
94
|
+
liminal: liminal_crossings.size,
|
|
95
|
+
avg_ambiguity: average_ambiguity,
|
|
96
|
+
avg_potential: average_creative_potential
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def prune_completed
|
|
103
|
+
return if @crossings.size < MAX_TRANSITIONS
|
|
104
|
+
|
|
105
|
+
@crossings.reject! { |_, c| c.incorporated? }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveLiminal
|
|
8
|
+
module Helpers
|
|
9
|
+
class ThresholdCrossing
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :origin, :destination, :domain, :phase,
|
|
13
|
+
:ambiguity, :creative_potential, :progress,
|
|
14
|
+
:quality, :created_at, :ticks_in_liminal
|
|
15
|
+
attr_accessor :status
|
|
16
|
+
|
|
17
|
+
def initialize(origin:, destination:, domain: :general, ambiguity: DEFAULT_AMBIGUITY)
|
|
18
|
+
@id = SecureRandom.uuid
|
|
19
|
+
@origin = valid_state(origin, ORIGIN_STATES)
|
|
20
|
+
@destination = valid_state(destination, DESTINATION_STATES)
|
|
21
|
+
@domain = domain.to_sym
|
|
22
|
+
@ambiguity = ambiguity.to_f.clamp(0.0, 1.0).round(10)
|
|
23
|
+
@creative_potential = compute_creative_potential
|
|
24
|
+
@progress = 0.0
|
|
25
|
+
@phase = :separation
|
|
26
|
+
@quality = :ambiguous
|
|
27
|
+
@status = :active
|
|
28
|
+
@ticks_in_liminal = 0
|
|
29
|
+
@created_at = Time.now
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def advance!
|
|
33
|
+
return self if @status == :completed
|
|
34
|
+
|
|
35
|
+
@ticks_in_liminal += 1
|
|
36
|
+
@progress = (@progress + CRYSTALLIZATION_RATE).clamp(0.0, 1.0).round(10)
|
|
37
|
+
update_phase!
|
|
38
|
+
update_ambiguity!
|
|
39
|
+
@creative_potential = compute_creative_potential
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def dissolve!
|
|
44
|
+
@ambiguity = (@ambiguity + DISSOLUTION_RATE).clamp(0.0, 1.0).round(10)
|
|
45
|
+
@creative_potential = compute_creative_potential
|
|
46
|
+
@quality = :dissolving
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def crystallize!
|
|
51
|
+
@ambiguity = (@ambiguity - AMBIGUITY_RESOLUTION).clamp(0.0, 1.0).round(10)
|
|
52
|
+
@progress = (@progress + CRYSTALLIZATION_RATE).clamp(0.0, 1.0).round(10)
|
|
53
|
+
@quality = :crystallizing
|
|
54
|
+
update_phase!
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def complete!
|
|
59
|
+
@status = :completed
|
|
60
|
+
@phase = :incorporation
|
|
61
|
+
@progress = 1.0
|
|
62
|
+
@ambiguity = 0.0
|
|
63
|
+
@quality = :crystallizing
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def liminal? = @phase == :margin && @status == :active
|
|
68
|
+
def separated? = @phase == :separation
|
|
69
|
+
def incorporated? = @status == :completed
|
|
70
|
+
def peak_liminality? = @ambiguity >= LIMINAL_PEAK
|
|
71
|
+
def fertile? = @creative_potential >= CREATIVE_POTENTIAL_PEAK
|
|
72
|
+
|
|
73
|
+
def ambiguity_label = Constants.label_for(AMBIGUITY_LABELS, @ambiguity)
|
|
74
|
+
def potential_label = Constants.label_for(POTENTIAL_LABELS, @creative_potential)
|
|
75
|
+
def progress_label = Constants.label_for(PROGRESS_LABELS, @progress)
|
|
76
|
+
|
|
77
|
+
def to_h
|
|
78
|
+
{
|
|
79
|
+
id: @id,
|
|
80
|
+
origin: @origin,
|
|
81
|
+
destination: @destination,
|
|
82
|
+
domain: @domain,
|
|
83
|
+
phase: @phase,
|
|
84
|
+
ambiguity: @ambiguity,
|
|
85
|
+
creative_potential: @creative_potential,
|
|
86
|
+
progress: @progress,
|
|
87
|
+
quality: @quality,
|
|
88
|
+
status: @status,
|
|
89
|
+
ticks_in_liminal: @ticks_in_liminal,
|
|
90
|
+
liminal: liminal?,
|
|
91
|
+
peak_liminality: peak_liminality?,
|
|
92
|
+
fertile: fertile?,
|
|
93
|
+
ambiguity_label: ambiguity_label,
|
|
94
|
+
potential_label: potential_label,
|
|
95
|
+
progress_label: progress_label,
|
|
96
|
+
created_at: @created_at.iso8601
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def compute_creative_potential
|
|
103
|
+
peak_dist = (@ambiguity - CREATIVE_POTENTIAL_PEAK).abs
|
|
104
|
+
(1.0 - peak_dist).clamp(0.0, 1.0).round(10)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def update_phase!
|
|
108
|
+
if @progress >= INCORPORATION_THRESHOLD
|
|
109
|
+
@phase = :incorporation
|
|
110
|
+
complete!
|
|
111
|
+
elsif @progress >= SEPARATION_THRESHOLD
|
|
112
|
+
@phase = :margin
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def update_ambiguity!
|
|
117
|
+
if @phase == :margin
|
|
118
|
+
@ambiguity = (@ambiguity + AMBIGUITY_GROWTH).clamp(0.0, 1.0).round(10)
|
|
119
|
+
elsif @phase == :incorporation
|
|
120
|
+
@ambiguity = (@ambiguity - AMBIGUITY_RESOLUTION).clamp(0.0, 1.0).round(10)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def valid_state(state, list)
|
|
125
|
+
sym = state.to_sym
|
|
126
|
+
list.include?(sym) ? sym : list.first
|
|
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 CognitiveLiminal
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveLiminal
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def begin_crossing(origin:, destination:, domain: :general, ambiguity: nil, engine: nil, **)
|
|
11
|
+
eng = engine || @default_engine
|
|
12
|
+
crossing = eng.begin_crossing(origin: origin, destination: destination, domain: domain,
|
|
13
|
+
ambiguity: ambiguity || Helpers::Constants::DEFAULT_AMBIGUITY)
|
|
14
|
+
{ success: true, crossing: crossing.to_h }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def advance_crossing(crossing_id:, engine: nil, **)
|
|
18
|
+
eng = engine || @default_engine
|
|
19
|
+
crossing = eng.advance_crossing(crossing_id: crossing_id)
|
|
20
|
+
return { success: false, error: 'crossing not found' } unless crossing
|
|
21
|
+
|
|
22
|
+
{ success: true, crossing: crossing.to_h }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def dissolve_crossing(crossing_id:, engine: nil, **)
|
|
26
|
+
eng = engine || @default_engine
|
|
27
|
+
crossing = eng.dissolve_crossing(crossing_id: crossing_id)
|
|
28
|
+
return { success: false, error: 'crossing not found' } unless crossing
|
|
29
|
+
|
|
30
|
+
{ success: true, crossing: crossing.to_h }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def crystallize_crossing(crossing_id:, engine: nil, **)
|
|
34
|
+
eng = engine || @default_engine
|
|
35
|
+
crossing = eng.crystallize_crossing(crossing_id: crossing_id)
|
|
36
|
+
return { success: false, error: 'crossing not found' } unless crossing
|
|
37
|
+
|
|
38
|
+
{ success: true, crossing: crossing.to_h }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def active_crossings(engine: nil, **)
|
|
42
|
+
eng = engine || @default_engine
|
|
43
|
+
crossings = eng.active_crossings
|
|
44
|
+
{ success: true, count: crossings.size, crossings: crossings.map(&:to_h) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def liminal_crossings(engine: nil, **)
|
|
48
|
+
eng = engine || @default_engine
|
|
49
|
+
crossings = eng.liminal_crossings
|
|
50
|
+
{ success: true, count: crossings.size, crossings: crossings.map(&:to_h) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fertile_crossings(engine: nil, **)
|
|
54
|
+
eng = engine || @default_engine
|
|
55
|
+
crossings = eng.fertile_crossings
|
|
56
|
+
{ success: true, count: crossings.size, crossings: crossings.map(&:to_h) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def liminal_status(engine: nil, **)
|
|
60
|
+
eng = engine || @default_engine
|
|
61
|
+
report = eng.liminal_report
|
|
62
|
+
{ success: true, **report }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'cognitive_liminal/version'
|
|
4
|
+
require_relative 'cognitive_liminal/helpers/constants'
|
|
5
|
+
require_relative 'cognitive_liminal/helpers/threshold_crossing'
|
|
6
|
+
require_relative 'cognitive_liminal/helpers/liminal_engine'
|
|
7
|
+
require_relative 'cognitive_liminal/runners/cognitive_liminal'
|
|
8
|
+
require_relative 'cognitive_liminal/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module CognitiveLiminal
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveLiminal::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'responds to runner methods' do
|
|
7
|
+
expect(client).to respond_to(:begin_crossing, :advance_crossing, :liminal_status)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'runs a full liminal lifecycle' do
|
|
11
|
+
result = client.begin_crossing(origin: :analytical, destination: :creative, domain: :cognitive)
|
|
12
|
+
crossing_id = result[:crossing][:id]
|
|
13
|
+
|
|
14
|
+
5.times { client.advance_crossing(crossing_id: crossing_id) }
|
|
15
|
+
client.dissolve_crossing(crossing_id: crossing_id)
|
|
16
|
+
10.times { client.crystallize_crossing(crossing_id: crossing_id) }
|
|
17
|
+
|
|
18
|
+
status = client.liminal_status
|
|
19
|
+
expect(status[:total_crossings]).to eq(1)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveLiminal::Helpers::LiminalEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#begin_crossing' do
|
|
7
|
+
it 'creates a threshold crossing' do
|
|
8
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
9
|
+
expect(crossing.origin).to eq(:analytical)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#advance_crossing' do
|
|
14
|
+
it 'advances known crossing' do
|
|
15
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
16
|
+
engine.advance_crossing(crossing_id: crossing.id)
|
|
17
|
+
expect(crossing.ticks_in_liminal).to eq(1)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'returns nil for unknown crossing' do
|
|
21
|
+
expect(engine.advance_crossing(crossing_id: 'bad')).to be_nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#dissolve_crossing' do
|
|
26
|
+
it 'dissolves known crossing' do
|
|
27
|
+
crossing = engine.begin_crossing(origin: :focused, destination: :diffuse)
|
|
28
|
+
original = crossing.ambiguity
|
|
29
|
+
engine.dissolve_crossing(crossing_id: crossing.id)
|
|
30
|
+
expect(crossing.ambiguity).to be > original
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#crystallize_crossing' do
|
|
35
|
+
it 'crystallizes known crossing' do
|
|
36
|
+
crossing = engine.begin_crossing(origin: :intuitive, destination: :deliberate)
|
|
37
|
+
original = crossing.ambiguity
|
|
38
|
+
engine.crystallize_crossing(crossing_id: crossing.id)
|
|
39
|
+
expect(crossing.ambiguity).to be < original
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#active_crossings' do
|
|
44
|
+
it 'returns active crossings' do
|
|
45
|
+
engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
46
|
+
expect(engine.active_crossings.size).to eq(1)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe '#completed_crossings' do
|
|
51
|
+
it 'returns completed crossings' do
|
|
52
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
53
|
+
20.times { crossing.advance! }
|
|
54
|
+
expect(engine.completed_crossings.size).to eq(1)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#liminal_crossings' do
|
|
59
|
+
it 'returns crossings in margin phase' do
|
|
60
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
61
|
+
5.times { crossing.advance! }
|
|
62
|
+
expect(engine.liminal_crossings.size).to eq(1)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe '#fertile_crossings' do
|
|
67
|
+
it 'returns fertile crossings' do
|
|
68
|
+
engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
69
|
+
expect(engine.fertile_crossings.size).to eq(1)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe '#crossings_by_domain' do
|
|
74
|
+
it 'filters by domain' do
|
|
75
|
+
engine.begin_crossing(origin: :analytical, destination: :creative, domain: :cognitive)
|
|
76
|
+
engine.begin_crossing(origin: :focused, destination: :diffuse, domain: :emotional)
|
|
77
|
+
expect(engine.crossings_by_domain(domain: :cognitive).size).to eq(1)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe '#average_ambiguity' do
|
|
82
|
+
it 'returns 0.0 with no active crossings' do
|
|
83
|
+
expect(engine.average_ambiguity).to eq(0.0)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'returns average for active crossings' do
|
|
87
|
+
engine.begin_crossing(origin: :analytical, destination: :creative, ambiguity: 0.4)
|
|
88
|
+
engine.begin_crossing(origin: :focused, destination: :diffuse, ambiguity: 0.6)
|
|
89
|
+
expect(engine.average_ambiguity).to eq(0.5)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe '#average_creative_potential' do
|
|
94
|
+
it 'returns 0.0 with no active crossings' do
|
|
95
|
+
expect(engine.average_creative_potential).to eq(0.0)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#liminal_density' do
|
|
100
|
+
it 'returns 0.0 with no crossings' do
|
|
101
|
+
expect(engine.liminal_density).to eq(0.0)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#most_liminal' do
|
|
106
|
+
it 'returns sorted by ambiguity descending' do
|
|
107
|
+
engine.begin_crossing(origin: :analytical, destination: :creative, ambiguity: 0.3)
|
|
108
|
+
high = engine.begin_crossing(origin: :focused, destination: :diffuse, ambiguity: 0.8)
|
|
109
|
+
expect(engine.most_liminal(limit: 1).first.id).to eq(high.id)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#liminal_report' do
|
|
114
|
+
it 'includes all report fields' do
|
|
115
|
+
report = engine.liminal_report
|
|
116
|
+
expect(report).to include(
|
|
117
|
+
:total_crossings, :active_count, :liminal_count, :completed_count,
|
|
118
|
+
:fertile_count, :peak_count, :average_ambiguity, :average_potential,
|
|
119
|
+
:liminal_density, :most_liminal
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe '#to_h' do
|
|
125
|
+
it 'includes summary fields' do
|
|
126
|
+
hash = engine.to_h
|
|
127
|
+
expect(hash).to include(:total_crossings, :active, :liminal, :avg_ambiguity, :avg_potential)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveLiminal::Helpers::ThresholdCrossing do
|
|
4
|
+
subject(:crossing) { described_class.new(origin: :analytical, destination: :creative) }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns a UUID id' do
|
|
8
|
+
expect(crossing.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'stores origin' do
|
|
12
|
+
expect(crossing.origin).to eq(:analytical)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'stores destination' do
|
|
16
|
+
expect(crossing.destination).to eq(:creative)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'defaults to separation phase' do
|
|
20
|
+
expect(crossing.phase).to eq(:separation)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'defaults ambiguity' do
|
|
24
|
+
expect(crossing.ambiguity).to eq(0.5)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'starts with active status' do
|
|
28
|
+
expect(crossing.status).to eq(:active)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'starts with 0 ticks' do
|
|
32
|
+
expect(crossing.ticks_in_liminal).to eq(0)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'validates origin state' do
|
|
36
|
+
bad = described_class.new(origin: :nonexistent, destination: :creative)
|
|
37
|
+
expect(bad.origin).to eq(:analytical)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#advance!' do
|
|
42
|
+
it 'increments ticks' do
|
|
43
|
+
crossing.advance!
|
|
44
|
+
expect(crossing.ticks_in_liminal).to eq(1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'increases progress' do
|
|
48
|
+
original = crossing.progress
|
|
49
|
+
crossing.advance!
|
|
50
|
+
expect(crossing.progress).to be > original
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'transitions to margin phase' do
|
|
54
|
+
5.times { crossing.advance! }
|
|
55
|
+
expect(crossing.phase).to eq(:margin)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'eventually completes' do
|
|
59
|
+
20.times { crossing.advance! }
|
|
60
|
+
expect(crossing.incorporated?).to be true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'does not advance completed crossings' do
|
|
64
|
+
20.times { crossing.advance! }
|
|
65
|
+
ticks = crossing.ticks_in_liminal
|
|
66
|
+
crossing.advance!
|
|
67
|
+
expect(crossing.ticks_in_liminal).to eq(ticks)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#dissolve!' do
|
|
72
|
+
it 'increases ambiguity' do
|
|
73
|
+
original = crossing.ambiguity
|
|
74
|
+
crossing.dissolve!
|
|
75
|
+
expect(crossing.ambiguity).to be > original
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'sets quality to dissolving' do
|
|
79
|
+
crossing.dissolve!
|
|
80
|
+
expect(crossing.quality).to eq(:dissolving)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe '#crystallize!' do
|
|
85
|
+
it 'decreases ambiguity' do
|
|
86
|
+
original = crossing.ambiguity
|
|
87
|
+
crossing.crystallize!
|
|
88
|
+
expect(crossing.ambiguity).to be < original
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'increases progress' do
|
|
92
|
+
original = crossing.progress
|
|
93
|
+
crossing.crystallize!
|
|
94
|
+
expect(crossing.progress).to be > original
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'sets quality to crystallizing' do
|
|
98
|
+
crossing.crystallize!
|
|
99
|
+
expect(crossing.quality).to eq(:crystallizing)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe '#complete!' do
|
|
104
|
+
it 'sets status to completed' do
|
|
105
|
+
crossing.complete!
|
|
106
|
+
expect(crossing.status).to eq(:completed)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'sets progress to 1.0' do
|
|
110
|
+
crossing.complete!
|
|
111
|
+
expect(crossing.progress).to eq(1.0)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'clears ambiguity' do
|
|
115
|
+
crossing.complete!
|
|
116
|
+
expect(crossing.ambiguity).to eq(0.0)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe '#liminal?' do
|
|
121
|
+
it 'is false in separation' do
|
|
122
|
+
expect(crossing.liminal?).to be false
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'is true in margin phase' do
|
|
126
|
+
5.times { crossing.advance! }
|
|
127
|
+
expect(crossing.liminal?).to be true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe '#peak_liminality?' do
|
|
132
|
+
it 'is true when ambiguity is high' do
|
|
133
|
+
5.times { crossing.dissolve! }
|
|
134
|
+
expect(crossing.peak_liminality?).to be true
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe '#fertile?' do
|
|
139
|
+
it 'detects creative potential peak' do
|
|
140
|
+
expect(crossing.fertile?).to be true
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe '#to_h' do
|
|
145
|
+
it 'includes all fields' do
|
|
146
|
+
hash = crossing.to_h
|
|
147
|
+
expect(hash).to include(
|
|
148
|
+
:id, :origin, :destination, :domain, :phase, :ambiguity,
|
|
149
|
+
:creative_potential, :progress, :quality, :status,
|
|
150
|
+
:ticks_in_liminal, :liminal, :peak_liminality, :fertile,
|
|
151
|
+
:ambiguity_label, :potential_label, :progress_label, :created_at
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveLiminal::Runners::CognitiveLiminal do
|
|
4
|
+
let(:engine) { Legion::Extensions::CognitiveLiminal::Helpers::LiminalEngine.new }
|
|
5
|
+
let(:runner) do
|
|
6
|
+
obj = Object.new
|
|
7
|
+
obj.extend(described_class)
|
|
8
|
+
obj.instance_variable_set(:@default_engine, engine)
|
|
9
|
+
obj
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#begin_crossing' do
|
|
13
|
+
it 'returns success with crossing hash' do
|
|
14
|
+
result = runner.begin_crossing(origin: :analytical, destination: :creative, engine: engine)
|
|
15
|
+
expect(result[:success]).to be true
|
|
16
|
+
expect(result[:crossing][:origin]).to eq(:analytical)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#advance_crossing' do
|
|
21
|
+
it 'returns success for known crossing' do
|
|
22
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
23
|
+
result = runner.advance_crossing(crossing_id: crossing.id, engine: engine)
|
|
24
|
+
expect(result[:success]).to be true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'returns failure for unknown crossing' do
|
|
28
|
+
result = runner.advance_crossing(crossing_id: 'bad', engine: engine)
|
|
29
|
+
expect(result[:success]).to be false
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#dissolve_crossing' do
|
|
34
|
+
it 'returns success for known crossing' do
|
|
35
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
36
|
+
result = runner.dissolve_crossing(crossing_id: crossing.id, engine: engine)
|
|
37
|
+
expect(result[:success]).to be true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#crystallize_crossing' do
|
|
42
|
+
it 'returns success for known crossing' do
|
|
43
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
44
|
+
result = runner.crystallize_crossing(crossing_id: crossing.id, engine: engine)
|
|
45
|
+
expect(result[:success]).to be true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe '#active_crossings' do
|
|
50
|
+
it 'returns active list' do
|
|
51
|
+
engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
52
|
+
result = runner.active_crossings(engine: engine)
|
|
53
|
+
expect(result[:count]).to eq(1)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#liminal_crossings' do
|
|
58
|
+
it 'returns liminal list' do
|
|
59
|
+
crossing = engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
60
|
+
5.times { crossing.advance! }
|
|
61
|
+
result = runner.liminal_crossings(engine: engine)
|
|
62
|
+
expect(result[:count]).to eq(1)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe '#fertile_crossings' do
|
|
67
|
+
it 'returns fertile list' do
|
|
68
|
+
engine.begin_crossing(origin: :analytical, destination: :creative)
|
|
69
|
+
result = runner.fertile_crossings(engine: engine)
|
|
70
|
+
expect(result[:count]).to eq(1)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe '#liminal_status' do
|
|
75
|
+
it 'returns comprehensive status' do
|
|
76
|
+
result = runner.liminal_status(engine: engine)
|
|
77
|
+
expect(result[:success]).to be true
|
|
78
|
+
expect(result).to include(:total_crossings, :liminal_density)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_liminal'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Helpers
|
|
8
|
+
module Lex; end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module Logging
|
|
13
|
+
def self.method_missing(_, *) = nil
|
|
14
|
+
def self.respond_to_missing?(_, _ = false) = true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
RSpec.configure do |config|
|
|
19
|
+
config.expect_with :rspec do |expectations|
|
|
20
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
21
|
+
end
|
|
22
|
+
config.mock_with :rspec do |mocks|
|
|
23
|
+
mocks.verify_partial_doubles = true
|
|
24
|
+
end
|
|
25
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
26
|
+
config.order = :random
|
|
27
|
+
Kernel.srand config.seed
|
|
28
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-liminal
|
|
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 liminality — the creative in-between states during cognitive mode
|
|
27
|
+
transitions with ambiguity, dissolution, and crystallization
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".github/workflows/ci.yml"
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".rspec"
|
|
37
|
+
- ".rubocop.yml"
|
|
38
|
+
- CLAUDE.md
|
|
39
|
+
- Gemfile
|
|
40
|
+
- README.md
|
|
41
|
+
- lex-cognitive-liminal.gemspec
|
|
42
|
+
- lib/legion/extensions/cognitive_liminal.rb
|
|
43
|
+
- lib/legion/extensions/cognitive_liminal/client.rb
|
|
44
|
+
- lib/legion/extensions/cognitive_liminal/helpers/constants.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_liminal/helpers/liminal_engine.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_liminal/helpers/threshold_crossing.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_liminal/runners/cognitive_liminal.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_liminal/version.rb
|
|
49
|
+
- spec/legion/extensions/cognitive_liminal/client_spec.rb
|
|
50
|
+
- spec/legion/extensions/cognitive_liminal/helpers/liminal_engine_spec.rb
|
|
51
|
+
- spec/legion/extensions/cognitive_liminal/helpers/threshold_crossing_spec.rb
|
|
52
|
+
- spec/legion/extensions/cognitive_liminal/runners_spec.rb
|
|
53
|
+
- spec/legion/extensions/cognitive_liminal_spec.rb
|
|
54
|
+
- spec/spec_helper.rb
|
|
55
|
+
homepage: https://github.com/LegionIO/lex-cognitive-liminal
|
|
56
|
+
licenses:
|
|
57
|
+
- MIT
|
|
58
|
+
metadata:
|
|
59
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-liminal
|
|
60
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-liminal
|
|
61
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-liminal/blob/origin/README.md
|
|
62
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-liminal/blob/origin/CHANGELOG.md
|
|
63
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-liminal/issues
|
|
64
|
+
rubygems_mfa_required: 'true'
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '3.4'
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.6.9
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Liminal threshold states between cognitive modes for LegionIO agents
|
|
82
|
+
test_files: []
|