docs(demo): add demo concept, scenarios, data, guide and runbook docs
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.
This commit is contained in:
@@ -12,6 +12,17 @@ design decision and visual evidence.
|
|||||||
|
|
||||||
All people, companies, vehicles, bookings and documents are synthetic. The workflows, validation, integrations, audit logging and access boundaries are intended to be real.
|
All people, companies, vehicles, bookings and documents are synthetic. The workflows, validation, integrations, audit logging and access boundaries are intended to be real.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
The demo presents itself as **Northstar Mobility**, a fictitious Belgian camper/van
|
||||||
|
rental company — the login screen, a permanent "Synthetische demo" indicator, an in-app
|
||||||
|
guided tour (Demo Guide), a curated `/scenarios` overview, and an "Over deze demo" page
|
||||||
|
all make the fictional context, synthetic-data status, and real-vs-simulated boundaries
|
||||||
|
explicit without any verbal explanation. See `docs/demo-release/` for the full demo
|
||||||
|
concept, the five named scenarios, the seed/date-anchoring strategy, the guided-tour
|
||||||
|
design, and the operational runbook (5-minute and 10-minute demo flows, reset, redeploy,
|
||||||
|
rollback).
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
The PoC implements:
|
The PoC implements:
|
||||||
@@ -77,6 +88,9 @@ and `artifacts/final-acceptance/summary.md` for the original M0–M7 acceptance
|
|||||||
- `artifacts/design-validation/` — baseline audit, Stitch direction references and implemented responsive captures.
|
- `artifacts/design-validation/` — baseline audit, Stitch direction references and implemented responsive captures.
|
||||||
- `docs/functional-completion/` — the functional-completion audit and pre-work server baseline.
|
- `docs/functional-completion/` — the functional-completion audit and pre-work server baseline.
|
||||||
- `artifacts/functional-completion/` — functional-completion acceptance evidence.
|
- `artifacts/functional-completion/` — functional-completion acceptance evidence.
|
||||||
|
- `docs/demo-release/` — demo concept, scenarios, seed/date-anchoring strategy, guided
|
||||||
|
tour, and runbook.
|
||||||
|
- `artifacts/demo-release/` — demo-productization acceptance evidence.
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@@ -100,9 +114,9 @@ All defaults are configurable via `.env` (see `.env.example`).
|
|||||||
## Quality gates
|
## Quality gates
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make test # backend: pytest (117 tests)
|
make test # backend: pytest (127 tests)
|
||||||
make lint # backend: ruff + mypy (strict, zero errors)
|
make lint # backend: ruff + mypy (strict, zero errors)
|
||||||
make e2e # frontend: Playwright end-to-end (37 tests, live stack required)
|
make e2e # frontend: Playwright end-to-end (56 tests, live stack required)
|
||||||
```
|
```
|
||||||
|
|
||||||
Frontend build/typecheck: `cd frontend && npm run build` (`tsc -b && vite build`).
|
Frontend build/typecheck: `cd frontend && npm run build` (`tsc -b && vite build`).
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# PoC runbook
|
# PoC runbook
|
||||||
|
|
||||||
|
For the demo-specific 5-minute/10-minute walkthroughs, reset behaviour, Unraid
|
||||||
|
redeploy/rollback steps and troubleshooting, see `docs/demo-release/demo-runbook.md`.
|
||||||
|
This document covers general environment bootstrap and n8n setup.
|
||||||
|
|
||||||
## Bootstrap (clean checkout)
|
## Bootstrap (clean checkout)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Demo concept
|
||||||
|
|
||||||
|
## The fictional problem
|
||||||
|
|
||||||
|
**Northstar Mobility** is a fictional Belgian camper/van rental company with roughly 50
|
||||||
|
vehicles, one main location, a rental team, an operations manager, and a small workshop.
|
||||||
|
Northstar Mobility does not exist — this name and every customer, vehicle, booking and
|
||||||
|
procedure in the demo are synthetic. Before MobilityOps, Northstar Mobility's bookings,
|
||||||
|
returns, customer records and maintenance history lived in spreadsheets and verbal
|
||||||
|
hand-offs, so problems (duplicate customers, incorrect odometer readings, double-booked
|
||||||
|
vehicles) only surfaced late, after they had already caused friction. MobilityOps shows
|
||||||
|
how one connected system detects these problems early and lets an Operations Manager
|
||||||
|
resolve them under audit, with automation handling the routine follow-up.
|
||||||
|
|
||||||
|
## Target audience
|
||||||
|
|
||||||
|
Anyone evaluating how MobilityOps approaches operational data-quality and hand-off
|
||||||
|
problems for a small rental fleet: Operations Managers, Rental Employees, and reviewers
|
||||||
|
assessing the approach. No account setup or backend knowledge is required to start —
|
||||||
|
the login screen explains the fictional context and offers a guided path.
|
||||||
|
|
||||||
|
## Demo scope
|
||||||
|
|
||||||
|
This is a focused proof of concept, not a full ERP. In scope: vehicle and booking
|
||||||
|
management, return processing with server-side evaluation, five data-quality detection
|
||||||
|
rules each with one bounded resolution flow, a full audit trail, and orchestration via a
|
||||||
|
real n8n instance. Explicitly out of scope: accounting, payments, public reservations, a
|
||||||
|
generic CRM, inventory, HR, a second RAG stack, or autonomous write actions from any
|
||||||
|
external tool.
|
||||||
|
|
||||||
|
## What's really implemented
|
||||||
|
|
||||||
|
All of the following is functional code, not a mockup or hardcoded screen:
|
||||||
|
|
||||||
|
- Role-based authentication and server-verified sessions (Operations Manager, Rental
|
||||||
|
Employee), enforced both server-side and in the frontend's navigation/route guards.
|
||||||
|
- Vehicle and booking management, with return preview/commit sharing one authoritative
|
||||||
|
evaluation function so they can never drift.
|
||||||
|
- Five data-quality rules (possible duplicate customer, missing required field, odometer
|
||||||
|
regression, booking overlap, vehicle status conflict), each with exactly one bounded,
|
||||||
|
audited resolution endpoint.
|
||||||
|
- A full audit trail with before/after state, actor, correlation IDs, and a
|
||||||
|
"view related events" link reusing that correlation.
|
||||||
|
- Reliable outbox-based delivery to a real n8n instance, with bounded retries and
|
||||||
|
stale-delivery recovery.
|
||||||
|
- Docker Compose deployment and an automated test suite (backend pytest, Ruff, mypy,
|
||||||
|
and a full Playwright end-to-end suite covering the demo experience itself).
|
||||||
|
- A demo manifest (`GET /api/v1/demo/manifest`) as the single source of truth for the
|
||||||
|
demo's identity, synthetic-data status, reset state, scenario readiness, and honest
|
||||||
|
per-integration labels — the frontend never hardcodes what mode it's in.
|
||||||
|
|
||||||
|
## What's synthetic
|
||||||
|
|
||||||
|
The organisation, all customers, vehicles, bookings, maintenance history, the knowledge
|
||||||
|
base's procedures, and the five prepared demo scenarios are entirely fictional. No data
|
||||||
|
refers to a real person, vehicle, or company. Every seeded email uses the `.test`
|
||||||
|
reserved domain. See [`demo-data.md`](demo-data.md) for exactly how this data is
|
||||||
|
generated and kept fresh across resets.
|
||||||
|
|
||||||
|
## What's not yet live-connected
|
||||||
|
|
||||||
|
- **RAGcore**: not connected. The knowledge assistant uses a local, deterministic
|
||||||
|
keyword-matching "demo knowledge base" built from five procedure documents, honestly
|
||||||
|
labelled as such everywhere in the UI (never presented as RAGcore). A
|
||||||
|
`RAGcoreKnowledgeProvider` HTTP adapter exists and is unit-tested, ready to take over
|
||||||
|
the same interface once a real RAGcore backend is available — swapping providers is a
|
||||||
|
configuration change (`KNOWLEDGE_PROVIDER`), not a UI change.
|
||||||
|
- **ITWorx MCP Hub**: not connected. Registration is disabled by default
|
||||||
|
(`MCP_HUB_REGISTRATION_ENABLED=false`) and the UI always shows "Not connected" —
|
||||||
|
never a fabricated successful registration.
|
||||||
|
|
||||||
|
## Where to go next
|
||||||
|
|
||||||
|
- [`demo-scenarios.md`](demo-scenarios.md) — the five guided scenarios and their fixed records.
|
||||||
|
- [`demo-data.md`](demo-data.md) — seed strategy and date-anchoring.
|
||||||
|
- [`demo-guide.md`](demo-guide.md) — the in-app 8-step guided tour.
|
||||||
|
- [`demo-runbook.md`](demo-runbook.md) — how to run, reset, and redeploy the demo.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Demo data strategy
|
||||||
|
|
||||||
|
## Three separate concepts
|
||||||
|
|
||||||
|
It's important to keep these distinct — they solve different problems:
|
||||||
|
|
||||||
|
1. **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, and `docs/13-seed-and-demo-scenarios.md` all link against.
|
||||||
|
2. **Date anchoring** — the mechanism that keeps "today"/"near-future"/"currently
|
||||||
|
overlapping" scenarios true to whenever the environment was actually last reset,
|
||||||
|
described below.
|
||||||
|
3. **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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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`](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:
|
||||||
|
|
||||||
|
```python
|
||||||
|
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-RETURN` always 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-NEXT` and 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 `failed` immediately after reset, not
|
||||||
|
silently auto-healed by the background dispatcher (which only claims `pending` rows).
|
||||||
|
- The date-anchoring shift and the `demo_data_seeded` audit 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.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# The in-app Demo Guide
|
||||||
|
|
||||||
|
## What it is (and isn't)
|
||||||
|
|
||||||
|
The Demo Guide is a compact, reusable side panel (a bottom sheet on mobile) that walks an
|
||||||
|
Operations Manager through eight fixed steps covering the demo's core functionality. It is
|
||||||
|
**not** a generic tutorial engine and **not** a source of business logic — every action it
|
||||||
|
prompts is a real action against the real API; the guide only narrates, links, and tracks
|
||||||
|
progress. It duplicates no business logic: routes for the two scenario-backed steps
|
||||||
|
(return, duplicate-merge) are resolved from the live `GET /api/v1/demo/manifest` response
|
||||||
|
rather than hardcoded, so they can never point at a stale or missing record.
|
||||||
|
|
||||||
|
## Where it lives
|
||||||
|
|
||||||
|
- `frontend/src/data/demoGuideSteps.ts` — the eight step definitions (title, what you'll
|
||||||
|
see, why it matters, the exact start action, the expected outcome, and a `route()`
|
||||||
|
function).
|
||||||
|
- `frontend/src/context/DemoGuideContext.tsx` — open/close state and step progress,
|
||||||
|
persisted to `sessionStorage` only (browser-local, never touches auth or business
|
||||||
|
state; a "Demo opnieuw voorbereiden" restart or a new browser session starts fresh).
|
||||||
|
- `frontend/src/components/DemoGuide.tsx` — the panel itself and its topbar trigger
|
||||||
|
(`DemoGuideTrigger`, Operations-Manager-only, since all eight steps require that role).
|
||||||
|
|
||||||
|
## The eight steps
|
||||||
|
|
||||||
|
1. **Understand the operational state** — the dashboard's readiness and attention queue.
|
||||||
|
2. **Open the booking needing attention** — `BK-DEMO-RETURN`, resolved from the manifest.
|
||||||
|
3. **Process the return with an odometer anomaly** — same booking; the return form is
|
||||||
|
pre-filled with the suspicious reading (see [`demo-scenarios.md`](demo-scenarios.md)
|
||||||
|
scenario 1).
|
||||||
|
4. **Handle the created data-quality issue** — the fresh odometer-regression issue that
|
||||||
|
step 3 just created.
|
||||||
|
5. **Review and merge the possible duplicate customer** — `DQ-DEMO-DUPLICATE`.
|
||||||
|
6. **Ask the procedure assistant a question** — one of the suggested questions.
|
||||||
|
7. **Check automation and the audit trail**.
|
||||||
|
8. **Review what's real, simulated, or not yet connected** — the About page.
|
||||||
|
|
||||||
|
## How progression works
|
||||||
|
|
||||||
|
- **"Ga naar deze stap"** navigates to the step's resolved route without marking it done.
|
||||||
|
- **"Volgende"** marks the current step complete and advances the index (used for steps
|
||||||
|
with no dedicated in-page continuation, like the knowledge-assistant step).
|
||||||
|
- Several real actions (a successful return, a resolved data-quality issue) show their
|
||||||
|
own **"Ga verder met de demo"** button that both completes the current step and
|
||||||
|
navigates straight to the next one — this is how the guide chains through steps 3→4→5
|
||||||
|
without a detour back through the panel's own controls.
|
||||||
|
- The step list on the panel lets you jump directly to any step.
|
||||||
|
- **"Demo opnieuw voorbereiden"** calls the real reset endpoint, resets the guide's own
|
||||||
|
progress, and returns to the login screen — mirroring the existing sidebar reset
|
||||||
|
control, not a separate implementation.
|
||||||
|
|
||||||
|
## A known, deliberate limitation: suggested questions stay in English
|
||||||
|
|
||||||
|
The demo knowledge base's five procedure documents (`knowledge/procedures/*.md`) are
|
||||||
|
written in English. Verified empirically while building step 6: an equivalent Dutch
|
||||||
|
question returned `insufficient` evidence against the demo provider, while the English
|
||||||
|
original returned `grounded`. Rather than mistranslate the demo's centerpiece "grounded
|
||||||
|
answer" feature into something that silently answers incorrectly, the suggested questions
|
||||||
|
on `/knowledge` and this guide's step 6 instructions stay in English, with the guide
|
||||||
|
explicitly explaining why in Dutch. Retranslating the procedure documents themselves was
|
||||||
|
judged out of scope for a demo-productization pass (see the language-split note in
|
||||||
|
`docs/demo-release/current-demo-gap-audit.md`, gap #11).
|
||||||
|
|
||||||
|
## Layout note: the panel reserves space, it doesn't overlap content
|
||||||
|
|
||||||
|
On desktop the panel is a fixed 400px-wide right-side overlay. `Layout.tsx` adds a
|
||||||
|
`guide-open` class to the main workspace while the guide is open, which reserves
|
||||||
|
`padding-right` so page content reflows aside instead of sitting underneath the panel —
|
||||||
|
this was a real bug found and fixed while building the full guided-demo Playwright test
|
||||||
|
(see `PROJECT_STATE.md`, Batch 6): without it, the return form's "Review return" button
|
||||||
|
was unclickable while the guide was open at ordinary desktop widths.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# Demo runbook
|
||||||
|
|
||||||
|
## Starting the demo (any environment)
|
||||||
|
|
||||||
|
Open the deployed URL (Unraid review: `http://192.168.10.150:1236`; local:
|
||||||
|
`http://localhost:1228`). The login screen names the fictional organisation, states that
|
||||||
|
all data is synthetic and all workflows are really implemented, and offers:
|
||||||
|
|
||||||
|
- **Start begeleide demo** — logs in as Operations Manager and opens the Demo Guide at
|
||||||
|
step 1.
|
||||||
|
- **Verken als Operations Manager** / **Verken als Rental Employee** — free exploration,
|
||||||
|
no guide.
|
||||||
|
|
||||||
|
No password is shown or required for either path.
|
||||||
|
|
||||||
|
## Five-minute demo (guided)
|
||||||
|
|
||||||
|
1. Click **Start begeleide demo**.
|
||||||
|
2. Follow steps 1–3: dashboard → the booking needing attention → confirm the pre-filled
|
||||||
|
odometer-anomaly return.
|
||||||
|
3. Step 4: resolve the data-quality issue the return just created (any decision is fine
|
||||||
|
for a quick pass — "Retain canonical" is the fastest).
|
||||||
|
4. Step 6: ask a suggested knowledge question and show the cited source.
|
||||||
|
5. Step 7: show the automation/audit trail link-through.
|
||||||
|
6. Close with step 8, the About page's honest real/synthetic/not-connected breakdown.
|
||||||
|
|
||||||
|
## Ten-minute demo (guided + one extra scenario)
|
||||||
|
|
||||||
|
Do the five-minute path above, then from `/scenarios`:
|
||||||
|
|
||||||
|
- Run **scenario 2** (duplicate customer merge) if not already done via the guide's own
|
||||||
|
step 5.
|
||||||
|
- Run **scenario 3** (booking overlap) — `/data-quality`, resolve `DQ-DEMO-OVERLAP`.
|
||||||
|
- Run **scenario 4** (failed automation retry) — `/automation`, filter to failed, retry.
|
||||||
|
|
||||||
|
All five scenarios can be run in any order and are independent of each other.
|
||||||
|
|
||||||
|
## Resetting the environment
|
||||||
|
|
||||||
|
Any Operations Manager can reset from: the sidebar ("Reset demo data"), the Demo Guide
|
||||||
|
panel ("Demo opnieuw voorbereiden"), or the About page (points to the sidebar control).
|
||||||
|
Reset requires confirmation, rebuilds the deterministic dataset with a fresh date anchor,
|
||||||
|
runs a server-side scenario-integrity check, and signs the acting session out (the server
|
||||||
|
invalidates the session as part of reset). It only ever touches MobilityOps's own tables
|
||||||
|
— never shared n8n, RAGcore, or MCP data, other containers, or volumes. It can be
|
||||||
|
disabled entirely via `DEMO_ALLOW_RESET=false` if an environment must not be rebuildable.
|
||||||
|
|
||||||
|
## Redeploying to Unraid
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From a clean local checkout on the target branch/commit:
|
||||||
|
git archive --format=tar.gz -o /tmp/mobilityops-source.tar.gz HEAD
|
||||||
|
scp /tmp/mobilityops-source.tar.gz unraid:/mnt/user/appdata/mobilityops/.deploy/source-<short-sha>.tar.gz
|
||||||
|
ssh unraid "cd /mnt/user/appdata/mobilityops \
|
||||||
|
&& tar -xzf .deploy/source-<short-sha>.tar.gz \
|
||||||
|
&& echo <full-sha> > .deploy/source-revision"
|
||||||
|
|
||||||
|
# Rebuild only what changed (api and/or web); db is never rebuilt:
|
||||||
|
ssh unraid "cd /mnt/user/appdata/mobilityops \
|
||||||
|
&& docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml up --build -d api web"
|
||||||
|
|
||||||
|
# Confirm migrations and reseed:
|
||||||
|
ssh unraid "cd /mnt/user/appdata/mobilityops \
|
||||||
|
&& docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml exec -T api python -m alembic current \
|
||||||
|
&& docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml exec -T api python -m app.cli seed --reset"
|
||||||
|
```
|
||||||
|
|
||||||
|
Extraction preserves the server's existing `.env` and named volumes (Postgres data, n8n
|
||||||
|
data) — the tarball never contains `.env` since it's gitignored. Never edit source
|
||||||
|
directly on the server; never deploy uncommitted changes.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
`.deploy/source-revision` on the server records exactly which commit is live. Prior
|
||||||
|
source tarballs remain in `.deploy/` for rollback: extract an earlier
|
||||||
|
`source-<short-sha>.tar.gz`, update `source-revision` to match, and re-run the rebuild
|
||||||
|
step above. Database rollback is out of scope for this demo (migrations are additive;
|
||||||
|
there has been no destructive migration on this branch).
|
||||||
|
|
||||||
|
## Server safety (Unraid)
|
||||||
|
|
||||||
|
Only touch the `mobilityops` Compose project's own `api`/`web` services (and `db` only
|
||||||
|
via migrations, never manually). Never stop other containers, run `docker system prune`,
|
||||||
|
delete unrecognised images/networks/volumes, delete the MobilityOps database, overwrite
|
||||||
|
the server `.env`, print secrets, start a second permanent n8n instance, or activate
|
||||||
|
guessed RAGcore/MCP URLs. PostgreSQL is never exposed externally.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **A scenario shows "Niet beschikbaar" on `/scenarios`**: it has already been resolved
|
||||||
|
since the last reset (expected once you've worked through it) — reset to restore it.
|
||||||
|
- **Knowledge question returns "insufficient evidence"**: check the question is in
|
||||||
|
English and close to one of the suggested phrasings — the demo knowledge base is
|
||||||
|
English-only (see `demo-guide.md`).
|
||||||
|
- **n8n shows a failed delivery that isn't the seeded demo one**: check `/automation`'s
|
||||||
|
filter and retry — bounded retries mean it should self-heal within
|
||||||
|
`n8n_max_attempts` attempts, or can be retried manually by an Operations Manager.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# Demo scenarios
|
||||||
|
|
||||||
|
Five fixed scenarios are surfaced at `/scenarios` and via `GET /api/v1/demo/manifest`'s
|
||||||
|
`scenarios` array, which derives each scenario's `ready`/`blocked_reason` from the actual
|
||||||
|
underlying records — never hardcoded. All five are re-findable, using the same public
|
||||||
|
references, after every demo reset (see [`demo-data.md`](demo-data.md)).
|
||||||
|
|
||||||
|
## 1 — Odometer regression on return
|
||||||
|
|
||||||
|
- **Fixed records**: booking `BK-DEMO-RETURN` on vehicle `MO-024`.
|
||||||
|
- **Role**: Rental Employee or Operations Manager.
|
||||||
|
- **Problem demonstrated**: a return is submitted with an odometer reading lower than the
|
||||||
|
vehicle's canonical reading — usually a data-entry mistake or a return logged against
|
||||||
|
the wrong vehicle.
|
||||||
|
- **Flow**: open the booking. The return form is pre-filled with a suspicious reading
|
||||||
|
below `MO-024`'s canonical odometer (the demo never asks a visitor to invent one), with
|
||||||
|
a callout explaining why. Review the server-evaluated preview, then confirm. The
|
||||||
|
canonical odometer is never silently lowered; a data-quality issue is opened
|
||||||
|
automatically and the outcome is queued for delivery to n8n.
|
||||||
|
- **Continue**: the result panel links to the vehicle, automation status, and audit
|
||||||
|
trail, plus (inside the guided tour) a "Ga verder met de demo" button.
|
||||||
|
|
||||||
|
## 2 — Possible duplicate customer
|
||||||
|
|
||||||
|
- **Fixed records**: customers `CUS-0012` and `CUS-0178` (same email/phone), issue
|
||||||
|
`DQ-DEMO-DUPLICATE`.
|
||||||
|
- **Role**: Operations Manager (merge is a bounded, audited action).
|
||||||
|
- **Problem demonstrated**: two customer profiles that are very likely the same person,
|
||||||
|
registered twice — a risk for split booking history and duplicate contact.
|
||||||
|
- **Flow**: open the issue, compare both profiles field by field, choose which survives,
|
||||||
|
and merge. The losing profile becomes a tombstone linked to the survivor; its bookings
|
||||||
|
are rewired. The result is recorded in the audit trail.
|
||||||
|
|
||||||
|
## 3 — Overlapping bookings
|
||||||
|
|
||||||
|
- **Fixed records**: vehicle `MO-016`, bookings `BK-DEMO-OVERLAP-A`/`BK-DEMO-OVERLAP-B`,
|
||||||
|
issue `DQ-DEMO-OVERLAP`.
|
||||||
|
- **Role**: Operations Manager.
|
||||||
|
- **Problem demonstrated**: the same vehicle committed to two overlapping reservations —
|
||||||
|
a legacy-import-style conflict that a normal booking command would reject outright.
|
||||||
|
- **Flow**: open the issue, choose which of the two bookings to block; the other keeps
|
||||||
|
its current status.
|
||||||
|
|
||||||
|
## 4 — Failed automation, retried
|
||||||
|
|
||||||
|
- **Fixed record**: a seeded outbox event (`00000000-0000-4000-8000-000000000020`,
|
||||||
|
`BK-H-0020`) that is durably `failed` immediately after every reset — a safe, simulated
|
||||||
|
connection error, not a deliberately destabilised n8n configuration.
|
||||||
|
- **Role**: Operations Manager.
|
||||||
|
- **Problem demonstrated**: bounded retries and visible failure/recovery state for
|
||||||
|
workflow delivery, rather than a silent drop.
|
||||||
|
- **Flow**: open Automation, filter to failed deliveries, retry the event; it moves out
|
||||||
|
of the failed filter once delivered.
|
||||||
|
|
||||||
|
## 5 — Grounded procedure question
|
||||||
|
|
||||||
|
- **Role**: Rental Employee or Operations Manager.
|
||||||
|
- **Problem demonstrated**: an operational question gets an answer with a citation from
|
||||||
|
the demo knowledge base — or an honest "insufficient evidence" if nothing indexed
|
||||||
|
answers it — never an invented answer.
|
||||||
|
- **Flow**: open Knowledge, click one of the suggested questions (English, matching the
|
||||||
|
indexed procedure content — see the note in [`demo-guide.md`](demo-guide.md) about why
|
||||||
|
suggested questions stay in English even though the rest of the demo narrates in
|
||||||
|
Dutch), and inspect the cited source and evidence state.
|
||||||
|
|
||||||
|
## A sixth scenario exists but isn't one of the five requested
|
||||||
|
|
||||||
|
`docs/13-seed-and-demo-scenarios.md`'s **S3** ("missing inspection before next booking",
|
||||||
|
vehicle `MO-031`) is also seeded and shows up in the attention queue; it isn't surfaced
|
||||||
|
on `/scenarios` because the brief names five specific scenarios, but it remains available
|
||||||
|
for anyone exploring the attention queue directly.
|
||||||
Reference in New Issue
Block a user