Adds artifacts/fleet-ops-release/final-summary.md with the complete evidence trail for this release: commits, branding, locale/translation/knowledge-base coverage, adaptive Demo Guide behaviour per breakpoint, Data Quality/Automation/Audit/clickable-row improvements, full test results (backend, lint, build, 92 Playwright tests) re-run against the local stack, an isolated clean-checkout drill, and both the feature-branch and post-merge master deployments to Unraid -- plus 10 screenshots across the three languages, desktop and mobile. Updates PROJECT_STATE.md with the corresponding summary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
17 KiB
17 KiB
Fleet Ops release — final-product-polish evidence
Result: PASS
Commits
- Original feature-branch baseline before this task:
257a4cf(docs(polish): audit finale demo-afwerking) - Feature-branch commits added this task, on
feat/mobilityops-functional-completion:337f871— polish: rebrand to Fleet Ops, add trilingual i18n, adaptive demo guide, and UX overhaul845db14— fix: mobile topbar overflow at 421-440px and add trilingual responsive coverage
- Feature branch final commit:
845db14e172539b1d10e40f6a3249a72122deb41 masterbefore merge (verified against the previously recorded baseline):e0c7ed60112510687627d20a957af91c8b9db7f8— unchanged, no unexpected commits, no conflicts (confirmed viagit merge-treedry run before merging)- Merge commit on
master:18a765d62345ea9a6660d04fb868f218cf4d0b6e(merge: release Fleet Ops multilingual demo,--no-ff) - Final
mastercommit (pushed and deployed):18a765d62345ea9a6660d04fb868f218cf4d0b6e - Deployed commit on Unraid (
.deploy/source-revision):18a765d62345ea9a6660d04fb868f218cf4d0b6e - Feature branch was not deleted, per instruction.
URL
- Live review deployment:
http://192.168.10.150:1236
Visible branding
- Product name "Fleet Ops" (with a space) visible in: sidebar brand lockup, browser tab title, login screen, footer product line, About page heading ("What Fleet Ops is and isn't" / "Wat Fleet Ops wel en niet is" / "Ce que Fleet Ops est et n'est pas"), demo badge popover, dashboard copy, all 3 languages.
- No visible "MobilityOps" or "PoC"/"proof of concept" wording remains in user-facing copy (verified by full-page inspection of all main routes in all 3 languages plus a targeted source grep for stray hardcoded strings). The repository, Docker image names, and internal git history retain "MobilityOps" (out of scope; not user-visible).
- Retained technical identifiers (unchanged, as instructed): API paths (
/api/v1/...), Docker Compose project name (mobilityops), internal vehicle/customer reference prefixes (MO-,CUS-), Gitea repository name.
Supported locales
nl-BE(default for a fresh session, unauthenticated visitor)en-GBfr-BE- Persisted via
localStoragekeyfleetops.language; survives refresh, logout/login, and demo reset. No flags used — accessible<select>language picker (visible name/code) in the topbar (desktop/tablet) and inside the mobile navigation drawer (≤960px, to avoid topbar overflow).document.documentElement.langkept in sync. All dates/numbers rendered viaIntl.DateTimeFormat/Intl.NumberFormat(Europe/Brusselstimezone).
Translation coverage
frontend/e2e/i18n-coverage.spec.ts: recursively compares every key path across all 3 locale files for all 14 namespaces (common, auth, navigation, dashboard, fleet, bookings, returns, quality, knowledge, integrations, audit, demo, errors, accessibility) and fails the build on any missing key or empty string value. 2/2 passed in every gate run this task (local, clean-checkout, and live-deployment runs).- Command:
npx playwright test e2e/i18n-coverage.spec.ts --project=chromium
Knowledge-base locales
knowledge/procedures/{nl-BE,en-GB,fr-BE}/— 11 procedure documents per language (samedocument_ids across languages so citations stay stable): vehicle checkout, vehicle return, damage handling, odometer anomalies, cleaning checklist, maintenance escalation, customer documents, privacy, booking conflicts, roles/escalation, and a new vehicle availability procedure (added this task to cover the "vehicle-available-again" guided-demo step explicitly).DemoKnowledgeProvidernow retrieves per-language (only searches the UI-selected language's corpus), with localized "no match"/"low confidence" boilerplate text per language; the frontend passes the active UI language on every/api/v1/knowledge/questionsand/api/v1/knowledge/statuscall.- Verified live in all 3 languages this task (see Browser evidence below): NL/EN/FR suggested questions each return grounded, correctly-cited, same-language answers.
- Backend unit tests:
test_demo_provider_grounds_damage_question_in_dutch,test_demo_provider_grounds_damage_question_in_french,test_demo_provider_health_reports_document_count_per_language,test_demo_provider_insufficient_evidence_message_is_localized— all passing.
Demo Guide — adaptive per breakpoint
- Extra-wide desktop (≥1440px): docked rail (
.demo-guide-panel.is-wide), fixed 420px minimum width, no drop shadow (reads as part of the layout), never auto-collapses. Verified:demo-guide.spec.ts→ "wide desktop viewport docks the guide as a rail that never collapses to a chip". - Standard desktop/tablet (701–1439px): floating non-modal panel that auto-collapses to a persistent, closable progress chip ("Demo-gids · stap X van Y") the instant "Ga naar deze stap" is used; chip has its own expand action and a separate close (×) control; reopens on one click; content reflow padding shrinks to 0 while collapsed so nothing is permanently blocked. Verified: 3 dedicated tests in
demo-guide.spec.ts. - Mobile (≤700px): bottom sheet with collapsed / half / full states, a drag-handle button that cycles states, no horizontal overflow, primary actions (Volgende/Ga naar deze stap) reachable in the half state. Verified:
demo-guide.spec.ts→ "mobile viewport shows a bottom sheet with collapsed/half/full states and no horizontal overflow", plusdemo-accessibility.spec.ts→ "demo guide is usable as a mobile bottom sheet". - Cross-cutting (4D): "Ga naar deze stap" scrolls the on-page target into view, moves programmatic focus to it (
tabindex=-1+.focus()), and applies a 2.2s outline pulse (.demo-guide-highlight, disabled underprefers-reduced-motion); Escape collapses the standard-tier panel first, then closes it on a second press; progress (currentIndex/completed) persists insessionStorageacross navigation and reload. Verified:demo-guide.spec.ts→ "Escape collapses the standard-tier panel, then closes it" and "going to a step scrolls, focuses and highlights the on-page target". - Fixed along the way: two dangling
aria-labelledbyreferences (SectionHeadingnever actually set the referencedid) on Dashboard and Data Quality Issue Detail panels.
Data Quality Workbench improvements
- Replaced plain radio rows with accessible
.choice-cardselectable tiles (title, consequence detail,:has(input:checked)/.is-selectedstate, visible focus ring, hover state) across the duplicate-customer survivor choice, odometer-regression decision, and booking-overlap block choice. - Clear action hierarchy: primary resolve/apply/merge action uses
.button-primary; defer uses a de-emphasized.button-tertiary; reject uses.button-tertiary-destructive(muted, turns critical-red only on hover) — no longer visually competing with the recommended resolution. - Technical evidence (
evidence_json) collapsed by default behind a localized "Technical details"<details>disclosure. - Contrast/opacity audited: no unintended overlays, disabled-looking text, or weak borders found beyond the (fixed) dangling-aria-labelledby issue.
Terminology mapping
- Achieved via the i18next namespace architecture itself rather than a separate module: technical codes (rule types, statuses, action codes, integration states) resolve through dedicated JSON keys (
quality:ruleTypes.*,quality:list.status*,audit:actions.*,integrations:statusLabels.*,fleet:statuses.*,bookings:statuses.*) with a human label in all 3 languages; raw technical values (correlation IDs, full UUIDs, raw evidence JSON) are confined to "Technical details" disclosures. Example mappings implemented:possible_duplicate_customer→ "Possible duplicate customer"/"Mogelijke dubbele klant"/"Client peut-être en double";demo_login→ "Logged in"/"Ingelogd"/"Connecté"; n8ndegraded→ "Retry available"/"Opnieuw proberen mogelijk"/"Nouvelle tentative possible";not_configured/disabled→ "Not connected"/"Niet gekoppeld"/"Non connecté".
Automation / audit improvements
- Automation ledger: succeeded events group and collapse when >3 in view ("Show N succeeded jobs"/"Hide individual jobs"), filter chips (needs-attention/recent/succeeded/all), meaningful short refs (
AUT-RET-####derived from the aggregate ref, full UUID behind a<details>), localized event types and statuses. - Audit trail: events grouped by
correlation_idinto one card with a human action-label heading (audit:actions.*), related-event count and an expandable technical list; readable before/after diff (ChangeDiffcomponent: humanized field names,set to/was/X → Yphrasing) instead of raw JSON by default; short reference (AUD-XXXXXXXX) with full UUID and correlation ID behind "Technical details".
Attention Queue / clickable rows
- Full "stretched link" pattern applied to: Attention Queue, Today's movements, Vehicles table, Bookings table, Data Quality table. Entire row is one activation target (pointer cursor, hover state, keyboard-focusable, Enter/Space activates), secondary in-row links (e.g. the vehicle reference inside a booking row) remain independently clickable via
.cell-link { z-index: 2 }layered above the row overlay. - Dedicated tests in
frontend/e2e/clickable-rows.spec.ts(8 tests): click on empty row space, keyboard focus + Enter, mobile-viewport click, secondary-link independence, correct routing for each of the 5 surfaces, pointer-cursor/focus-ring check.
Test results (all commands re-run against this exact final state)
Backend (local dev stack, clean-checkout instance, and live Unraid deployment — all three, all green)
docker compose exec api pytest -q → 131 passed
docker compose exec api ruff check . → All checks passed!
docker compose exec api mypy app → Success: no issues found in 48 source files
Frontend
cd frontend && npm run build → tsc -b && vite build: success
Playwright (92 tests; run against local dev stack, the isolated clean-checkout stack, and the live Unraid deployment — 92/92 passed in all three runs)
npx playwright test --project=chromium
Suites: demo-accessibility, demo-entry, demo-guide (including the 3 new adaptive-breakpoint tests, chip close-control test, Escape test, scroll/focus/highlight test), demo-legibility, demo, guided-demo-full, i18n-coverage, interactive-elements, responsive-i18n (7 breakpoints × 3 languages = 21 tests), ui-redesign, clickable-rows (new, 8 tests).
Clean-checkout drill (evidence)
Performed in an isolated environment (separate Compose project mobilityops-clean, separate host ports 8129/1229, no shared volumes or n8n) so the user's existing long-running dev/n8n environment was never touched:
git cloneof the local repository at commit845db14(feature branch, pre-merge) into a scratch directory.cp .env.example .env(project name and ports overridden for isolation only).docker compose up --build -d db api web— migrations ran automatically on API startup.docker compose exec api python -m app.cli seed --reset— deterministic seed loaded (users:2, customers:180, vehicles:50, bookings:246, inspections:75, maintenance:40, data_quality_issues:26, workflow_runs:20).docker compose exec api pytest -q→ 131 passed.ruff check .→ clean.mypy app→ clean.npm ci && npm run build→ clean build.npx playwright test --project=chromium(pointed at the isolated stack viaMOBILITYOPS_PUBLIC_URL) → 92 passed.- Live browser verification in English and French (Dutch already covered as the automated-suite default): guided-demo dashboard, knowledge-assistant grounded answers in both languages with correct same-language citations.
POST /api/v1/demo/reset→scenario_integrity: {"all_ready": true, "not_ready": []}.- Isolated stack torn down (
docker compose down -v) — original dev environment (containers, n8n owner account/workflows) confirmed untouched and healthy throughout.
No PASS was claimed from pre-existing containers at any point — every gate above ran against a stack built from empty volumes.
Server deployment evidence
- Deployed via the established safe method:
git archivefrom the exact commit →scpto.deploy/source-<sha>.tar.gzon Unraid → extract → update.deploy/source-revision→docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml up --build -d api web(db never rebuilt; server.envand named volumes — Postgres, n8n — preserved throughout). - Deployed twice this task: once for the feature branch (
845db14) for pre-merge live validation, once for the mergedmaster(18a765d) for the final release. - Post-deploy, both times: migrations confirmed at head (
e7b08389f47f), reseed run,pytest/ruff/mypyre-run in the container (all green), full 92-test Playwright suite re-run against the live URL (all green), console/network inspected via live browser (no errors, all/api/*calls 200), demo reset performed,scenario_integrity.all_ready: trueconfirmed both times. - Real shared n8n instance (
http://192.168.10.150:5678) integration confirmed live: the seeded failed-demo automation event correctly shows "Retry available"/"Opnieuw proberen mogelijk" (not the rawdegradedstring) on the Integration pulse card.
Responsive / accessibility
- No-horizontal-overflow verified across the full 7-breakpoint matrix (1440×1000, 1280×800, 1024×768, 768×1024, 430×932, 390×844, 360×800) in all 3 languages (
responsive-i18n.spec.ts, 21 tests) plus the original 4-breakpoint English suite (ui-redesign.spec.ts). - Real bug found and fixed during this pass: the new topbar language switcher pushed the 421–440px range into horizontal overflow (the existing "compact topbar" breakpoint stopped at 420px). Fixed by widening that breakpoint to 440px; re-verified clean at exactly 430px in all 3 languages.
- Focus-visible outlines,
prefers-reduced-motionhandling (demo-guide highlight pulse, bottom-sheet height transitions), and keyboard reachability verified viademo-accessibility.spec.tsand the new adaptive-guide/clickable-row tests.
Screenshots
artifacts/fleet-ops-release/screenshots/:
01-login-nl.jpg— login screen, Dutch default, language selector visible02-dashboard-nl-desktop.jpg— dashboard, Dutch, Attention Queue + Integration status03-data-quality-choice-cards.jpg— Data Quality Workbench choice-card redesign (duplicate-customer merge)04-integrations-nl.jpg— Integrations page, grouped/filterable automation ledger05-audit-trail-nl.jpg— Audit trail, correlation-grouped human action labels06-dashboard-en-desktop.jpg— dashboard, English07-dashboard-fr-desktop.jpg— dashboard, French08-about-fr.jpg— About page, French, confirming full rebrand + translated content09-mobile-guide-bottom-sheet.png— mobile bottom sheet, half state (390×844)10-mobile-guide-full.png— mobile bottom sheet, full state (390×844)
Known limitations
- Data-quality evidence "summary" strings (the free-text detail line under each Attention Queue/Data Quality row, e.g. "exact email; exact phone; similar name") remain English-only — these are generated deep in the deterministic rule engine as diagnostic strings, not yet converted to message codes. The rule-type label, status, and all surrounding UI are fully localized; only this one diagnostic fragment is not. Documented as a follow-up, not blocking.
- RAGcore and ITWorx MCP Hub remain honestly labelled as not live-connected (unchanged from prior milestones) — the demo knowledge base is the multilingual, fully-verified stand-in.
- Vehicle/customer internal reference prefixes (
MO-,CUS-) were left unchanged; they are generic internal codes, not user-visible "MobilityOps" branding, and changing them was out of scope for this task. - Automated live-browser evidence for the guided demo was captured in Dutch (via the automated Playwright suite, which defaults to the app's own nl-BE default) and manually spot-checked live in English and French (knowledge assistant, dashboard, About page); a full manual click-through of all 8 guided-demo steps was not repeated live in all 3 languages beyond the automated
guided-demo-full.spec.ts(Dutch) and the targeted EN/FR checks documented above, given the exhaustive automated coverage already exercising the same code paths per language viaresponsive-i18n.spec.tsandi18n-coverage.spec.ts.
Rollback procedure
.deploy/source-revisionon Unraid records the exact deployed commit (18a765d62345ea9a6660d04fb868f218cf4d0b6e).- Prior tarballs remain in
.deploy/on the server, including.deploy/source-845db14.tar.gz(feature branch, pre-merge) and.deploy/source-4a268c7.tar.gz(previous release, pre-polish). - To roll back: extract the desired
source-<short-sha>.tar.gz, update.deploy/source-revisionto match, and re-rundocker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml up --build -d api web. Database migrations on this branch are additive only; no destructive migration was introduced.