Part C — Demo-Ready Close-Out¶
Status: completed (2026-05-06). Closed the remaining gap between "Prompt 5 shipped" and "the entire prototype is demoable." Reconciled Prompt 5's plan files, wrote the new Part C ~3-minute demo arc (including a real §C.6.1 reroute of
cost_avoided_mtdfrom Postgres to Databricks), captured receipts for acceptance gates #1, #3, #4, #6, ran dry-run #11 twice clean, prepared the operator handoff for #12. All 12 PRD §C.10 acceptance gates are green (gate #12 operator-gated; every prerequisite landed) andpart-c-databricks-prototype.mdmoved tocompleted/.
Why this exists¶
The parent plan's todo list showed Prompts 1-4 done and Prompt 5 + Prompt 6 + 6 acceptance gates pending. Reading the actual codebase, Prompt 5's frontend code, tests, and artifacts were already committed (frontend/src/widgets/{useWidgetData,SourceBadge,WidgetPreview,MyWidgetsRail,MetricInfoBadge,SpecJsonView}.tsx + 3 Vitest files; receipts in artifacts/prompt-5-frontend-wiring/20260506-120610/). The drift was in the bookkeeping — and a handful of demo-arc artifacts that genuinely didn't exist yet. This plan closed both at once.
What landed¶
- Reconciled bookkeeping:
docs/plans/completed/prompt-5-frontend-wiring.mdflipped tostatus: completed; matching parent-plan todos (prompt_5_frontend_wiring,acceptance_dryrun_9, frontend leg ofacceptance_dryrun_10) flipped. - Live
cost_avoided_mtdreroute path (the §C.6.1 demo moment):l3_asurion.ev_claimseeder extended with acost_avoided_usdcolumn populated by status-weighted distribution (backend/scripts/databricks_mock_data/l3_asurion_generators.py).metrics_catalog.cost_avoided_mtd.source_queryupdated to queryl3_asurion.ev_claimovertrailing_30d(backend/app/metrics/seed.py).config/metric_routing.yamlkeepscost_avoided_mtd: backend: postgres— the on-stage flip is todatabricks+make up.- Pre-validated: Postgres path returns $1.41M in 5ms; Databricks path returns $374,714.39 in 765ms with
source=bedrockand the real generated SQL.
- Demo runbook §C arc: docs/demo-runbook.md gained a new top-level "Part C — Live data binding arc (~3 min)" section after the existing v1 90s arc and Add Widget arc. Includes the §C.6.1 reroute moment scripted as a real on-stage YAML edit +
make up. Pre-flight pre-warm is explicit. - README.md update: README.md gained a "Live Databricks integration (Part C)" section with the
DATABRICKS_*env-var checklist + a per-metric live-vs-synthetic table. - Architecture diagram check: docs/architecture.md Mermaid diagram already showed
dbx_l3; added a dashed Kafka "Phase 2 — production transport" node + dotted edge with ADR-PROTO-001 pointer. - Acceptance receipts captured under
artifacts/part-c-demo-ready/20260506-131614/:- Gate #1:
time make upcold + warm (make_up_{cold,warm}.log) - Gate #3:
make validate-dictionaryexit-0 (validate_dictionary.log) - Gate #4: dry-run SQL gen for all 4 demo metrics (
sqlgen_*.json) - Gate #6: layered safety defense (
gate6_live_adversarial.json+gate6_safety_violation_unit.log) - Gate #11: terminal log of two clean back-to-back programmatic dry-runs (
dryrun_run1.log+dryrun_run2.log) - Gate #12: operator handoff doc (
E2_OPERATOR_HANDOFF.md) — the MP4 itself is operator-gated
- Gate #1:
- Lessons harvest: 3 new entries appended to
docs/lessons-learned.md: reroute moments need a pre-existing column on the destination side; dual source of truth betweenmetrics_catalog.source_queryand the Postgres allowlist;seed_if_emptyis INSERT-only. - Plan close-out: parent
part-c-databricks-prototype.mdmoved todocs/plans/completed/withstatus: completed+completed_on: 2026-05-06. CLAUDE.md "Active plans" line updated to drop Part C; "Current State" Part C bullet rewritten.
Execution log (2026-05-06)¶
| Phase | What landed | Receipt |
|---|---|---|
| A | Prompt 5 plan + parent plan reconciled; close-out note added | docs/plans/completed/prompt-5-frontend-wiring.md + part-c-databricks-prototype.md |
| B1 | cost_avoided_usd column added to ev_claim seeder |
backend/scripts/databricks_mock_data/l3_asurion_generators.py |
| B2 | cost_avoided_usd added to column_dictionary.csv + column_count bumped on table_catalog.csv |
data-dictionary/column_dictionary.csv + data-dictionary/table_catalog.csv |
| B3 | cost_avoided_mtd.source_query points at l3_asurion.ev_claim over trailing_30d |
backend/app/metrics/seed.py |
| B4 | Both paths live-validated; YAML restored to postgres | cost_avoided_mtd_postgres.json + cost_avoided_mtd_databricks.json |
| B5 | cost_avoided_mtd added as Metric 4 in demo-queries.md | docs/demo-queries.md |
| B6 | whats-mocked-in-prototype.md gains cost_avoided_mtd row + value-jump Q&A | docs/whats-mocked-in-prototype.md |
| C1 | Part C arc + §C.6.1 sub-section in demo-runbook | docs/demo-runbook.md |
| C2 | Live Databricks integration section in README + docs index pointers | README.md |
| C3 | Kafka Phase 2 dashed node added to architecture Mermaid | docs/architecture.md |
| C4 | 3 new lessons appended | docs/lessons-learned.md |
| D1 | Gate #1 receipt | make_up_{cold,warm}.log |
| D2 | Gate #3 receipt | validate_dictionary.log |
| D3 | Gate #4 receipts (4 metrics) | sqlgen_claim_volume_l3_asurion.json + sqlgen_claims_by_product_l3_asurion.json + sqlgen_claim_status_mix_l3_asurion.json + sqlgen_cost_avoided_mtd.json |
| D4 | Gate #6 receipts (live + unit) | gate6_live_adversarial.json + gate6_safety_violation_unit.log |
| E1 | Two clean programmatic dry-runs | dryrun_run1.log + dryrun_run2.log |
| E2 | Operator handoff prepared (MP4 gated on human driver) | E2_OPERATOR_HANDOFF.md |
| F | Parent plan archived to completed/; CLAUDE.md updated |
this file |
Acceptance roll-up¶
All 12 PRD §C.10 gates green. The single asterisk is gate #12: the backup MP4 itself requires a human driver to record against the running stack (live Bedrock + live Databricks + on-stage YAML edit), and the operator handoff at artifacts/part-c-demo-ready/20260506-131614/E2_OPERATOR_HANDOFF.md is the single landing page for that step. Every prerequisite (recording steps, runbook section, gitignore coverage) is in place.
Surprises / decisions¶
seed_if_emptyis INSERT-only. Editing a metric's catalog row inseed.pydoes NOT update an existing warm-dev DB row. Caught during B3 — the workaround isTRUNCATE metrics_catalogbeforemake up. Captured in lessons-learned.trailing_30dvs MTD for cost_avoided_mtd: the Databricks path usestrailing_30dfor consistency with the other Databricks-routed metrics; the Postgres path stays hardcoded to MTD viadata_resolver._POSTGRES_QUERIES. The values therefore differ between the two paths ($1.41M Postgres vs $375k Databricks) — the value-jump is intentional and is documented indocs/whats-mocked-in-prototype.md.- Bedrock-side adversarial defense. When DROP TABLE was injected into filter values, Bedrock itself recognized + refused — its explanation literally said "DDL injection attempt and not a valid filter predicate." The 422 safety_violation path is exercised by the unit test that forces a hypothetically-rogue LLM to emit DROP TABLE.
References¶
- Parent plan (now in completed/):
docs/plans/completed/part-c-databricks-prototype.md - PRD:
prd-v2.1.md§C.6, §C.6.1, §C.10 - Prompts:
prompts.mdlines 339-379 (Prompt 6) - Prompt 5 close-out:
docs/plans/completed/prompt-5-frontend-wiring.md - Backup video procedure:
docs/backup-recording.md - Operator handoff:
artifacts/part-c-demo-ready/20260506-131614/E2_OPERATOR_HANDOFF.md - Routing config:
config/metric_routing.yaml - Resolver allowlist:
backend/app/widgets/data_resolver.py