Module: Legion::Extensions::MindGrowth::Runners::ConsensusBuilder

Extended by:
ConsensusBuilder
Included in:
ConsensusBuilder
Defined in:
lib/legion/extensions/mind_growth/runners/consensus_builder.rb

Constant Summary collapse

CONSENSUS_THRESHOLD =
0.67
DISAGREEMENT_ESCALATION_THRESHOLD =
0.5
DECIDED_CONSENSUSES =
%i[approved rejected].freeze

Instance Method Summary collapse

Instance Method Details

#consensus_summary(charter_id:) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/legion/extensions/mind_growth/runners/consensus_builder.rb', line 86

def consensus_summary(charter_id:, **)
  proposals = fetch_pending_proposals(charter_id)

  decided = []
  pending = []

  proposals.each do |entry|
    pid = entry[:proposal_id]
    tally = tally_swarm_votes(charter_id: charter_id, proposal_id: pid)

    record = { proposal_id: pid, consensus: tally[:consensus],
               approve_count: tally[:approve_count], reject_count: tally[:reject_count] }

    if DECIDED_CONSENSUSES.include?(tally[:consensus])
      decided << record
    else
      pending << record
    end
  end

  { success: true, proposals: proposals, decided: decided, pending: pending }
end

#propose_to_swarm(charter_id:, proposal_id:, proposer_agent_id:) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/legion/extensions/mind_growth/runners/consensus_builder.rb', line 14

def propose_to_swarm(charter_id:, proposal_id:, proposer_agent_id:, **)
  if swarm_available?
    pending = fetch_pending_proposals(charter_id)
    pending << { proposal_id: proposal_id, proposer_agent_id: proposer_agent_id,
                 proposed_at: Time.now.utc.iso8601 }

    Legion::Extensions::Swarm::Runners::Workspace.workspace_put(
      charter_id: charter_id,
      key:        'pending_proposals',
      value:      pending,
      author:     'mind-growth'
    )
  else
    Runners::Governance.submit_proposal(proposal_id: proposal_id)
  end

  { success: true, proposal_id: proposal_id }
end

#resolve_disagreement(charter_id:, proposal_id:) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/legion/extensions/mind_growth/runners/consensus_builder.rb', line 72

def resolve_disagreement(charter_id:, proposal_id:, **)
  tally = tally_swarm_votes(charter_id: charter_id, proposal_id: proposal_id)

  resolution = if tally[:total].positive? &&
                  (tally[:approve_count].to_f / tally[:total]) >= DISAGREEMENT_ESCALATION_THRESHOLD &&
                  (tally[:approve_count].to_f / tally[:total]) < CONSENSUS_THRESHOLD
                 :escalated_to_human
               else
                 :rejected_by_default
               end

  { success: true, resolution: resolution }
end

#tally_swarm_votes(charter_id:, proposal_id:) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/legion/extensions/mind_growth/runners/consensus_builder.rb', line 53

def tally_swarm_votes(charter_id:, proposal_id:, **)
  votes = fetch_votes(charter_id, proposal_id)

  approve_count = votes.count { |v| v[:vote].to_s == 'approve' }
  reject_count  = votes.count { |v| v[:vote].to_s == 'reject' }
  total         = votes.size

  consensus = if !total.zero? && (approve_count.to_f / total) >= CONSENSUS_THRESHOLD
                :approved
              elsif !total.zero? && (reject_count.to_f / total) >= CONSENSUS_THRESHOLD
                :rejected
              else
                :no_consensus
              end

  { success: true, approve_count: approve_count, reject_count: reject_count,
    total: total, consensus: consensus, threshold: CONSENSUS_THRESHOLD }
end

#vote_in_swarm(charter_id:, proposal_id:, voter_agent_id:, vote:, rationale: nil) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/legion/extensions/mind_growth/runners/consensus_builder.rb', line 33

def vote_in_swarm(charter_id:, proposal_id:, voter_agent_id:, vote:, rationale: nil, **)
  vote_sym = vote.to_sym
  return { success: false, reason: :invalid_vote } unless %i[approve reject].include?(vote_sym)

  if swarm_available?
    votes = fetch_votes(charter_id, proposal_id)
    votes << { voter_agent_id: voter_agent_id, vote: vote_sym, rationale: rationale,
               cast_at: Time.now.utc.iso8601 }

    Legion::Extensions::Swarm::Runners::Workspace.workspace_put(
      charter_id: charter_id,
      key:        "votes:#{proposal_id}",
      value:      votes,
      author:     'mind-growth'
    )
  end

  { success: true, vote: vote_sym }
end