lex-cognitive-momentum 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: 0e7b33e7708bc1bf42bd957b9743baad50481c23c13b75bbacf14e142f552bc2
4
+ data.tar.gz: 1982bd8b1f91415fd02d99663fb38f246f2b39f3ac5fea48c6a7d4d66103d7a0
5
+ SHA512:
6
+ metadata.gz: b011ae7562052c6772ae73d9200920610fb41817479ea45ac8df17d921b5e893072e09a4789adf876328bfcbd94f70a75bc1dbea4e677f6fbe7622351c96494d
7
+ data.tar.gz: 0d908b1f44a6a583e1ff9ecb7122c64b8c4ed68f8673b508bb709d45eac8782f265592f11555ca56eddeab4b51e9d88dee8359352d55179dc1863546bc3044e4
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # lex-cognitive-momentum
2
+
3
+ Physics-inspired idea momentum engine for LegionIO cognitive agents. Each idea has mass (entrenchment) and velocity (rate of change). Momentum = mass × velocity. Reinforcement builds momentum; challenges reduce it; friction decays velocity each cycle.
4
+
5
+ ## What It Does
6
+
7
+ - Five idea types: `belief`, `goal`, `hypothesis`, `plan`, `intuition`
8
+ - Reinforce ideas to increase mass and velocity
9
+ - Challenge ideas to penalize velocity and reduce mass
10
+ - Apply external forces via Newton's second law (F = ma)
11
+ - Friction decays velocity each tick cycle
12
+ - Prune ideas that reach zero momentum (at rest)
13
+ - Identify surging (positive velocity), reversing (negative velocity), and entrenched (high mass) ideas
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ # Create an idea
19
+ result = runner.create_cognitive_idea(
20
+ content: 'microservices will reduce coupling',
21
+ idea_type: :hypothesis, domain: :architecture,
22
+ mass: 0.5, velocity: 0.0
23
+ )
24
+ idea_id = result[:idea][:id]
25
+
26
+ # Reinforce it
27
+ runner.reinforce_cognitive_idea(idea_id: idea_id)
28
+ # => { success: true, idea: { mass: 0.55, velocity: 0.1, momentum: 0.055, ... } }
29
+
30
+ # Challenge it
31
+ runner.challenge_cognitive_idea(idea_id: idea_id)
32
+ # => { success: true, idea: { velocity: -0.05, ... } }
33
+
34
+ # Apply external force
35
+ runner.apply_cognitive_force(idea_id: idea_id, force: 0.3)
36
+
37
+ # Tick maintenance (friction + prune)
38
+ runner.update_cognitive_momentum
39
+
40
+ # Identify what's moving
41
+ runner.surging_ideas_report
42
+ runner.entrenched_ideas_report
43
+ ```
44
+
45
+ ## Development
46
+
47
+ ```bash
48
+ bundle install
49
+ bundle exec rspec
50
+ bundle exec rubocop
51
+ ```
52
+
53
+ ## License
54
+
55
+ MIT
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMomentum
6
+ module Helpers
7
+ class Client
8
+ include Runners::CognitiveMomentum
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMomentum
6
+ module Helpers
7
+ module Constants
8
+ # Idea categories
9
+ IDEA_TYPES = %i[belief goal hypothesis plan intuition].freeze
10
+
11
+ # Momentum = mass * velocity
12
+ # Mass = importance/entrenchment (resists change)
13
+ # Velocity = rate of change/spread
14
+
15
+ DEFAULT_MASS = 0.5
16
+ DEFAULT_VELOCITY = 0.0
17
+ MASS_FLOOR = 0.1
18
+ MASS_CEILING = 1.0
19
+ VELOCITY_FLOOR = -1.0
20
+ VELOCITY_CEILING = 1.0
21
+
22
+ # Friction reduces velocity each cycle
23
+ FRICTION_RATE = 0.05
24
+
25
+ # Force needed to change velocity (F = m * a, so a = F / m)
26
+ # Higher mass = more force needed to accelerate
27
+ MIN_FORCE = 0.01
28
+
29
+ # Reinforcement increases mass (entrenchment)
30
+ REINFORCE_MASS_BOOST = 0.05
31
+ REINFORCE_VELOCITY_BOOST = 0.1
32
+
33
+ # Challenge reduces velocity and slightly reduces mass
34
+ CHALLENGE_VELOCITY_PENALTY = 0.15
35
+ CHALLENGE_MASS_REDUCTION = 0.02
36
+
37
+ # Momentum labels
38
+ MOMENTUM_LABELS = {
39
+ (0.5..) => :surging,
40
+ (0.2...0.5) => :building,
41
+ (0.0...0.2) => :coasting,
42
+ (-0.2...0.0) => :slowing,
43
+ (..-0.2) => :reversing
44
+ }.freeze
45
+
46
+ # Inertia labels (based on mass)
47
+ INERTIA_LABELS = {
48
+ (0.8..) => :immovable,
49
+ (0.6...0.8) => :entrenched,
50
+ (0.4...0.6) => :moderate,
51
+ (0.2...0.4) => :flexible,
52
+ (..0.2) => :volatile
53
+ }.freeze
54
+
55
+ MAX_IDEAS = 200
56
+ MAX_HISTORY = 500
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveMomentum
8
+ module Helpers
9
+ class Idea
10
+ include Constants
11
+
12
+ attr_reader :id, :content, :idea_type, :domain, :mass, :velocity,
13
+ :reinforcement_count, :challenge_count, :created_at, :last_updated_at
14
+
15
+ def initialize(content:, idea_type:, domain:, mass: DEFAULT_MASS)
16
+ @id = SecureRandom.uuid
17
+ @content = content
18
+ @idea_type = idea_type
19
+ @domain = domain
20
+ @mass = mass.clamp(MASS_FLOOR, MASS_CEILING)
21
+ @velocity = DEFAULT_VELOCITY
22
+ @reinforcement_count = 0
23
+ @challenge_count = 0
24
+ @created_at = Time.now.utc
25
+ @last_updated_at = @created_at
26
+ end
27
+
28
+ def momentum
29
+ @mass * @velocity
30
+ end
31
+
32
+ def momentum_label
33
+ MOMENTUM_LABELS.find { |range, _| range.cover?(momentum) }&.last || :coasting
34
+ end
35
+
36
+ def inertia_label
37
+ INERTIA_LABELS.find { |range, _| range.cover?(@mass) }&.last || :moderate
38
+ end
39
+
40
+ def reinforce!
41
+ @reinforcement_count += 1
42
+ @mass = (@mass + REINFORCE_MASS_BOOST).clamp(MASS_FLOOR, MASS_CEILING)
43
+ @velocity = (@velocity + REINFORCE_VELOCITY_BOOST).clamp(VELOCITY_FLOOR, VELOCITY_CEILING)
44
+ @last_updated_at = Time.now.utc
45
+ end
46
+
47
+ def challenge!
48
+ @challenge_count += 1
49
+ @velocity = (@velocity - CHALLENGE_VELOCITY_PENALTY).clamp(VELOCITY_FLOOR, VELOCITY_CEILING)
50
+ @mass = (@mass - CHALLENGE_MASS_REDUCTION).clamp(MASS_FLOOR, MASS_CEILING)
51
+ @last_updated_at = Time.now.utc
52
+ end
53
+
54
+ def apply_force(force:)
55
+ acceleration = force / @mass
56
+ @velocity = (@velocity + acceleration).clamp(VELOCITY_FLOOR, VELOCITY_CEILING)
57
+ @last_updated_at = Time.now.utc
58
+ end
59
+
60
+ def apply_friction!
61
+ return if @velocity.zero?
62
+
63
+ @velocity = if @velocity.positive?
64
+ [(@velocity - FRICTION_RATE), 0.0].max
65
+ else
66
+ [(@velocity + FRICTION_RATE), 0.0].min
67
+ end
68
+ @last_updated_at = Time.now.utc
69
+ end
70
+
71
+ def surging?
72
+ momentum >= 0.5
73
+ end
74
+
75
+ def reversing?
76
+ momentum <= -0.2
77
+ end
78
+
79
+ def at_rest?
80
+ @velocity.abs < 0.01
81
+ end
82
+
83
+ def entrenched?
84
+ @mass >= 0.8
85
+ end
86
+
87
+ def to_h
88
+ {
89
+ id: @id,
90
+ content: @content,
91
+ idea_type: @idea_type,
92
+ domain: @domain,
93
+ mass: @mass,
94
+ velocity: @velocity,
95
+ momentum: momentum,
96
+ momentum_label: momentum_label,
97
+ inertia_label: inertia_label,
98
+ reinforcement_count: @reinforcement_count,
99
+ challenge_count: @challenge_count,
100
+ created_at: @created_at,
101
+ last_updated_at: @last_updated_at
102
+ }
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMomentum
6
+ module Helpers
7
+ class MomentumEngine
8
+ include Constants
9
+
10
+ attr_reader :history
11
+
12
+ def initialize
13
+ @ideas = {}
14
+ @history = []
15
+ end
16
+
17
+ def create_idea(content:, idea_type:, domain:, mass: DEFAULT_MASS)
18
+ evict_oldest if @ideas.size >= MAX_IDEAS
19
+
20
+ idea = Idea.new(content: content, idea_type: idea_type, domain: domain, mass: mass)
21
+ @ideas[idea.id] = idea
22
+ record_history(:created, idea.id)
23
+ idea
24
+ end
25
+
26
+ def reinforce_idea(idea_id:)
27
+ idea = @ideas[idea_id]
28
+ return { success: false, reason: :not_found } unless idea
29
+
30
+ idea.reinforce!
31
+ record_history(:reinforced, idea_id)
32
+ { success: true, momentum: idea.momentum, mass: idea.mass, velocity: idea.velocity }
33
+ end
34
+
35
+ def challenge_idea(idea_id:)
36
+ idea = @ideas[idea_id]
37
+ return { success: false, reason: :not_found } unless idea
38
+
39
+ idea.challenge!
40
+ record_history(:challenged, idea_id)
41
+ { success: true, momentum: idea.momentum, mass: idea.mass, velocity: idea.velocity }
42
+ end
43
+
44
+ def apply_force(idea_id:, force:)
45
+ idea = @ideas[idea_id]
46
+ return { success: false, reason: :not_found } unless idea
47
+
48
+ idea.apply_force(force: force)
49
+ record_history(:force_applied, idea_id)
50
+ { success: true, momentum: idea.momentum, velocity: idea.velocity }
51
+ end
52
+
53
+ def surging_ideas
54
+ @ideas.values.select(&:surging?)
55
+ end
56
+
57
+ def reversing_ideas
58
+ @ideas.values.select(&:reversing?)
59
+ end
60
+
61
+ def entrenched_ideas
62
+ @ideas.values.select(&:entrenched?)
63
+ end
64
+
65
+ def ideas_by_type(idea_type:)
66
+ @ideas.values.select { |idea| idea.idea_type == idea_type }
67
+ end
68
+
69
+ def ideas_by_domain(domain:)
70
+ @ideas.values.select { |idea| idea.domain == domain }
71
+ end
72
+
73
+ def highest_momentum(limit: 5)
74
+ @ideas.values.sort_by { |idea| -idea.momentum }.first(limit)
75
+ end
76
+
77
+ def most_entrenched(limit: 5)
78
+ @ideas.values.sort_by { |idea| -idea.mass }.first(limit)
79
+ end
80
+
81
+ def apply_friction_all
82
+ @ideas.each_value(&:apply_friction!)
83
+ end
84
+
85
+ def prune_at_rest
86
+ ids = @ideas.select { |_id, idea| idea.at_rest? && idea.mass <= MASS_FLOOR + 0.01 }.keys
87
+ ids.each { |idea_id| @ideas.delete(idea_id) }
88
+ ids.size
89
+ end
90
+
91
+ def to_h
92
+ {
93
+ total_ideas: @ideas.size,
94
+ surging_count: surging_ideas.size,
95
+ reversing_count: reversing_ideas.size,
96
+ entrenched_count: entrenched_ideas.size,
97
+ avg_momentum: avg_momentum,
98
+ history_count: @history.size
99
+ }
100
+ end
101
+
102
+ private
103
+
104
+ def avg_momentum
105
+ return 0.0 if @ideas.empty?
106
+
107
+ @ideas.values.sum(&:momentum) / @ideas.size
108
+ end
109
+
110
+ def evict_oldest
111
+ oldest_id = @ideas.min_by { |_id, idea| idea.last_updated_at }&.first
112
+ @ideas.delete(oldest_id) if oldest_id
113
+ end
114
+
115
+ def record_history(event, idea_id)
116
+ @history << { event: event, idea_id: idea_id, at: Time.now.utc }
117
+ @history.shift while @history.size > MAX_HISTORY
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMomentum
6
+ module Runners
7
+ module CognitiveMomentum
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def create_cognitive_idea(content:, idea_type:, domain:, mass: nil, **)
12
+ idea = engine.create_idea(
13
+ content: content,
14
+ idea_type: idea_type.to_sym,
15
+ domain: domain.to_sym,
16
+ mass: mass || Helpers::Constants::DEFAULT_MASS
17
+ )
18
+ Legion::Logging.debug "[cognitive_momentum] create id=#{idea.id[0..7]} " \
19
+ "type=#{idea_type} domain=#{domain}"
20
+ { success: true, idea: idea.to_h }
21
+ end
22
+
23
+ def reinforce_cognitive_idea(idea_id:, **)
24
+ result = engine.reinforce_idea(idea_id: idea_id)
25
+ Legion::Logging.debug "[cognitive_momentum] reinforce id=#{idea_id[0..7]} " \
26
+ "momentum=#{result[:momentum]&.round(3)}"
27
+ result
28
+ end
29
+
30
+ def challenge_cognitive_idea(idea_id:, **)
31
+ result = engine.challenge_idea(idea_id: idea_id)
32
+ Legion::Logging.debug "[cognitive_momentum] challenge id=#{idea_id[0..7]} " \
33
+ "momentum=#{result[:momentum]&.round(3)}"
34
+ result
35
+ end
36
+
37
+ def apply_cognitive_force(idea_id:, force:, **)
38
+ result = engine.apply_force(idea_id: idea_id, force: force)
39
+ Legion::Logging.debug "[cognitive_momentum] force id=#{idea_id[0..7]} " \
40
+ "f=#{force} momentum=#{result[:momentum]&.round(3)}"
41
+ result
42
+ end
43
+
44
+ def surging_ideas_report(**)
45
+ ideas = engine.surging_ideas
46
+ Legion::Logging.debug "[cognitive_momentum] surging count=#{ideas.size}"
47
+ { success: true, ideas: ideas.map(&:to_h), count: ideas.size }
48
+ end
49
+
50
+ def reversing_ideas_report(**)
51
+ ideas = engine.reversing_ideas
52
+ Legion::Logging.debug "[cognitive_momentum] reversing count=#{ideas.size}"
53
+ { success: true, ideas: ideas.map(&:to_h), count: ideas.size }
54
+ end
55
+
56
+ def entrenched_ideas_report(**)
57
+ ideas = engine.entrenched_ideas
58
+ Legion::Logging.debug "[cognitive_momentum] entrenched count=#{ideas.size}"
59
+ { success: true, ideas: ideas.map(&:to_h), count: ideas.size }
60
+ end
61
+
62
+ def highest_momentum_ideas(limit: 5, **)
63
+ ideas = engine.highest_momentum(limit: limit)
64
+ Legion::Logging.debug "[cognitive_momentum] highest_momentum count=#{ideas.size}"
65
+ { success: true, ideas: ideas.map(&:to_h), count: ideas.size }
66
+ end
67
+
68
+ def ideas_by_domain(domain:, **)
69
+ ideas = engine.ideas_by_domain(domain: domain.to_sym)
70
+ Legion::Logging.debug '[cognitive_momentum] by_domain ' \
71
+ "domain=#{domain} count=#{ideas.size}"
72
+ { success: true, ideas: ideas.map(&:to_h), count: ideas.size }
73
+ end
74
+
75
+ def update_cognitive_momentum(**)
76
+ engine.apply_friction_all
77
+ pruned = engine.prune_at_rest
78
+ Legion::Logging.debug "[cognitive_momentum] friction+prune pruned=#{pruned}"
79
+ { success: true, pruned: pruned }
80
+ end
81
+
82
+ def cognitive_momentum_stats(**)
83
+ stats = engine.to_h
84
+ Legion::Logging.debug "[cognitive_momentum] stats total=#{stats[:total_ideas]}"
85
+ { success: true }.merge(stats)
86
+ end
87
+
88
+ private
89
+
90
+ def engine
91
+ @engine ||= Helpers::MomentumEngine.new
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMomentum
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'cognitive_momentum/version'
4
+ require_relative 'cognitive_momentum/helpers/constants'
5
+ require_relative 'cognitive_momentum/helpers/idea'
6
+ require_relative 'cognitive_momentum/helpers/momentum_engine'
7
+ require_relative 'cognitive_momentum/runners/cognitive_momentum'
8
+ require_relative 'cognitive_momentum/helpers/client'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module CognitiveMomentum
13
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-cognitive-momentum
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 ideas with mass (importance) and velocity (change rate). Heavy
27
+ ideas resist change (inertia), reinforced ideas accelerate, friction slows unreinforced
28
+ ideas. Based on Newtonian dynamics metaphor.
29
+ email:
30
+ - matthewdiverson@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - README.md
36
+ - lib/legion/extensions/cognitive_momentum.rb
37
+ - lib/legion/extensions/cognitive_momentum/helpers/client.rb
38
+ - lib/legion/extensions/cognitive_momentum/helpers/constants.rb
39
+ - lib/legion/extensions/cognitive_momentum/helpers/idea.rb
40
+ - lib/legion/extensions/cognitive_momentum/helpers/momentum_engine.rb
41
+ - lib/legion/extensions/cognitive_momentum/runners/cognitive_momentum.rb
42
+ - lib/legion/extensions/cognitive_momentum/version.rb
43
+ homepage: https://github.com/LegionIO/lex-cognitive-momentum
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ homepage_uri: https://github.com/LegionIO/lex-cognitive-momentum
48
+ source_code_uri: https://github.com/LegionIO/lex-cognitive-momentum
49
+ documentation_uri: https://github.com/LegionIO/lex-cognitive-momentum
50
+ changelog_uri: https://github.com/LegionIO/lex-cognitive-momentum/blob/master/CHANGELOG.md
51
+ bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-momentum/issues
52
+ rubygems_mfa_required: 'true'
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.4'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.6.9
68
+ specification_version: 4
69
+ summary: Physics-inspired cognitive dynamics for LegionIO
70
+ test_files: []