Skip to content

Commit 6459fd0

Browse files
docs(demo): add scene-by-scene live-demo flow runbook
Presenter-oriented runbook that ties each click and screen in the live SPDCI federated-eligibility demo to its talking points and expected audience reaction. Includes a pre-demo state table, ten scene breakdowns (frame → empty SP → SR import → CEL rule → enroll → audit drill-down → live SR verify → failure-mode recap → architecture → roadmap), a 5-minute lightning cut, pre-canned Q&A anchors (including the marital_status filtering question we probed today), and a pre-flight checklist so the host can verify environment health before the audience walks in. Complements the existing briefing sheet (narrative + personas + glossary), CEL-to-DCI internals walkthrough, and modules/reset reference.
1 parent 8dfe9f5 commit 6459fd0

1 file changed

Lines changed: 285 additions & 0 deletions

File tree

scripts/demo/SPDCI_DEMO_FLOW.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# OpenSPP DCI Demo Flow
2+
3+
---
4+
5+
## Pre-demo state (assumed before slide 1)
6+
7+
| Side | State |
8+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
9+
| SP | Fresh database. Modules installed (`spp_dci_openspp_dr` + `spp_dci_openg2p`). 0 registrants. DR data source URL set. |
10+
| DR | `openspp_dr` database with 8 approved disability assessments seeded for `IND-NSR-0001/0003/0005/0007/0009/0011/0013/0015`. DCI bypass flags set. |
11+
| OpenG2P SR | Live at `partner-nsr.play.openg2p.org`. 15 personas seeded by OpenG2P. No SP-side action needed. |
12+
| Demo program | "Disability Assistance" defined on SP. Eligibility rule: `has_disability == true && is_poor == "low"`. |
13+
14+
Run `scripts/demo/reset_spdci_demo.py` to guarantee a clean
15+
cache + draft memberships if you ran a dry run earlier.
16+
17+
---
18+
19+
## Scene 1 — Frame the problem (1 min, slide-only)
20+
21+
**Slide content**: the narrative paragraph from the briefing.
22+
23+
> A government runs a social-protection program — **Disability Assistance** — that
24+
> targets registrants who are **both** living with a disability **and** classified as
25+
> poor. The two facts live in two independent registries owned by two independent
26+
> agencies. OpenSPP composes the eligibility decision by querying both over the DCI
27+
> standard, in real time, with a single click.
28+
29+
**Talking points**
30+
31+
- Two registries, two owners, one decision.
32+
- The hard problem isn't computing eligibility — it's federating data across independent
33+
agencies without replicating their records.
34+
- SPDCI is the standard we use to do it without per-vendor glue.
35+
36+
**Transition**: "Let's see what the platform looks like before any of this is wired in."
37+
38+
---
39+
40+
## Scene 2 — The empty SP (1 min, live)
41+
42+
**On screen**: SP UI → Registry → Registrants list. Empty.
43+
44+
**Presenter does**: nothing yet. Just lets the audience see "0 registrants".
45+
46+
**Talking points**
47+
48+
- This is a clean OpenSPP install. We haven't pre-loaded data; we'll populate the
49+
registry the way an operator would in production.
50+
- The SP doesn't know about the Social Registry's records yet. Names, identifiers,
51+
demographics — all live on the SR.
52+
- We're going to pull a list of registrants from the SR using DCI search-sync, let the
53+
operator review them, and import the chosen ones.
54+
55+
**Transition**: "Watch how the operator pulls registrants from the Social Registry."
56+
57+
---
58+
59+
## Scene 3 — Import from the Social Registry (2 min, live)
60+
61+
**On screen**: SP UI → Registry → **Import from External Registry**.
62+
63+
**Presenter does**:
64+
65+
1. Click **Registry → Import from External Registry**.
66+
2. Form opens. Note **Source Registry** is pre-filled with **Social Registry**.
67+
- _One sentence aside_: "That's the OpenG2P SR endpoint. It's just a DCI data source
68+
— production deployments configure their own."
69+
3. Discovery mode: **Identifier range sweep** (the default).
70+
4. Range: `IND-NSR-` `0001` to `0015`, pad `4`.
71+
5. Auto-enroll: pick **Disability Assistance**.
72+
6. Click **Preview**.
73+
74+
**On screen**: 15 preview rows fill in. Each row carries given_name, surname, sex,
75+
birth_date pulled fresh from the SR over DCI.
76+
77+
**Talking points**
78+
79+
- Every row on the preview is the result of a separate DCI search-sync request to the
80+
live SR. The SR returned name + demographics + identifier metadata in the DCI standard
81+
envelope.
82+
- The wizard captured **only the minimum**: name, sex, birth_date, UIN. The rich
83+
attributes (income_level, marital_status, employment_status) stay on the SR — we'll
84+
fetch them on demand at eligibility time.
85+
- All 15 rows are pre-selected because none of them exist on the SP yet.
86+
- If we re-ran the wizard, already-imported rows would show "already on SP" and be
87+
unchecked by default.
88+
89+
**Presenter does**: Click **Import Selected**.
90+
91+
**On screen**: "15 registrant(s) imported." Wizard closes; navigate back to Registrants
92+
list to show the 15 new partners with their UIN reg_ids.
93+
94+
**Transition**: "Now the SP has identifiers. It doesn't yet have disability data or
95+
poverty data. Let's run eligibility and watch the federation kick in."
96+
97+
---
98+
99+
## Scene 4 — The program and its CEL rule (1 min, live)
100+
101+
**On screen**: SP UI → Programs → **Disability Assistance**.
102+
103+
**Presenter does**: open the program form. Scroll to the eligibility rule.
104+
105+
**Slide content** (recap):
106+
107+
```
108+
has_disability == true && is_poor == "low"
109+
```
110+
111+
**Talking points**
112+
113+
- This is a **CEL** (Common Expression Language) rule. Google's open-source expression
114+
DSL. We use it because CEL is vendor-neutral and the operator can change a rule
115+
without touching code.
116+
- `has_disability` and `is_poor` are CEL **variables**. Each one is bound to a registry
117+
behind the scenes:
118+
- `has_disability` → OpenSPP-DR (Disability Registry) over DCI
119+
- `is_poor` → OpenG2P SR (Social Registry) over DCI, reading `income_level`
120+
- The operator doesn't see "Disability Registry" or "OpenG2P" in the rule. They see
121+
semantic names. Swapping the backing source is a configuration change, not a code
122+
change.
123+
- The rule will evaluate true for any registrant who is both flagged as disabled by the
124+
DR _and_ classified `income_level == "low"` by the SR.
125+
126+
**Transition**: "One click."
127+
128+
---
129+
130+
## Scene 5 — Enroll Eligible (2 min, live, the centerpiece)
131+
132+
**On screen**: Disability Assistance program form, top bar showing membership counts (15
133+
draft / 0 enrolled).
134+
135+
**Presenter does**: click **Enroll Eligible**.
136+
137+
**What audiences see**: a few seconds of work, then the count flips to **4 enrolled / 11
138+
draft**.
139+
140+
**Talking points** (while it runs):
141+
142+
- Under the hood, the platform just fired **30 DCI requests** in parallel: 15 to the DR
143+
for `has_disability`, 15 to the SR for `is_poor`.
144+
- Every response was cached in `spp.data.value` with a TTL.
145+
- Every fetch was audited in `spp.dci.fetch.audit` for compliance.
146+
- The CEL executor compiled the rule into a SQL query that AND-joined the two cached
147+
datasets to produce the final eligibility set.
148+
- The whole thing took under five seconds in this demo. In production this is the
149+
bottleneck you'd tune with caching and async pre-fetch.
150+
151+
**On screen after**: the 4 enrolled — Alex Rivera, Morgan Cole, Taylor Brooks, Sam
152+
Hayes. (Names match the briefing sheet's expected outcome.)
153+
154+
**Slide content** (split view recommended):
155+
156+
| Why these 4? | Each is in **both** registries with both predicates true |
157+
| ---------------------------- | -------------------------------------------------------- |
158+
| Alex Rivera (IND-NSR-0001) | DR: has_disability=true • SR: income_level=low |
159+
| Morgan Cole (IND-NSR-0004) | DR: has_disability=true • SR: income_level=low |
160+
| Taylor Brooks (IND-NSR-0010) | DR: has_disability=true • SR: income_level=low |
161+
| Sam Hayes (IND-NSR-0013) | DR: has_disability=true • SR: income_level=low |
162+
163+
**Transition**: "Let's see why one of them got picked — by drilling into the audit
164+
trail."
165+
166+
---
167+
168+
## Scene 6 — Drill into one decision (2 min, live)
169+
170+
**On screen**: SP UI → click Alex Rivera in the enrolled list.
171+
172+
**Presenter does**: open Alex's partner form. Navigate to the audit log / DCI fetch
173+
audit related list (depending on UI exposure, this may be via the developer menu or a
174+
dedicated tab).
175+
176+
**Talking points**
177+
178+
- Each row here is a DCI fetch that contributed to Alex's eligibility. We see two: one
179+
to the DR for has_disability, one to the SR for is_poor.
180+
- The audit row carries the message_id (the DCI envelope's identifier), the registry
181+
that answered, the timestamp, and the resolved value. We can reconstruct the exact
182+
wire conversation for compliance review months later.
183+
- This is the "show your work" property of SPDCI: a federated eligibility decision is
184+
reproducible, attributable, and audit-ready.
185+
186+
**Optional**: open one row, show the envelope/message detail.
187+
188+
**Transition**: "And to prove these aren't synthetic numbers, let's see the same data on
189+
the source registry."
190+
191+
---
192+
193+
## Scene 7 — Same record on the live SR (1 min, live)
194+
195+
**On screen**: terminal with a `curl` or browser tab to OpenG2P's playground.
196+
197+
**Presenter does**: run a one-line `curl` (or open the UI) against
198+
`partner-nsr.play.openg2p.org`:
199+
200+
```bash
201+
# Pre-canned with auth + envelope already filled in.
202+
./scripts/demo/probe_openg2p.sh IND-NSR-0001
203+
```
204+
205+
(If you don't have a pre-canned probe, fall back to the wizard's preview of
206+
`IND-NSR-0001` — the same JSON travels over the wire.)
207+
208+
**On screen**: the SR's response showing the same Alex Rivera record with
209+
`income_level: "low"` in `additional_attributes` or `reg_records[0]`.
210+
211+
**Talking points**
212+
213+
- Same identifier, same record, returned by an independent OpenG2P-hosted registry.
214+
We're not mocking anyone — this is a live, third-party endpoint.
215+
- The DR side is our own OpenSPP instance acting as a DCI server. Different agency,
216+
different platform, both speaking the same protocol.
217+
218+
**Transition**: "Let's recap and look at what's interesting beyond the happy path."
219+
220+
---
221+
222+
## Scene 8 — Recap and what each failure mode tells us (1 min, slide)
223+
224+
**Slide content** (compact version of the briefing's verdict table):
225+
226+
| Failure mode | Example | What it shows |
227+
| -------------- | ---------------- | ------------------------------------------------------------------------------------------ |
228+
| Income not low | Kim Lee (medium) | SR says "not poor enough" — DCI returned a value, rule rejected it |
229+
| No DR record | Priya Rivera | DR returned not_found — variable resolved to null, rule rejected |
230+
| Both fail | Noah Rivera | Two failed predicates — no enrollment, no PII pulled from either side beyond identity |
231+
| Empty income | (several rows) | SR returned a record but `income_level` was unset — treated as null, fails strict equality |
232+
233+
**Talking points**
234+
235+
- The interesting thing isn't the 4 enrolled — it's that the platform handled four
236+
different failure modes without bespoke code. Each is just "a CEL predicate evaluated
237+
false, in a specific way."
238+
- This is what "configuration over code" looks like in practice: an operator changes the
239+
rule to `has_disability == true || is_poor == "low"` and 11 of the 15 enroll instead
240+
of 4. No deployment, no Python.
241+
242+
---
243+
244+
## Scene 9 — Architecture map (1 min, slide)
245+
246+
**Slide content**: the topology diagram from the briefing.
247+
248+
**Talking points**
249+
250+
- Three independent processes, two HTTP boundaries, one decision.
251+
- The SP is the **client** for both registries — it doesn't host their data, it queries
252+
them on demand.
253+
- DCI is the protocol that lets us bring on a third or fourth registry later (CRVS, IBR,
254+
MOSIP eSignet for ID verification) without rewriting the eligibility logic. New
255+
registry = new data source + new CEL variable; the rule grammar is unchanged.
256+
257+
---
258+
259+
## Scene 10 — Where this goes next (1 min, slide)
260+
261+
**Slide content**: three forward-looking bullets.
262+
263+
- **More registries**: CRVS for civil events, IBR for cross-program dedup, MOSIP eSignet
264+
for OIDC-based ID verification. All slot in via the same SPDCI bridge.
265+
- **Async pre-warm**: long-running cohort scans move off the request path and into queue
266+
jobs, with operator-visible progress.
267+
- **Configurable consent**: per-source consent purpose codes, signed by the SP's
268+
registered DCI sender key.
269+
270+
---
271+
272+
## If you only have 5 minutes (lightning version)
273+
274+
Drop scenes 2, 6, 7, 9, 10. Keep:
275+
276+
1. Scene 1 — Frame the problem
277+
2. Scene 3 — Import from SR (skip the wizard internals, just show the final 15-row
278+
preview and import)
279+
3. Scene 4 — The rule
280+
4. Scene 5 — Enroll Eligible → 4 / 15
281+
5. Scene 8 — The failure-mode table
282+
283+
---
284+
285+

0 commit comments

Comments
 (0)