made_core/events/
deliberation_completed.rs1use serde::{Deserialize, Serialize};
4
5use crate::events::envelope::EventEnvelope;
6use crate::value_objects::{DurationMs, ProposalId, Score, Specialty, TaskId};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct DeliberationCompletedEvent {
10 #[serde(flatten)]
11 envelope: EventEnvelope,
12 task_id: TaskId,
13 specialty: Specialty,
14 winner_proposal_id: ProposalId,
15 winner_score: Score,
16 num_candidates: u32,
17 duration: DurationMs,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
23 external_context_bundle_id: Option<String>,
24}
25
26impl DeliberationCompletedEvent {
27 #[must_use]
28 pub fn new(
29 envelope: EventEnvelope,
30 task_id: TaskId,
31 specialty: Specialty,
32 winner_proposal_id: ProposalId,
33 winner_score: Score,
34 num_candidates: u32,
35 duration: DurationMs,
36 ) -> Self {
37 Self::new_with_context(
38 envelope,
39 task_id,
40 specialty,
41 winner_proposal_id,
42 winner_score,
43 num_candidates,
44 duration,
45 None,
46 )
47 }
48
49 #[must_use]
54 pub fn new_with_context(
55 envelope: EventEnvelope,
56 task_id: TaskId,
57 specialty: Specialty,
58 winner_proposal_id: ProposalId,
59 winner_score: Score,
60 num_candidates: u32,
61 duration: DurationMs,
62 external_context_bundle_id: Option<String>,
63 ) -> Self {
64 Self {
65 envelope,
66 task_id,
67 specialty,
68 winner_proposal_id,
69 winner_score,
70 num_candidates,
71 duration,
72 external_context_bundle_id,
73 }
74 }
75
76 #[must_use]
77 pub fn envelope(&self) -> &EventEnvelope {
78 &self.envelope
79 }
80 #[must_use]
81 pub fn task_id(&self) -> &TaskId {
82 &self.task_id
83 }
84 #[must_use]
85 pub fn specialty(&self) -> &Specialty {
86 &self.specialty
87 }
88 #[must_use]
89 pub fn winner_proposal_id(&self) -> &ProposalId {
90 &self.winner_proposal_id
91 }
92 #[must_use]
93 pub fn winner_score(&self) -> Score {
94 self.winner_score
95 }
96 #[must_use]
97 pub fn num_candidates(&self) -> u32 {
98 self.num_candidates
99 }
100 #[must_use]
101 pub fn duration(&self) -> DurationMs {
102 self.duration
103 }
104 #[must_use]
105 pub fn external_context_bundle_id(&self) -> Option<&str> {
106 self.external_context_bundle_id.as_deref()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::value_objects::EventId;
114 use time::macros::datetime;
115
116 #[test]
117 fn accessors_return_fields() {
118 let env = EventEnvelope::new(
119 EventId::new("e").unwrap(),
120 datetime!(2026-04-15 12:00:00 UTC),
121 "s",
122 None,
123 )
124 .unwrap();
125 let ev = DeliberationCompletedEvent::new(
126 env,
127 TaskId::new("t").unwrap(),
128 Specialty::new("triage").unwrap(),
129 ProposalId::new("p").unwrap(),
130 Score::new(0.87).unwrap(),
131 3,
132 DurationMs::from_millis(900),
133 );
134 assert_eq!(ev.num_candidates(), 3);
135 assert_eq!(ev.winner_score().get(), 0.87);
136 assert_eq!(ev.duration().get(), 900);
137 }
138}