Journey: SNAP mid-certification material income change → recert nudge → notice

On this page

An applicant files a single-person SNAP application through the real applicant portal. A caseworker records the household’s wages, approves and certifies the case, then records a substantial mid-certification income increase the household reports. The renewals materiality subscriber runs a non-persisting dry-run against the determination of record, finds the change material (it would flip the verdict), and in ONE commit raises a recertification nudge and emits the event that generates the change-in-circumstances Notice of Action — the notice is a consequence of the material-change evaluation itself, not of anything the caseworker does next. The caseworker confirms the notice, then files the recert from the nudge (recording intent only). The applicant then signs back in and reads that letter — the report → act → notice → see-it arc that 7 CFR 273.12(a)(5)(v) bounds for simplified reporters, shown across BOTH portals side by side.

NOTE

Reproducibility tier: Fully manual. Every step below can be reproduced live, by hand, through the two served portals — the applicant files the case at /apply in one browser and the caseworker works it at /cases in another. No harness, given-library, or pre-seeded case is required; the case is the one the presenter files during the demo.

IMPORTANT

This walkthrough is paired with the automated spec tests/e2e/specs/journey-snap-income-materiality.spec.ts, covering scenario snap.change.income-exceeds-130pct-mid-period. The screenshots below are captured by that spec (worker shots step-NN-…, applicant shots app-NN-…). See Verifying the journey to run it.

Personas & credentials

Persona Credentials / identity Role in the journey

Applicant

Files at the portal /apply wizard (:8090). On submission a reference number (HH-xxxxxxxx) + a 12-digit passcode (dddd-dddd-dddd) are shown once — the applicant signs back in at /lookup with that pair.

Files the single-person SNAP application; later signs back in to read the change-in-circumstances letter.

Caseworker

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

Records the wages, approves + certifies the case, records the material income increase, confirms the notice it generates, and files the recert from the nudge.

Bring-up & reset

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

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

cargo xtask migrate rollback           # restore the pre-run DB snapshot

Use a fresh browser (or an incognito window) per persona so the applicant’s /lookup session and the caseworker’s Keycloak session do not collide. The Demo Runbook owns the full side-by-side driving detail.

Concrete precondition

The exact inputs this journey depends on (the #979/#991 bar — the applicant side is as reproducible as the worker side):

  • Applicant files (portal /apply): a single-person household — head of household only, no other members.

    • Head: first name Marcus, last name Delgado, date of birth 1987-05-19.

    • Safety question: Standard protections are fine; income step: No one works right now (the portal authors no income — the worker records it), quick-check amounts left at 0.

  • Caseworker records (worker portal):

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

    • Material increase: income type wages, $9,000 / month, effective today, employer Northside Logistics — a construction extreme robustly above any gross-income limit, so the materiality dry-run flips the verdict.

    • Certification period: start today, end twelve months ahead (a ~12-month window opening today).

The walkthrough, step by step

# Screen Action Expected Screenshot

01

Applicant

At /apply, work the wizard: Begin application → save the Application ID → About you (Marcus Delgado, DOB 1987-05-19) → Who lives with you? (add no one) → Standard protections are fineNo one works right now (leave the quick-check amounts at 0) → ReviewSubmit application.

The Application submitted screen reveals the reference number (HH-…) + the 12-digit passcode. Note them down — they are shown only once.

Applicant credential reveal

02

Caseworker

Open the case (/cases/<HH-…>?program=snap) → Income tab → + Add income for the head → income type wages, amount 800, frequency monthly, effective date todayAdd.

The wages row is recorded against the head — the portal-filed case had zero income, so this is the real income the determination will read.

Baseline income recorded

03

Caseworker

Determination tab → Action ▾Run Determination.

The SNAP determination returns Approved with a monthly benefit — the eligible baseline the certification needs.

Approved determination

04

Caseworker

On the Determination tab, open the program group’s Actions panel → Create certification; enter start today and end twelve months aheadCreate certification.

Redirects to the Household tab, whose Certification card now shows the period (start – end) and type — the household is now certified.

Certification created

05

Caseworker

Income tab → + Add income for the head → income type wages, amount 9000, frequency monthly, effective date today, employer Northside LogisticsAdd.

The increased-wage row is recorded against the head (the same income-claim the determination reads).

Income change recorded

06

Caseworker

Open the Notices tab (the materiality subscriber runs shortly after the change; refresh if needed).

A new notice row — the change-in-circumstances Notice of Action — appears beyond the pre-change baseline. It was generated by the material-change evaluation at step 05 (change → materiality dry-run → event → notice); no recert action has been taken yet.

Change-in-circumstances notice

07

Caseworker

Open the Renewals tab.

A pending recert nudge row appears — recorded by the SAME material-change evaluation that generated the notice.

Recert nudge

08

Caseworker

On the Renewals tab, click File recert on the nudge.

Redirects back to the case; the filed nudge drops off the pending list — the decision was recorded end-to-end (BFF → renewals → DB stamp). Filing records the worker’s intent only; it generates no notice.

Recert filed

09

Applicant

Back in the applicant browser, go to /lookup, enter the reference number
passcode from step 01 → Continue.

Lands on the applicant Home with the returning-applicant greeting.

Applicant home

10

Applicant

Open the Letters page and click the most recent letter.

The change-in-circumstances Notice of Action the caseworker generated is now visible to the applicant — the outcome reflected back across the portal boundary.

Applicant letter

Expected outcome / oracle

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

  • The recorded income increase is a construction extreme — robustly over any gross-income limit — so the materiality subscriber’s non-persisting dry-run flips the verdict Approved → Denied. That flip is what makes the change material and raises the nudge; the amount is an input, not an asserted value.

  • A change-in-circumstances Notice of Action row appears on the Notices tab beyond the pre-change baseline — a notice-count increase, and it appears BEFORE any recert action is taken (the NOA pipeline fired end to end from the change alone: change → materiality subscriber → notice).

  • The recert nudge appears on the Renewals tab, then drops off the pending list after the caseworker files — a pure presence-then-absence relation.

  • That letter reflects back to the applicant — a non-empty subject appears on the applicant Letters page after they sign back in.

The baseline-wage and material-increase amounts are construction inputs (robustly inside / outside any gross-income limit), so the journey holds under any jurisdiction’s values.

Honest scope

  • Filing the recert records the worker’s intent only. The nudge is stamped filed_recert and drops off pending; provisioning the follow-up recert application is a tracked concern, so the journey asserts the nudge is actioned, not a brand-new certification.

  • The applicant portal captures identity + household composition only, never amounts, and drops the apply address at finalize. This journey has no address-dependent logic, so no address is authored; all income is recorded by the caseworker.

  • The spec’s notice oracle asserts a notice-count increase beyond a STABLE pre-change baseline — it waits for the approval determination’s own NOA to land before measuring, so the increase cannot be satisfied by that late-arriving letter — rather than matching a notice subject. In the live demo the new row is recognizable directly by its change_in_circumstances type chip.

  • The negative leg — an immaterial ($1) income change raising no nudge — is a bounded-wait smoke check in the spec (a second in-file test, not a published demo beat); the exhaustive immaterial coverage lives at the materiality unit
    proptest layer and the renewals pending-guard integration test.

Verifying the journey

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

The spec files a single-person SNAP application through the real /apply wizard on an unauthenticated portal context, then drives the worker steps above through the portal BFF — recording the baseline wages, approving, opening the certification via the Create certification action, recording the material income change, confirming the change notice (asserted BEFORE any recert action, encoding the true causation), then polling the renewals nudge and filing the recert — before signing the applicant back in to read the letter. It captures the step screenshots into test-results/e2e/walkthroughs/… for commit into docs/modules/ROOT/assets/images/walkthroughs/…. A second in-file test is a bounded-wait smoke check that an immaterial ($1) income change raises no nudge.

Edit this page · default