Journey: A lapsed SNAP certification churns back through reapplication

On this page

An applicant files a SNAP application through the real portal front door. The caseworker approves it, then backdates a certification so the case falls out of coverage (a lapse). The household churns back: the caseworker files a reapplication against the same household, re-runs the determination, and recertifies over a current period — returning the household to in-force coverage. The applicant signs back in to view their home. The journey doubles as the human-followable demonstration that a time-relative precondition (a certification dated in the past) can be constructed through the worker portal with no clock fakery or seeded SQL (#979).

NOTE

Reproducibility tier: Worker-driven. The applicant files the original SNAP application live at /apply and can sign back in at /lookup to view /home; the substantive churn beats (backdating the certification, filing the reapplication, recertifying) are caseworker-only — there is no applicant-portal "reapply" affordance yet (honest scope). The aging that makes the case "lapsed" lives in the certification end date the worker backdates, not in the application, so the original application is an ordinary now-dated portal filing.

IMPORTANT

This walkthrough is paired with the automated spec tests/e2e/specs/journey-snap-recert-churn.spec.ts, covering scenario snap.certification.closure-churn-reapply. The screenshots below are captured by that spec (applicant shots app-NN-…, worker shots step-NN-…). See Verifying the journey to run it.

Personas & credentials

Persona Credentials / identity Role in the journey

Applicant (Rosa Marlowe)

Files at the portal /apply flow; the Application ID (HH-…) + 12-digit passcode shown on submit are the /lookup sign-in.

Files the original SNAP application; signs back in to view their home after the churn.

Caseworker

Worker portal (:8080), Keycloak login jane.caseworker / password (see UAT Facilitator Guide).

Records the wage + approves, backdates a certification to lapse the case, files the reapplication, re-determines, and recertifies over a current period.

Bring-up & reset

cargo xtask dev start --profile full   # full devstack (SNAP + applications + renewals + notices + portal)
cargo xtask seed --seed 42             # deterministic fixtures

Reset between runs so the demo starts from a clean slate:

cargo xtask migrate rollback           # restore the golden DB snapshot
# + a fresh browser / incognito window per run (applicant + worker sessions)

The Demo Runbook owns the full driving detail.

Concrete precondition

The exact inputs this journey depends on (the #979/#991 bar):

  • Applicant files at /apply (composition + categories only — no amounts): a single working-age adult — head of household only, no dependentsRosa Marlowe, DOB 1985-06-15, at a Georgia address; income step No one works right now.

  • Earned income the caseworker records: income type wages, $300 / month, effective today — a construction extreme robustly below any gross-income limit (an input, not an asserted value).

  • The backdated (lapsed) certification the caseworker records: start ~14 months ago, end ~2 months ago — a wholly-past window, so the household is immediately lapsed (no in-force coverage). The lapse lives in this end date, not in the application: the original portal filing is now-dated, and the #978 program-aware selector (received_at DESC, timestamp precision) binds Run Determination to the later reapplication because it is filed after the original.

  • The forward recertification: start today, end ~12 months ahead (a ~12-month window spanning today).

The walkthrough, step by step

# Screen Action Expected Screenshot

01

Applicant

At /apply: Begin applicationAbout you (Rosa Marlowe, DOB 1985-06-15) → Household (just me) → Safety (standard protections) → Income (no one works right now; quick-check 0) → ReviewSubmit application.

The Application submitted reveal shows the HH-… reference + 12-digit passcode. Save both.

Application submitted

02

Caseworker

Open the case (/cases/<HH-…>?program=snap) → Income tab → + Add income for Rosa → wages, $300 / month, effective todayAdd. Then Action ▾Run Determination.

Approved — the household is financially eligible. (No certification on file yet.)

Case opened

03

Caseworker

Determination tab → the program group’s Actions panel → Create certification; enter a past period — start ~14 months ago, end ~2 months ago.

The form accepts the backdated dates — only "end after start" is enforced, so a wholly-past period is valid.

Backdated cert form

04

Caseworker

Submit; the handler redirects to the Household tab.

The certification period shows an end date in the past — the household is lapsed (no in-force coverage).

Lapsed period

05

Caseworker

Action ▾File application, tick SNAP, and submit.

A new SNAP application (the reapplication) is filed against the same household.

Reapplication filed

06

Caseworker

Action ▾Run Determination.

Approved — the household is still financially eligible, independent of the lapsed certification (the received_at DESC selector binds this run to the reapplication).

Re-determined approved

07

Caseworker

Determination tab → ActionsCreate certification; enter a forward period — start today, end ~12 months ahead.

Succeeds — the lapsed certification no longer blocks a new one — and the household is back in coverage; a new Notice of Action appears on the Notices tab.

Back in coverage

08

Applicant

Sign back in at /lookup with the HH-… reference + passcode → land on /home.

The home greeting renders — the same applicant, back in their own portal after the churn.

Applicant home

Expected outcome / oracle

The oracle is relational / derived, never a dollar figure or a policy threshold:

  • The household’s in-force certification end date moves from before today (lapsed) to on/after today (covered), driven only by a real reapplication → re-determination → re-certification.

  • The in-force certification after the churn is a different period from the lapsed one (a new certification row, not the same row edited).

  • A new notice row appears on the case Notices tab, above the count baselined before the reapplication (the NOA pipeline fired end to end: determination → event → notice).

Both ends of the coverage relation are compared to today — never to a policy value — so the journey holds under any jurisdiction’s certification-period rules.

Honest scope

  • Closure is not a system event. No scheduler closes a case when its certification end date passes, and the determination reads no certification state (eligibility is fact-driven). The "lapse" is the absence of an in-force certification (most recent cert end date < today) — exactly how the renewals overdue feed observes it — not a stored "closed" status.

  • Reapplication = a fresh initial application. The 7 CFR 273.14(b)(2) 30-day late-renewal-vs-new-application proration branch is not implemented; every reapplication is a full re-determination.

  • The churn-back beats are worker-only. There is no applicant-portal "reapply" affordance yet, so the reapplication + recertification are caseworker actions on the case the applicant originally filed. The backdating lives in the production certification-date fields (typed through the real worker cert-create form), not in clock fakery or seeded SQL.

Verifying the journey

cargo xtask e2e --devstack-profile full -- \
  specs/journey-snap-recert-churn.spec.ts --project journey

The spec files an approved single-adult SNAP application through the real /apply wizard, then drives the churn arc through the worker portal — recording the wage, approving, backdating a certification to lapse the case, filing the reapplication, re-determining to Approved, and recertifying forward — before the applicant signs back in to view their home. It captures the app-NN/step-NN screenshots into test-results/e2e/walkthroughs/… for commit into docs/modules/ROOT/assets/images/walkthroughs/…. The correctness oracle is the coverage transition: the household’s in-force certification end date moves from before today (lapsed) to on/after today (covered), a pure relation to today with no policy value asserted.

Edit this page · default