1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Clone, Debug, Serialize, Deserialize, Default)]
7pub struct DecisionPolicyRules {
8 #[serde(default)]
9 pub voting: VotingRules,
10 #[serde(default)]
11 pub objection_handling: ObjectionHandlingRules,
12 #[serde(default)]
13 pub evaluation: EvaluationRules,
14 #[serde(default)]
15 pub commitment: CommitmentRules,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct VotingRules {
20 #[serde(default = "default_algorithm")]
21 pub algorithm: String,
22 #[serde(default = "default_threshold")]
23 pub threshold: f64,
24 #[serde(default)]
25 pub quorum: QuorumRules,
26 #[serde(default)]
27 pub weights: HashMap<String, f64>,
28}
29
30impl Default for VotingRules {
31 fn default() -> Self {
32 Self {
33 algorithm: default_algorithm(),
34 threshold: default_threshold(),
35 quorum: QuorumRules::default(),
36 weights: HashMap::new(),
37 }
38 }
39}
40
41fn default_algorithm() -> String {
42 "none".into()
43}
44
45fn default_threshold() -> f64 {
46 0.5
47}
48
49#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct QuorumRules {
52 #[serde(default = "default_quorum_type", rename = "type")]
53 pub quorum_type: String,
54 #[serde(default)]
55 pub value: f64,
56}
57
58impl Default for QuorumRules {
59 fn default() -> Self {
60 Self {
61 quorum_type: default_quorum_type(),
62 value: 0.0,
63 }
64 }
65}
66
67fn default_quorum_type() -> String {
68 "count".into()
69}
70
71#[derive(Clone, Debug, Serialize, Deserialize)]
72pub struct ObjectionHandlingRules {
73 #[serde(default, alias = "critical_severity_vetoes")]
75 pub critical_severity_vetoes: bool,
76 #[serde(default = "default_veto_threshold")]
77 pub veto_threshold: u32,
78}
79
80impl Default for ObjectionHandlingRules {
81 fn default() -> Self {
82 Self {
83 critical_severity_vetoes: false,
84 veto_threshold: default_veto_threshold(),
85 }
86 }
87}
88
89fn default_veto_threshold() -> u32 {
90 1
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize)]
94pub struct EvaluationRules {
95 #[serde(default)]
96 pub required_before_voting: bool,
97 #[serde(default)]
98 pub minimum_confidence: f64,
99}
100
101impl Default for EvaluationRules {
102 fn default() -> Self {
103 Self {
104 required_before_voting: false,
105 minimum_confidence: 0.0,
106 }
107 }
108}
109
110pub use super::CommitmentRules;
115
116#[derive(Clone, Debug, Serialize, Deserialize, Default)]
119pub struct ProposalPolicyRules {
120 #[serde(default)]
121 pub acceptance: ProposalAcceptanceRules,
122 #[serde(default)]
123 pub counter_proposal: CounterProposalRules,
124 #[serde(default)]
125 pub rejection: RejectionRules,
126 #[serde(default)]
127 pub commitment: CommitmentRules,
128}
129
130#[derive(Clone, Debug, Serialize, Deserialize)]
131pub struct ProposalAcceptanceRules {
132 #[serde(default = "default_acceptance_criterion")]
133 pub criterion: String,
134}
135
136impl Default for ProposalAcceptanceRules {
137 fn default() -> Self {
138 Self {
139 criterion: default_acceptance_criterion(),
140 }
141 }
142}
143
144fn default_acceptance_criterion() -> String {
145 "all_parties".into()
146}
147
148#[derive(Clone, Debug, Default, Serialize, Deserialize)]
149pub struct CounterProposalRules {
150 #[serde(default)]
151 pub max_rounds: usize,
152}
153
154#[derive(Clone, Debug, Default, Serialize, Deserialize)]
155pub struct RejectionRules {
156 #[serde(default)]
157 pub terminal_on_any_reject: bool,
158}
159
160#[derive(Clone, Debug, Serialize, Deserialize, Default)]
163pub struct TaskPolicyRules {
164 #[serde(default)]
165 pub assignment: TaskAssignmentRules,
166 #[serde(default)]
167 pub completion: TaskCompletionRules,
168 #[serde(default)]
169 pub commitment: CommitmentRules,
170}
171
172#[derive(Clone, Debug, Default, Serialize, Deserialize)]
173pub struct TaskAssignmentRules {
174 #[serde(default)]
175 pub allow_reassignment_on_reject: bool,
176}
177
178#[derive(Clone, Debug, Default, Serialize, Deserialize)]
179pub struct TaskCompletionRules {
180 #[serde(default)]
181 pub require_output: bool,
182}
183
184#[derive(Clone, Debug, Serialize, Deserialize, Default)]
187pub struct HandoffPolicyRules {
188 #[serde(default)]
189 pub acceptance: HandoffAcceptanceRules,
190 #[serde(default)]
191 pub commitment: CommitmentRules,
192}
193
194#[derive(Clone, Debug, Default, Serialize, Deserialize)]
195pub struct HandoffAcceptanceRules {
196 #[serde(default)]
197 pub implicit_accept_timeout_ms: u64,
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize, Default)]
203pub struct QuorumPolicyRules {
204 #[serde(default)]
205 pub threshold: QuorumThreshold,
206 #[serde(default)]
207 pub abstention: AbstentionRules,
208 #[serde(default)]
209 pub commitment: CommitmentRules,
210}
211
212#[derive(Clone, Debug, Serialize, Deserialize)]
214pub struct QuorumThreshold {
215 #[serde(default = "default_threshold_type", rename = "type")]
216 pub threshold_type: String,
217 #[serde(default)]
218 pub value: f64,
219}
220
221impl Default for QuorumThreshold {
222 fn default() -> Self {
223 Self {
224 threshold_type: default_threshold_type(),
225 value: 0.0,
226 }
227 }
228}
229
230fn default_threshold_type() -> String {
231 "n_of_m".into()
232}
233
234#[derive(Clone, Debug, Serialize, Deserialize)]
235pub struct AbstentionRules {
236 #[serde(default)]
237 pub counts_toward_quorum: bool,
238 #[serde(default = "default_interpretation")]
239 pub interpretation: String,
240}
241
242impl Default for AbstentionRules {
243 fn default() -> Self {
244 Self {
245 counts_toward_quorum: false,
246 interpretation: default_interpretation(),
247 }
248 }
249}
250
251fn default_interpretation() -> String {
252 "neutral".into()
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn decision_policy_rules_defaults() {
261 let rules = DecisionPolicyRules::default();
262 assert_eq!(rules.voting.algorithm, "none");
263 assert!((rules.voting.threshold - 0.5).abs() < f64::EPSILON);
264 assert_eq!(rules.voting.quorum.quorum_type, "count");
265 assert!(!rules.objection_handling.critical_severity_vetoes);
266 assert_eq!(rules.objection_handling.veto_threshold, 1);
267 assert!(!rules.evaluation.required_before_voting);
268 assert!((rules.evaluation.minimum_confidence).abs() < f64::EPSILON);
269 assert_eq!(rules.commitment.authority, "initiator_only");
270 assert!(rules.commitment.designated_roles.is_empty());
271 assert!(!rules.commitment.require_vote_quorum);
272 }
273
274 #[test]
275 fn decision_policy_rules_deserialization() {
276 let json = serde_json::json!({
277 "voting": {
278 "algorithm": "majority",
279 "threshold": 0.6,
280 "quorum": { "type": "percentage", "value": 75.0 },
281 "weights": { "agent://fraud": 2.0, "agent://growth": 1.0 }
282 },
283 "objection_handling": {
284 "critical_severity_vetoes": true,
285 "veto_threshold": 2
286 },
287 "evaluation": {
288 "required_before_voting": true,
289 "minimum_confidence": 0.8
290 },
291 "commitment": {
292 "authority": "designated_role",
293 "designated_roles": ["agent://lead"],
294 "require_vote_quorum": true
295 }
296 });
297
298 let rules: DecisionPolicyRules = serde_json::from_value(json).unwrap();
299 assert_eq!(rules.voting.algorithm, "majority");
300 assert!((rules.voting.threshold - 0.6).abs() < f64::EPSILON);
301 assert_eq!(rules.voting.quorum.quorum_type, "percentage");
302 assert!((rules.voting.quorum.value - 75.0).abs() < f64::EPSILON);
303 assert_eq!(*rules.voting.weights.get("agent://fraud").unwrap(), 2.0);
304 assert!(rules.objection_handling.critical_severity_vetoes);
305 assert_eq!(rules.objection_handling.veto_threshold, 2);
306 assert!(rules.evaluation.required_before_voting);
307 assert!((rules.evaluation.minimum_confidence - 0.8).abs() < f64::EPSILON);
308 assert_eq!(rules.commitment.authority, "designated_role");
309 assert_eq!(rules.commitment.designated_roles, vec!["agent://lead"]);
310 assert!(rules.commitment.require_vote_quorum);
311 }
312
313 #[test]
314 fn partial_deserialization_fills_defaults() {
315 let json = serde_json::json!({
316 "voting": { "algorithm": "unanimous" }
317 });
318 let rules: DecisionPolicyRules = serde_json::from_value(json).unwrap();
319 assert_eq!(rules.voting.algorithm, "unanimous");
320 assert!((rules.voting.threshold - 0.5).abs() < f64::EPSILON);
321 assert!(!rules.objection_handling.critical_severity_vetoes);
322 assert_eq!(rules.objection_handling.veto_threshold, 1);
323 }
324
325 #[test]
326 fn proposal_policy_rules_defaults() {
327 let rules = ProposalPolicyRules::default();
328 assert_eq!(rules.acceptance.criterion, "all_parties");
329 assert_eq!(rules.counter_proposal.max_rounds, 0);
330 assert!(!rules.rejection.terminal_on_any_reject);
331 assert_eq!(rules.commitment.authority, "initiator_only");
332 }
333
334 #[test]
335 fn proposal_policy_rules_deserialization() {
336 let json = serde_json::json!({
337 "acceptance": { "criterion": "counterparty" },
338 "counter_proposal": { "max_rounds": 3 },
339 "rejection": { "terminal_on_any_reject": true },
340 "commitment": { "authority": "any_participant" }
341 });
342 let rules: ProposalPolicyRules = serde_json::from_value(json).unwrap();
343 assert_eq!(rules.acceptance.criterion, "counterparty");
344 assert_eq!(rules.counter_proposal.max_rounds, 3);
345 assert!(rules.rejection.terminal_on_any_reject);
346 assert_eq!(rules.commitment.authority, "any_participant");
347 }
348
349 #[test]
350 fn task_policy_rules_defaults() {
351 let rules = TaskPolicyRules::default();
352 assert!(!rules.assignment.allow_reassignment_on_reject);
353 assert!(!rules.completion.require_output);
354 assert_eq!(rules.commitment.authority, "initiator_only");
355 }
356
357 #[test]
358 fn task_policy_rules_deserialization() {
359 let json = serde_json::json!({
360 "assignment": { "allow_reassignment_on_reject": true },
361 "completion": { "require_output": true },
362 "commitment": { "authority": "initiator_only" }
363 });
364 let rules: TaskPolicyRules = serde_json::from_value(json).unwrap();
365 assert!(rules.assignment.allow_reassignment_on_reject);
366 assert!(rules.completion.require_output);
367 }
368
369 #[test]
370 fn handoff_policy_rules_defaults() {
371 let rules = HandoffPolicyRules::default();
372 assert_eq!(rules.acceptance.implicit_accept_timeout_ms, 0);
373 assert_eq!(rules.commitment.authority, "initiator_only");
374 }
375
376 #[test]
377 fn handoff_policy_rules_deserialization() {
378 let json = serde_json::json!({
379 "acceptance": { "implicit_accept_timeout_ms": 5000 },
380 "commitment": { "authority": "any_participant" }
381 });
382 let rules: HandoffPolicyRules = serde_json::from_value(json).unwrap();
383 assert_eq!(rules.acceptance.implicit_accept_timeout_ms, 5000);
384 assert_eq!(rules.commitment.authority, "any_participant");
385 }
386
387 #[test]
388 fn quorum_policy_rules_defaults() {
389 let rules = QuorumPolicyRules::default();
390 assert_eq!(rules.threshold.threshold_type, "n_of_m");
391 assert!((rules.threshold.value).abs() < f64::EPSILON);
392 assert!(!rules.abstention.counts_toward_quorum);
393 assert_eq!(rules.abstention.interpretation, "neutral");
394 assert_eq!(rules.commitment.authority, "initiator_only");
395 }
396
397 #[test]
398 fn quorum_policy_rules_deserialization() {
399 let json = serde_json::json!({
400 "threshold": { "type": "percentage", "value": 75.0 },
401 "abstention": { "counts_toward_quorum": true, "interpretation": "implicit_reject" },
402 "commitment": { "authority": "initiator_only" }
403 });
404 let rules: QuorumPolicyRules = serde_json::from_value(json).unwrap();
405 assert_eq!(rules.threshold.threshold_type, "percentage");
406 assert!((rules.threshold.value - 75.0).abs() < f64::EPSILON);
407 assert!(rules.abstention.counts_toward_quorum);
408 assert_eq!(rules.abstention.interpretation, "implicit_reject");
409 }
410}