Skip to main content

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}