lex-cognitive-greenhouse 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: 9a71509bc945dd7588f4a2cf2a722b69d427ce360fffa357a83c2fae666358d1
4
+ data.tar.gz: 9309329468ab8978901ffa082d579e54df189e67ce749728d467ca64bacb5c50
5
+ SHA512:
6
+ metadata.gz: 9bb343b5f66b60cb866523398c573e64a64e2b6fa6f3593db4b1f1c6018d344a59d7a9b20349be6ff59bb6fabfcaed99631e462e2e3331e7afebc966df389728
7
+ data.tar.gz: 7de1e9b47418c1f91c945aec904495c5df91155fe885221fbe365493e0669ab91590005699c7464e853f0d06a3388f3bf2ca22a2f38a5c34ddeb3c864d6346fa
@@ -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,64 @@
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: 300
24
+
25
+ Metrics/BlockLength:
26
+ Max: 40
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+ Metrics/AbcSize:
31
+ Max: 25
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Max: 15
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Max: 17
38
+
39
+ Metrics/ParameterLists:
40
+ Max: 8
41
+ MaxOptionalParameters: 8
42
+
43
+ Style/Documentation:
44
+ Enabled: false
45
+
46
+ Style/SymbolArray:
47
+ Enabled: true
48
+
49
+ Style/FrozenStringLiteralComment:
50
+ Enabled: true
51
+ EnforcedStyle: always
52
+
53
+ Style/OneClassPerFile:
54
+ Exclude:
55
+ - 'spec/spec_helper.rb'
56
+
57
+ Naming/FileName:
58
+ Enabled: false
59
+
60
+ Naming/PredicateMethod:
61
+ Enabled: false
62
+
63
+ Naming/PredicatePrefix:
64
+ Enabled: false
data/CLAUDE.md ADDED
@@ -0,0 +1,110 @@
1
+ # lex-cognitive-greenhouse
2
+
3
+ **Level 3 Leaf Documentation**
4
+ - **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
5
+ - **Gem**: `lex-cognitive-greenhouse`
6
+
7
+ ## Purpose
8
+
9
+ Models a controlled cognitive growth environment with seasonal cycles and environmental conditions. Unlike the open-air garden, the greenhouse maintains explicit environmental variables (temperature, humidity, light) and advances through four seasons that alter growth rates. Plants grow only when environment quality meets minimum thresholds. Season transitions automatically adjust environmental conditions. Plants that reach the harvestable stage (`:bloom`) can be harvested, removing them from the active plant set. The engine tracks quality scores and growth stage distribution across plants.
10
+
11
+ ## Gem Info
12
+
13
+ | Field | Value |
14
+ |---|---|
15
+ | Gem name | `lex-cognitive-greenhouse` |
16
+ | Version | `0.1.0` |
17
+ | Namespace | `Legion::Extensions::CognitiveGreenhouse` |
18
+ | Ruby | `>= 3.4` |
19
+ | License | MIT |
20
+ | GitHub | https://github.com/LegionIO/lex-cognitive-greenhouse |
21
+
22
+ ## File Structure
23
+
24
+ ```
25
+ lib/legion/extensions/cognitive_greenhouse/
26
+ cognitive_greenhouse.rb # Top-level require
27
+ version.rb # VERSION = '0.1.0'
28
+ client.rb # Client class
29
+ helpers/
30
+ constants.rb # Seasons, stages, plant types, rates, ideal conditions, labels
31
+ plant.rb # Plant value object (with quality tracking)
32
+ greenhouse_engine.rb # Engine: plants, environment, seasons, growth, harvest
33
+ runners/
34
+ cognitive_greenhouse.rb # Runner module (extend self)
35
+ ```
36
+
37
+ ## Key Constants
38
+
39
+ | Constant | Value | Meaning |
40
+ |---|---|---|
41
+ | `SEASONS` | array | `[:spring, :summer, :autumn, :winter]` |
42
+ | `GROWTH_STAGES` | array | `[:seed, :sprout, :vegetative, :budding, :bloom, :dormant]` |
43
+ | `PLANT_TYPES` | array | `[:concept, :theory, :insight, :belief, :intention, :memory, :intuition, :analogy]` |
44
+ | `MAX_PLANTS` | 50 | Plant cap |
45
+ | `GROWTH_RATE` | per-season hash | `:spring => 0.08`, `:summer => 0.10`, `:autumn => 0.05`, `:winter => 0.01` |
46
+ | `IDEAL_TEMPERATURE` | 0.65 | Optimal temperature for growth quality |
47
+ | `IDEAL_HUMIDITY` | 0.7 | Optimal humidity |
48
+ | `IDEAL_LIGHT` | 0.75 | Optimal light level |
49
+ | `CONDITION_LABELS` | nested hash | Labels for temperature, humidity, light levels |
50
+ | `CONDITION_WEIGHTS` | hash | `temperature: 0.4, humidity: 0.3, light: 0.3` |
51
+ | `STAGE_ADVANCE_THRESHOLD` | 0.65 | Minimum quality to advance to next stage |
52
+ | `MIN_QUALITY_FOR_GROWTH` | 0.40 | Minimum quality required for any growth |
53
+ | `HARVESTABLE_STAGE` | `:bloom` | Stage at which plants can be harvested |
54
+
55
+ ## Helpers
56
+
57
+ ### `Plant`
58
+
59
+ A cognitive idea growing under controlled conditions.
60
+
61
+ - `initialize(plant_type:, domain:, content:, quality: 0.5, plant_id: nil)`
62
+ - `grow!(rate, quality)` — advances growth; stage advances when growth crosses threshold and quality >= `STAGE_ADVANCE_THRESHOLD`
63
+ - `harvestable?` — stage == `:bloom`
64
+ - `dormant?`
65
+ - `growth_label`, `quality_label`
66
+ - `to_h`
67
+
68
+ ### `GreenhouseEngine`
69
+
70
+ - `create_plant(plant_type:, domain:, content:)` — returns `{ created:, plant_id:, plant: }` or capacity error
71
+ - `plant_in_greenhouse(plant_id:)` — marks plant as active in greenhouse (separate from creation)
72
+ - `grow_all!` — applies season's growth rate to all qualifying plants; skips plants where environment quality < `MIN_QUALITY_FOR_GROWTH`
73
+ - `adjust_environment(temperature: nil, humidity: nil, light: nil)` — updates environmental variables; recalculates overall quality
74
+ - `cycle_season` — advances to next season in cycle; auto-adjusts temperature/humidity/light to seasonal defaults
75
+ - `harvest(plant_id:)` — removes harvestable plant; returns plant data
76
+ - `greenhouse_report` — full stats including season, environment quality, stage distribution
77
+
78
+ ## Runners
79
+
80
+ **Module**: `Legion::Extensions::CognitiveGreenhouse::Runners::CognitiveGreenhouse`
81
+
82
+ Uses `extend self` pattern.
83
+
84
+ | Method | Key Args | Returns |
85
+ |---|---|---|
86
+ | `plant_idea` | `plant_type:`, `domain:`, `content:` | `{ success:, plant_id:, plant: }` |
87
+ | `tend_greenhouse` | — | `{ success:, grown: }` — runs grow_all! |
88
+ | `adjust_environment` | `temperature: nil`, `humidity: nil`, `light: nil` | `{ success:, environment: }` |
89
+ | `advance_season` | — | `{ success:, season:, environment: }` |
90
+ | `harvest_ideas` | — | `{ success:, harvested: [...] }` |
91
+ | `greenhouse_status` | — | `{ success:, report: }` |
92
+
93
+ Private: `greenhouse(engine)` — memoized `GreenhouseEngine`. Logs via `log_debug` helper.
94
+
95
+ ## Integration Points
96
+
97
+ - **`lex-cognitive-garden`**: Garden is open-air and unbounded; greenhouse is controlled and capacity-capped (50 plants). Greenhouse is suitable for high-value concepts requiring precise conditions; garden for broader cultivation.
98
+ - **`lex-cognitive-genesis`**: Harvested bloom-stage plants are prime candidates for genesis seed input. The greenhouse accelerates concept maturation before genesis processing.
99
+ - **`lex-memory`**: Harvested concepts should be stored as semantic traces in lex-memory immediately after harvest.
100
+
101
+ ## Development Notes
102
+
103
+ - `grow_all!` applies the current season's growth rate but only when environment quality exceeds `MIN_QUALITY_FOR_GROWTH` (0.40). In poor conditions (e.g., winter with low quality), plants do not grow.
104
+ - `cycle_season` wraps around after `:winter` back to `:spring`. The auto-adjusted environmental values reflect the season's characteristic conditions.
105
+ - `harvest` is destructive — it removes the plant from the engine's tracking. The returned hash is the only record after harvest.
106
+ - In-memory only.
107
+
108
+ ---
109
+
110
+ **Maintained By**: Matthew Iverson (@Esity)
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop', '~> 1.75', require: false
11
+ gem 'rubocop-rspec', require: false
12
+ gem 'simplecov'
13
+ end
14
+
15
+ gem 'legion-gaia', path: '../../legion-gaia'
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lex-cognitive-greenhouse (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
+ docile (1.4.1)
15
+ json (2.19.1)
16
+ json-schema (6.2.0)
17
+ addressable (~> 2.8)
18
+ bigdecimal (>= 3.1, < 5)
19
+ language_server-protocol (3.17.0.5)
20
+ lint_roller (1.1.0)
21
+ mcp (0.8.0)
22
+ json-schema (>= 4.1)
23
+ parallel (1.27.0)
24
+ parser (3.3.10.2)
25
+ ast (~> 2.4.1)
26
+ racc
27
+ prism (1.9.0)
28
+ public_suffix (7.0.5)
29
+ racc (1.8.1)
30
+ rainbow (3.1.1)
31
+ rake (13.3.1)
32
+ regexp_parser (2.11.3)
33
+ rspec (3.13.2)
34
+ rspec-core (~> 3.13.0)
35
+ rspec-expectations (~> 3.13.0)
36
+ rspec-mocks (~> 3.13.0)
37
+ rspec-core (3.13.6)
38
+ rspec-support (~> 3.13.0)
39
+ rspec-expectations (3.13.5)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.13.0)
42
+ rspec-mocks (3.13.8)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.13.0)
45
+ rspec-support (3.13.7)
46
+ rspec_junit_formatter (0.6.0)
47
+ rspec-core (>= 2, < 4, != 2.12.0)
48
+ rubocop (1.85.1)
49
+ json (~> 2.3)
50
+ language_server-protocol (~> 3.17.0.2)
51
+ lint_roller (~> 1.1.0)
52
+ mcp (~> 0.6)
53
+ parallel (~> 1.10)
54
+ parser (>= 3.3.0.2)
55
+ rainbow (>= 2.2.2, < 4.0)
56
+ regexp_parser (>= 2.9.3, < 3.0)
57
+ rubocop-ast (>= 1.49.0, < 2.0)
58
+ ruby-progressbar (~> 1.7)
59
+ unicode-display_width (>= 2.4.0, < 4.0)
60
+ rubocop-ast (1.49.1)
61
+ parser (>= 3.3.7.2)
62
+ prism (~> 1.7)
63
+ rubocop-rspec (3.9.0)
64
+ lint_roller (~> 1.1)
65
+ rubocop (~> 1.81)
66
+ ruby-progressbar (1.13.0)
67
+ simplecov (0.22.0)
68
+ docile (~> 1.1)
69
+ simplecov-html (~> 0.11)
70
+ simplecov_json_formatter (~> 0.1)
71
+ simplecov-html (0.13.2)
72
+ simplecov_json_formatter (0.1.4)
73
+ unicode-display_width (3.2.0)
74
+ unicode-emoji (~> 4.1)
75
+ unicode-emoji (4.2.0)
76
+
77
+ PLATFORMS
78
+ arm64-darwin-25
79
+ ruby
80
+
81
+ DEPENDENCIES
82
+ lex-cognitive-greenhouse!
83
+ rake
84
+ rspec (~> 3.13)
85
+ rspec_junit_formatter
86
+ rubocop (~> 1.75)
87
+ rubocop-rspec
88
+ simplecov
89
+
90
+ BUNDLED WITH
91
+ 2.6.9
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # lex-cognitive-greenhouse
2
+
3
+ Controlled cognitive growth environment for brain-modeled agentic AI in the LegionIO ecosystem.
4
+
5
+ ## What It Does
6
+
7
+ Models a seasonal, environmentally controlled space for maturing cognitive ideas. Plants (concepts, theories, insights, beliefs) grow at rates determined by the current season (spring through winter) only when environmental quality (temperature, humidity, light) meets minimum thresholds. Environment can be manually adjusted or automatically shifted by advancing seasons. Plants that reach the bloom stage can be harvested, removing them from the active set. The greenhouse provides finer control than an open garden — limited capacity, explicit conditions, and seasonal cycles.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ require 'legion/extensions/cognitive_greenhouse'
13
+
14
+ client = Legion::Extensions::CognitiveGreenhouse::Client.new
15
+
16
+ # Plant an idea in the greenhouse
17
+ result = client.plant_idea(plant_type: :insight, domain: :systems, content: 'eventual consistency trade-offs')
18
+ plant_id = result[:plant_id]
19
+
20
+ # Adjust environment for optimal growth
21
+ client.adjust_environment(temperature: 0.65, humidity: 0.70, light: 0.75)
22
+ # => { success: true, environment: { quality: 0.69, season: :spring, ... } }
23
+
24
+ # Tend all plants (applies seasonal growth rate)
25
+ client.tend_greenhouse
26
+ # => { success: true, grown: 1 }
27
+
28
+ # Advance the season
29
+ client.advance_season
30
+ # => { success: true, season: :summer, environment: { ... } }
31
+
32
+ # Harvest bloomed plants
33
+ client.harvest_ideas
34
+ # => { success: true, harvested: [{ plant_id: "...", type: :insight, ... }] }
35
+
36
+ # Full status
37
+ client.greenhouse_status
38
+ # => { success: true, report: { plant_count: 0, season: :summer, ... } }
39
+ ```
40
+
41
+ ## Development
42
+
43
+ ```bash
44
+ bundle install
45
+ bundle exec rspec
46
+ bundle exec rubocop
47
+ ```
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/cognitive_greenhouse/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-cognitive-greenhouse'
7
+ spec.version = Legion::Extensions::CognitiveGreenhouse::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Cognitive Greenhouse'
12
+ spec.description = 'Protected environment for growing ideas from seeds to mature concepts — ' \
13
+ 'temperature, humidity, and light as nurturing conditions, with seasonal cycles'
14
+ spec.homepage = 'https://github.com/LegionIO/lex-cognitive-greenhouse'
15
+ spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 3.4'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-greenhouse'
20
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-greenhouse'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-greenhouse'
22
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-greenhouse/issues'
23
+ spec.metadata['rubygems_mfa_required'] = 'true'
24
+
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.require_paths = ['lib']
29
+ spec.add_development_dependency 'legion-gaia'
30
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ class Client
7
+ include Runners::CognitiveGreenhouse
8
+
9
+ def initialize(greenhouse: nil, **)
10
+ @greenhouse = greenhouse || Helpers::Greenhouse.new
11
+ @default_engine = Helpers::GreenhouseEngine.new(greenhouse: @greenhouse)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :greenhouse
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ module Helpers
7
+ module Constants
8
+ SEASONS = %i[spring summer autumn winter].freeze
9
+
10
+ GROWTH_STAGES = %i[seed sprout sapling mature bloom].freeze
11
+
12
+ PLANT_TYPES = %i[
13
+ hypothesis
14
+ insight
15
+ question
16
+ pattern
17
+ analogy
18
+ metaphor
19
+ theory
20
+ observation
21
+ ].freeze
22
+
23
+ MAX_PLANTS = 50
24
+
25
+ # Growth rate multiplier per season (spring blooms, winter slows)
26
+ GROWTH_RATE = {
27
+ spring: 1.4,
28
+ summer: 1.2,
29
+ autumn: 0.9,
30
+ winter: 0.5
31
+ }.freeze
32
+
33
+ # Ideal ranges per condition [min_ideal, max_ideal]
34
+ IDEAL_TEMPERATURE = [18.0, 26.0].freeze
35
+ IDEAL_HUMIDITY = [0.55, 0.80].freeze
36
+ IDEAL_LIGHT = [0.60, 0.95].freeze
37
+
38
+ CONDITION_LABELS = {
39
+ temperature: {
40
+ (0.0...10.0) => :frozen,
41
+ (10.0...18.0) => :cool,
42
+ (18.0...26.0) => :optimal,
43
+ (26.0...34.0) => :warm,
44
+ (34.0..50.0) => :hot
45
+ },
46
+ humidity: {
47
+ (0.0...0.30) => :arid,
48
+ (0.30...0.55) => :dry,
49
+ (0.55...0.80) => :optimal,
50
+ (0.80...0.90) => :humid,
51
+ (0.90..1.0) => :saturated
52
+ },
53
+ light: {
54
+ (0.0...0.20) => :dark,
55
+ (0.20...0.40) => :dim,
56
+ (0.40...0.60) => :moderate,
57
+ (0.60...0.95) => :optimal,
58
+ (0.95..1.0) => :intense
59
+ }
60
+ }.freeze
61
+
62
+ # How much each condition contributes to environment quality
63
+ CONDITION_WEIGHTS = {
64
+ temperature: 0.35,
65
+ humidity: 0.30,
66
+ light: 0.35
67
+ }.freeze
68
+
69
+ # Health change per grow! or wilt! call
70
+ GROW_HEALTH_BOOST = 0.08
71
+ WILT_HEALTH_DRAIN = 0.12
72
+
73
+ # Stage advance threshold (health must be above this)
74
+ STAGE_ADVANCE_THRESHOLD = 0.65
75
+
76
+ # Minimum environment quality to allow growth
77
+ MIN_QUALITY_FOR_GROWTH = 0.40
78
+
79
+ # Harvest only fully bloomed plants
80
+ HARVESTABLE_STAGE = :bloom
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ module Helpers
7
+ class Greenhouse
8
+ include Constants
9
+
10
+ SEASON_CYCLE = Constants::SEASONS.freeze
11
+
12
+ attr_reader :temperature, :humidity, :light_level, :season, :plants, :created_at, :cycles_completed
13
+
14
+ def initialize(temperature: 22.0, humidity: 0.65, light_level: 0.75, season: :spring)
15
+ raise ArgumentError, "unknown season: #{season.inspect}" unless Constants::SEASONS.include?(season)
16
+
17
+ @temperature = temperature.clamp(0.0, 50.0)
18
+ @humidity = humidity.clamp(0.0, 1.0)
19
+ @light_level = light_level.clamp(0.0, 1.0)
20
+ @season = season
21
+ @plants = []
22
+ @created_at = Time.now.utc
23
+ @cycles_completed = 0
24
+ end
25
+
26
+ def plant!(seedling)
27
+ raise ArgumentError, 'seedling must be a Seedling instance' unless seedling.is_a?(Seedling)
28
+
29
+ return { planted: false, reason: :greenhouse_full, capacity: Constants::MAX_PLANTS } if @plants.size >= Constants::MAX_PLANTS
30
+
31
+ @plants << seedling
32
+ { planted: true, id: seedling.id, total_plants: @plants.size }
33
+ end
34
+
35
+ def adjust_conditions(temperature: nil, humidity: nil, light_level: nil)
36
+ @temperature = temperature.clamp(0.0, 50.0) if temperature
37
+ @humidity = humidity.clamp(0.0, 1.0) if humidity
38
+ @light_level = light_level.clamp(0.0, 1.0) if light_level
39
+
40
+ {
41
+ temperature: @temperature,
42
+ humidity: @humidity,
43
+ light_level: @light_level,
44
+ quality: environment_quality.round(10)
45
+ }
46
+ end
47
+
48
+ def cycle_season!
49
+ current_index = SEASON_CYCLE.index(@season)
50
+ @season = SEASON_CYCLE[(current_index + 1) % SEASON_CYCLE.size]
51
+ @cycles_completed += 1
52
+ { season: @season, cycle: @cycles_completed }
53
+ end
54
+
55
+ def environment_quality
56
+ temp_score = score_condition(@temperature, Constants::IDEAL_TEMPERATURE[0], Constants::IDEAL_TEMPERATURE[1], 0.0, 50.0)
57
+ humidity_score = score_condition(@humidity, Constants::IDEAL_HUMIDITY[0], Constants::IDEAL_HUMIDITY[1], 0.0, 1.0)
58
+ light_score = score_condition(@light_level, Constants::IDEAL_LIGHT[0], Constants::IDEAL_LIGHT[1], 0.0, 1.0)
59
+
60
+ (temp_score * Constants::CONDITION_WEIGHTS[:temperature]) +
61
+ (humidity_score * Constants::CONDITION_WEIGHTS[:humidity]) +
62
+ (light_score * Constants::CONDITION_WEIGHTS[:light])
63
+ end
64
+
65
+ def harvest_blooms
66
+ bloomed, remaining = @plants.partition { |p| p.growth_stage == Constants::HARVESTABLE_STAGE }
67
+ @plants = remaining
68
+ bloomed.map(&:to_h)
69
+ end
70
+
71
+ def condition_snapshot
72
+ {
73
+ temperature: @temperature,
74
+ humidity: @humidity,
75
+ light_level: @light_level,
76
+ season: @season,
77
+ quality: environment_quality.round(10),
78
+ temp_label: label_for(:temperature, @temperature),
79
+ humidity_label: label_for(:humidity, @humidity),
80
+ light_label: label_for(:light, @light_level)
81
+ }
82
+ end
83
+
84
+ def active_plants
85
+ @plants.reject { |p| p.growth_stage == :bloom }
86
+ end
87
+
88
+ private
89
+
90
+ def score_condition(value, ideal_min, ideal_max, abs_min, abs_max)
91
+ return 1.0 if value.between?(ideal_min, ideal_max)
92
+
93
+ if value < ideal_min
94
+ distance = ideal_min - value
95
+ 1.0 - (distance / (ideal_min - abs_min + 0.001)).clamp(0.0, 1.0)
96
+ else
97
+ distance = value - ideal_max
98
+ 1.0 - (distance / (abs_max - ideal_max + 0.001)).clamp(0.0, 1.0)
99
+ end
100
+ end
101
+
102
+ def label_for(condition, value)
103
+ Constants::CONDITION_LABELS[condition].each do |range, label|
104
+ return label if range.cover?(value)
105
+ end
106
+ :unknown
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ module Helpers
7
+ class GreenhouseEngine
8
+ include Constants
9
+
10
+ attr_reader :greenhouse
11
+
12
+ def initialize(greenhouse: nil)
13
+ @greenhouse = greenhouse || Greenhouse.new
14
+ end
15
+
16
+ def create_plant(plant_type:, domain:, content:, growth_stage: :seed, health: 0.5)
17
+ raise ArgumentError, "unknown plant_type: #{plant_type.inspect}" unless Constants::PLANT_TYPES.include?(plant_type)
18
+
19
+ Seedling.new(
20
+ plant_type: plant_type,
21
+ domain: domain,
22
+ content: content,
23
+ growth_stage: growth_stage,
24
+ health: health
25
+ )
26
+ end
27
+
28
+ def plant_in_greenhouse(plant_type:, domain:, content:, growth_stage: :seed, health: 0.5, **)
29
+ seedling = create_plant(
30
+ plant_type: plant_type,
31
+ domain: domain,
32
+ content: content,
33
+ growth_stage: growth_stage,
34
+ health: health
35
+ )
36
+ result = @greenhouse.plant!(seedling)
37
+ result.merge(seedling: seedling.to_h)
38
+ end
39
+
40
+ def grow_all!
41
+ conditions = current_conditions
42
+ rate = Constants::GROWTH_RATE.fetch(@greenhouse.season, 1.0)
43
+ grew_count = 0
44
+ results = []
45
+
46
+ @greenhouse.plants.each do |plant|
47
+ result = plant.grow!(conditions.merge(rate: rate))
48
+ results << result.merge(id: plant.id)
49
+ grew_count += 1 if result[:grew]
50
+ end
51
+
52
+ { grew: grew_count, total: @greenhouse.plants.size, results: results, season: @greenhouse.season }
53
+ end
54
+
55
+ def adjust_environment(temperature: nil, humidity: nil, light_level: nil, **)
56
+ @greenhouse.adjust_conditions(
57
+ temperature: temperature,
58
+ humidity: humidity,
59
+ light_level: light_level
60
+ )
61
+ end
62
+
63
+ def cycle_season
64
+ result = @greenhouse.cycle_season!
65
+
66
+ # Apply seasonal light/temp shifts automatically
67
+ case result[:season]
68
+ when :spring
69
+ @greenhouse.adjust_conditions(temperature: 20.0, humidity: 0.70, light_level: 0.75)
70
+ when :summer
71
+ @greenhouse.adjust_conditions(temperature: 26.0, humidity: 0.60, light_level: 0.90)
72
+ when :autumn
73
+ @greenhouse.adjust_conditions(temperature: 16.0, humidity: 0.65, light_level: 0.65)
74
+ when :winter
75
+ @greenhouse.adjust_conditions(temperature: 10.0, humidity: 0.55, light_level: 0.45)
76
+ end
77
+
78
+ result.merge(conditions: @greenhouse.condition_snapshot)
79
+ end
80
+
81
+ def harvest
82
+ blooms = @greenhouse.harvest_blooms
83
+ { harvested: blooms.size, blooms: blooms }
84
+ end
85
+
86
+ def greenhouse_report
87
+ plants_by_stage = Constants::GROWTH_STAGES.to_h do |stage|
88
+ [stage, @greenhouse.plants.count { |p| p.growth_stage == stage }]
89
+ end
90
+
91
+ healthy_count = @greenhouse.plants.count(&:healthy?)
92
+ wilting_count = @greenhouse.plants.count(&:wilting?)
93
+
94
+ {
95
+ total_plants: @greenhouse.plants.size,
96
+ plants_by_stage: plants_by_stage,
97
+ healthy: healthy_count,
98
+ wilting: wilting_count,
99
+ conditions: @greenhouse.condition_snapshot,
100
+ cycles: @greenhouse.cycles_completed
101
+ }
102
+ end
103
+
104
+ private
105
+
106
+ def current_conditions
107
+ {
108
+ temperature: @greenhouse.temperature,
109
+ humidity: @greenhouse.humidity,
110
+ light_level: @greenhouse.light_level
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveGreenhouse
8
+ module Helpers
9
+ class Seedling
10
+ include Constants
11
+
12
+ STAGE_INDEX = Constants::GROWTH_STAGES.each_with_index.to_h.freeze
13
+
14
+ attr_reader :id, :plant_type, :domain, :content, :growth_stage, :health,
15
+ :root_depth, :planted_at, :last_tended
16
+
17
+ def initialize(plant_type:, domain:, content:, growth_stage: :seed, health: 0.5, root_depth: 0.0)
18
+ raise ArgumentError, "unknown plant_type: #{plant_type.inspect}" unless Constants::PLANT_TYPES.include?(plant_type)
19
+ raise ArgumentError, "unknown growth_stage: #{growth_stage.inspect}" unless Constants::GROWTH_STAGES.include?(growth_stage)
20
+
21
+ @id = SecureRandom.uuid
22
+ @plant_type = plant_type
23
+ @domain = domain
24
+ @content = content
25
+ @growth_stage = growth_stage
26
+ @health = health.clamp(0.0, 1.0)
27
+ @root_depth = root_depth.clamp(0.0, 1.0)
28
+ @planted_at = Time.now.utc
29
+ @last_tended = Time.now.utc
30
+ end
31
+
32
+ def grow!(conditions = {})
33
+ quality = environment_quality(conditions)
34
+ return { grew: false, reason: :poor_environment, quality: quality.round(10) } if quality < Constants::MIN_QUALITY_FOR_GROWTH
35
+
36
+ boost = (Constants::GROW_HEALTH_BOOST * quality).round(10)
37
+ @health = (@health + boost).clamp(0.0, 1.0)
38
+ @root_depth = (@root_depth + (boost * 0.5)).clamp(0.0, 1.0)
39
+ @last_tended = Time.now.utc
40
+
41
+ advanced = maybe_advance_stage!
42
+ {
43
+ grew: true,
44
+ health: @health.round(10),
45
+ root_depth: @root_depth.round(10),
46
+ stage: @growth_stage,
47
+ stage_change: advanced,
48
+ quality: quality.round(10)
49
+ }
50
+ end
51
+
52
+ def wilt!(stress = 0.5)
53
+ drain = (Constants::WILT_HEALTH_DRAIN * stress.clamp(0.0, 1.0)).round(10)
54
+ @health = (@health - drain).clamp(0.0, 1.0)
55
+ @last_tended = Time.now.utc
56
+ { wilted: true, health: @health.round(10), stress: stress.clamp(0.0, 1.0).round(10) }
57
+ end
58
+
59
+ def bloom!
60
+ return { bloomed: false, reason: :not_mature } unless mature?
61
+
62
+ @growth_stage = :bloom
63
+ @health = (@health + 0.10).clamp(0.0, 1.0)
64
+ @last_tended = Time.now.utc
65
+ { bloomed: true, health: @health.round(10), id: @id }
66
+ end
67
+
68
+ def healthy?
69
+ @health >= 0.6
70
+ end
71
+
72
+ def wilting?
73
+ @health < 0.3
74
+ end
75
+
76
+ def mature?
77
+ @growth_stage == :mature
78
+ end
79
+
80
+ def to_h
81
+ {
82
+ id: @id,
83
+ plant_type: @plant_type,
84
+ domain: @domain,
85
+ content: @content,
86
+ growth_stage: @growth_stage,
87
+ health: @health.round(10),
88
+ root_depth: @root_depth.round(10),
89
+ planted_at: @planted_at,
90
+ last_tended: @last_tended
91
+ }
92
+ end
93
+
94
+ private
95
+
96
+ def environment_quality(conditions)
97
+ return 0.5 if conditions.empty?
98
+
99
+ temp_score = score_temperature(conditions.fetch(:temperature, 22.0))
100
+ humidity_score = score_humidity(conditions.fetch(:humidity, 0.65))
101
+ light_score = score_light(conditions.fetch(:light_level, 0.75))
102
+
103
+ (temp_score * Constants::CONDITION_WEIGHTS[:temperature]) +
104
+ (humidity_score * Constants::CONDITION_WEIGHTS[:humidity]) +
105
+ (light_score * Constants::CONDITION_WEIGHTS[:light])
106
+ end
107
+
108
+ def score_temperature(value)
109
+ score_in_range(value, Constants::IDEAL_TEMPERATURE[0], Constants::IDEAL_TEMPERATURE[1], 0.0, 50.0)
110
+ end
111
+
112
+ def score_humidity(value)
113
+ score_in_range(value, Constants::IDEAL_HUMIDITY[0], Constants::IDEAL_HUMIDITY[1], 0.0, 1.0)
114
+ end
115
+
116
+ def score_light(value)
117
+ score_in_range(value, Constants::IDEAL_LIGHT[0], Constants::IDEAL_LIGHT[1], 0.0, 1.0)
118
+ end
119
+
120
+ def score_in_range(value, ideal_min, ideal_max, abs_min, abs_max)
121
+ return 1.0 if value.between?(ideal_min, ideal_max)
122
+
123
+ range_span = (abs_max - abs_min).to_f
124
+ return 0.0 if range_span.zero?
125
+
126
+ if value < ideal_min
127
+ distance = ideal_min - value
128
+ 1.0 - (distance / (ideal_min - abs_min + 0.001)).clamp(0.0, 1.0)
129
+ else
130
+ distance = value - ideal_max
131
+ 1.0 - (distance / (abs_max - ideal_max + 0.001)).clamp(0.0, 1.0)
132
+ end
133
+ end
134
+
135
+ def maybe_advance_stage!
136
+ return false if @growth_stage == :bloom
137
+ return false if @health < Constants::STAGE_ADVANCE_THRESHOLD
138
+
139
+ current_index = STAGE_INDEX[@growth_stage]
140
+ next_stage = Constants::GROWTH_STAGES[current_index + 1]
141
+ return false unless next_stage
142
+ return false if next_stage == :bloom # bloom! is explicit
143
+
144
+ @growth_stage = next_stage
145
+ true
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ module Runners
7
+ module CognitiveGreenhouse
8
+ extend self
9
+
10
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
11
+
12
+ def plant_idea(plant_type:, domain:, content:, growth_stage: :seed, health: 0.5, engine: nil, **)
13
+ raise ArgumentError, "unknown plant_type: #{plant_type.inspect}" unless Helpers::Constants::PLANT_TYPES.include?(plant_type)
14
+
15
+ e = engine || default_engine
16
+ result = e.plant_in_greenhouse(
17
+ plant_type: plant_type,
18
+ domain: domain,
19
+ content: content,
20
+ growth_stage: growth_stage,
21
+ health: health
22
+ )
23
+
24
+ Legion::Logging.debug "[greenhouse] planted #{plant_type} in #{domain}: #{result[:seedling][:id][0..7]}" if defined?(Legion::Logging)
25
+ { success: result[:planted], result: result }
26
+ rescue ArgumentError => e
27
+ { success: false, error: e.message }
28
+ end
29
+
30
+ def tend_greenhouse(engine: nil, **)
31
+ e = engine || default_engine
32
+ result = e.grow_all!
33
+ Legion::Logging.debug "[greenhouse] grow_all: grew=#{result[:grew]} total=#{result[:total]} season=#{result[:season]}" if defined?(Legion::Logging)
34
+ { success: true, result: result }
35
+ rescue ArgumentError => e
36
+ { success: false, error: e.message }
37
+ end
38
+
39
+ def adjust_environment(temperature: nil, humidity: nil, light_level: nil, engine: nil, **)
40
+ e = engine || default_engine
41
+ result = e.adjust_environment(temperature: temperature, humidity: humidity, light_level: light_level)
42
+ Legion::Logging.debug "[greenhouse] conditions adjusted: quality=#{result[:quality]}" if defined?(Legion::Logging)
43
+ { success: true, result: result }
44
+ rescue ArgumentError => e
45
+ { success: false, error: e.message }
46
+ end
47
+
48
+ def advance_season(engine: nil, **)
49
+ e = engine || default_engine
50
+ result = e.cycle_season
51
+ Legion::Logging.debug "[greenhouse] season cycled to #{result[:season]}" if defined?(Legion::Logging)
52
+ { success: true, result: result }
53
+ rescue ArgumentError => e
54
+ { success: false, error: e.message }
55
+ end
56
+
57
+ def harvest_ideas(engine: nil, **)
58
+ e = engine || default_engine
59
+ result = e.harvest
60
+ Legion::Logging.debug "[greenhouse] harvested #{result[:harvested]} blooms" if defined?(Legion::Logging)
61
+ { success: true, result: result }
62
+ rescue ArgumentError => e
63
+ { success: false, error: e.message }
64
+ end
65
+
66
+ def greenhouse_status(engine: nil, **)
67
+ e = engine || default_engine
68
+ result = e.greenhouse_report
69
+ { success: true, result: result }
70
+ rescue ArgumentError => e
71
+ { success: false, error: e.message }
72
+ end
73
+
74
+ private
75
+
76
+ def default_engine
77
+ @default_engine ||= Helpers::GreenhouseEngine.new
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveGreenhouse
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative 'cognitive_greenhouse/version'
5
+ require_relative 'cognitive_greenhouse/helpers/constants'
6
+ require_relative 'cognitive_greenhouse/helpers/seedling'
7
+ require_relative 'cognitive_greenhouse/helpers/greenhouse'
8
+ require_relative 'cognitive_greenhouse/helpers/greenhouse_engine'
9
+ require_relative 'cognitive_greenhouse/runners/cognitive_greenhouse'
10
+ require_relative 'cognitive_greenhouse/client'
11
+
12
+ module Legion
13
+ module Extensions
14
+ module CognitiveGreenhouse
15
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-cognitive-greenhouse
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: Protected environment for growing ideas from seeds to mature concepts
27
+ — temperature, humidity, and light as nurturing conditions, with seasonal cycles
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
+ - Gemfile.lock
41
+ - README.md
42
+ - lex-cognitive-greenhouse.gemspec
43
+ - lib/legion/extensions/cognitive_greenhouse.rb
44
+ - lib/legion/extensions/cognitive_greenhouse/client.rb
45
+ - lib/legion/extensions/cognitive_greenhouse/helpers/constants.rb
46
+ - lib/legion/extensions/cognitive_greenhouse/helpers/greenhouse.rb
47
+ - lib/legion/extensions/cognitive_greenhouse/helpers/greenhouse_engine.rb
48
+ - lib/legion/extensions/cognitive_greenhouse/helpers/seedling.rb
49
+ - lib/legion/extensions/cognitive_greenhouse/runners/cognitive_greenhouse.rb
50
+ - lib/legion/extensions/cognitive_greenhouse/version.rb
51
+ homepage: https://github.com/LegionIO/lex-cognitive-greenhouse
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ homepage_uri: https://github.com/LegionIO/lex-cognitive-greenhouse
56
+ source_code_uri: https://github.com/LegionIO/lex-cognitive-greenhouse
57
+ documentation_uri: https://github.com/LegionIO/lex-cognitive-greenhouse
58
+ changelog_uri: https://github.com/LegionIO/lex-cognitive-greenhouse
59
+ bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-greenhouse/issues
60
+ rubygems_mfa_required: 'true'
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.6.9
76
+ specification_version: 4
77
+ summary: LEX Cognitive Greenhouse
78
+ test_files: []