Coverage for integrations / social / voting_rules.py: 100.0%

23 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-12 04:49 +0000

1""" 

2Context-Based Constitutional Voting Rules. 

3 

4Decision contexts determine who can vote and how votes are weighted. 

5Security changes require human-only votes; operational tuning can be 

6agent-decided with human override. 

7 

8Integrates with ThoughtExperimentService.cast_vote() and tally_votes(). 

9""" 

10 

11# ─── Voter Rule Definitions ────────────────────────────────────────── 

12 

13VOTER_RULES = { 

14 'security_guardrail': { 

15 'agent_can_vote': False, 

16 'human_required': True, 

17 'agent_weight': 0.0, 

18 'human_weight': 1.0, 

19 'approval_threshold': 0.8, 

20 'steward_required': True, 

21 }, 

22 'technical_improvement': { 

23 'agent_can_vote': True, 

24 'human_required': True, 

25 'agent_weight': 0.6, 

26 'human_weight': 1.0, 

27 'approval_threshold': 0.5, 

28 'steward_required': False, 

29 }, 

30 'business_revenue': { 

31 'agent_can_vote': True, 

32 'human_required': True, 

33 'agent_weight': 0.8, 

34 'human_weight': 1.0, 

35 'approval_threshold': 0.5, 

36 'steward_required': False, 

37 }, 

38 'operational_tuning': { 

39 'agent_can_vote': True, 

40 'human_required': False, 

41 'agent_weight': 1.0, 

42 'human_weight': 1.0, 

43 'approval_threshold': 0.3, 

44 'steward_required': False, 

45 }, 

46} 

47 

48# Default rules for unclassified decisions 

49DEFAULT_RULES = { 

50 'agent_can_vote': True, 

51 'human_required': True, 

52 'agent_weight': 0.6, 

53 'human_weight': 1.0, 

54 'approval_threshold': 0.5, 

55 'steward_required': False, 

56} 

57 

58# ─── Context Classification ────────────────────────────────────────── 

59 

60# Keywords that map to decision contexts (checked against title + hypothesis) 

61_CONTEXT_KEYWORDS = { 

62 'security_guardrail': [ 

63 'security', 'guardrail', 'master key', 'circuit breaker', 

64 'kill switch', 'permission', 'access control', 'authentication', 

65 'certificate', 'encryption', 'firewall', 'vulnerability', 

66 ], 

67 'technical_improvement': [ 

68 'performance', 'optimization', 'refactor', 'architecture', 

69 'algorithm', 'latency', 'throughput', 'scalability', 

70 'bug fix', 'improvement', 'upgrade', 'migration', 

71 ], 

72 'business_revenue': [ 

73 'revenue', 'pricing', 'monetization', 'subscription', 

74 'trading', 'investment', 'profit', 'ad revenue', 

75 'business model', 'marketplace', 'commercial', 

76 ], 

77 'operational_tuning': [ 

78 'threshold', 'timeout', 'interval', 'batch size', 

79 'cache', 'tuning', 'parameter', 'configuration', 

80 'polling', 'retry', 'rate limit', 

81 ], 

82} 

83 

84 

85def classify_decision_context(experiment_dict: dict) -> str: 

86 """Classify a thought experiment into a decision context. 

87 

88 Scans title + hypothesis for keywords. Returns the best-matching 

89 context or 'technical_improvement' as default. 

90 """ 

91 text = ' '.join([ 

92 (experiment_dict.get('title') or ''), 

93 (experiment_dict.get('hypothesis') or ''), 

94 ]).lower() 

95 

96 scores = {} 

97 for context, keywords in _CONTEXT_KEYWORDS.items(): 

98 score = sum(1 for kw in keywords if kw in text) 

99 if score > 0: 

100 scores[context] = score 

101 

102 if not scores: 

103 return 'technical_improvement' 

104 

105 return max(scores, key=scores.get) 

106 

107 

108def get_voter_rules(context: str) -> dict: 

109 """Return voter rules for a decision context.""" 

110 return VOTER_RULES.get(context, DEFAULT_RULES) 

111 

112 

113def check_voter_eligibility(experiment_dict: dict, voter_type: str) -> dict: 

114 """Check if a voter is eligible to vote on an experiment. 

115 

116 Returns: {'eligible': bool, 'reason': str, 'context': str, 'rules': dict} 

117 """ 

118 context = experiment_dict.get('decision_context') or \ 

119 classify_decision_context(experiment_dict) 

120 rules = get_voter_rules(context) 

121 

122 # Humans can always vote 

123 if voter_type == 'human': 

124 return { 

125 'eligible': True, 

126 'reason': 'human_always_eligible', 

127 'context': context, 

128 'rules': rules, 

129 } 

130 

131 # Agent eligibility depends on context 

132 if not rules['agent_can_vote']: 

133 return { 

134 'eligible': False, 

135 'reason': f'agents_cannot_vote_on_{context}', 

136 'context': context, 

137 'rules': rules, 

138 } 

139 

140 return { 

141 'eligible': True, 

142 'reason': 'agent_eligible', 

143 'context': context, 

144 'rules': rules, 

145 }