fix: safe status-recommendation flow, MO-016 order independence, brand constant, message codes

- Add a single shared, pure vehicle-status evaluator (app/services/vehicle_status.py)
  used identically by the data-quality scanner, a new non-mutating status-recommendation
  preview endpoint, and a transactional apply endpoint with optimistic-concurrency token
  revalidation -- eliminates the old opaque "calculate and apply" action and the unsafe
  "maintenance + active booking -> auto rented" shortcut. Frontend
  DataQualityIssueDetail.tsx now shows a review/decide/confirm panel with localized
  why/evidence/consequence text in nl-BE/en-GB/fr-BE, with an exact "Change status to
  <status>" confirm action per the brief.
- Fix MO-016 issue-order dependency: resolving the booking-overlap issue before vs.
  after the status-conflict issue now converges on the same final vehicle status,
  proven by test_mo_016_status_conflict_recommendation_is_order_independent.
- Make "Fleet Ops" a non-localizable brand constant (frontend/src/product.ts,
  backend PRODUCT_NAME) via {{productName}} interpolation everywhere the brand name
  appeared in locale prose; add a permanent test guarding against a translation file
  ever defining the brand name or an "appName" key again.
- Convert dynamic backend prose to stable message codes + params: return status
  reasons, audit field/actor-type labels, automation last_error, and search
  section/vehicle/booking/issue results all now carry codes the frontend localizes,
  with raw technical text demoted to a "Technical details" disclosure.
- docs/fleet-ops-correction/: gap audit, i18n inventory, and the vehicle-status
  decision table documenting the evaluator's rules and safe-status principles.

148 backend tests + Ruff + mypy green; Alembic migration verified upgrade/downgrade;
frontend tsc/build and the i18n-coverage Playwright suite green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
NuklearRabbit
2026-08-03 21:37:34 +02:00
co-authored by Claude Sonnet 5
parent 18344bc8b7
commit 6deb95524d
68 changed files with 1734 additions and 225 deletions
@@ -40,6 +40,29 @@
"was": "{{field}}: was {{value}}",
"changed": "{{field}}: {{before}} → {{after}}"
},
"actorTypes": {
"user": "User",
"system": "System",
"service": "Service"
},
"fields": {
"status": "Status",
"operational_status": "Operational status",
"registration_number": "Registration number",
"make": "Make",
"model": "Model",
"location": "Location",
"first_name": "First name",
"last_name": "Last name",
"email": "Email",
"phone": "Phone",
"postal_code": "Postal code",
"city": "City",
"booking_end_odometer_km": "Booking end odometer",
"vehicle_odometer_km": "Vehicle odometer",
"survivor": "Retained profile",
"loser": "Merged profile"
},
"actions": {
"demo_login": "Logged in",
"demo_logout": "Logged out",
+1 -1
View File
@@ -3,7 +3,7 @@
"orgLine": "Demo organisation: {{orgName}} (fictional)",
"headline1": "Every hand-off.",
"headline2": "One clear view.",
"defaultDescription": "Fleet Ops brings vehicle, booking and operational data together, supports rental processes, detects data-quality problems and automates controlled follow-up steps.",
"defaultDescription": "{{productName}} brings vehicle, booking and operational data together, supports rental processes, detects data-quality problems and automates controlled follow-up steps.",
"footnote": "Synthetic demo · no real customer or vehicle data · resettable at any time",
"accessEyebrow": "Demo access",
"accessHeading": "Choose how to start",
+1 -2
View File
@@ -1,5 +1,4 @@
{
"appName": "Fleet Ops",
"orgName": "Northstar Mobility",
"brandTagline": "Control Centre",
"actions": {
@@ -35,7 +34,7 @@
"fr-BE": "Français"
},
"footer": {
"productLine": "Fleet Ops Demo",
"productLine": "{{productName}} Demo",
"locale": "Europe/Brussels · Synthetic demo data"
},
"demo": {
+4 -4
View File
@@ -83,7 +83,7 @@
"whatYouWillSee": "An overview of what's functionally implemented in this demo, what's synthetic, and which integrations aren't live yet.",
"whyItMatters": "A demo is only convincing if visitors can verify for themselves what really works and what's still ahead.",
"startAction": "Read the 'About this demo' page.",
"expectedOutcome": "You can explain yourself what Fleet Ops is and isn't, with no verbal explanation needed."
"expectedOutcome": "You can explain yourself what {{productName}} is and isn't, with no verbal explanation needed."
}
}
},
@@ -146,16 +146,16 @@
},
"about": {
"eyebrow": "About this demo",
"title": "What Fleet Ops is and isn't",
"title": "What {{productName}} is and isn't",
"description": "{{orgName}} is a fictional rental organisation that makes this demo tangible — not a real company.",
"loading": "Loading demo information…",
"ctaTitle": "Prefer to jump right in?",
"ctaBody": "The guided demo walks through all eight steps above in practice.",
"ctaButton": "Start guided demo",
"problemTitle": "The fictional problem",
"problemBody": "{{orgName}} rents around 50 campers and vans from one main location. Bookings, returns, customer records and maintenance used to live in separate spreadsheets and verbal hand-offs, so problems (duplicate customers, incorrect odometer readings, double-booked vehicles) only surfaced late. Fleet Ops shows how one connected system flags these problems early and lets them be resolved under control.",
"problemBody": "{{orgName}} rents around 50 campers and vans from one main location. Bookings, returns, customer records and maintenance used to live in separate spreadsheets and verbal hand-offs, so problems (duplicate customers, incorrect odometer readings, double-booked vehicles) only surfaced late. {{productName}} shows how one connected system flags these problems early and lets them be resolved under control.",
"scopeTitle": "Who it's for and its scope",
"scopeBody": "This demo is for anyone who wants to see how Fleet Ops tackles operational problems at a small rental company: Operations Managers and Rental Employees, and anyone evaluating the approach. The scope is deliberately focused on one connected proof of concept — no accounting, no payments, no public reservations, no full CRM or ERP.",
"scopeBody": "This demo is for anyone who wants to see how {{productName}} tackles operational problems at a small rental company: Operations Managers and Rental Employees, and anyone evaluating the approach. The scope is deliberately focused on one connected proof of concept — no accounting, no payments, no public reservations, no full CRM or ERP.",
"realTitle": "What really works",
"realBody": "Everything below is functional code, not just a mockup: role-based access and sessions, vehicle and booking management, return processing with server-side validation, five data-quality rules each with its own resolution step, a full audit trail, automated delivery to n8n with bounded retries, Docker-based deployment, and an automated test suite (backend and Playwright end-to-end).",
"syntheticTitle": "What's synthetic",
+9 -1
View File
@@ -63,6 +63,14 @@
"noQualityIssues": "No quality issues recorded.",
"fuel": "Fuel {{percent}}%",
"damage": "Damage",
"technicalWarning": "Technical warning"
"technicalWarning": "Technical warning",
"inspectionTypes": {
"departure": "Departure inspection",
"return": "Return inspection"
},
"maintenanceCategories": {
"periodic_service": "Periodic service",
"repair": "Repair"
}
}
}
@@ -65,6 +65,13 @@
"noAction": "—",
"eventTypes": {
"vehicle.returned.v1": "Vehicle return processed"
},
"errorCodes": {
"connectionError": "The workflow service was temporarily unreachable. The return was safely saved and can be retried.",
"malformedPayload": "The job contained incomplete data and could not be delivered. The underlying data remains safely stored.",
"remoteReportedFailure": "The workflow service declined the delivery. The job can be retried.",
"staleLeaseRecovered": "This job was recovered after an earlier delivery attempt stalled without a result.",
"unknownError": "An unexpected error occurred while delivering this job."
}
}
}
@@ -24,7 +24,7 @@
"Which checks are required before checkout?"
],
"emptyTitle": "Evidence before answers",
"emptyDescription": "Ask about returns, damage, inspections or another indexed procedure. Fleet Ops will not invent an answer when evidence is missing.",
"emptyDescription": "Ask about returns, damage, inspections or another indexed procedure. {{productName}} will not invent an answer when evidence is missing.",
"retrievalFlow": {
"question": "Question",
"sources": "Sources",
@@ -27,12 +27,22 @@
"resetConfirmYes": "Yes, reset",
"resetCancel": "Cancel",
"resetFailed": "Could not reset demo data.",
"searchLabel": "Search Fleet Ops",
"searchLabel": "Search {{productName}}",
"searchPlaceholder": "Search fleet, booking or section…",
"searchShortcutHint": "Ctrl K",
"searchSearching": "Searching…",
"searchUnavailable": "Search is unavailable right now.",
"searchNoResults": "No matches for \"{{query}}\".",
"searchVehicleSummary": "{{make}} {{model}} · {{location}}",
"searchSections": {
"overview": "Operations dashboard",
"fleet": "Vehicle registry",
"bookings": "Rental bookings",
"quality": "Quality workbench",
"knowledge": "Procedure assistant",
"integrations": "Automation and integration status",
"audit": "Audit history"
},
"switchRole": "Switch role",
"switchRoleTitle": "Switch demo role",
"languageSwitcherLabel": "Change language"
+40 -5
View File
@@ -180,13 +180,48 @@
"heading": "Resolve the status conflict",
"description": "One authoritative rule recommends a corrected operational status for this vehicle.",
"currentStatus": "Current status",
"calculateAndApply": "Calculate and apply recommended status",
"confirmTitle": "Confirm status change",
"confirmBody": "Apply the authoritative recommended status for this vehicle?",
"reviewRecommendation": "Review recommendation",
"loadingRecommendation": "Loading recommendation…",
"recommendationFailed": "Could not load a recommendation.",
"recommendedStatus": "Recommended status",
"whyHeading": "Why",
"evidenceHeading": "Facts",
"consequenceHeading": "Consequence",
"consequence": {
"statusWillChange": "The vehicle status will change to {{status}}.",
"issueWillBeRechecked": "This quality issue will be re-checked.",
"changeWillBeAudited": "The change will be recorded in the audit trail.",
"bookingsNotDeleted": "The bookings themselves are not deleted."
},
"changeStatusTo": "Change status to {{status}}",
"applying": "Applying…",
"confirmYes": "Yes, apply",
"applied": "Applied {{status}} — {{reason}}",
"applyFailed": "Could not apply a recommended status."
"applyFailed": "Could not apply a recommended status.",
"staleRecommendation": "The situation has changed since this recommendation was shown. Review it again before applying.",
"reviewAgain": "Review recommendation again",
"manualReview": {
"heading": "Manual review required",
"body": "The facts for this vehicle contradict each other. An Operations Manager needs to review this in person rather than apply an automatic status change.",
"hint": "Use 'Defer' or 'Reject' below, or investigate the vehicle and the related bookings manually."
},
"noConflict": {
"heading": "No change needed",
"body": "This vehicle's current status already matches the facts."
},
"evidence": {
"activeBookings": "Active booking(s): {{refs}}",
"overlappingBookings": "Overlapping bookings: {{pairs}}",
"serviceThresholdReached": "Odometer reading ({{odometer}} km) has reached the service threshold ({{threshold}} km).",
"openOverlapIssue": "Open booking-overlap issue: {{ref}}"
},
"reasonCodes": {
"vehicle.active_rental": "This vehicle has an active rental but is not recorded as rented. The status is corrected to reflect the actual situation.",
"vehicle.service_threshold_reached": "This vehicle has reached its service threshold. It is set to maintenance so it is not deployed before servicing takes place.",
"vehicle.booking_conflict": "This vehicle has two overlapping bookings. Blocking it prevents it from being considered available again before the booking conflict is resolved.",
"vehicle.rental_ended": "The rental for this vehicle has ended and there is no active booking left. It is set back to available.",
"vehicle.manual_review_required": "The facts for this vehicle contradict each other (for example, an active booking while the vehicle is in maintenance, or a service threshold reached alongside an active rental). This requires a human review rather than an automatic status change.",
"vehicle.no_conflict": "This vehicle's current status already matches the facts. No change is needed."
}
}
}
}
+9 -1
View File
@@ -36,6 +36,13 @@
"odometerRegressionWarning": "Submitted odometer ({{submitted}} km) is below the canonical reading ({{canonical}} km). The canonical odometer will not change, and a data-quality issue will be opened.",
"nextBookingRisk": "Next booking {{ref}} starts {{when}} — may be affected by this return.",
"nextBookingLowRisk": "Next booking {{ref}} starts {{when}} — low risk.",
"reasonCodes": {
"returnBlockedDamageAndTechnical": "Both damage and a technical warning were reported on this return.",
"returnBlockedDamage": "Damage was reported on this return.",
"returnBlockedTechnicalWarning": "A technical warning was reported on this return.",
"returnServiceThresholdReached": "The odometer reading has reached the {{threshold_km}} km service threshold.",
"returnRoutedToCleaning": "No damage, technical warning or service threshold reached; the vehicle goes to cleaning."
},
"commitList": {
"inspection": "Create a return inspection",
"updateAtomic": "Update the booking and vehicle atomically",
@@ -66,8 +73,9 @@
"continueDemo": "Continue the demo"
},
"scenario": {
"ariaLabel": "Demo scenario",
"title": "Demo scenario: odometer anomaly",
"body": "This vehicle currently reads {{odometer}} km. The form below is pre-filled with a return reading below that — a sign of a data-entry mistake or a mixed-up vehicle. Confirm the return to see how Fleet Ops detects and handles this.",
"body": "This vehicle currently reads {{odometer}} km. The form below is pre-filled with a return reading below that — a sign of a data-entry mistake or a mixed-up vehicle. Confirm the return to see how {{productName}} detects and handles this.",
"preparing": "Preparing scenario…"
}
}