Asurion Command Center — Live Demo Runbook¶
Companion to prd.md §4.1, §12.1, §15, §17. Do not edit the Cursor plan file; this document is the operational source for presenters.
Roles¶
| Role | Responsibility |
|---|---|
| Driver (Jane) | Clicks only on Executive Overview path; reads KPIs aloud sparingly. |
| Operator | Starts stack, runs make demo-reset, then python simulator/emit.py (or Make target) at the Driver’s cue. |
| Tech backup | Watches API logs / browser console; applies fallback lines if Bedrock or WebSocket fails. |
Pre-flight (T−10 minutes)¶
docker compose up --build -dfrom repo root; wait until API health returns 200 (curl -sf http://localhost:8000/health).- Open dashboard:
http://localhost:3080(Compose default; change the port mapping indocker-compose.ymlif it conflicts locally). - Run
make demo-resetonce to establish baseline seed (Active Issues baseline matches PRD narrative 1,286 after reset in default seed). - Close other bandwidth-heavy apps; prefer localhost demo (mitigates venue Wi‑Fi per PRD §17).
- Confirm Operator has
simulator/scenario_1.yamlandsimulator/emit.pyready; API base URLhttp://localhost:8000unless overridden byCOMMAND_CENTER_API.
Baseline numbers (PRD §4.1)¶
After reset, the UI should align with seeded state (exact values depend on db/seed.py; defaults target PRD copy):
| KPI / element | Expected at rest (default seed) |
|---|---|
| Active Issues | 1,286 (open + in_progress issue sessions) |
| Claims in Progress | 642 |
| Next Best Actions Taken (%) | 87% (PRD §17 plausibility — 120 approved / (120 + 18) ≈ 0.87) |
| Cost Avoided (MTD) | $1.41M baseline; approve adds +$210 ticker delta |
| Customer Satisfaction | 4.6 / 5 (P1 static, mockup parity) |
| Demo customer | John Doe (cust_demo_001) |
| Demo device | iPhone 14 Pro, IMEI 356789012345678 (dev_demo_001) |
| Claim id in narrative | CLM-88122 |
| Top Issues rows | Device screen damage / Battery not holding charge / Device not powering on / Lost or stolen device / Accidental liquid damage |
If your seed differs, update this table once and keep one canonical printout for judges.
Simulator trigger order (Scenario 1)¶
Executed by simulator/emit.py reading simulator/scenario_1.yaml:
| Step | Delay (YAML) | event_type |
Purpose |
|---|---|---|---|
| 1 | 0 ms | device.damage_detected |
Opens new issue session; Active Issues +1 |
| 2 | ~2000 ms | claim.submitted |
Claim on session; timeline |
| 3 | ~500 ms | payment.authorized |
Payment; triggers recommendation pipeline |
| 4 | (server) | recommendation.generated |
Internal: AI card appears |
| 5 | (live) | — | Driver clicks card → detail |
| 6 | (live) | recommendation.approved + outcome |
Driver clicks Approve |
Target spacing: damage → claim within ~2s (PRD §4.1 step 4); YAML uses delay_ms so total drift stays within ±5% for dry-run sign-off (PRD §15 item 2).
Live script (~90s journey + buffer)¶
0:00 — Landing
Driver: “Executive view — one pane for customer, device, and ops signals.” Wait for KPI skeleton ≤1s then numbers.
0:15 — Operator runs simulator
Say nothing for 2–3s; let KPI tick 1,286 → 1,287 (or your baseline +1).
0:25 — Timeline
Point to new timeline rows: damage → claim → payment.
0:35 — AI recommendation
Live High Impact card appears at the top of the AI panel: Approve repair — Screen damage. iPhone 14 Pro · Claim #CLM-88122. Below it sit four visual-stub cards (Preventive / Risk Detected / SLA Risk / Customer Comms) tagged visual stub per PRD §5.2 — call them out only if a judge asks.
0:45 — Drill-in (right-side panel, PRD §5.1 #11)
Click the live card. The right column loads the Customer & Device Detail: iPhone 14 Pro / IMEI / John Doe + email + phone + customer-since + View 360 Profile + four reason codes (coverage, battery, slot, inventory) + uBreakiFix Dallas · ETA 48h · inventory tight.
1:00 — Approve
Click Approve in the right panel. Within 5s: Active Issues −1 (1,287 → 1,286), Cost Avoided MTD +$210 (Replacement $250 − Actual $40), NBA % +0.1%, timeline appends outcome.recorded — Repair center assigned, alert Repair scheduled.
1:15 — Close loop
Alerts / activity shows confirmation; timeline shows Repair center assigned (or equivalent PRD string).
1:20 — Add Widget arc (≈30s, optional but on-script)
Click + Add Widget in the header. The full-screen modal opens with the chat on the left and the Pipeline tab auto-selected on the right, showing a ReactFlow DAG of the 8-node Clarifier graph. Driver: "This is the same Spine-Clarifier pattern from our R&D framework — LangGraph with a human-in-the-loop pause. Watch the nodes light up as each stage completes." Pick the "Cost avoided by region for the last 7 days as a chart" sample prompt (or type one). The pipeline graph animates in real time: contextLoader → intentExtractor → metricMatcher → gapDetector → questionPrioritizer nodes turn green with completion dots, then the graph pauses at the specSynthesizer node (amber "interrupted" state with HITL badge) and asks 1 question (e.g. "In one short phrase, what measure should this widget show?"). Driver answers (e.g. "Cost avoided in USD") and clicks Submit answers. The graph resumes, specSynthesizer → critic light up, and the right panel auto-switches to the Preview tab showing a live Recharts line chart driven by mock_data. Driver clicks Add to dashboard; the modal closes and the widget appears in the new My widgets rail under the KPIs. Driver: "Same Event → Context → AI → Action loop — but now applied to dashboard composition itself. Live data binding is the next slice."
Stop talking at ~2:00 of the v1 arc. Reserve remaining time for architecture + pattern slides (see pitch/) OR for the Part C live data binding arc below if Part C is on-script for this audience.
Part C — Live data binding arc (~3 min, on-script for the data-eng-audience demo)¶
Mirrors PRD v2.1 §C.6 timing table. Only run this section when the audience cares about Databricks lineage (it's the headline for engineering reviewers). For exec audiences, stop at the v1 90s arc.
Part C pre-flight (T−5 minutes — additive to the v1 pre-flight above)¶
- Confirm
.envhas liveDATABRICKS_HOST/DATABRICKS_HTTP_PATH/DATABRICKS_TOKENandAWS_PROFILE=hackathon-async(per README § Live Databricks integration (Part C)). aws sso login --sso-session hackathonso the api container's AWS_SESSION_TOKEN is fresh — Bedrock SQL gen will fail withExpiredTokenExceptionotherwise (the failure is loud and visible per ADR-008, but it costs you 30s of stage time you don't have).make seed-databricks-l3 --resetif you haven't seeded the workspace today (~25s); idempotent thereafter.- Pre-warm. ~30s before showtime, hit the three Databricks-routed tile widgets via
curl -sf -X POST http://localhost:8000/v1/widgets/<wid>/data -d '{"refresh":true}' > /dev/nullso Bedrock's first-call cost (model warm-up) and the Serverless Starter cold-start are both amortized into the cache window before the audience sees the dashboard. The 300scache_secondsper config/metric_routing.yaml covers a typical 3-min arc end-to-end. - Reset YAML to default state:
cost_avoided_mtd:MUST bebackend: postgresat curtain. The §C.6.1 moment is the AUDIENCE's first time seeing it flip.
Part C arc — timing¶
| Time (cumulative) | Beat | What the audience sees |
|---|---|---|
| 0:00–0:20 | The promise | Driver: "v1 was synthetic data on Postgres. The hard problem isn't the dashboard — it's binding tiles to a real warehouse without inviting LLM hallucinations. Watch." Hover over any v1 KPI tile → green Postgres · Xs ago SourceBadge. |
| 0:20–0:50 | Add a Databricks-routed widget live | Click + Add Widget. Type "claim volume in the last 30 days from the Asurion l3 schema". Clarifier resolves to claim_volume_l3_asurion, pauses at synthesizer, Driver answers "Single KPI, integer count". Click Add to dashboard. Tile appears in My widgets rail showing 5,000 (or whatever the live COUNT(DISTINCT claim_id) is) with a purple Databricks · Xs ago ▾ badge. Click the badge — generated SQL expands inline. Driver: "That SQL came from Bedrock, anchored to a metric_catalog row, validated by sqlglot, executed against workspace.l3_asurion.ev_claim. Five layers of safety; zero hallucinations." |
| 0:50–1:20 | Honest mocking | Driver: "Watch what happens when Databricks goes away." Operator (in a separate terminal) runs docker exec 2026-hackathon-api-1 sh -c 'export DATABRICKS_TOKEN=bogus' then make up. Tile re-fetches; SourceBadge flips to amber Mock · live data unavailable. The other v1 tiles stay green-Postgres-live. Driver: "It didn't pretend. ADR-008: mocks are opt-in, never silent fallback. The widget's spec.mock_data is showing because the resolver explicitly chose it after Databricks was unreachable — and the badge tells you exactly why (databricks_auth_error)." Operator restores the real token + make up; tile flips back to purple. |
| 1:20–2:30 | §C.6.1 — Reroute an existing tile (see sub-section below) | Cost Avoided (MTD) starts the demo green. Driver flips the YAML on stage. Tile's value AND SourceBadge change live. |
| 2:30–3:00 | Close | Driver: "Same dashboard. One YAML edit. Different backend, same renderer, real data on both sides." Pause. Take questions. |
§C.6.1 — Reroute an existing tile (1:20–2:30 sub-section)¶
The single most rehearsed moment in the Part C arc. Goal: the audience sees a tile they've been looking at the whole demo change source without any frontend change.
The setup. cost_avoided_mtd has been on screen for ~2 minutes showing $1.41M with a green Postgres · Xs ago badge.
The on-stage edit. Driver opens config/metric_routing.yaml in a side-by-side editor (Operator pre-positioned this terminal pane). The cursor is already on the cost_avoided_mtd block. Driver narrates and types:
Save. Run make up (~5-7s warm rebuild). The boot validator re-checks routing; the api comes back healthy. The tile's useWidgetData polling cadence (spec.data_intent.refresh_seconds = 60s) re-fetches on the next tick — Driver clicks the tile's manual refresh button to skip the wait. The tile re-renders:
- Value drops from $1.41M (Postgres synthetic from
outcomes) to ~$375k (Databricks live trailing-30d SUM overl3_asurion.ev_claim.cost_avoided_usd). - SourceBadge flips green → purple. Badge text:
Databricks · Xs ago ▾. - Click the badge → generated SQL expands. The audience sees:
SELECT COALESCE(SUM(cost_avoided_usd), 0) AS value FROM l3_asurion.ev_claim WHERE claim_started_dttm_utc >= current_timestamp() - INTERVAL 30 DAYS LIMIT 5000.
Driver delivers the line: "Same renderer. Same widget id. Same KPI tile. The data resolver looked up the routing for cost_avoided_mtd, saw databricks, asked the SQL generator for SQL anchored to the catalog row's source_query template, sqlglot-validated it, executed against the warehouse, returned the row. The frontend just re-rendered whatever the resolver gave it."
Pause. Let the audience absorb. Take a single Q if asked. Then optionally restore: edit YAML back to backend: postgres, make up, refresh — tile flips back to green Postgres · Xs ago $1.41M. (Most demos skip the restore — the purple state is the headline.)
Part C fallback lines¶
Cold-start visible (>5s on the §C.6.1 refresh). "Serverless Starter cold-starts on first query — that's why the runbook says pre-warm 30s before. Production runs on a Pro warehouse where this is sub-second. Phase 2 wiring." Don't apologize, don't speculate at length; the cache hit on the next call (within cache_seconds) is your proof.
Bedrock returns 503 / ExpiredTokenException mid-arc. Tile shows amber Mock · live data unavailable. Driver: "The ADR-008 contract just fired — Bedrock unavailable, the resolver returned the widget's baked-in mock_data and the badge is telling you so. Same mechanism as the killing-Databricks moment a minute ago." Operator (in private terminal) refreshes AWS SSO, make up, refreshes the tile. Do NOT pretend the tile is live during this gap.
YAML typo at the on-stage edit. Boot validator fires RuntimeError and make up exits non-zero. Driver: "And that's the boot validator catching what would otherwise be a silent runtime failure — every catalog metric needs a routing answer at startup." Operator fixes the typo (databricks is one s, not databriks); make up; demo resumes.
Stop talking at ~3:00 of the Part C arc. Total demo budget: 90s v1 + 30s Add Widget + 3 min Part C = ~5 min. Reserve remaining time for architecture + pattern slides.
Fallback lines (do not improvise under stress)¶
Yellow banner: “Reconnecting…” (WebSocket)¶
“The decision engine and database are still live — we lost the push channel for a second. I’ll refresh once; metrics reconcile on reconnect.”
Action: Single hard refresh; if still down, switch to backup recording (see backup-recording.md).
Bedrock / LLM slow or error (rationale empty or generic)¶
“The ranked action and reason codes are deterministic; the paragraph is an executive explanation layer. In production we’d enforce SLOs; here we use the same template fallback we’d ship for resilience.”
Action: Do not reload; point at reason codes. API uses ADR-003-style template when model unavailable.
KPIs look “wrong” after one click¶
“MTD cost avoided is baselined from historical synthetic outcomes so one approval shows a credible increment, not the whole program value.”
Post-demo Q&A hooks (optional one-liners)¶
- “Why not Pub/Sub + BigQuery?” → ADR-001: same pattern (durable + hot + pub/sub), faster hackathon path; production mapping on architecture slide.
- “Is the LLM deciding?” → No: rules + weighted features pick the action; model only narrates within reason codes (PRD §10).
- Connected chips green? → Visual stubs; no outbound integrations in MVP (PRD §3.2, open-questions-resolved.md).
- “Why are 4 of the 5 AI cards labelled
visual stub?” → PRD §5.2 explicitly cuts Risk Detected / SLA Risk / Customer Comms badges from P0; Preventive is also a stub here. Only the live High Impact card runs through the real decisioning pipeline; the others demonstrate the panel’s future shape on the same architecture (§20 reusability).
Backup recording slot¶
After a clean dry-run, follow backup-recording.md — the v1 90s arc steps are listed first, the Part C ~3 min arc (the data-eng demo headline) is the second block. Prefer saving the MP4 to artifacts/backup-demo.mp4 (create artifacts/ locally; the path is gitignored via .gitignore line 7 artifacts/*.mp4). After saving, leave the file at exactly artifacts/backup-demo.mp4 so the demo machine can play it from the runbook tab without path edits.
Operator handoff (Part C close-out 2026-05-06): the backup video itself is gated on a human driver — Phase E2 of
docs/plans/completed/part-c-demo-ready.mdends at "operator records and saves toartifacts/backup-demo.mp4". Every prerequisite the recording depends on (env, seed, pre-warm script, on-script narration,§C.6.1reroute timing, fallback lines) is captured in backup-recording.md and the §C.6.1 sub-section above. When the operator finishes the recording, replace this block's last sentence with the recording date so future readers know the artifact is current.
Add Widget fallback lines¶
LLM (Bedrock) slow or unreachable¶
"The clarifier ranks questions and validates the spec deterministically — the model only fills the slots. The default mode is live Bedrock per ADR-008; if the modal shows the rose 'LLM unavailable' banner, that's the system telling you Bedrock failed cleanly instead of pretending to succeed. For demos on machines without AWS, run
make up-offlineahead of time — the dashboard header shows an 'Offline mode' pill and the same flow runs against the deterministicMockLlmend-to-end."
Action: If the demo is meant to be live and you see the error banner, kill the session, fix AWS creds (or the model id — usually BEDROCK_MODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0 for tool-use), make up, and re-run. If the demo is offline-by-intent, restart with make up-offline so the offline pill is visible the whole time. Do not expect the live path to silently substitute MockLlm — that fallback was removed by ADR-008.
Validation error in the right pane¶
"The Pydantic discriminator caught a malformed spec — same gate that production would enforce. Adjust the answer on the left or restart the session."
Action: Click Cancel in the chat footer (or the modal Close), reopen via + Add Widget, and try a tighter prompt.
Reset discipline (PRD §15 item 10)¶
After each full run-through:
Confirm three consecutive full passes during rehearsal (simulator → approve → KPIs → Add Widget → feed). The reset truncates the widgets table along with everything else (see backend/app/seed.py) so chat-built widgets do not leak between runs.