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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.rubocop.yml +64 -0
- data/CLAUDE.md +110 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +91 -0
- data/README.md +51 -0
- data/lex-cognitive-greenhouse.gemspec +30 -0
- data/lib/legion/extensions/cognitive_greenhouse/client.rb +20 -0
- data/lib/legion/extensions/cognitive_greenhouse/helpers/constants.rb +85 -0
- data/lib/legion/extensions/cognitive_greenhouse/helpers/greenhouse.rb +112 -0
- data/lib/legion/extensions/cognitive_greenhouse/helpers/greenhouse_engine.rb +117 -0
- data/lib/legion/extensions/cognitive_greenhouse/helpers/seedling.rb +151 -0
- data/lib/legion/extensions/cognitive_greenhouse/runners/cognitive_greenhouse.rb +83 -0
- data/lib/legion/extensions/cognitive_greenhouse/version.rb +9 -0
- data/lib/legion/extensions/cognitive_greenhouse.rb +18 -0
- metadata +78 -0
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
data/.rspec
ADDED
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,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: []
|