# 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 an inspection or maintenance reading below the canonical odometer. Never lower the canonical value automatically. 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` (updates a named related booking's reading and the vehicle's canonical odometer together). A `correct_reading` value below the current canonical is rejected, since it would not resolve the regression, not silently applied. ## 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 — the CSV seed rows don't carry a stable evidence fingerprint, so the literal `(..., evidence fingerprint)` scheme from an earlier draft of this rule was dropped as unworkable for seeded data; re-implementing it would need to reconcile with that. 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.