Files
MobilityOps/docs/07-data-quality.md
T
NuklearRabbit 81e3fd63bd
MobilityOps acceptance / backend (push) Failing after 19s
MobilityOps acceptance / frontend (push) Successful in 25s
MobilityOps acceptance / e2e (push) Skipped
M54: harden operations and demo resilience
2026-08-24 03:31:03 +02:00

87 lines
4.2 KiB
Markdown

# Data-quality rules
Run rules after seed/import, after relevant commands and through an explicit scan service. Rules must be deterministic and explainable.
## DQ-01 Possible duplicate customer
Signals and example weights:
- exact normalized email: 60;
- exact normalized phone: 50;
- exact postal code: 10;
- strong normalized full-name similarity: up to 30.
Open an issue at score >= 70. Store individual signals; do not expose a mysterious AI-only confidence.
Resolution:
- merge into selected survivor;
- reject as not duplicate;
- defer.
Merge rewires booking references, preserves the loser as a tombstone and audits before/after values.
## DQ-02 Missing required field
Required for active customers: first name, last name and at least one of email or phone. Required for active vehicles: registration number, make, model and location.
Resolution: `POST /provide-fields` accepts only the fields the entity type actually
requires (rejects anything else), applies them, and re-runs the same missing-field check.
The issue resolves only once nothing required remains missing; a partial submission
updates the record and its evidence but leaves the issue open.
## DQ-03 Odometer regression
Flag a newly recorded checkout, return-inspection or maintenance reading below the
canonical odometer. The deterministic import scan compares booking, inspection and
maintenance readings in chronological order. A return inspection's actual completion
time is authoritative over the booking's planned end, so an early return cannot be
mistaken for a later regression. Never lower the canonical value automatically.
When another regression is recorded while the vehicle already has an open DQ-03 issue,
append the new source reference and signal to that issue. Do not silently discard the
new evidence and do not open parallel issues for the same vehicle.
Resolution: `POST /resolve-odometer-regression` offers exactly two bounded decisions —
`retain_canonical` (the submitted reading is treated as erroneous; canonical is
untouched) or `correct_reading`. The correction option is offered only for an explicitly
named, correctable booking reading; checkout-only and maintenance-only issues can only
retain the canonical value. A corrected return updates the booking, its related return
inspection and the vehicle's canonical odometer in one transaction. A
`correct_reading` value below the current canonical is rejected, since it would not
resolve the regression, not silently applied. When one open issue contains several
source readings, correcting one removes only that booking/inspection evidence and keeps
the issue open until every remaining signal has received a bounded decision.
## DQ-04 Booking overlap
Flag overlapping `reserved` or `active` bookings for one vehicle. Normal write APIs reject new overlaps; the seed/import path may create one controlled legacy conflict.
Resolution: `POST /resolve-overlap` blocks one of the two named overlapping bookings
(minimal safe resolution, not a scheduling calendar) and re-verifies no
reserved/active overlap remains among the issue's related bookings before resolving.
## DQ-05 Vehicle status conflict
Examples:
- status `available` while an active booking exists;
- status `rented` without an active booking;
- status `available` while critical open quality issue exists;
- status `maintenance` with an active booking.
Resolution: `POST /apply-recommended-status` computes a recommendation from one
authoritative function mirroring the conditions above, applies it, and re-runs the same
function to confirm the conflict is actually gone before resolving.
## Lifecycle
Detection is idempotent by `(rule_type, entity_type, entity_id)` while open. Source-aware
rules additionally store stable evidence identity. For DQ-03 this is the tuple
`(source_type, later_ref, later_km)`: an explicit `retain_canonical` decision suppresses
only that exact fact on later scans. A changed reading or a different source is actionable
again. Resolved issues remain historical. Reintroduced evidence creates a new issue whose
evidence carries `reopened_from` (the prior issue's reference) and `previous_decision`
(its resolved status), so a repeat problem is never presented as if no decision was ever
made.