Journey: SNAP change of circumstances during a pending fair hearing

On this page

A SNAP household files an application through the applicant portal and is certified. The caseworker then schedules a real adverse action — a termination of the enrollment — and the household files a fair hearing bound to that action: the PAMMS Chart B2 continued-benefits election rides the binding, so benefits continue while the hearing is pending. While that hearing is still open, an unrelated change of circumstances occurs (a move to pricier housing). The caseworker acts on the change and re-determines; the benefit rises, the household files a second hearing on the new action — a narrative grievance that carries no continued benefits (there is no adverse action to bind: the benefit went up) — and the original hearing is undisturbed — the coexistence that 7 CFR 273.15(k) bounds. The applicant then signs back in and views the outcome.

NOTE

Reproducibility tier: Worker-driven. The applicant files the SNAP front door live (and signs back in to view /home + /letters), but every substantive beat — recording the wage, the rents + the mailing address, opening the certification, ensuring the enrollment + scheduling the termination, and filing both fair hearings — is caseworker-driven: the applicant portal has no change-reporting or appeals-filing UI. For a live demo, present the applicant filing + view-back on the portal and the whole middle of the story on the worker portal. All dates are real dates — this journey needs no clock helpers.

IMPORTANT

This walkthrough is paired with the automated spec tests/e2e/specs/journey-snap-change-during-pending-hearing.spec.ts, covering scenario snap.hearings.change-during-pending-hearing. The screenshots below are captured by that spec. See Verifying the journey to run it.

Personas & credentials

Persona Credentials / identity Role in the journey

Applicant

Files at the portal /apply flow; a reference id (HH-…) + 12-digit passcode are shown on submission — the same pair signs back in at /lookup. (See Applicant Portal Seed Credentials for the seeded cast if you prefer a pre-filed case.)

Files the SNAP application; at the end signs back in to view the outcome. The mid-journey appeal beats are worker-only (no applicant appeals UI).

Caseworker

Worker portal, Keycloak login jane.caseworker / password (a see-all worker; see UAT Facilitator Guide).

Records the wage, both rents + the mailing address; opens the certification; ensures the live enrollment + schedules the termination; files both appeals; re-determines; confirms the notice.

Bring-up & reset

cargo xtask dev start --profile full     # SNAP + appeals + portal + notices
cargo xtask seed --seed 42               # deterministic fixtures

Reset between manual runs: cargo xtask migrate rollback (~5–10s; the Postgres DBs only) plus a fresh browser context / incognito for clean applicant + worker sessions. The Demo Runbook owns the full bring-up, snapshot/rollback, and per-journey starting-point detail.

Concrete precondition

The applicant files, at /apply, a size-3 household — an adult head with no income entered at filing plus two dependent children:

Member Name Date of birth Relationship

Head

Denise Harmon

1985-06-15

self

Child

Micah Harmon

2015-03-10

child

Child

Nadia Harmon

2018-07-22

child

The portal captures identity + composition only — never amounts — so the case starts with zero income/expense facts. The caseworker then records, on this exact household:

  • Wages — head, $1,500 / month, effective today (income type wages, employer Peachtree Foods).

  • Baseline rent — head, $600 / month, effective today (above the 50%-of-income threshold so it yields an excess-shelter deduction, below the cap).

  • Mailing address — head, type mailing, 100 Peachtree St NW, Atlanta, GA 30303, effective today. A portal-filed application drops the applicant’s apply address at finalize (#1137), and the notices recipient gate refuses a head with no usable postal address — without this fact no notice or letter beat below can pass.

  • Certification window — start = 2 months ago, end = 4 months ahead.

  • Enrollment + termination — an enrollment over the same window (auto-enroll usually opens it from the approval event first, so the manual create may legitimately hit the one-live-enrollment fence, #1130 — either outcome leaves a live enrollment), then a scheduled termination, reason income exceeds limit (#1103) — the real adverse action appeal #1 binds to.

  • Appeal #1 — filed against the scheduled termination; the Chart B2 continued-benefits election rides the binding, with the repayment-obligation disclosure attested on the form. No adverse-action effective date is typed anywhere (backdating/future-dating died with #1098).

  • Rent increase — head, a second rent row of $300 / month, effective today (the unrelated move; total shelter becomes $900).

  • Appeal #2 — filed with no adverse action selected (the benefit rose, so there is nothing to bind): a narrative grievance, which can never carry continued benefits.

These amounts are construction inputs, never asserted policy values.

The walkthrough, step by step

# Screen Action Expected Screenshot

01

Applicant

At /apply, work through the wizard: Begin application (save the shown HH-… + passcode), About you (Denise Harmon, DOB 1985-06-15), Household (add Micah + Nadia), Safety (standard protections), Income (no one works; 0 / 0), ReviewSubmit application.

The Application submitted screen shows the one-time HH-… reference id + 12-digit passcode.

Application submitted

02

Caseworker

Open the case (/cases/<HH-…>?program=snap) → Income tab → the head’s + Add income → type wages, 1500, frequency monthly, employer Peachtree Foods, effective date → Add.

The wage row is recorded — the earner’s income the portal did not capture.

Wages recorded

03

Caseworker

Open the Expenses tab → + Add expense for the head → expense type rent, 600, frequency monthly, an effective date → Add.

The rent row is recorded — the household’s baseline shelter cost.

Baseline rent recorded

04

Caseworker

On the Determination tab, open the Action ▾ menu → Run Determination.

Approved with a monthly benefit — the baseline (and the determination the certification + appeal #1 attach to).

Baseline determination

05

Caseworker

Open the Address section → the head’s + Add address → type mailing, 100 Peachtree St NW, Atlanta, GA 30303, effective date → Add.

The mailing address is recorded. The portal-filed case dropped the apply address at finalize (#1137); without this fact the notices recipient gate blocks the NOA and the outcome letter, and steps 12 + 14 cannot pass.

(no screenshot — not captured by the paired spec)

06

Caseworker

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

Redirects to the Household tab, whose Certification card now shows the period.

Certification created

07

Caseworker

Actions panel → Create enrollment over the same window → submit; then Actions panel → Schedule termination, reason income exceeds limitSchedule termination.

The create either succeeds or reports the one-live-enrollment fence (auto-enroll already opened it, #1130) — both leave a live enrollment. The scheduled-action card then appears: the real adverse action appeal #1 binds to (#1103).

Termination scheduled

08

Caseworker

Actions panel → File appeal; pick the requestor (the head is pre-selected), select the scheduled termination as the contested adverse action, and check the repayment-obligation disclosed attestation → File appeal.

Redirects to the Appeals tab, which shows one pending appeal with continued benefits granted — the Chart B2 election rode the binding, and the synchronous fenced stay landed before the grant committed.

Appeal filed

09

Caseworker

Open the Expenses tab → + Add expense for the head → expense type rent, 300, frequency monthly, an effective date → Add. (Models the move’s shelter-cost consequence — see the honest-scope note.)

A second rent row is recorded; the household’s total shelter cost rises to $900.

Higher rent recorded

10

Caseworker

Return to the Determination tab → Action ▾Run Determination again.

Still Approved, but the monthly benefit is higher than the baseline (the larger excess-shelter deduction lowered net income).

Higher benefit

11

Caseworker

Actions panel → File appeal again — leave the adverse action unselected (the benefit rose; there is nothing to bind) → File appeal.

The Appeals tab now shows two appeals: the first still pending with continued benefits, the second a narrative grievance showing no continued benefits — post-#1098 a grievance can never carry them. The asymmetry is itself an oracle.

Second appeal filed

12

Caseworker

Open the Notices tab.

A notice_of_action entry — the determination lifecycle fired the NOA pipeline end-to-end — with a downloadable PDF.

Notice of action

13

Applicant

At /lookup, sign back in with the HH-… + passcode from step 01 → land on /home.

The applicant home greeting renders for the household that filed.

Applicant home

14

Applicant

Open /letters → open the most-recent letter.

The determination Notice of Action reached the applicant’s Letters page — the loop closes on the same portal the case was filed from.

Applicant letter

Expected outcome / oracle

The oracle is relational, never a policy dollar figure:

  1. Benefit rises. The post-change re-determination’s monthly benefit is strictly greater than the baseline — acting on the unrelated shelter increase raised the excess-shelter deduction, which lowered net income and raised the allotment.

  2. Appeal #2 contests the new action. Appeal #2’s persisted contested determination is the post-change determination and differs from appeal #1’s — a real "on the new action" check, not a bare count of two appeals.

  3. The original hearing is undisturbed. Appeal #1, re-read after the re-determination, is still pending — acting on the unrelated change did not disturb the pending hearing (appeals and determinations are isolated, ADR-001).

  4. Continued-benefits asymmetry. Appeal #1 — bound to the scheduled termination — carries continued benefits; appeal #2 — a grievance about a benefit increase — provably carries none (post-#1098 a narrative filing can never carry continued benefits). The asymmetry is visible in the browser on the two appeal cards.

Honest scope

  • The applicant files the front door only. The portal has no change-reporting or appeals-filing UI, so every mid-journey beat (the wage, both rents, the address, the certification, the enrollment + termination, and both appeals) is caseworker-driven; the applicant’s role is the initial filing plus the final view-back.

  • No appeal reason and no worker attribution on file. The file-appeal form records who is filing (a household member) but no appeal reason/basis and no worker attribution — the appeals contract carries neither on filing (only a decision later carries a basis).

  • Appeal #2’s "hearing on the new action" is narrative. The grievance is filed against the household’s current (post-change) determination, but the appeals service does not verify the contested determination changed anything; the spec’s oracle pins the persisted determination id instead. Appeal #1’s adverse action, by contrast, is a real scheduled termination (#1103).

  • The changed posture is not signalled to the hearing authority. There is no mechanism linking a re-determination to a pending hearing’s authority, so "the hearing authority is notified of the changed posture" is not modelled — the journey demonstrates the structural coexistence (a pending hearing undisturbed, the benefit adjusted, a second hearing requested).

  • Shelter is the sum of shelter-cost rows. The move is modelled as its shelter-cost consequence: a second rent row on the Expenses tab (the endpoint adds rows, it does not edit). The in-UI address surface is real — this journey uses it to record the head’s mailing address (#1137) — but the rent rows, not the address, are what the determination reads.

Verifying the journey

cargo xtask e2e --devstack-profile full -- \
  specs/journey-snap-change-during-pending-hearing.spec.ts --project journey

The spec files the SNAP application through the real /apply portal front door (on a separate applicant context), then drives the worker steps above through the portal BFF — recording the wage, both rents + the mailing address, running the two determinations, opening the certification, ensuring the enrollment + scheduling the termination, and filing both appeals (appeal #1 action-bound with the Chart B2 election, appeal #2 a narrative grievance) — before signing the applicant back in to view /home + /letters. It captures the applicant (app-) and worker (step-) screenshots into test-results/e2e/walkthroughs/… for commit into docs/modules/ROOT/assets/images/walkthroughs/…. The correctness oracles are read back from the appeals service: the benefit rises, appeal #2’s contested determination is the post-change one (distinct from appeal #1’s), appeal #1 stays pending across the re-determination, and only appeal #1 carries continued benefits.

Edit this page · default