Documents the demo-productization work from this branch: the Northstar Mobility fictional concept and scope, the five named scenarios and their fixed records, the seed/date-anchoring strategy (including the real bug it fixed), the in-app Demo Guide's design and the English-suggested- questions decision, and an operational runbook covering 5/10-minute demo flows, reset, Unraid redeploy and rollback. Updates README with current test counts and pointers to the new docs.
4.2 KiB
Demo data strategy
Three separate concepts
It's important to keep these distinct — they solve different problems:
- Deterministic records — every seeded entity has a stable public reference
(
BK-DEMO-RETURN,DQ-DEMO-DUPLICATE,MO-024,CUS-0012, ...). These references never change between seed generations or resets; they are what the five demo scenarios, the Demo Guide, anddocs/13-seed-and-demo-scenarios.mdall link against. - Date anchoring — the mechanism that keeps "today"/"near-future"/"currently overlapping" scenarios true to whenever the environment was actually last reset, described below.
- The reset date — the real wall-clock moment a reset actually happened. This changes every time someone resets; it's the input to date anchoring, not a fixed record.
Generating the seed dataset
python seed/generate_seed.py --anchor 2026-08-01 --seed 20260801
This produces the committed CSVs in seed/*.csv with absolute ISO timestamps
authored relative to a fixed anchor date (2026-08-01). Target scale: 50 vehicles, 180
customers (including three duplicate pairs), ~245 historical/current/future bookings,
realistic inspections and maintenance history, and the fixed quality/workflow scenarios
described in demo-scenarios.md. Names, towns and vehicle makes are
drawn from believable Flemish/Kempen-region pools; all emails use the .test domain.
Date anchoring
backend/app/seed_loader.py defines:
SEED_AUTHORED_ANCHOR = date(2026, 8, 1) # matches generate_seed.py's --anchor
Every seed/reset computes shift = today - SEED_AUTHORED_ANCHOR and applies it to every
seeded booking, inspection, maintenance and outbox timestamp before insertion. Public
references and entity relationships are never touched by the shift — only datetime
columns move. This means:
BK-DEMO-RETURNalways ends "today" (or very close to it) relative to whenever you actually reset, not relative to the frozen 2026-08-01 authoring date.BK-DEMO-NEXTand the overlap-scenario bookings always read as "near future".- The shift is recomputed fresh on every reset, so scenarios never decay as real time
passes between resets — this was a real, confirmed bug before this fix (see
docs/demo-release/current-demo-gap-audit.md, gap #3): the environment would drift further out of sync with every day it wasn't reset, and a reset didn't fix it because nothing re-anchored the underlying stored dates.
load_seed() returns the resolved anchor_date (real today) and seeded_at timestamp,
and records a demo_data_seeded audit event carrying both the resolved anchor and the
original authoring anchor, so the shift applied on any given reset stays traceable via
the audit trail.
dashboard.py::_today() uses real wall-clock UTC date (not a frozen setting) to filter
"today's movements", consistent with the shifted data.
Reset
POST /api/v1/demo/reset (Operations Manager only, and only if DEMO_ALLOW_RESET=true)
clears all MobilityOps tables, reloads the seed with a fresh date shift, re-runs the
data-quality scan, and runs a server-side scenario-integrity check
(scenario_integrity_report() in backend/app/services/demo_manifest.py) confirming all
five named scenarios are actually present and ready — recorded in both the response body
and the demo_reset audit event's metadata. Reset only ever affects MobilityOps's own
tables; it never touches shared n8n, RAGcore, or MCP data, other containers, or volumes.
Seed-validation tests
backend/tests/test_seed.py proves, after every reset:
- S1 (
BK-DEMO-RETURN/MO-024) is active with no end odometer recorded yet. - S2 (
CUS-0012/CUS-0178/DQ-DEMO-DUPLICATE) is open with matching evidence. - S4 (
MO-016/BK-DEMO-OVERLAP-A/-B/DQ-DEMO-OVERLAP) genuinely overlaps in time. - S5 (the seeded failed outbox event) is durably
failedimmediately after reset, not silently auto-healed by the background dispatcher (which only claimspendingrows). - The date-anchoring shift and the
demo_data_seededaudit marker are both correct.
backend/tests/test_demo_manifest.py additionally proves that all five manifest
scenarios report ready: true with no blocked_reason right after a fresh reset.