Plan: Demo-ready, live-verified, dual-persona journey walkthroughs
On this page
Status
| Step | Description | Status |
|---|---|---|
0 |
Branch + this plan committed/nav-linked; D3 shared e2e helpers built; portal→worker hand-off smoke-tested |
Done (2026-07-08) — smoke passed 10/10; helpers in |
1 |
Workflow A — parallel authoring: per-journey spec upgrade to dual-persona portal flow + walkthrough rewrite to the D2 format |
Done (2026-07-08) — 10 journeys via Workflow A + #6 ele-grant authored by the primary (deferred-drain vs pre-determination-consent nuance). |
2 |
Live walk every journey serially; fix-or-unpublish; capture dual-persona screenshots |
Done (2026-07-08) — all 12 journey specs pass live ( |
3 |
Shared files (nav / index / snap.toml / CHANGELOG / Demo Runbook); |
Done (2026-07-08) — Demo Runbook + index note + CHANGELOG + two inventory describe-label bindings; |
Epic: &61
Issues: #991 (retrofit shipped walkthroughs to the #979 concrete-precondition bar)
Branch: feature/991-demo-ready-journeys
Context
We have 11 shipped SNAP journey walkthroughs, authored against e2e specs that
build every case through the endpoint-driven given-library (SnapCaseBuilder) —
never the real applicant portal. So the human walkthroughs are caseworker-only,
the applicant precondition is vague, and none have been walked by hand end-to-end
— several are suspected broken/incomplete. A live demo needs a
side-by-side applicant + caseworker experience for every published journey,
plus a reliable reset between runs.
Goal: every published journey walkthrough is a complete, live-verified, dual-persona manual script a presenter can drive on demand, plus one Demo Runbook. Any journey that cannot be walked honestly and isn’t fixable in this MR (and isn’t externally blocked) is unpublished — we do not publish broken work.
Owner decisions: applicant side = live front-door + view; #9 = harness bring-up, #12 = dropped; verification = full live walk of every journey; orchestrate with workflows (parallel authoring; the live walk is the one serial bottleneck).
Review resolutions (round-1 findings → fixes baked in below)
| Finding | Resolution |
|---|---|
C1 |
Two distinct loops. My automated verification runs each journey via |
C2 portal helper on wrong origin/auth (journey project = worker :8080 + caseworker storageState; portal = :8090) |
The journey |
C3 |
The portal helper adds a roster-resolution step: after finalize it calls |
H4 even |
The helper waits for + parses |
H5 #6 ELE order wrong — grant fires from a deferred prior approval, drained by consent ( |
#6 order corrected: front-door (2 kids, no ELE opt-in) → worker Run Determination → Approved (defers the grant) → worker Record ELE consent → badge (drains the deferral, BEFORE re-determination) → re-determine (re-affirms). |
H6 global "every journey through the portal, both personas" contradicts #8/#9/#10 |
Tier-specific verification gates (see Verification): Fully-manual, Worker-driven, and Harness tiers each have their own acceptance criteria; notice-silent journeys assert no notice step. |
M7 |
Runbook states rollback = the Postgres DBs only; use a fresh browser context / incognito per journey (fresh applicant + worker sessions); in-flight events drain harmlessly; Garage PDFs accumulate harmlessly; Keycloak unaffected. Heavier pristine reset ( |
M8 runbook commands not real ( |
All commands prefixed |
M9 #4 drops |
Add |
M10 #3 "file recert" overstates (records intent only) |
The #3 walkthrough preserves the honest-scope caveat: filing records intent ( |
L11 screenshot path differs container vs host |
Use |
Scope
In scope:
-
D1 — a new Demo Runbook (
runbooks/demo-runbook.adoc, nav-linked). -
D2 — a uniform dual-persona walkthrough format applied to every published journey.
-
D3 — shared e2e helpers + per-journey spec upgrades that drive the real applicant portal front door.
-
Live verification of every published journey; fix-in-MR or unpublish.
Out of scope:
-
#12 (ADH→IHE) — no walkthrough, no UI; tracked by #995. Nothing to publish or unpublish.
-
New applicant-portal capabilities (change reporting, appeals filing) — worker-driven beats stay worker-side (honest scope).
Governing rules (non-negotiable)
-
One MR. No batching unless a fix is externally blocked (partner integration / large new subsystem). Test/doc hardening is not "external".
-
Fix-or-unpublish. Live-walk breakage → root-cause → fix in this MR if fixable; if externally blocked, keep only if still honestly walkable (documented); else unpublish.
-
Unpublish mechanics (reversible): move the page to
docs/…/walkthroughs/unpublished/, remove itsnav.adocxref
walkthroughs/index.adocrow, and incompliance/scenario-inventory/snap.tomlswap thewalkthroughbinding forwalkthrough_blocked_by = ["#<issue>"](issue-backed → pairing gate stays green; scenario staysCovered [Journey]via its still-passing e2e-spec binding). File the breakage issue; note in CHANGELOG. -
No deferral of critical functionality unless externally blocked.
Design
D1 — Demo Runbook (new page runbooks/demo-runbook.adoc, nav-linked)
-
One-time bring-up:
cargo xtask dev start --profile full→cargo xtask seed --seed 42 --households 50(fresh stack) →cargo xtask migrate snapshot(~5-10s golden state) →cargo xtask dev status(read ephemeral ports). -
Reset between MANUAL journeys:
cargo xtask migrate rollback(~5-10s;pg_restore --cleanper DB). DB-only (M7): also open a fresh browser context / incognito per journey for clean applicant + worker sessions; queued events + Garage PDFs accumulate harmlessly; Keycloak/ports unchanged. For a fully-cold pristine start:cargo xtask dev clean --confirm && cargo xtask dev start --profile full && cargo xtask seed --seed 42(~minutes). -
Credentials: worker table (all
password;jane.caseworker) fromuser-testing-guide.adoc; applicant castHH-ca570001..04+ passcodes fromrunbooks/demo-applicant-credentials.adoc; live-front-door applicants use theHH-…+passcode from the post-submit reveal. -
Per-journey index table: journey | tier | exact starting point | link.
-
Gotchas: ephemeral ports (never hardcode); cross-program REQUIRES
--profile full; do notgit pushduring the demo (pre-push reseeds the live stack — re-snapshotafter); SOPS/keycloak cold-start → preferdev refresh; 30s JWKS debounce on first login.
D2 — Uniform dual-persona walkthrough format (all published journeys)
-
Reproducibility tier admonition (Fully manual / Worker-driven / Harness).
-
Personas & credentials — Applicant (
/apply→/lookup) + Caseworker. -
Bring-up & reset — exact
cargo xtask …starting commands +migrate rollback. -
Concrete precondition (#991/#979) — exact applicant inputs (composition, DOBs, the income/expense/address the worker will record) from the paired spec.
-
Step-by-step, interleaved — one ordered table
# | Screen | Action | Expected | Screenshot,Screen∈ {Applicant, Caseworker}; every action in sequence. -
Applicant post-outcome views (
/homehero,/lettersPDF), Expected outcome / oracle, Honest scope, Verify (the--project journeycommand).
D3 — Shared e2e helpers + journey-spec upgrades
Build once, first in tests/e2e/lib/ (serialize — shared file), smoke-test,
then fan out:
-
fileSnapApplicationViaPortal(applicantPage, {applicant, members})— drives the real/applywizard on a portal context, waits for + parsesPOST /apply/finalize(H4), scrapes the reveal code+passcode, thenGET /v1/households/{id}/full(C3) → returns{householdId, applicationId, referenceCode, passcode, headPersonId, memberPersonIds}. -
viewApplicantHome(applicantPage, {referenceCode, passcode})—/lookuplogin →/home;openLatestLetter(applicantPage)—/letters→ open the notice PDF. -
addAddressViaUi(workerPage, {…})(M9) — worker adds a baseline address via the #983 add-form (/actions/address/add), sibling tochangeAddressViaUi.
Then upgrade each portal-reproducible journey spec: default page = worker
(caseworker auth, :8080); open a separate applicant context on
CANOPY_PORTAL_CSP_BASE_URL (:8090) for the portal beats (C2). Flow: applicant
files front-door → worker records facts + acts → applicant signs back in to view,
capturing both personas' shots (app- applicant, step- worker) via the
container path (L11). Backdated journeys keep the given-lib for the aged parts
(portal apply is now()-dated).
Per-journey disposition (all 12)
| # | Journey | Tier | Plan / live-walk risk |
|---|---|---|---|
1 |
thirty-day-determination-noa |
Fully manual |
Portal front-door ($900 recorded by worker) → determine → NOA; applicant views approved hero + letter. |
2 |
lifecycle / lottery-winnings |
Fully manual |
Front-door ($300) → cert → worker adds income |
3 |
income-materiality |
Fully manual |
Front-door ($800) → cert → worker adds wages 9000 → recert nudge → file recert (records intent only, M10) → change notice. |
4 |
shelter-cascade (#983) |
Fully manual |
Front-door (size-3) → cert → worker adds baseline address ( |
5 |
change-during-pending-hearing |
Worker-driven |
Front-door (size-3); worker records rent + files 2 appeals (future effective date). No applicant appeal UI (honest scope). |
6 |
ele-grant |
Fully manual |
Front-door: head + 2 children under ELE age, no Express-Lane opt-in. Worker Run Determination → Approved (defers) → Record ELE consent → badge (drains, pre-re-determination, H5) → re-determine. |
7 |
cross-program-report |
Worker-driven |
Portal SNAP front-door; worker files TANF (2nd program, worker-only) + income → both deny. |
8 |
recert-churn (#979 exemplar) |
Worker-driven |
RESOLVED to dual-persona. The feared |
9 |
overpayment-recompute |
Harness (worker-only) |
Confirmed architecturally worker-only (owner-approved kept as Harness). The recompute needs a determination whose effective date is months in the past (the claim looks back over prior issuances); a now-dated portal application yields a zero-width lookback window, and no worker backdate-determination action exists — so the aged case is harness-built and mints no |
10 |
upheld-decision-overpayment |
Worker-driven (notice-silent) |
Front-door (head $800 + 1 child); worker enrollment + issuances + backdated-timely appeal (worker-typed dates) + record decision Upheld. No notice step asserted (notice-silent path, H6/honest scope). |
11 |
transitional-benefits-on-tanf-closure |
Worker-driven |
Portal SNAP front-door (head $0 + 2 children); worker files TANF, adds wages, re-determines TANF → denied → TSNAP freeze. |
12 |
adh-not-established-ihe-claim (#981) |
DROP |
No walkthrough; no UI (service-only). Leave the orphan spec; never present it as a published journey. Tracked by #995. Nothing to unpublish. |
Steps
Step 0 — primary, serial
Branch feature/991-demo-ready-journeys; build the D3 shared helpers; smoke-test
fileSnapApplicationViaPortal end-to-end (proves the portal→worker hand-off — the
root-cause check) before anything else.
Step 1 — Workflow A (parallel authoring), one agent per journey
Each reads its walkthrough + spec + given-lib, upgrades its journey spec to the
dual-persona portal flow (Step-0 helpers) and rewrites its walkthrough .adoc to
D2. Agents touch only their own journey files (their .adoc + .spec.ts) —
never the shared files (nav/index/snap.toml/CHANGELOG/runbook/helpers), which the
primary owns. Each returns a report + "looks broken / needs code fix" flags.
Step 2 — live walk (primary, serial on the one shared stack)
Prep: cargo xtask dev start --profile full → seed --seed 42. For each journey
(order below): run its upgraded spec cargo xtask e2e --devstack-profile full — specs/journey-<n>.spec.ts --project journey (e2e self-seeds; C1) → confirm every
step + oracle + both personas' screenshots → apply the captured PNGs. On
breakage: diagnose → fix in-MR (spawn a focused fix agent; Workflow B fans out
disjoint service-code fixes in parallel with worktree isolation) → re-run; if
unfixable/externally-blocked → unpublish + file the issue.
Order: #1 #2 #3 #4 #6 (fully manual) → #5 #7 #10 #11 (worker-driven) → #8 (selector risk) → #9 (harness) → runbook → audit → battery.
Files Touched
| File | Change |
|---|---|
|
All rewritten to D2 (or moved to |
|
New Demo Runbook (bring-up + reset + per-journey index). |
|
Runbook nav-link; any unpublished-journey removals. |
|
|
|
Upgraded to the dual-persona portal flow. |
|
Regenerated (plain PNG). |
|
Concrete-precondition comments; |
service source (canopy-web / canopy-portal / …) |
Only as needed to fix a broken step. |
|
|
Verification — tier-specific gates (H6)
-
Fully manual (#1,#2,#3,#4,#6): the upgraded spec passes end-to-end — applicant files via the portal context, worker records facts + acts, applicant signs back in to view
/home+/letters; both-persona screenshots render; journey oracle holds. -
Worker-driven (#5,#7,#8,#10,#11): applicant files the SNAP front door via the portal context + can view
/home//letters; all substantive steps are worker-side and pass; no applicant mid-journey action asserted. #10 asserts no notice (notice-silent). #8’s feared selector tie proved not real (timestamp- precisionreceived_at DESC), so it converted cleanly to dual-persona. -
Harness (#9, worker-only): the harness constructs the aged case; the worker recompute + notice steps pass live; no applicant view (the harness-built case mints no
/lookupcredential — owner-approved, follow-up issue filed). -
Cross-cutting:
cargo xtask scenarios auditclean (no dangling shots; unpublished journeys carry issue-backedwalkthrough_blocked_by);quality-budgetsLOCKED;clippy/fmtclean; ADR-011 clean; full pre-push battery green (journey specs are demo-gated — run explicitly in the walk, not in the battery).
Documentation Updates
-
Antora walkthroughs — all published journeys rewritten to D2; new Demo Runbook.
-
nav.adoc+walkthroughs/index.adoc— runbook link; any unpublish removals. -
compliance/scenario-inventory/snap.toml— preconditions;walkthrough_blocked_byon unpublish. -
CHANGELOG.adoc— entry under== Unreleased.