Skip to main content

domain/
data_quality.rs

1//! Data-quality findings that guard the source-fact → domain → workflow chain.
2//!
3//! These contracts keep messy provider facts visible instead of silently normalizing them:
4//! blocking issues stop unsafe automation, reviewable issues remain attached to analytics
5//! facts and manager briefs, and BI-visible status lets labor-cost reports explain when
6//! data hygiene—not actual demand or staffing—is driving an apparent exception.
7
8use serde::{Deserialize, Serialize};
9
10use crate::source;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13/// Path segment vocabulary for source fields that can fail validation.
14pub enum FieldSegment {
15    /// Reservation record participating in the workflow.
16    Reservation,
17    /// Stay data-quality finding for cleanup or review.
18    Stay,
19    /// Source data-quality finding for cleanup or review.
20    Source,
21    /// Customer record id data-quality finding for cleanup or review.
22    CustomerRecordId,
23    /// Pet record id data-quality finding for cleanup or review.
24    PetRecordId,
25    /// Location record id data-quality finding for cleanup or review.
26    LocationRecordId,
27    /// Service type record id data-quality finding for cleanup or review.
28    ServiceTypeRecordId,
29    /// Status data-quality finding for cleanup or review.
30    Status,
31    /// Owner pet relationship data-quality finding for cleanup or review.
32    OwnerPetRelationship,
33    /// Record id data-quality finding for cleanup or review.
34    RecordId,
35    /// Endpoint data-quality finding for cleanup or review.
36    Endpoint,
37    /// Payload hash data-quality finding for cleanup or review.
38    PayloadHash,
39    /// Raw payload ref data-quality finding for cleanup or review.
40    RawPayloadRef,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44/// Reservation fields whose absence or ambiguity can block workflow projections.
45pub enum ReservationField {
46    /// Customer record id data-quality finding for cleanup or review.
47    CustomerRecordId,
48    /// Pet record id data-quality finding for cleanup or review.
49    PetRecordId,
50    /// Location record id data-quality finding for cleanup or review.
51    LocationRecordId,
52    /// Service type record id data-quality finding for cleanup or review.
53    ServiceTypeRecordId,
54    /// Status data-quality finding for cleanup or review.
55    Status,
56    /// Owner pet relationship data-quality finding for cleanup or review.
57    OwnerPetRelationship,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61/// Stay/read-model fields checked before analytics and labor views trust a record.
62pub enum StayField {
63    /// Id data-quality finding for cleanup or review.
64    Id,
65    /// Pet record id data-quality finding for cleanup or review.
66    PetRecordId,
67    /// Location record id data-quality finding for cleanup or review.
68    LocationRecordId,
69    /// Status data-quality finding for cleanup or review.
70    Status,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74/// Source-ingestion metadata fields needed to preserve provenance and auditability.
75pub enum SourceField {
76    /// Record id data-quality finding for cleanup or review.
77    RecordId,
78    /// Endpoint data-quality finding for cleanup or review.
79    Endpoint,
80    /// Payload hash data-quality finding for cleanup or review.
81    PayloadHash,
82    /// Raw payload ref data-quality finding for cleanup or review.
83    RawPayloadRef,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87/// Semantic path to the source field that produced a data-quality issue.
88pub enum FieldPath {
89    /// Reservation record participating in the workflow.
90    Reservation(ReservationField),
91    /// Stay data-quality finding for cleanup or review.
92    Stay(StayField),
93    /// Source data-quality finding for cleanup or review.
94    Source(SourceField),
95}
96
97impl FieldPath {
98    /// Builds a reservation-field path for a source-data-quality issue.
99    pub const fn reservation(field: ReservationField) -> Self {
100        Self::Reservation(field)
101    }
102
103    /// Builds a stay/read-model-field path for a source-data-quality issue.
104    pub const fn stay(field: StayField) -> Self {
105        Self::Stay(field)
106    }
107
108    /// Builds a source-metadata-field path for a source-data-quality issue.
109    pub const fn source(field: SourceField) -> Self {
110        Self::Source(field)
111    }
112
113    /// Returns stable path segments for repair queues, BI dimensions, and manager review.
114    pub const fn segments(&self) -> &'static [FieldSegment] {
115        match self {
116            Self::Reservation(ReservationField::CustomerRecordId) => {
117                &[FieldSegment::Reservation, FieldSegment::CustomerRecordId]
118            }
119            Self::Reservation(ReservationField::PetRecordId) => {
120                &[FieldSegment::Reservation, FieldSegment::PetRecordId]
121            }
122            Self::Reservation(ReservationField::LocationRecordId) => {
123                &[FieldSegment::Reservation, FieldSegment::LocationRecordId]
124            }
125            Self::Reservation(ReservationField::ServiceTypeRecordId) => {
126                &[FieldSegment::Reservation, FieldSegment::ServiceTypeRecordId]
127            }
128            Self::Reservation(ReservationField::Status) => {
129                &[FieldSegment::Reservation, FieldSegment::Status]
130            }
131            Self::Reservation(ReservationField::OwnerPetRelationship) => &[
132                FieldSegment::Reservation,
133                FieldSegment::OwnerPetRelationship,
134            ],
135            Self::Stay(StayField::Id) => &[FieldSegment::Stay, FieldSegment::RecordId],
136            Self::Stay(StayField::PetRecordId) => &[FieldSegment::Stay, FieldSegment::PetRecordId],
137            Self::Stay(StayField::LocationRecordId) => {
138                &[FieldSegment::Stay, FieldSegment::LocationRecordId]
139            }
140            Self::Stay(StayField::Status) => &[FieldSegment::Stay, FieldSegment::Status],
141            Self::Source(SourceField::RecordId) => &[FieldSegment::Source, FieldSegment::RecordId],
142            Self::Source(SourceField::Endpoint) => &[FieldSegment::Source, FieldSegment::Endpoint],
143            Self::Source(SourceField::PayloadHash) => {
144                &[FieldSegment::Source, FieldSegment::PayloadHash]
145            }
146            Self::Source(SourceField::RawPayloadRef) => {
147                &[FieldSegment::Source, FieldSegment::RawPayloadRef]
148            }
149        }
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154/// Kind of hygiene defect found while validating source facts.
155pub enum Kind {
156    /// Missing required field data-quality finding for cleanup or review.
157    MissingRequiredField {
158        /// Field fact promoted into this data quality contract.
159        field: FieldPath,
160    },
161    /// Assumption in force data-quality finding for cleanup or review.
162    AssumptionInForce {
163        /// Assumption fact promoted into this data quality contract.
164        assumption: source::reservation::Assumption,
165    },
166    /// Unknown source status data-quality finding for cleanup or review.
167    UnknownSourceStatus {
168        /// Provider status text retained before normalization.
169        observed: source::ObservedStatus,
170    },
171    /// Conflicting timestamps data-quality finding for cleanup or review.
172    ConflictingTimestamps,
173    /// Duplicate source record data-quality finding for cleanup or review.
174    DuplicateSourceRecord,
175    /// Ambiguous owner pet relationship data-quality finding for cleanup or review.
176    AmbiguousOwnerPetRelationship,
177    /// Unmapped service type data-quality finding for cleanup or review.
178    UnmappedServiceType,
179    /// Location scope ambiguity data-quality finding for cleanup or review.
180    LocationScopeAmbiguity,
181    /// Payment state conflict data-quality finding for cleanup or review.
182    PaymentStateConflict,
183    /// Checkout evidence missing data-quality finding for cleanup or review.
184    CheckoutEvidenceMissing,
185    /// Unclosed reservation data-quality finding for cleanup or review.
186    UnclosedReservation,
187    /// Incomplete pet profile data-quality finding for cleanup or review.
188    IncompletePetProfile,
189    /// Missing vaccination record data-quality finding for cleanup or review.
190    MissingVaccinationRecord,
191    /// Sensitive payload quarantined data-quality finding for cleanup or review.
192    SensitivePayloadQuarantined,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
196/// Severity of a hygiene defect and its effect on read-model or workflow safety.
197pub enum Severity {
198    /// Informational data-quality finding for cleanup or review.
199    Informational,
200    /// Warning data-quality finding for cleanup or review.
201    Warning,
202    /// Blocking data-quality finding for cleanup or review.
203    Blocking,
204    /// Critical data-quality finding for cleanup or review.
205    Critical,
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
209/// Lifecycle state for a data-quality issue as staff acknowledge or repair it.
210pub enum ResolutionStatus {
211    /// Open data-quality finding for cleanup or review.
212    Open,
213    /// Acknowledged data-quality finding for cleanup or review.
214    Acknowledged,
215    /// Ignored data-quality finding for cleanup or review.
216    Ignored,
217    /// Repaired data-quality finding for cleanup or review.
218    Repaired,
219    /// Superseded data-quality finding for cleanup or review.
220    Superseded,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224/// Evidence-backed data-quality issue attached to source records and derived facts.
225pub struct Issue {
226    kind: Kind,
227    severity: Severity,
228    provenance: source::Provenance,
229    source_record_ref: source::RecordRef,
230    detected_at: source::Timestamp,
231    resolution_status: ResolutionStatus,
232    visible_to_bi: bool,
233    workflow_blocking: bool,
234}
235
236impl Issue {
237    /// Creates an open data-quality issue from provenance and validated issue metadata.
238    pub fn new(
239        kind: Kind,
240        severity: Severity,
241        provenance: source::Provenance,
242        detected_at: source::Timestamp,
243        workflow_blocking: bool,
244    ) -> Self {
245        let source_record_ref = source::RecordRef::from_provenance(&provenance);
246        Self {
247            kind,
248            severity,
249            provenance,
250            source_record_ref,
251            detected_at,
252            resolution_status: ResolutionStatus::Open,
253            visible_to_bi: true,
254            workflow_blocking,
255        }
256    }
257
258    /// Returns the kind for this data quality value.
259    pub fn kind(&self) -> Kind {
260        self.kind.clone()
261    }
262
263    /// Returns this data quality value's severity.
264    pub const fn severity(&self) -> Severity {
265        self.severity
266    }
267
268    /// Returns this data quality value's source system.
269    pub const fn source_system(&self) -> source::System {
270        self.source_record_ref.system()
271    }
272
273    /// Returns this data quality value's source record ref.
274    pub const fn source_record_ref(&self) -> &source::RecordRef {
275        &self.source_record_ref
276    }
277
278    /// Returns this data quality value's provenance.
279    pub const fn provenance(&self) -> &source::Provenance {
280        &self.provenance
281    }
282
283    /// Returns this data quality value's detected at.
284    pub const fn detected_at(&self) -> &source::Timestamp {
285        &self.detected_at
286    }
287
288    /// Returns this data quality value's resolution status.
289    pub const fn resolution_status(&self) -> ResolutionStatus {
290        self.resolution_status
291    }
292
293    /// Returns this data quality value's visible to bi.
294    pub const fn visible_to_bi(&self) -> bool {
295        self.visible_to_bi
296    }
297
298    /// Returns whether this issue must stop automation or projection into labor workflows.
299    pub const fn workflow_blocking(&self) -> bool {
300        self.workflow_blocking
301    }
302}