Commit Graph
41 Commits
Author SHA1 Message Date
NuklearRabbit 81e3fd63bd M54: harden operations and demo resilience
MobilityOps acceptance / backend (push) Failing after 19s
MobilityOps acceptance / frontend (push) Successful in 25s
MobilityOps acceptance / e2e (push) Skipped
2026-08-24 03:31:03 +02:00
NuklearRabbit 00191e9b54 M48: harden demo operations and offsite recovery
MobilityOps acceptance / backend (push) Failing after 20s
MobilityOps acceptance / frontend (push) Successful in 28s
MobilityOps acceptance / e2e (push) Skipped
2026-08-21 22:17:49 +02:00
NuklearRabbit acd8b82b09 M44: harden release integrity and assurance
MobilityOps acceptance / backend (push) Failing after 20s
MobilityOps acceptance / frontend (push) Successful in 26s
MobilityOps acceptance / e2e (push) Skipped
2026-08-21 18:32:02 +02:00
NuklearRabbit ae39a8947f M39: harden application and acceptance gates
MobilityOps acceptance / backend (push) Failing after 45s
MobilityOps acceptance / frontend (push) Successful in 32s
MobilityOps acceptance / e2e (push) Skipped
2026-08-17 03:17:44 +02:00
NuklearRabbit 9dfbd7c4bf M36: deepen operational and mobile UX 2026-08-10 22:53:45 +02:00
NuklearRabbit efab8d816f M31: verify RAG inventory and polish attention queue
MobilityOps acceptance / backend (push) Canceled after 0s
MobilityOps acceptance / frontend (push) Canceled after 0s
2026-08-10 20:58:26 +02:00
NuklearRabbit 29325b6c27 M29: polish recruiter engineering story
MobilityOps acceptance / backend (push) Canceled after 0s
MobilityOps acceptance / frontend (push) Canceled after 0s
2026-08-10 18:13:18 +02:00
NuklearRabbit 90cc3cf378 M25: expose provenance-aware knowledge statistics 2026-08-10 16:04:39 +02:00
NuklearRabbit 0935901f11 M24: implement privacy governance 2026-08-10 15:56:03 +02:00
NuklearRabbit c3f1cfc699 M21: add optional organisation identity 2026-08-10 15:35:25 +02:00
NuklearRabbit 5d7a5e7359 M19: harden performance and recovery
MobilityOps acceptance / backend (push) Canceled after 0s
MobilityOps acceptance / frontend (push) Canceled after 0s
2026-08-10 12:51:41 +02:00
NuklearRabbit fe06ff75a1 M18: implement operational workspaces 2026-08-10 12:41:28 +02:00
NuklearRabbit 8030753dbc M17: ground knowledge and integration evidence 2026-08-10 12:27:17 +02:00
NuklearRabbit 15bdbe40ac M12: complete daily operations cycle 2026-08-10 03:15:46 +02:00
NuklearRabbit 4a3c3bd0a9 M11: implement operational booking lifecycle 2026-08-10 03:06:30 +02:00
NuklearRabbit aacf0e04bc fix: preserve mobile record status 2026-08-10 01:14:20 +02:00
NuklearRabbit f2cdad194c UX: implement visual product roadmap 2026-08-10 01:05:07 +02:00
NuklearRabbitandClaude Sonnet 5 c2b8268927 fix: form field alignment, raw maintenance text, and static movements list
- .form-grid labels (missing-field form, odometer-regression correction fields)
  and the odometer/overlap note textareas had no stacked label-above-input
  styling at all -- the shared rule only covered .filters/.return-form, so these
  fell back to default inline browser layout with mismatched input widths.
  Extended the existing rule to cover .form-grid and label:has(> textarea).

- Vehicle maintenance list showed the raw, untranslated seed text
  ("Synthetic scheduled service record") regardless of locale -- purely
  decorative and 1:1 redundant with the (already-translated) category. Replaced
  it with the record's real odometer reading, mirroring the sibling
  Inspections tab's pattern.

- "Today's movements" was always the same fixed 4 bookings (2 returns, 2
  departures) on every reset, reading as a static mockup rather than live
  fleet activity. Added 8 more bookings anchored to land on "today" across 8
  additional vehicles, spread through the day.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 18:29:56 +02:00
NuklearRabbit 6f77a30dce fix: stop the prepared demo failure from degrading n8n integration health
The demo seed plants exactly one failed delivery (BK-H-0020) to demonstrate
retry and audit. Because derive_n8n_status() counted any failure, every fresh
reset pinned the n8n integration to "degraded" -- the demo showed a warning
about a prop, which tells a viewer something untrue about the automation.

The seeded failure now carries its own error code, demoScenarioTimeout, rather
than the generic connectionError a real timeout produces. No column and no
migration: last_error_code already existed, is already surfaced to the UI and is
already localizable.

- integration status splits failed into unexpected_failed and
  demo_scenario_failed; only unexpected failures may move the state. A staged
  failure alone leaves n8n operational.
- latest_failure_at is a health signal and now ignores the staged failure;
  latest_demo_scenario_at reports it separately.
- /api/v1/workflows exposes is_demo_scenario. The Automation page labels the run
  as a prepared demo scenario, explains that it is a simulated temporary failure
  that does not affect automation health, and offers a distinct "retry demo
  scenario" action. Translated in nl-BE, en-GB and fr-BE.
- the carve-out stays narrow: a real failure still degrades n8n, and a genuine
  later failure of the same event overwrites the demo code with the real one.
- the retry itself is unchanged and real: the event goes back on the outbox and
  the dispatcher delivers it to n8n like any other, so 19+1 becomes 20+0 only on
  an actual round trip. The audit records which kind of failure was retried.

Tests that assert on the seeded scenario now reseed first, since earlier test
files legitimately mutate the outbox and the suite shares one database.

Verified locally against a real PostgreSQL 16: 181 passed, ruff clean, mypy
clean (50 files), tsc clean, frontend build clean. Not deployed and not
browser-verified.
2026-08-05 14:07:05 +00:00
NuklearRabbitandClaude Sonnet 5 727c19a779 M9: MCP Hub locale/correlation propagation, real Hub health check, fix stale test image
Fixed two concrete gaps in the MCP knowledge-search endpoint: no locale field
existed at all (now nl-BE/en-GB/fr-BE, wired to the knowledge provider's
existing language param), and the correlation ID was always freshly minted,
ignoring any inbound X-Correlation-Id header. Added a shared dependency and
applied it to all four MCP endpoints so Fleet Ops's own audit log preserves
the Hub's real correlation ID end to end.

MCP_HUB_BASE_URL/MCP_PROVIDER_ID were declared in .env.example but never read
anywhere. Since the Hub's own registration is catalog-driven (it never needs
Fleet Ops to push a registration call), wired mcp_hub_base_url for a real Hub
reachability health check instead of an unneeded self-registration call.

Renamed Fleet Ops's own internal audit tool labels mobilityops_* -> fleet_ops_*
(mirrored in contracts/mcp-tools.json with mobilityops_* kept as deprecated
aliases); documented that the live Hub connector's own dotted tool namespace
is a separate, Hub-owned naming layer, deliberately not touched.

Automation page's MCP card now shows real evidence (last tool/client/count/
timestamp, honest no-evidence state) instead of just the registration flag.

Also fixed a real methodology gap found mid-session: compose.yaml's api
service has no bind mount, so `docker compose run --rm api` silently tests a
stale image until rebuilt. Re-ran every local gate after rebuilding; fixed one
genuinely stale test assertion and two lint line-length errors surfaced by
that rebuild. 176 tests passing, ruff clean, mypy clean (50 files).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 13:30:24 +02:00
NuklearRabbitandClaude Sonnet 5 34df66d28c M8: GUI polish, n8n workflow-3 fixes, RAGcore retrieval root-cause and fix
GUI: dashboard Attention Queue presents a curated severity mix instead of pure
severity-sort (grouped Now/Today/Later headers); Today's Movements seed data
curated so a fresh reset shows a credible day (2+ departures, 2+ returns), with
a new seed-integrity test; About Demo restructured into a compact grid with
progressive disclosure for technical sections; Duplicate Merge shows match/conflict
counts, hides matching fields by default, and previews the final merged record
before confirmation.

Repo hygiene: removed a stray empty `backend;C` directory and an untracked 31MB
zip export; `.gitignore` now excludes future archive exports.

n8n: fixed invalid JSON (a missing `},` between two node objects) in the committed
`fleet-ops-vehicle-return.json` -- the file could not be parsed. Live-validated
workflow 3 (RAGcore Procedure Sync): found and fixed a real defect (three body
parameters had a stray trailing `}}`) and a missing Error Workflow wiring, both
via the safe `n8n import:workflow` CLI path; exported the corrected, still-
inactive workflow as the new source of truth and updated MANIFEST.md/check_drift.py.
Publishing it (starts real daily unattended runs) remains a separate decision.

RAGcore: root-caused and fixed (live, approved) the "zero retrieval candidates"
bug -- a filesystem permission bug (`embedding_profiles.json` unreadable by the
app's own runtime user) that broke every retrieval call before it reached Qdrant.
Every other suspect (grants, scope resolution, Qdrant filters, embeddings) was
verified healthy first. Found a second, deeper gap: the reranker adapter calls
an Ollama HTTP route that does not exist on the deployed Ollama version, so
`/v1/answers` still returns `not_answerable`. `KNOWLEDGE_PROVIDER` stays `demo`
until that is resolved on the RAGcore side. Evidence-based MCP Hub integration
status (real tool-call audit history, not just a boolean flag) replaces the old
`configured`/`not_configured` guess. Full findings in
`docs/final-integrations/current-state-audit.md`.

Backend: 172 tests passing, ruff clean, mypy clean (50 files). Frontend: tsc
clean, production build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 13:05:02 +02:00
NuklearRabbit 77208b857a fix: prevent topbar overflow from an unbreakable Dutch role-name translation
Correctly translating auth.json's roleOperationsManager from the old
two-word "Operations Manager" (which could wrap at the space) to the
single Dutch compound word "Operationsmanager" (which cannot) pushed the
topbar's .operator block past its 1024px-breakpoint budget, caught by
the existing responsive-i18n.spec.ts overflow test. Fixed with
overflow-wrap: anywhere on the role/name text and min-width: 0 on their
flex-item wrapper, rather than reverting the correct translation.
2026-08-04 03:09:17 +02:00
NuklearRabbit d17af1c52a feat: centralize API error localization
Replace the err instanceof ApiError ? err.message : t(fallback) anti-
pattern -- which showed raw English backend text for the common case and
only used the localized fallback for the rare network-failure case -- at
all 13 call sites across 7 files.

New frontend/src/api/errorMessages.ts (describeApiError) resolves a
caught error to a localized {title, explanation, nextStep?, technical}
by checking the 32 known AppError codes first, then known HTTP statuses
(401/403/404/409/422/500), then a fully generic fallback. New
ApiErrorNotice (PageChrome.tsx) renders title/explanation/nextStep with
the raw text demoted to a "Technical details"/"Details techniques"
disclosure -- never shown as the primary message.

ApiError itself is split out of client.ts into a standalone
api/apiError.ts with no import.meta.env dependency, so errorMessages.ts
(and its tests) can be loaded outside a Vite/browser context.
2026-08-04 03:08:07 +02:00
NuklearRabbitandClaude Sonnet 5 2e4fb43f09 fix: localize the primary data-quality evidence summary (live-caught on Unraid)
Live validation on the deployed fix branch caught a real bug: every data-quality
issue's top-of-page "Evidence summary" line rendered the raw, always-English legacy
evidence.summary string unconditionally -- in all three languages -- even though the
backend has been emitting structured, localizable evidence.signals for a while
(app/services/data_quality.py already documented this exact intent). The frontend
side of that conversion was never finished.

- DataQualityIssueDetail.tsx now renders evidence.signals through the operator's
  locale as the primary summary; the raw evidence.summary string is only visible
  inside "Technical details" (via the existing EvidenceDisclosure JSON dump).
- The four DQ-DEMO-* seed rows that anchor the guided demo's scripted scenarios now
  carry real, accurate signals computed at seed time (duplicate-customer's similarity
  score is the actual SequenceMatcher ratio on the seeded names, not invented) instead
  of only a legacy English sentence.
- Rows with no structured signals (generic filler seed data) fall back to the raw
  text rather than showing a blank summary; the one known placeholder string gets its
  own localized rendering so it never displays as English filler either.
- New regression test: the vehicle_status_conflict evidence summary must show
  localized text and must never contain the specific raw English sentence that was
  live-visible before this fix, in all 3 languages.

151 backend tests, Ruff, mypy green; full local Playwright suite green (a couple of
sequential-run-only flakes, both confirmed to pass in isolation and unrelated to this
change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 00:44:06 +02:00
NuklearRabbitandClaude Sonnet 5 6deb95524d 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>
2026-08-03 21:37:34 +02:00
NuklearRabbitandClaude Sonnet 5 845db14e17 fix: mobile topbar overflow at 421-440px and add trilingual responsive coverage
The 420px "compact topbar" breakpoint left a gap: at 421-440px the demo-guide trigger,
badge, operator block and logout button together overflowed the viewport (introduced by
this session's language-switcher addition). Widen the breakpoint to 440px.

Adds a dedicated Playwright spec asserting no horizontal overflow across the brief's full
7-breakpoint matrix (1440x1000 down to 360x800) in all three supported languages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 18:41:14 +02:00
NuklearRabbitandClaude Sonnet 5 337f8716bb polish: rebrand to Fleet Ops, add trilingual i18n, adaptive demo guide, and UX overhaul
Rebrands the product from MobilityOps to Fleet Ops across the UI, backend defaults and
knowledge base, and makes nl-BE/en-GB/fr-BE full first-class languages: i18next with
eager-bundled per-namespace resources, a persisted accessible language switcher (topbar
and mobile drawer), locale-aware date/number formatting, and a coverage test that fails
the build on any missing or empty translation key.

Backend dynamic content (demo scenarios, blocked-reason text, integration status) moves
from fixed English/Dutch prose to stable message codes + params so the frontend can
localize it; the demo knowledge base gains a fully translated NL/EN/FR procedure corpus
(11 documents each) with per-language retrieval and localized evidence-state messages.

The Demo Guide becomes breakpoint-adaptive: a docked rail on extra-wide desktop, a
floating panel that auto-collapses to a persistent, closable progress chip on standard
desktop/tablet, and a collapsed/half/full bottom sheet on mobile -- with scroll+focus+
highlight on "go to this step", Escape handling, and reduced-motion support.

The Data Quality Workbench gets accessible choice-card decisions with a clear primary/
secondary/tertiary action hierarchy; the Automation ledger groups repeated successes and
uses meaningful short refs; the Audit trail groups events by correlation id with human
action labels and readable before/after diffs. Attention Queue, Today's movements,
Vehicles, Bookings and Data Quality rows are fully clickable (stretched-link pattern)
with independent secondary links, keyboard support and mobile touch targets.

Fixes a topbar overflow on mobile caused by the new language switcher (moved into the
mobile drawer at <=960px) and two dangling aria-labelledby references introduced this
session. Updates all affected Playwright specs for the new nl-BE default and the new
Audit/DemoGuide DOM structure, and adds new i18n-coverage, demo-guide-adaptive and
clickable-rows specs. 131 backend tests, Ruff and mypy, and 71 Playwright tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 18:33:22 +02:00
NuklearRabbit 07d5605812 test(demo): add full guided-demo walkthrough and targeted demo tests
Adds one comprehensive Playwright test that walks a fresh Operations
Manager session through all 8 Demo Guide steps performing the real
action at each one, then restores the environment. Writing it surfaced
a real desktop layout bug: the Demo Guide's fixed side panel overlapped
main content with no reflow, making the return form's "Review return"
button unclickable while the guide was open at ordinary viewport widths.
Fixed by reserving layout space via a guide-open class. Also adds mobile
bottom-sheet, keyboard-reachability, and console-error checks.
2026-08-03 15:12:29 +02:00
NuklearRabbit 5fa4fe0811 feat(demo): plain-language integration status, richer audit, reset integrity
Integration status badges across Dashboard/Automation now show honest
plain-language labels instead of raw backend state strings (and fix a
few states that had no matching CSS colour class at all). Audit trail
gets a "view related events" action reusing the existing correlation_id
filter. About page gains scope/architecture/security/testing sections
and a guided-demo entry point. POST /api/v1/demo/reset now runs and
records a server-side scenario-integrity check. Also fixes a second real
race condition (caught by the return-review e2e test): the odometer
scenario pre-fill now resolves before ReturnForm mounts instead of
patching its value in after the fact.
2026-08-03 15:00:11 +02:00
NuklearRabbit ddc3a98e4b feat(demo): make return, data-quality and knowledge flows demo-legible
Fixes a real honesty bug in Knowledge.tsx (body copy named "RAGcore" while
the active provider is the demo one) and a second real bug discovered
while fixing it: the brief's suggested Dutch questions would silently
return "insufficient evidence" against the English-only demo knowledge
base -- verified empirically and fixed by keeping suggested questions in
English. The return flow now pre-fills the odometer-regression scenario's
suspicious reading instead of asking a visitor to invent one, and links
to automation/audit after committing. Data-quality issues get a shared
plain-language "what's wrong / why it matters" explainer per rule type,
a post-resolution confirmation with audit/vehicle links, and a "demo
scenario's only" list filter. Also fixes a real async race where the
odometer pre-fill could clobber text a visitor had already started typing.
2026-08-03 14:41:02 +02:00
NuklearRabbit 9fff84dc68 feat(demo): add Demo Guide (8-step guided tour) and scenario overview
Adds a compact "Probeer een demonstratiescenario" page listing the 5 named
scenarios with live readiness from the manifest, plus a Demo Guide side
panel (bottom sheet on mobile) that walks an Operations Manager through
all 8 steps with per-step context, live-resolved routes, sessionStorage
progress, and a "Demo opnieuw voorbereiden" restart. Login's guided-demo
CTA now actually opens the guide. Fixes a real mobile topbar overflow the
new guide trigger introduced.
2026-08-03 14:15:15 +02:00
NuklearRabbit ac427f4427 feat(demo): add demo manifest, Dutch demo entry, permanent badge and About page
Adds GET /api/v1/demo/manifest as a single source of truth for the demo's
fictional org identity (Northstar Mobility -- surfacing the project's
already-locked tenant name), synthetic-data/reset state, and live scenario
readiness. Rewrites the login screen in Dutch with an honest, no-password
demo entry and a guided-demo entry point, replaces the loud full-width
demo banner with a subtle badge + popover, and adds a compact About page
explaining what's real vs. synthetic vs. not yet connected.
2026-08-03 13:45:55 +02:00
NuklearRabbit 4437b8792a feat(search): add role-aware backend search and truthful n8n status
Two new endpoints. GET /api/v1/search returns bounded typed results
(vehicle, booking, data-quality-issue, application section) instead of the
frontend guessing routes from regex patterns against public-ref prefixes;
data-quality and manager-only sections are filtered server-side by role,
and customers are deliberately never returned since no customer detail
route exists in this PoC.

GET /api/v1/integrations/status aggregates outbox delivery counts
(pending/delivering/succeeded/failed) into a single truthful n8n state
(disabled/unavailable/degraded/operational/no_evidence) instead of the UI
showing whichever status the single most recent event happened to be in --
a vehicle_status_conflict-style bug where one stale failure or one lucky
success could misreport the dispatcher's actual health.

Also fixes a real config gap this surfaced: MCP_HUB_REGISTRATION_ENABLED
was documented in .env.example but had no corresponding Settings field, so
it was silently ignored by pydantic-settings' extra="ignore" and never
actually read anywhere in the codebase.
2026-08-02 06:41:56 +02:00
NuklearRabbit 1f292e14bb fix(deploy): reuse shared server n8n 2026-08-02 03:56:17 +02:00
NuklearRabbit d0f34e6933 feat(ui): introduce Control Rail design system and shell 2026-08-02 03:27:00 +02:00
NuklearRabbit b511ba2dbc M5: implement RAGcore knowledge integration
KnowledgeProvider protocol with a deterministic TF-IDF-weighted extractive demo provider (never generative, always cites real excerpts) and a RAGcore HTTP adapter that degrades cleanly to unavailable. Knowledge nav + chat-style Q&A UI with source cards and honest grounded/insufficient/unavailable states. 57 backend tests passing, ruff clean. Fixed a real relevance bug (generic terms like "vehicle" crowding out distinctive matches) via IDF weighting, found by testing the actual S6 scenario. Verified end-to-end in the browser: grounded damage question cites both expected procedures; unrelated question honestly returns insufficient evidence with no fabrication.
2026-08-01 22:57:06 +02:00
NuklearRabbit 59d663a43a M4: implement n8n automation
Outbox dispatcher (background thread, FOR UPDATE SKIP LOCKED claim, exponential backoff, no transaction held during HTTP I/O). n8n callback endpoint with shared-secret auth and idempotency by event ID. Automation nav + UI with manual retry. 49 backend tests passing, ruff clean. Fixed a crash-on-redelivery bug in seeded outbox payloads and made the dispatcher defensive against malformed payloads. Verified the full live round trip against a real n8n instance: return -> outbox -> dispatcher -> n8n workflow -> callback -> succeeded, including the S5 failed-retry demo scenario.
2026-08-01 22:39:25 +02:00
NuklearRabbit a7cbeaae3b M3: implement Data Quality Workbench
Five rule scanners (duplicate customers, missing fields, odometer regression, booking overlap, status conflict) run automatically after seed and via an explicit scan endpoint. Issue defer/reject/merge-customers endpoints with transactional customer merge (booking rewiring, tombstone, audit). Data Quality nav + workbench UI with two-column duplicate comparison and inline (non-native) confirm. Dashboard attention items now link to issues. 35 backend tests passing, ruff clean. Fixed a real false-positive bug in odometer-regression detection found through iteration on seed data, and two TS narrowing errors. Verified end-to-end via browser: S2 merge and S4 overlap scenarios.
2026-08-01 22:11:06 +02:00
NuklearRabbit 0091c57c7f M2: implement vehicle return vertical slice
Transactional return command with idempotency, row-lock concurrency control, odometer-regression handling, vehicle status derivation, outbox event, audit trail. Result-summary UI on booking detail. 26 backend tests passing, ruff clean. Verified end-to-end via browser against S1 demo scenario; fixed two real defects found only through browser testing (UI state loss on status transition, unflushed UUID default).
2026-08-01 21:49:46 +02:00
NuklearRabbit 03c5b60235 M1: implement operational core
Demo auth, seed import/reset, dashboard, vehicle/booking list+detail, audit trail. Backend: 19 tests passing, ruff clean. Frontend: React Router shell, typed API client, responsive pages. Verified end-to-end via curl and browser.
2026-08-01 21:20:53 +02:00
NuklearRabbit 24188d9b10 Initial MobilityOps build pack (docs, contracts, scaffold) 2026-08-01 20:34:43 +02:00