domain/daycare/incident.rs
1//! Daycare incident disposition rules that preserve pet-safety review gates.
2//!
3//! ## Operator-summary
4//!
5//! This module supports the daycare incident-disposition queue that decides whether an event
6//! remains a staff note, requires owner-message review, needs manager review, or suspends
7//! group play pending review. It can reduce labor by converting the reported severity into
8//! an explicit restriction and review gate that daycare eligibility must honor.
9//!
10//! It must not automate live customer notice, group-play reinstatement, manager approval,
11//! medical/legal conclusions, or incident closure. Authoritative facts remain the original
12//! incident report, classified severity, affected pet, required gate, and later reviewer
13//! decision. Review gates protect pets, customers, and staff by requiring customer-message
14//! approval before owner notice and manager approval before clearing manager-review or
15//! group-play-suspension outcomes.
16//!
17//! ```
18//! use domain::{daycare, entities, policy};
19//! use uuid::Uuid;
20//!
21//! let pet_id = entities::PetId(Uuid::nil());
22//! let disposition = daycare::incident::Classifier
23//! .classify(pet_id, daycare::incident::Severity::SuspendGroupPlay);
24//!
25//! assert_eq!(disposition.required_gate(), Some(policy::ReviewGate::ManagerApproval));
26//! assert_eq!(
27//! disposition.restriction(),
28//! daycare::incident::Restriction::SuspendedPendingManagerReview { pet_id },
29//! );
30//! ```
31
32use super::*;
33use crate::{entities, policy};
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36/// Daycare incident handling policy that determines notes, customer notice, and group-play suspension.
37pub enum Policy {
38 /// Incident is recorded as a staff note without extra customer or manager workflow.
39 StaffNoteOnly,
40 /// Incident requires manager review and customer-message approval before follow-up.
41 ManagerReviewAndCustomerNotice,
42 /// Incident should suspend group play until manager review clears the pet.
43 SuspendGroupPlayPendingReview,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
47/// Severity classification supplied to the incident classifier.
48pub enum Severity {
49 /// Incident is recorded as a staff note without extra customer or manager workflow.
50 StaffNoteOnly,
51 /// Incident requires customer notice but does not itself suspend group play.
52 OwnerNotice,
53 /// Incident requires manager review before staff close the disposition.
54 ManagerReview,
55 /// Incident should suspend group play pending manager review.
56 SuspendGroupPlay,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60/// Operational restriction created by a daycare incident disposition.
61pub enum Restriction {
62 /// Incident creates no active restriction on future daycare attendance.
63 None,
64 /// Pet whose group-play access is suspended pending manager review.
65 SuspendedPendingManagerReview {
66 /// Pet whose group-play access stays suspended until manager review clears it.
67 pet_id: PetId,
68 },
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72/// Classified incident outcome carrying restriction and review-gate evidence.
73pub struct Disposition {
74 /// Pet whose group-play access is suspended pending manager review.
75 pub pet_id: PetId,
76 /// Severity used to choose restriction and review gates.
77 pub severity: Severity,
78 restriction: Restriction,
79 required_gate: Option<policy::ReviewGate>,
80}
81
82impl Disposition {
83 /// Returns the operational restriction that eligibility policy must honor.
84 pub const fn restriction(&self) -> Restriction {
85 self.restriction
86 }
87
88 /// Returns the human review gate required before the incident disposition can be cleared.
89 pub fn required_gate(&self) -> Option<policy::ReviewGate> {
90 self.required_gate.clone()
91 }
92}
93
94#[derive(Debug, Clone, Default)]
95/// Deterministic classifier that maps incident severity to restrictions and review gates.
96pub struct Classifier;
97
98impl Classifier {
99 /// Classifies an incident severity for a pet into a disposition staff can act on.
100 pub fn classify(&self, pet_id: entities::PetId, severity: Severity) -> Disposition {
101 let (restriction, required_gate) = match severity {
102 Severity::StaffNoteOnly => (Restriction::None, None),
103 Severity::OwnerNotice => (
104 Restriction::None,
105 Some(policy::ReviewGate::CustomerMessageApproval),
106 ),
107 Severity::ManagerReview => {
108 (Restriction::None, Some(policy::ReviewGate::ManagerApproval))
109 }
110 Severity::SuspendGroupPlay => (
111 Restriction::SuspendedPendingManagerReview { pet_id },
112 Some(policy::ReviewGate::ManagerApproval),
113 ),
114 };
115 Disposition {
116 pet_id,
117 severity,
118 restriction,
119 required_gate,
120 }
121 }
122}