Skip to content

Fix SNAP expected contribution rounding per 7 CFR 273.10(e) - #9318

Merged
hua7450 merged 8 commits into
mainfrom
fix/snap-expected-contribution-rounding
Aug 25, 2026
Merged

Fix SNAP expected contribution rounding per 7 CFR 273.10(e)#9318
hua7450 merged 8 commits into
mainfrom
fix/snap-expected-contribution-rounding

Conversation

@hua7450

@hua7450 hua7450 commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the rounding in snap_expected_contribution, which previously computed floor(net_income) * 0.3 with no rounding of the product.

Fixes #9312

Legal basis

  • 7 CFR 273.10(e)(1)(ii)(A): net income is rounded to the nearest dollar — 1–49 cents down, 50–99 cents up (half-up, so np.floor(x + 0.5) rather than np.round's half-to-even).
  • 7 CFR 273.10(e)(2)(ii)(A)(1): 30 percent of net income is rounded up to the next higher dollar — equivalent to rounding the allotment down to the nearest lower whole dollar (7 U.S.C. 2017(a)).
  • The product is rounded to cents before np.ceil so float32 noise (e.g. 50 * 0.3 = 15.0000009) cannot push an exact dollar amount up to the next dollar.

Impact

Benefits shift weakly downward only (EC_new >= EC_old always): at most ~$1.10/month (up to ~$0.99 from rounding 30% up, plus up to ~$0.30 when net income rounds upward), i.e. $1–$13 per year on annual totals.

Tests

  • New snap_normal_allotment_basis_of_issuance.yaml (13 cases): 3-person allotments at net-income boundaries validated against the FNS Basis of Coupon/EBT Issuance table (Oct 1, 2025), including the net income $3.49 vs $3.50 half-up boundary and the exact-multiple $50/$51 rows.
  • New snap_categorical_eligibility_ceiling.yaml (3 cases): categorically eligible units above the issuance-table maximum (2-person gets the minimum allotment, 3-person gets $0 but stays flagged eligible per 7 CFR 273.10(e)(2)(iii) not converting to denial in the model).
  • Updated 16 existing baseline expectations whose old values carried fractional cents (e.g. 230.40 → 230) — artifacts of the missing rounding.

Partner contract tests (updated with approval)

64 SNAP pins under tests/policy/baseline/partners/analytics_coverage/ shift by $1–$13/year under the corrected rounding and were updated with Ziming's approval:

  • edge_cases (8 files, 31 pins): pins updated and every arithmetic comment rewritten to the new chain (round net half-up → 30% rounded up), e.g. EC 337.20 → ceil(1,124 × 0.3) = 338.
  • signatures (ca/federal, 33 pins): raw model-output pins updated (these were masked in the first CI run because the job aborted on the edge-case failures).

The change responds directly to the API partner's own rounding report, which serves as partner notice.

Test plan

  • 16 new tests pass locally
  • Updated baseline files pass locally
  • Updated partner files pass locally (29 + 18 + 46 cases)
  • CI fully green

🤖 Generated with Claude Code

Round net income to the nearest dollar (1-49 cents down, 50-99 cents up,
7 CFR 273.10(e)(1)(ii)(A)) and round 30 percent of net income up to the
next higher dollar (7 CFR 273.10(e)(2)(ii)(A)(1)), replacing the prior
floor-then-multiply formula. Add boundary tests against the FNS Basis of
Issuance table and categorical-eligibility ceiling tests.

Fixes #9312

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (57743a2) to head (8df8c50).
⚠️ Report is 15 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9318   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         1    -2     
  Lines           65        14   -51     
=========================================
- Hits            65        14   -51     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

hua7450 and others added 4 commits August 20, 2026 09:37
The 31 SNAP pins in the analytics-coverage partner tests shift by $1-$11
per year under the legally correct rounding (net income to the nearest
dollar, 30 percent rounded up to the next dollar). Approved by Ziming.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g fix

The 33 signature-replay SNAP pins (masked in the first CI run by the
edge-case failures) shift under the corrected rounding, and the
edge-case comments now show the ceil-of-30-percent arithmetic that
produces the updated pins. Approved by Ziming.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The last partner layer (masked while earlier directories failed): five
SNAP pins shift under the corrected rounding; 2025 totals become whole
dollars. Verified locally with the full partner folder (630 passed).
Approved by Ziming.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…into fix/snap-expected-contribution-rounding
@hua7450
hua7450 marked this pull request as ready for review August 24, 2026 15:03
@hua7450
hua7450 requested a review from DTrim99 August 24, 2026 15:03
@DTrim99

DTrim99 commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Program Review — SNAP expected-contribution rounding (7 CFR 273.10(e))

PR #9318 "Fix SNAP expected contribution rounding per 7 CFR 273.10(e)". Single code change: snap_expected_contribution.py. CI 32 pass; 0 behind main.

The fix replaces floor(net) * 0.3 with a two-stage rounding chain:

net_income   = np.floor(snap_net_income + 0.5)          # net income half-up (273.10(e)(1)(ii)(A))
contribution = np.round(net_income * 0.3, 2)            # cents guard against float noise
return np.ceil(contribution)                            # 30% rounded up (273.10(e)(2)(ii)(A)(1))

Verdict: the rounding is correct and well-audited against the CFR and the FNS FY2026 issuance table. The one item that must be resolved before merge is a process gate — confirming partner sign-off on the 12 partner-contract test files — not a correctness defect.

Source Documents

  • 7 CFR 273.10(e)(1)(ii)(A) — "Round down each … calculation that ends in 1 through 49 cents and round up each calculation that ends in 50 through 99 cents" → net income rounded HALF-UP to nearest dollar.
  • 7 CFR 273.10(e)(2)(ii)(A)(1) — "round the 30 percent of net income up to the nearest higher dollar" → expected contribution CEIL.
  • 7 CFR 273.10(e)(2)(ii)(A)(2) — allotment rounded down to nearest lower dollar (mathematically equivalent to rounding EC up); 7 USC 2017(a) requires a whole-dollar allotment.
  • FNS FY2026 Basis of Coupon/EBT Issuance table (eff. Oct 1 2025), 48 states + DC.

Critical (Must Fix)

None. All computed values are verified correct against the CFR and the FNS table. (The partner-sign-off item below is a mandatory pre-merge process gate, not a correctness defect — see Should Address #1.)

Should Address

  1. Partner-contract test sign-off — confirm on record before merge (TOP ITEM).
    Files: tests/policy/baseline/partners/** — 12 files (amplifi/2025.yaml, amplifi/2026.yaml, impactica/2025.yaml, analytics_coverage/edge_cases/.../snap/{ca,co,federal,il,ks,ma,nc,wa}.yaml, analytics_coverage/signatures/{ca,federal}.yaml; dozens of pins total).
    Every changed pin is arithmetically correct: NEW = ceil(round(net_halfup × 0.3, 2)), OLD = the pre-fix floor(net) × 0.3 artifact (8 pins sampled, all reproduce). Changes are output-only (no input/entity/structure edits), all shift SNAP weakly DOWN, deltas $2–$13/yr — consistent with the rounding fix. The values are right. The gap is that the diff itself records NO partner approval or notice (no changelog note, no in-file comment); the author's stated approval and the partner's own rounding report are external to the diff. Per CLAUDE.md ("PARTNER API CONTRACT TESTS ARE NOT ORDINARY SNAPSHOTS"), the maintainer must run the three-question gate and confirm partner sign-off/notice is on record before these edits are accepted. Partner-facing risk: medium.

  2. Add an even-dollar .50 net-income test — half-up is not actually proven.
    File: snap_normal_allotment_basis_of_issuance.yaml. The existing $3.49→3 / $3.50→4 rows sit at an ODD-dollar boundary where half-up (floor(x+0.5)) and half-to-even (np.round) AGREE, so they do not pin the tie-break rule the PR's own comment is about. A refactor swapping in np.round would leave CI green while silently reintroducing banker's rounding. Add one even-dollar case: net $2.50 → half-up 3 (np.round → 2), 30% of 3 = 0.90 → ceil 1; or net $4.50 → half-up 5 (np.round → 4), 30% of 5 = 1.50 → ceil 2. Assert snap_net_income and snap_expected_contribution/snap_normal_allotment.

  3. Reference/citation nits (minor, label-only).
    (a) snap_expected_contribution.py:12 — the middle reference anchor #e_8_ii_A should be #e_1_ii_A; the code comment, changelog, and PR intent all cite 273.10(e)(1)(ii)(A), but e_8 encodes paragraph (e)(8). The third anchor #e_2_ii_A_1 is correct.
    (b) The allotment down-round is 7 CFR 273.10(e)(2)(ii)(A)(2), not (e)(2)(ii)(B) — (B) is the <$10 initial-month rule. The math is equivalent; label only.

Suggestions

  1. Float32 cents-guard — safe, no defect (reconciled). The code-validator raised whether np.round(x, 2) on a float32 MicroSeries fully kills float noise at microsim scale. The regulatory analysis resolves this: after stage 1, net income is an integer, so 0.3 × integer is always an exact multiple of $0.10 (smallest nonzero fraction $0.10, e.g. 0.3 × 37 = 11.10). A value like 15.01 can never legitimately arise; any residue in (0, 0.10) is pure float error. The ~1e-6 float32 noise is far below the $0.10 gap, so the cents-round provably removes noise without ever suppressing a legitimate ceil. The guard is correct and the float32 worry does not materialize. No change required.
  2. Monotonicity assertion (optional regression note). EC_new >= EC_old (benefit weakly down) is structurally guaranteed — both stages (half-up of net, ceil of 30%) only ever weakly increase EC — but is unasserted; visible only by eyeballing the ~16 baseline + partner diffs. Optionally add one integration case with an inline comment noting new-EC >= old-EC. Documentation, not a coverage hole.
  3. Rounding constants 0.5 and 2 inline — acceptable. Pure numeric-method constants (half-up offset; cents decimal places), not policy values, and both explained by adjacent CFR-citing comments. The only policy value (0.3) correctly comes from gov.usda.snap.expected_contribution. Optional readability: name them (CENTS = 2).

Value / Rule Audit — PASS

  • Net income half-up (273.10(e)(1)(ii)(A)): floor(x+0.5) confirmed correct; np.round (half-to-even) would be wrong here. 3.49→3, 3.50→4. ✓
  • 30% ceil (273.10(e)(2)(ii)(A)(1)): np.ceil confirmed. ✓
  • Cents guard: provably cannot suppress a legitimate ceil (30% of integer net is a multiple of $0.10; float noise « that gap). ✓
  • Allotment: integer_max_allotment − ceil(0.3 × round(net)) is an integer floored down — matches "nearest lower whole dollar"; no downstream double-round (grep confirms rounding lives only in snap_expected_contribution, snap_min_allotment, income-test limits). ✓
  • FNS FY2026 boundary rows (3-person, max $769): net $3.49 → allot 768 vs $3.50 → 767 (half-up boundary); net $50 → 754 vs $51 → 753 (exact-multiple / cents-guard). All confirmed against the FNS table. ✓
  • Monotonicity: EC_new >= EC_old for all net >= 0 (net = max_(0, gross−deductions) >= 0); benefit weakly down, bounded ~$1.10/month per household; every partner delta consistent. No benefit-raising regression exists. ✓
  • Partner pins: all sampled NEW values reproduce ceil(round(net_halfup×0.3,2)); all OLD reproduce the floor artifact; output-only; all weakly down; deltas $2–$13/yr. ✓
  • Categorical eligibility ceiling (snap_categorical_eligibility_ceiling.yaml): 2-person min-allotment $24; 3-person zero-benefit-but-eligible ($0, is_snap_eligible: true per (e)(2)(iii)); 3-person just-under-max $7. Correct. ✓

Validation Summary

Dimension Result
Regulatory (rounding chain vs 7 CFR 273.10(e)) PASS — all three rules confirmed verbatim; monotonicity guaranteed
Code patterns PASS — no policy value hardcoded; entity/period correct; changelog present (type fixed); float32 guard reconciled as safe
Test coverage GAP — cents-guard proven; half-up NOT proven (odd-dollar boundary only) → add even-dollar .50 case
Partner-contract VALUES CORRECT — but sign-off/notice not on record in diff; maintainer must confirm
Value / Rule audit PASS — CFR + FNS FY2026 boundary rows + partner pins all confirmed
CI 32 pass; 0 behind main

Review Severity: COMMENT

The fix is correct and thoroughly audited — rounding matches the CFR verbatim, boundary rows match the FNS FY2026 table, and every partner pin is arithmetically verified. Two items to close before merge, neither a correctness defect: (1) the maintainer must confirm partner sign-off/notice for the 12 partner-contract files is on record (process gate per CLAUDE.md), and (2) add one even-dollar .50 net-income test so the half-up-over-half-to-even choice cannot regress silently. Citation nits are minor.

Reviewed with Claude Code assistance.

Review response: add net $6.50 and $10.50 cases that pin half-up
rounding against round-half-to-even (the $3.50 boundary cannot
distinguish them), cite 7 CFR 273.10(e)(2)(ii)(A)(2) for the allotment
down-round equivalence, and deduplicate the Cornell CFR reference link.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hua7450

hua7450 commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

Response to the program review (commit 809c4d8):

Should Address 1 (partner sign-off): The maintainer (@hua7450) explicitly authorized the partner-contract test edits in this PR during review of the rounding fix ("yes, you can fix the partner folder"), after the three-question gate was raised. All 12 partner files were edited under that authorization; every pin was verified locally (full partner folder run: 630 passed).

Should Address 2 (half-up test): Added — with one correction to the suggested cases. Net $2.50 and $4.50 do not discriminate half-up from round-half-to-even: at $2.50, half-up→3 gives EC ceil(0.90)=1 while half-to-even→2 gives EC ceil(0.60)=1 — identical output (same at $4.50: EC 2 either way). The tie-break is only observable where the two candidate integers straddle an expected-contribution band boundary (ceil(0.3n) increments) with the lower integer even. Added the two smallest such cases to snap_normal_allotment_basis_of_issuance.yaml:

  • net $6.50 → half-up 7 → EC 3 → $782 (half-to-even would give 6 → EC 2 → $783)
  • net $10.50 → half-up 11 → EC 4 → $781 (half-to-even would give 10 → EC 3.00 exactly → 3 → $782, also exercising the exact-integer product)

Both verified locally (file passes 15/15).

Should Address 3a (anchor #e_8_ii_A#e_1_ii_A): Checked against the live Cornell page — the suggested fix is incorrect. Cornell's HTML assigns printed paragraph (e)(1) the anchor ids e_8_*; #e_8_ii_A lands exactly on the "Round down each income and allotment calculation that ends in 1 through 49 cents…" text, and #e_1_ii_A does not exist on the page. Separately, the two Cornell CFR references differed only by fragment, so they are now deduplicated to a single link with the cited paragraphs — 273.10(e)(1)(ii)(A) and (e)(2)(ii)(A)(1) — noted in an adjacent comment.

Should Address 3b ((e)(2)(ii)(B)): No file in this diff cites (e)(2)(ii)(B). The allotment down-round equivalence comment now cites 7 CFR 273.10(e)(2)(ii)(A)(2) (verified verbatim: "round the allotment down to the nearest lower dollar") alongside 7 USC 2017(a).

🤖 Generated with Claude Code

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 9318 review — Fix SNAP expected contribution rounding per 7 CFR 273.10(e)

PR: #9318 — "Fix SNAP expected contribution rounding per 7 CFR 273.10(e)" by hua7450 (fixes #9312)
Reviewed head SHA: 809c4d8e3e8157f830bfc4e6f5c91019de5dedbf
Merge base: 350ecc766e4467dc05093d197a3404b53e0c9e4d
Mode: full (6 review roles + 6 Phase-5 verifications, including one 600 DPI visual confirmation)
Recommended severity: REQUEST_CHANGES — 1 CRITICAL, 13 SHOULD ADDRESS, 15 SUGGESTION


Source Documents

Document Role Result
pr9318-review-context.md scoping 21 files: 1 variable .py, 1 changelog, 6 baseline SNAP test YAMLs (2 new), 13 partner contract YAMLs. No parameter files changed. CI 32/32 pass.
pr9318-review-regulatory.md program-reviewer 0 CRITICAL, 3 SHOULD, 6 SUGGESTION
pr9318-review-references.md reference-checker 0 CRITICAL, 4 SHOULD, 2 SUGGESTION
pr9318-review-code.md code-validator 0 CRITICAL, 4 SHOULD, 6 SUGGESTION
pr9318-review-tests.md edge-case-checker 1 CRITICAL, 7 SHOULD, 7 SUGGESTION
pr9318-review-pdf-issuance.md PDF audit (FNS Basis of Issuance) 15/15 test cases MATCH the official table
pr9318-review-pdf-rounding.md PDF audit (rounding method + FY2026 params) 42/43 values MATCH, 1 mismatch
pr9318-review-pdf-manifest.md PDF sourcing 3 PDFs obtained; official FNS-hosted Basis-of-Issuance copy unobtainable (403/CAPTCHA), MO DSS mirror verified as the FY2026 edition
pr9318-review-verification-queue.md Phase-4 triage 5 items routed to Phase 5
pr9318-review-codepath-1.md Phase 5C CONFIRMED — Hawaii minimum allotment
pr9318-review-codepath-2.md Phase 5C CONFIRMED — stale integration.yaml pins
pr9318-review-codepath-3.md Phase 5C CONFIRMED — stale snap.yaml:61 pin
pr9318-review-codepath-4.md Phase 5C REJECTED — California pin is correct (see Investigated and cleared)
pr9318-review-codepath-5.md Phase 5C CONFIRMED — the float guard is load-bearing
pr9318-review-mismatch-1.md Phase 5D (600 DPI) CONFIRMED MISMATCH — Hawaii minimum allotment
pr9318-review-run-state.md run metrics BEHIND=17, AHEAD=6

Note on the two-stage verification rule. A PDF value mismatch may be reported only if it passed
both code-path (5C) and visual (5D) verification. Exactly one item qualifies: the Hawaii
minimum allotment
(codepath-1 CONFIRMED, mismatch-1 CONFIRMED at 600 DPI). Codepath-2, -3 and -5
are test-pin and dtype findings with no disputed PDF page, so 5D visual verification is not
applicable to them — they were settled empirically by running the model against the PR head, not by
skipping a required step.


Branch Status

BEHIND = 17 commits, AHEAD = 6 commits relative to main.

The branch is 17 commits stale. A rebase onto upstream/main is recommended before merge
principally so the partner contract files (which other PRs also touch) are re-run against current
main rather than a 17-commit-old baseline, and so CI's green result reflects the merge state.

Staleness did not affect any finding in this report. Every Phase-5 verification was executed
against the PR-head snapshot (809c4d8e3e) with the snapshot's policyengine_us on sys.path[0],
and each verifier independently confirmed the loaded package resolved to the snapshot rather than
the editable install. Informational only.


Headline

The rounding-method determination — the heart of the regulatory verdict

7 CFR 273.10(e)(2)(ii)(A) permits two methods, and this PR implements method (A)(1) exactly.
It is not a hybrid and not a third method.

  • (A)(1): round the 30%-of-net-income contribution up to the next higher dollar →
    A = M − ceil(0.3·N). This is what the PR computes.
  • (A)(2): don't round the 30% at all; round the resulting allotment down
    A = floor(M − 0.3·N).
  • For integer M, floor(M − x) = M − ceil(x), and 7 CFR 273.10(e)(4)(ii) guarantees the maximum
    allotment is an integer for published fiscal-year values. The two methods therefore produce the
    same integer for every input
    , and both are identical to the 7 USC 2017(a) instruction "rounded
    to the nearest lower whole dollar." The implementation is simultaneously compliant with all three
    citations.
  • The net-income step is likewise correct: 7 CFR 273.10(e)(1)(ii)(A) requires 1–49¢ down and 50–99¢
    up, which is half-up, not np.round's banker's rounding. np.floor(x + 0.5) implements this
    faithfully, including at exactly .50.

Independent corroboration from the official FNS table. The Basis of Coupon/EBT Issuance table
(48 States & DC, October 1 2025) encodes allotment = max_allotment − ceil(0.30 × N). Round-up was
confirmed on boundary rows across household sizes 1, 2, 3, 5, 7 and 10, in each of which the
"truncate" prediction is a dollar higher than what the table prints (size 1 band 1-3 → 297 not
298; size 2 band 14-16 → 541 not 542; size 3 band 51-53 → 769 not 770; size 5 band 4-6 → 1181
not 1182; size 7 band 7-10 → 1568 not 1569; size 10 band 11-13 → 2221 not 2222). The
categorical evidence is that the table's first band is the singleton 0 - 0: under truncation
0.30 × 3 = $0.90 truncates to 0, so the opening band would necessarily be 0 - 3 and no 0 - 0
singleton could exist. It exists on every column.

All 15 cases in the new issuance test matched the official table exactly — zero differences of
any size. Note the new file snap_normal_allotment_basis_of_issuance.yaml contains 15 cases,
not the 13 the diff summary suggested (YAML entries at lines 8, 38, 68, 98, 128, 158, 188, 218,
248, 278, 308, 338, 369, 404, 436); all 15 were audited.

Direction of the fix is right. The old floor(net) * 0.3 truncated net income downward even at
50–99¢ and applied no rounding at all to the 30%. It understated the expected contribution,
overstated the allotment, and produced allotments in cents that appear nowhere in the FNS table —
mismatching 21 of 30 checked net-income values in the FY2026 3-person column.

Measured household impact

Verified over a grid of net incomes $0–$2,000 crossed with cent values {0, .01, .25, .49, .50, .51,
.75, .99}:

  • Expected contribution weakly increases; SNAP allotment weakly decreases. Monotone — no
    household's benefit can rise.
  • $0 to $1.20 per month ($0–$14.40/year), mean ≈ $0.60/month among households with positive net
    income; 95.0% of the grid moves. The $1.20 ceiling = up to +$0.30 from the net-income half-up
    correction plus up to just under +$1.00 from the ceil.
  • No eligibility flips. is_snap_eligible is unchanged in every partner file in the diff; only
    benefit amounts move.
  • Unaffected: $0-net-income households (max allotment either way), net incomes that are exact
    multiples of $10 with no cents, and 1–2 person households already floored at the minimum.

Critical

C1 — Two baseline test pins still assert the removed formula, hidden behind wide error margins

Confirmed by Phase-5 model runs (codepath-2 and codepath-3), not by inspection.

Frame this correctly: the PR's new values are the right ones. The defect is not that the model
is wrong — 47 / 611 follow directly from 7 CFR 273.10(e)(1)(ii)(A) and (e)(2)(ii)(A)(1), which
is exactly what the PR set out to implement. The defect is that these integration pins were not
updated alongside the 20 other baseline pins that were
, leaving stale expectations in the tree
that only survive because a wide absolute_error_margin swallows the entire effect of the change.
CI being 32/32 green is not evidence these cases are current.

(a) policyengine_us/tests/policy/baseline/gov/usda/snap/integration.yaml — case "Family of 3,
CBPP Example", period: 2022-01

Line Pinned Model output at PR head Δ Margin Masked?
integration.yaml:93 snap_expected_contribution: 154 * 0.346.20 47.00 0.80 5 (:54) yes
integration.yaml:100 snap_normal_allotment: 612 611.00 1.00 5 (:54) yes

snap_net_income here is a computed output, not an input, so the case does exercise the changed
formula. This is the only file under gov/usda/snap/ that reaches snap_expected_contribution
through a formula and was not touched by the PR — the sweep skipped it. The file is absent from the
21-file diff (the integration.yaml the PR does touch is the different file at
.../snap/income/ineligible_members/integration.yaml).

One correction to the original reviewers' framing, from codepath-2: line 93 does not encode the
old formula's output. The model's net income here is 153.59998, so the old formula returned
floor(153.59998) * 0.3 = 45.90, not 46.20. Line 93 is CBPP's own hand arithmetic on the article's
rounded $154, and it was already $0.30 off from the model before this PR — swallowed by the same
margin. This strengthens the finding: the ±5 margin has been hiding drift in both directions, and
the pin was never an assertion of model behaviour. The mechanical bound also holds generally — this
PR can move a single month by at most ~$1.10, so no SNAP case carrying a ±5 margin can ever
detect it.

(b) policyengine_us/tests/policy/baseline/gov/usda/snap/snap.yaml:61 — case "North Carolina
2025, yearly integration test"

Pinned snap: 71.75; model at PR head returns exactly 72.0 (float32, exact). Δ = 0.25 against
absolute_error_margin: 0.3 at snap.yaml:44 — masked. Month-by-month trace: Jan–Sep ineligible
($0), Oct–Dec eligible at the FY2026 minimum allotment $24 → 3 × $24 = $72. Every month is a whole
dollar; there is no $0.25 anywhere in the calculation.

This one is pre-existing — PR 9318 does not touch line 61 (its three hunks in this file start at
old line 88), and the pre-PR formula also yields $72 here because the expected contribution (~$550)
dwarfs the $298 max allotment and the result floors to the minimum either way. The pin drifted twice
under the margin: once when the FY2026 actual ($298) replaced a projection, and again when merged
commit cf165464a6 added statutory rounding to snap_min_allotment. Worth fixing as a drive-by
since the PR already edits this file.

Suggested fix (not applied — this review is read-only):

  • integration.yaml:9347; integration.yaml:100611. Leave the verbatim CBPP comment
    block at lines 46–99 untouched (line 46 declares it verbatim, and CBPP's "about $46 / about $612"
    is the source's rounding). Add a short comment noting the ~$1 divergence from CBPP is the
    expected consequence of the regulatory rounding rule. Tightening the ±5 margin below ~1 would
    additionally require re-deriving lines 79 (750.6) and 90 (153.59998).
  • snap.yaml:6172, and tighten snap.yaml:44 from 0.3 to 0.01.

Severity note for transparency: the edge-case-checker rated this CRITICAL; the code-validator
rated the same finding SHOULD-ADDRESS (SA-1), and codepath-2's verifier concurred with
SHOULD-ADDRESS/HIGH on the grounds that nothing ships broken and no user-facing number is wrong.
Per the merge-at-highest-priority rule it is reported here as CRITICAL. The maintainer may
reasonably treat it as a two-value blocking-but-trivial fix rather than a design problem.


Partner Contract Tests — maintainer decision required (A1)

13 of the 21 changed files are API partner contract tests, carrying ~59 changed expected-value
lines plus ~82 rewritten arithmetic comment lines:

Family Files Changed value lines
partners/amplifi/2025.yaml, 2026.yaml 2 1 + 3
partners/impactica/2025.yaml 1 1
partners/analytics_coverage/edge_cases/federal/nutrition/snap/federal, ca, ks, ma, nc, co, il, wa 8 17, 4, 3, 2, 2, 1, 1, 1
partners/analytics_coverage/signatures/ca.yaml, federal.yaml 2 23 + 10

Per this repository's CLAUDE.md, files under policyengine_us/tests/policy/baseline/partners/**
are API partner contract tests, not ordinary snapshots — changed expected outputs constitute a
partner-facing API change, and the three-question gate (are you sure you want to edit this file /
have you notified a team member / have you notified the API partner) must be run with the user
before any edit.

The PR body claims Ziming's approval and offers the API partner's own rounding report as partner
notice. This review cannot verify either claim.
Nothing in the diff, the CI record, or any
fetchable artifact corroborates that the approval exists or that the three-question gate was run
before the 13 files were edited. All three code-facing roles flagged it and all three explicitly
declined to judge it. This is something the maintainer must consciously accept before merge, not
something the review has cleared.

Corroboration that does exist (all mechanical, none of it a substitute for the approval):

  • Structural integrity — code-validator. All 13 files are SAME-KEYS: in every file the
    multiset of YAML keys on added lines is identical to that on removed lines. No key added, removed
    or renamed; no case added or deleted; no period or absolute_error_margin altered; no input
    block touched. Edits are confined to expected-output scalars and (in the 8 edge-case files) the
    arithmetic comments.
  • Direction — code-validator. Across the entire diff, baseline and partner, all 83 changed
    numeric expectations are the snap key and all 83 are weakly decreasing. Zero increases, zero
    non-snap numeric changes. Consistent with EC_new >= EC_old always.
  • No orphaned downstream pins. These files pin no aggregate depending on snap
    (household_net_income, spm_unit_benefits, spm_unit_net_income appear nowhere under
    tests/policy/baseline/partners/), so no companion value should have moved and didn't.
  • Arithmetic — edge-case-checker. Hand-derived 22 of ~59 changed partner values (every value
    whose rewritten comment carries full arithmetic), across all three families. Every one is
    consistent with the new formula. Worked example (federal aged-SSI): Jan–Sep net 785 →
    ceil(235.50) = 236 → 298 − 236 = 62 × 9 = 558; Oct–Dec net 777.62 → 778 → ceil(233.40) = 234 →
    308.52 − 234 = 74.52 × 3 = 223.57; total 781.57 ✓.
  • Cents fingerprint — all 59 values. Under the new formula a full-year 2026 total must equal
    9 × integer + 3 × (FY2027 max allotment − integer), so the cents are determined entirely by
    unit size
    : 1-person .57, 2-person .84, 3-person .17, 4-person .31, period-2025 .00.
    All 59 changed values conform. The old values had arbitrary cents (.91 .41 .51 .01 .26 .44 .10);
    the new ones collapse to exactly these five endings — a strong signal the fix was applied
    uniformly and no pin was hand-edited to a wrong value. No inconsistency found.
  • Magnitudes — regulatory role. All observed partner deltas are annual, all negative, none
    exceeding $14.40: impactica 2025 −$10.50; amplifi 2025 −$8.10; amplifi 2026 −$9.00 / −$3.60 /
    −$2.09; signatures/ca −$13.20 (= 12 × $1.10) and −$6.60; signatures/federal −$9.00. No
    eligibility flips
    in any partner file.

Should Address

A1 — Partner contract approval and notice are unverifiable

See the dedicated section above. policyengine_us/tests/policy/baseline/partners/** — 13 files,
~59 changed expected values. Maintainer gate, not a review clearance.

A2 — The code comment's equivalence claim is false in PolicyEngine's own projected years

policyengine_us/variables/gov/usda/snap/snap_expected_contribution.py:25-28

The comment states that rounding the 30% up is "equivalent to rounding the allotment down to the
nearest lower whole dollar (7 CFR 273.10(e)(2)(ii)(A)(2))". That holds only when
snap_max_allotment is a whole dollar. It is not in projected years:
parameters/gov/usda/snap/max_allotment.yaml carries uprating: gov.usda.snap.uprating, so
post-COLA months get fractional maxima. The PR's own diff demonstrates it — pins of 812.72,
565.28, 1029.10, 308.52 produce benefits like 156.72, 188.72, 74.52.

Consequence: option (1) yields a $156.72 monthly allotment where option (2) would yield
floor(812.72 − 654.9) = 157. No state issues an allotment in cents under either CFR branch, and
7 USC 2017(a) requires a whole-dollar allotment. This PR correctly fixes the contribution rounding
but leaves the allotment non-integer in projected years.

Resolution (either is defensible; both are outside this PR's minimal scope but should be filed
rather than dropped): round the uprated snap_max_allotment to a whole dollar (FNS publishes whole
dollars, so the projection should too), or apply option (2) by flooring snap_normal_allotment.
At minimum, qualify the comment so it does not assert an equivalence the model can violate.
(Merged: regulatory SHOULD 1 + references R4.)

A3 — Net-income rounding lives in one consumer, not at the source

snap_expected_contribution.py:24 vs
policyengine_us/variables/gov/usda/snap/eligibility/meets_snap_net_income_test.py:17 and
policyengine_us/variables/gov/usda/snap/income/snap_net_income_fpg_ratio.py:13

7 CFR 273.10(e)(2)(i)(A) compares "their net income, as calculated in paragraph (e)(1)" — i.e.
the rounded figure — to the net income standard. After this PR, snap_net_income is rounded
inside snap_expected_contribution but meets_snap_net_income_test still compares the raw,
cents-bearing value to the ceiling'd limit. The same regulatory quantity now has two different
values depending on which consumer reads it, and this PR introduces the split.

Numeric impact is small (an eligibility flip needs net income in the half-open band (L, L + 0.50)
for whole-dollar limit L), but the inconsistency is real. Cleaner: round at the source — make
snap_net_income return the rounded figure, or add a snap_rounded_net_income variable citing
(e)(1)(ii)(A) and have all three consumers read it. That fixes snap_net_income_fpg_ratio for free
and is the DRY placement for the floor(x + 0.5) expression. If deferred, file it.
(Merged: regulatory SHOULD 2 + code S-2.)

A4 — The reference tuple cites only one of the two governing paragraphs

snap_expected_contribution.py:11-15

reference = (
    "https://www.law.cornell.edu/uscode/text/7/2017#a",
    # 7 CFR 273.10(e)(1)(ii)(A) and (e)(2)(ii)(A)(1).
    "https://www.law.cornell.edu/cfr/text/7/273.10#e_2_ii_A_1",
)

The comment names two provisions but only one href is supplied, anchored on (e)(2)(ii)(A)(1).
A reader clicking it lands on the 30%-round-up paragraph and never sees the net-income half-up rule
that np.floor(net + 0.5) on line 24 implements. The comment's placement directly above a URL
covering only the second provision compounds it. Suggested:

reference = (
    # 7 USC 2017(a): allotment = thrifty food plan less 30% of income.
    "https://www.law.cornell.edu/uscode/text/7/2017#a",
    # 7 CFR 273.10(e)(1)(ii)(A): net income rounded 1-49 cents down, 50-99 cents up.
    "https://www.law.cornell.edu/cfr/text/7/273.10#e_1_ii_A",
    # 7 CFR 273.10(e)(2)(ii)(A)(1): 30% of net income rounded up to the next higher dollar.
    "https://www.law.cornell.edu/cfr/text/7/273.10#e_2_ii_A_1",
)

The #e_1_ii_A anchor form matches established repo usage (#d_2_ii_A in
parameters/gov/usda/wic/income/sources.yaml; #c_5, #a_1 elsewhere).

Related, same theme: three test comments cite bare (e)(2)(ii)(A) without the terminal (1)
tests/policy/baseline/gov/usda/snap/snap_normal_allotment_basis_of_issuance.yaml:4 and
tests/policy/baseline/gov/usda/snap/snap_categorical_eligibility_ceiling.yaml:~266. This matters
more than usual because (A)(2) prescribes the opposite mechanic; citing bare (A) points at a
fork rather than the branch taken. Append (1).
(Merged: regulatory SHOULD 3 + references R1, R5, R6 + code S-3.)

A5 — Both cited provisions are state options, presented as unconditional federal mandates

snap_expected_contribution.py:24 and :29

Verified verbatim from GPO govinfo XML, corroborated by Cornell LII:

  • (e)(1)(ii) intro: "the State agency shall use one of the following two procedures" — (A)
    is 1-49-down/50-99-up; (B) is "apply the rounding procedure … in effect for the State's TANF
    program", which may include the cents.
  • (e)(2)(ii)(A) intro: "the State agency shall round in one of the following ways" — (1)
    round the 30% up; (2) don't round the 30% and round the allotment down.

The code comments read as though (A) and (A)(1) are the uniform federal rule. The citations do
corroborate the arithmetic implemented, so this is not a wrong citation — but it overstates the
source. Because (A)(1) ≡ (A)(2), the allotment election is harmless; the (e)(1)(ii)(A) vs (B)
election is not a no-op
— a state that includes cents in income calculations would carry them
through the whole deduction chain, and no parameter models that today.

Minimum fix: add one clause to each comment, e.g. "…, the option PolicyEngine applies for all
states; states may instead elect (e)(1)(ii)(B) (TANF rounding)."
Longer term, consider a
gov.usda.snap.rounding.net_income parameter with a state breakdown, mirroring how other SNAP state
options (BBCE; DC/MD/NJ minimum allotment) are handled. Option (A) is the right default — it is what
the FNS issuance table assumes.
(Merged: references R2 + regulatory SUGGESTION 5 + pdf-rounding caveat.)

A6 — The FNS issuance table is cited via a mutable state-agency mirror

policyengine_us/tests/policy/baseline/gov/usda/snap/snap_normal_allotment_basis_of_issuance.yaml:3

# https://dssmanuals.mo.gov/wp-content/uploads/2022/09/snap-basis-issuance-table.pdf#page=1

The document is the right table — three independent confirmations: the printed header on every
page reads "Food & Nutrition Service / Basis of Coupon / EBT Issuance / 48 States & DC / October
1, 2025
" with an 8/14/2025 revision stamp and "Reduction Amt: 30%"; its 0 - 0 row reads
298/546/785/994/1183/1421/1571/1789/2007/2225, identical to the official FNS FY2026 maximum
allotments; and parameters/gov/usda/snap/max_allotment.yaml already carries those same values at
2025-10-01 citing https://www.fns.usda.gov/snap/allotment/cola/fy26. #page=1 is correct
(offset 0) and all eight cited bands were confirmed on the rendered page image.

Two problems remain:

  1. Authority. A federal USDA-FNS value cited to a state agency's website. Whether FNS
    publishes the full basis-of-issuance table on fns.usda.gov is unresolvedfns.usda.gov
    returns HTTP 403 to automated fetches and every search engine served a CAPTCHA. Needs a human
    with a browser. If no official copy exists, the mirror is acceptable under the "no government
    source exists" carve-out, but the comment should say so.
  2. Mutability — the real risk. The MO URL is replaced in place each October. Around 2026-10-01
    it will silently become the FY2027 table while the test header still claims "October 1, 2025"
    and "$785" — the reference will then actively contradict the test. Pin it to a Wayback capture
    (https://web.archive.org/web/20251101194701/https://dssmanuals.mo.gov/...#page=1, confirmed
    available) and add the official FNS COLA URL alongside it.

(Merged: references R3 + regulatory SUGGESTION 8 + pdf-issuance advisory.)

A7 — The float guard is load-bearing; document it as such and retract the suggestion to remove it

snap_expected_contribution.py:31contribution = np.round(net_income * expected_food_contribution, 2)

Three roles disagreed; codepath-5 settled it empirically. The code-validator (S-1) and
edge-case-checker (S1) were right; the regulatory role's SUGGESTION 4 — that the guard is an inert
no-op — used the wrong dtype and its recommendation to remove the guard is hereby retracted.

policyengine-core maps value_type = floatnumpy.float32
(policyengine_core/variables/config.py:24). Both snap_net_income and
snap_expected_contribution are float, the rate 0.3 is a Python float, and under both NEP-50
and legacy casting a float32 array times a Python float scalar stays float32. Confirmed by
introspection of the built system and by a live simulation: snap_net_income and
snap_expected_contribution both report dtype: float32.

Scan of ceil(round(floor(x+0.5) * 0.3, 2)) (guarded) vs ceil(floor(x+0.5) * 0.3) (unguarded):

dtype inputs divergences
float32 (the model's real dtype) integer net incomes 0..20000 677 (3.38%)
float32 integer net incomes 0..2000 (realistic SNAP range) 81 (4.05%)
float64 same ranges 0

Concrete diverging example, end to end in a real simulation using the PR's own new test household
(MO, 3 persons, tanf: 3108, housing_cost: 0, 2026-01 → net income $50):

GUARDED    net=[50.]  contrib=[15.]  allotment=[770.]  snap=[770.]
UNGUARDED  net=[50.]  contrib=[16.]  allotment=[769.]  snap=[769.]

float32(50) * 0.3 = 15.00000095367431640625, so an unguarded ceil returns 16. Removing the
guard would systematically overstate the contribution — and understate the allotment — by exactly
$1 for roughly 3–4% of households (those whose rounded net income is one of the ~34% of multiples of
$10 carrying upward float32 noise). The error is one-directional and always against the
household
. There are zero cases where the guard rounds down a legitimate round-up: because
net_income is an integer after floor(x + 0.5), net_income * 0.3 has at most one decimal digit
in exact arithmetic, so round(..., 2) reproduces the exact value and can never mask a true
fractional remainder. The guard is mathematically exact, not a fudge.

Actions:

  1. Keep the guard. The existing comment already states the reason correctly and sufficiently.
  2. The only test in the entire SNAP suite that would catch its removal is the net-$50 case at
    snap_normal_allotment_basis_of_issuance.yaml:278 (Δ = 1.0 > margin 0.1). Every other case — net
    0/3/4/6/7/10/11/13/14/51/3.49/3.50/6.50/10.50, the two snap_expected_contribution.yaml cases,
    both snap_categorical_eligibility_ceiling.yaml cases, integration.yaml — is non-diverging
    (float32(10 * 0.3) == 3.0 exactly). Add a comment on that case marking it as the float32
    regression guard, so a future cleanup does not delete both the guard and its only coverage.
  3. Assert snap_expected_contribution explicitly in the net-$10 and net-$50 cases (currently only
    the downstream allotment is pinned, and the comments say "30% = 3.00 exactly", which reads as a
    trivial case rather than the regression guard it is).

(Merged: tests S1 + code S-1 + regulatory SUGGESTION 4 [retracted] + codepath-5 CONFIRMED.)

A8 — No baseline case pins the rounding across the October-1 fiscal-year boundary

All 15 new cases and all 12 ineligible_members cases use period: 2026-01. The Oct–Dec months —
where an integer expected contribution is subtracted from a fractional, uprated max allotment
(the exact configuration behind A2) — are pinned only in the 13 partner contract files, which
reviewers must not edit, and in one work-requirements case. A whole-year period with month-keyed
output is the right tool. Suggested addition to
policyengine_us/tests/policy/baseline/gov/usda/snap/snap_normal_allotment_basis_of_issuance.yaml
(3-person MO, tanf: 2_640/yr = $220/month, housing_cost: 0, period: 2026):

    snap_expected_contribution:
      2026-09: 4      # net = 220 - 209 = 11 -> ceil(3.30) = 4
      2026-10: 2      # net = 220 - 216.38 = 3.62 -> rounds to 4 -> ceil(1.20) = 2

(tests S2.)

A9 — The smallest non-zero contribution boundary (net $0.50) is untested

The matrix covers net $0 (EC 0, full $785) and net $3 (EC 1) but not the point where the contribution
first becomes non-zero: net $0.49 → EC 0 → $785; net $0.50 → rounds to 1 → EC 1 → $784. This case
also discriminates the new rule from the old (floor(0.50) × 0.3 = 0 → $785) and is the cleanest
single pin of the half-up-vs-banker's-rounding fix, which is the subtlest part of the change.
Add to snap_normal_allotment_basis_of_issuance.yaml (3-person MO, tanf: 2_514).
(Merged: tests S3 + pdf-rounding MISSING-FROM-REPO 5.)

A10 — The minimum-allotment interaction with the new rounding is untested at its boundary

snap_normal_allotment = max_(min_allotment, max − EC).
policyengine_us/tests/policy/baseline/gov/usda/snap/snap_categorical_eligibility_ceiling.yaml
covers only the far side (max − EC deeply negative → $24 for 2p, $0 for 3p). The interesting boundary
— where the $1 shift this PR introduces is exactly what pushes the allotment below the $24 floor —
is untested. For a 2-person FY2026 unit:

target net income EC = ceil(0.3n) 546 − EC result
just above floor 1,736 521 25 snap 25
exactly at floor 1,740 522 24 snap 24
just below floor 1,745 524 22 snap 24 (min binds)

Under the old formula the middle row gave 546 − 522.00 = 24.00 and the third 546 − 523.50 = 22.50, so a case at net 1,740 pins that the ceil does not accidentally push a household off the
floor. (tests S4.)

A11 — Two partner pins now sit 0.0002 from a rounding tie

policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/il.yaml
documents net = 540.4998, and .../snap/federal.yaml (LUA case) documents net = 686.4998. Both
are two hundredths of a cent below the .5 half-up boundary. Any future FPG/SUA/LUA uprating that
nudges these by +$0.0002 flips the net dollar, the contribution, and the annual pin by $1–$3 — and
these are partner contract values, so a silent flip is partner-visible. Do not edit these files.
The fragility is worth the maintainer's awareness, and it independently argues for the baseline-side
.5 coverage in A8/A9 so the rounding rule is pinned somewhere robust rather than only at knife-edge
households. (tests S6.)

A12 — The new test files depend on three unasserted parameter facts

The whole 15-case band matrix in snap_normal_allotment_basis_of_issuance.yaml is constructed as
net = tanf/12 − 209. It silently assumes (a) the FY2026 1–3-person standard deduction is exactly
$209, (b) Missouri applies no standard utility allowance with housing_cost: 0, and (c) TANF is the
sole income. If any of those changes, all 15 cases shift together and the failure message points at
allotments rather than at the cause. Assert snap_standard_deduction: 209 and
snap_excess_shelter_expense_deduction: 0 in at least the first case. (tests S7.)

A13 — Test-file convention deviations in the two new files

  • Case naming. All 18 new cases lack the directory's Case N, … prefix and trailing period —
    snap_normal_allotment_basis_of_issuance.yaml:8,38,68,98,128,158,188,218,248,278,308,338,369,404,436
    and snap_categorical_eligibility_ceiling.yaml:7,41,79. Siblings do follow it
    (income/ineligible_members/integration.yaml:6). The descriptive names are genuinely informative,
    so Case N, 3-person household, net income $0 … receives $785. beats discarding them.
  • Person keys. parent / child1 / child2 at
    snap_normal_allotment_basis_of_issuance.yaml:13,15,17 (repeated per case) and
    snap_categorical_eligibility_ceiling.yaml:12,16,46,50,52; the local norm is person1 /
    person2 / person3 (income/ineligible_members/integration.yaml:11,16,18).
  • Underscore formatting. Within one file, tanf: values are written without underscores at
    snap_normal_allotment_basis_of_issuance.yaml:22,52,82,112,142,172,202,232,262,292,322 (2508,
    2544, …) but with them at :352,383,418,450 (2_549.88, 2_550, 2_586, 2_634). Repo style
    is 2_508 for values ≥ 1,000.
  • File naming. Neither new file mirrors a variable name; snap_normal_allotment.yaml and
    meets_snap_categorical_eligibility.yaml already exist. Consider appending to those, or moving
    these under a descriptive folder as integration.yaml, to keep the "test path mirrors variable
    path" convention intact.

(Merged: code SA-2, SA-3, SA-4 + tests G1 + regulatory SUGGESTION 9b.)


Suggestions

S1 — 7 CFR 273.10(e)(2)(iii) zero-benefit denial for 3+ person units is not modeled

snap_categorical_eligibility_ceiling.yaml case 2 honestly documents this ("PE does not convert a
zero-benefit 3+ unit into a denial") and pins is_snap_eligible: true with snap: 0. Benefit
dollars are unaffected, but downstream consumers of is_snap_eligible (categorical-eligibility
chains for school meals, Lifeline, etc.) see a household the reg would have denied. The reg does
give the state a choice between denial (A) and certify-and-suspend (B), so eligible-with-zero is
defensible — but pinning an acknowledged departure without an issue link makes it read as settled.
Add a # TODO(#NNNN) or link a tracking issue. Same for the (e)(2)(ii)(B) initial-month $10 floor,
which an annual model cannot express. Pre-existing, out of scope, worth filing.
(regulatory SUGGESTION 6 + tests G7.)

S2 — Changelog fragment carries a leading bullet the repo convention omits

changelog.d/fix-snap-expected-contribution-rounding.fixed.md begins with - Round SNAP …. Every
merged fragment in this repo starts with bare prose and towncrier's markdown template supplies the
- , so as written this renders - - Round SNAP … in CHANGELOG.md. Drop the leading - . (The
fragment is otherwise correct: top-level location, .fixed type, stem matching the branch, and both
CFR subsections cited.)

S3 — Stale float-margin and comment in the variable's own test file

policyengine_us/tests/policy/baseline/gov/usda/snap/snap_expected_contribution.yaml:3,34 keep
absolute_error_margin: 0.01 # Floating point issue. The expected outputs are now exact whole-dollar
multiples (250 * 12, 129 * 12), so both the margin and the stale comment can go, sharpening the
pin.

S4 — A partner comment's arithmetic is off by a cent (comment only, do not edit)

policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/ca.yaml
new comment reads "Annual = 9 × 127.00 + 3 × 156.72 = 1,613.17"; that arithmetic gives 1,613.16. The
asserted value 1613.17 is the true float result — the comment's rounding of the Oct–Dec monthly
figure is what is imprecise. Cosmetic, and it lives in a partner contract file, so it is recorded
rather than proposed as an edit.

S5 — The float32 guard degrades at extreme magnitudes (no benefit impact)

At net income ≈ $1e8, float32(N * 0.3) is 30000002.0, which round(·, 2) cannot clean (spacing
exceeds $0.005). Immaterial: snap_expected_contribution has no defined_for so it is computed for
every SPM unit, but it feeds only snap_normal_allotment, which is defined_for = "is_snap_eligible"
and floors at the minimum allotment. Recorded for completeness. (code S-4.)

S6 — Most new issuance cases do not assert the changed variable

snap_normal_allotment_basis_of_issuance.yaml asserts snap_expected_contribution at only lines
365, 396, 432, 464; the other eleven cases assert snap_normal_allotment / snap alone. A future
regression in the rounding surfaces as a downstream failure rather than pointing at the changed
variable. (code S-5; overlaps A7 action 3.)

S7 — absolute_error_margin: 0.1 on currency — consistent locally, looser than the written guideline

snap_normal_allotment_basis_of_issuance.yaml:10 (every case) and
snap_categorical_eligibility_ceiling.yaml:9,43,81 use 0.1; the guideline for currency is 0.01.
This matches the existing SNAP directory convention exactly
(income/ineligible_members/integration.yaml:8,34,62,…) and 0.1 is still an order of magnitude
tighter than the $1-scale effects under test. No change recommended — recorded only so the
deviation from the written guideline is on file. (code S-6.)

S8 — The guard assumes a rate with at most two decimals

snap_expected_contribution.py:31. At rate 0.3 the guard can never over-correct, because 3N/10 has
exactly one decimal digit and its nearest non-integer neighbours sit $0.10 away — twenty times the
$0.005 rounding radius. The residual risk is a parametric reform setting a rate with more than
two decimals: round-then-ceil then diverges from an exact ceil whenever the true product lands in
(k, k+0.005]. Concrete: rate 0.2001, net income $25 → exact ceil(5.0025) = 6, shipped code
gives 5. If that matters, np.ceil(x - 1e-6) is rate-agnostic; otherwise a one-line comment noting
the guard assumes a cents-precision rate is enough. (code S-1 residual.)

S9 — Test-coverage expansion opportunities

None of these are errors; they are untested surface.

  • Only household size 3 is exercised by the 15 new issuance cases. The table gives ten
    independent size columns on page 1; a size-indexing bug would not be caught. Highest-value
    additions: size 1 (0 - 0 → 298, 1 - 3 → 297 — the single most discriminating round-up row in
    the document) and size 10 (0 - 0 → 2225, 11 - 13 → 2221, which also exercises the
    additional-person extrapolation for sizes 9–10 that is not an explicit parameter row).
  • Only the extreme low end of the income range: net $0–$51, the first 17 of ~1,668 bands. Nothing
    tests magnitudes where float error is larger — e.g. size 7 net 4991 → 73, size 10 net 5004 → 723
    (both on page 45).
  • Band ends tested at one end only: bands 1-3 and 14-16. Adding net $1 (tanf: 2_532, EC 1 →
    $784) and net $16 (tanf: 2_700, EC 5 → $780) completes the symmetry the other bands have.
  • The variable's own test file is thin: snap_expected_contribution.yaml holds 2 cases, both
    period: 2022, both 4-person. A few cases feeding snap_net_income directly as an input and
    asserting snap_expected_contribution alone would isolate the rule (0 → 0; 0.49 → 0; 0.50 → 1;
    10 → 3; 428.50 → 129; 1,172.50 → 352).
  • Only one state (MO) and no non-contiguous region. The table covers 48 States & DC and MO is in
    scope, but AK/HI/GU/VI go untested — see also S11.
    (Merged: tests G2/G3/G4/G5 + pdf-issuance MISSING-FROM-REPO 1–4.)

S10 — Booleans asserted under a case-level margin

Both new files set absolute_error_margin: 0.1 at case level while asserting booleans
(is_snap_eligible, meets_snap_gross_income_test, meets_snap_categorical_eligibility). Since
0.1 < 1 the boolean assertions remain functional — not a defect — but the convention is no margin
on booleans; splitting them into a marginless case would match the reference. (tests G6.)

S11 — Store the FNS-published minimum allotments rather than deriving them

This is the durable fix for the confirmed Hawaii mismatch below. The FY2026 COLA memo publishes an
explicit seven-region minimum-allotment row; the repo derives the value from the rounded one-person
maximum allotment instead. Consider an FY-dated min_allotment/amount.yaml broken down by
snap_region (sourced to the COLA memo #page=5, exactly as max_allotment already is), or store
the unrounded one-person TFP cost as the derivation base. A Hawaii-only override would paper over the
mechanism and need re-checking every COLA year. Add YAML tests for at least one non-contiguous
region — policyengine_us/tests/policy/baseline/gov/usda/snap/snap_min_allotment.yaml has ten cases
and all ten are CONTIGUOUS_US (CA, NJ, DC, MD), which is why the divergence is unasserted.
Out of scope for this PR.

S12 — Guam and the U.S. Virgin Islands have no poverty guideline

policyengine_us/parameters/gov/hhs/fpg.yaml sets first_person and additional_person to 0 for
GU, PR and VI, but the FY2026 COLA memo groups Guam and the Virgin Islands with the 48
states and D.C. for income eligibility. Any GU/VI SPM unit routed through snap_fpg.py gets a $0
gross and net income standard and fails both tests — while the repo carries full GU/VI max-allotment
and standard-deduction columns. Territory support is half-present. Out of this PR's scope; a
coherence gap in the FY2026 parameter set.

S13 — FY2026 parameter references cite landing pages instead of the PDF with a page anchor

The FY2026 entries in parameters/gov/usda/snap/max_allotment.yaml,
income/deductions/standard.yaml, income/deductions/excess_shelter_expense/cap.yaml and
.../homeless/deduction.yaml all cite https://www.fns.usda.gov/snap/allotment/cola/fy26 (a landing
page), while the FY2016–FY2024 entries cite the PDF with #page=1 / #page=2. Per the parameters
convention, these should carry anchors: max allotments #page=4/#page=5, standard deductions
#page=6, shelter cap #page=6, homeless deduction #page=6, minimum allotments #page=5, asset
limits #page=7.

S14 — expected_contribution.yaml metadata is incomplete

policyengine_us/parameters/gov/usda/snap/expected_contribution.yaml has unit: /1 and a
reference, but no label and no period — both required by the parameter convention. It also
cites only 7 USC 2017(a); now that the variable's rounding rests on 7 CFR 273.10(e)(2)(ii)(A), the
parameter should carry that cite too. (The variable was correctly updated with both cites by this
PR.)

S15 — Document the upstream invariant that makes negative net income unreachable

snap_expected_contribution.py:24. Half-up rounding is asymmetric on negatives
(floor(-3.5 + 0.5) = -3), and a negative contribution would push the allotment above the maximum.
This is unreachable only because snap_net_income.py:16 returns max_(0, gross - deductions) — an
invariant that lives in a different file. Worth a one-line comment in the variable noting the
reliance. Relatedly, (e)(1)(ii)(A) says round each income and allotment calculation — the standard
deduction, earned-income deduction and shelter computations too — while the model rounds only at the
30% step. Pre-existing, not introduced here, and it affects none of the new cases (all monthly inputs
are exact), but it can produce ±$1 drift elsewhere.


Investigated and cleared

The California ineligible_members pin (225) — codepath-4 REJECTED, no defect

policyengine_us/tests/policy/baseline/gov/usda/snap/income/ineligible_members/integration.yaml:206-231,
Case 8 ("California delays OBBBA so refugees remain eligible in January 2026", period: 2026-01).

This was the single changed baseline pin the edge-case-checker could not close by hand, because it
depends on a CalWORKs grant entering SNAP unearned income — itself a multi-step state computation.
Phase-5 ran the model against the PR head and closed it. The model returns exactly 225.0; the
PR updated the pin correctly.

Step Value
gross income 2,000 earned + 475 CalWORKs unearned = 2,475
deductions 400 earned-income (20%) + 209 standard = 609
net income 2,475 − 609 = 1,866
expected contribution ceil(round(floor(1,866 + 0.5) × 0.30, 2)) = ceil(559.80) = 560
max allotment (3-person, FY2026) 785
snap_normal_allotment 785 − 560 = 225 ✓ new pin

The CalWORKs term: ca_tanf = (3/3) × min(max(1,175 − 700, 0), 1,175) = $475/month, flowing
straight into snap_unearned_income. Controlled comparison against the pre-PR tree reproduces the
old pin to float32 precision (225.19995 vs pinned 225.2), and the new tree reproduces 225
exactly — the 0.2 shift is entirely the new ceil (559.80 → 560). The margin also discriminates:
absolute_error_margin: 0.1 would fail on the old value (Δ = 0.19995 > 0.1), so this pin
genuinely guards the change. Not a defect; reported here only so the decomposition is on record.

Method note surfaced by this verifier and worth propagating: a script file run from another
directory silently imports the installed package instead of the snapshot. Verifications must run
with the snapshot on sys.path[0] (cd <snapshot> && python3 -c …).


PDF Audit Summary

Category Count Detail
Confirmed correct 57 15/15 new issuance-test cases matched the FNS Basis of Coupon/EBT Issuance table exactly (48 States & DC, Oct 1 2025, all on #page=01, offset 0); 42/43 FY2026 parameter values matched the official FNS tables — 63 max-allotment cells, 30 standard-deduction cells (incl. the easy-to-miss VI size-3 = $185 and HI size-5 = $300 irregularities), 5 shelter-cap values, the $198.99 homeless deduction, 6 of 7 derived minimum allotments, all 27 income-eligibility-standard cells, and both asset limits
Mismatches 1 Hawaii FY2026 minimum allotment: model $40 vs FNS $41 (Δ = $1.00/month). Passed both required stages — codepath-1 CONFIRMED (5C) and mismatch-1 CONFIRMED at 600 DPI (5D). See below.
Mismatches rejected 1 The California ineligible_members Case 8 pin (225) — codepath-4 REJECTED. Investigated and cleared above; it is a model-pin item, not a disputed PDF page value.
Unmodeled 4 7 CFR 273.10(e)(2)(iii) zero-benefit denial for 3+ person units; (e)(2)(ii)(B) $10 initial-month floor and proration generally; the published 165%-of-poverty table for elderly/disabled separate households (COLA memo #page=3) has no counterpart in the SNAP parameter tree; GU/VI poverty guidelines are 0 while GU/VI allotment columns exist
Pre-existing 1 The Hawaii minimum allotment — outside this PR's diff (see below)

The one confirmed mismatch — Hawaii FY2026 minimum allotment

Repo $40 vs FNS $41, source: FNS SNAP — Fiscal Year 2026 Cost-of-Living Adjustments
#page=5, table "MINIMUM SNAP ALLOTMENTS, OCTOBER 1, 2025 TO SEPTEMBER 30, 2026", Hawaii column.
Rendered at 600 DPI (5100 × 6600 px), cropped, and additionally upscaled 2× on the Hawaii cell to
rule out a glyph misread: it reads $41 — a dollar sign, a 4, and a 1. The full row as rendered
is 48 States & DC $24 / Guam $35 / USVI $31 / AK Urban $31 / AK Rural 1 $39 / AK Rural 2 $48 /
Hawaii $41, so no transposition is possible. The PDF text layer agrees exactly, and the memo's
own narrative independently states the 48-states figure of $24.

It is pre-existing — outside this PR's diff. grep -c "parameters/" pr9318-review-diff.txt → 0.
PR 9318 changes exactly one source file (snap_expected_contribution.py) plus test YAML and a
changelog fragment. snap_min_allotment.py, max_allotment.yaml and the whole min_allotment/
directory are untouched. The PR's rounding change cannot interact: the derived minimum does not
depend on the expected contribution, it merely floors the result. The two min_allotment hits inside
the diff are new test assertions only (snap_min_allotment: 24 for a Missouri/CONTIGUOUS_US unit and
0 for a 3-person unit) and both are correct against FNS.

It is neither a wrong stored constant nor a rounding-convention difference — it is the wrong
derivation base on a derived value.
There is no stored Hawaii constant;
policyengine_us/variables/gov/usda/snap/snap_min_allotment.py:18-28 computes
np.round(0.08 × max_allotment.main.HI.1) = np.round(0.08 × 506) = np.round(40.48) = 40.
Rounding convention is decisively ruled out: the repo's np.round matches FNS in six of seven
regions (24/31/39/48/35/31), and ceil would break Alaska Rural 1 (40 vs 39) and Guam (36 vs 35),
while half-up is identical to np.round at 40.48 since no tie is involved. What differs is the
base:

  • 7 CFR 273.10(e)(2)(ii)(C): the minimum is 8% of the maximum allotment for a household of
    one — what the repo implements and what its comment cites.
  • 7 USC 2017(a): the minimum is 8% of the cost of the thrifty food plan for a 1-member
    household — what FNS actually computes from.
  • 7 CFR 273.10(e)(4)(ii): the maximum allotment is itself the TFP cost rounded to the nearest
    lower dollar.

So the published maximum is a downward-rounded proxy for the statutory base. With floor(T) = 506,
T ∈ [506, 507) and 0.08T ∈ [40.48, 40.56); FNS publishing 41 forces T ≥ 506.25. The proxy
understates by exactly $1 iff 0.08 × max ends in .48, i.e. max ≡ 6 (mod 25), and never
overstates. FY2026 residues: 298→23, 385→10, 491→16, 598→23, 439→14, 383→8, 506→6 — Hawaii is the
only FY2026 region in the failure class. Historic HI size-1 values give residues 22, 13, 2, 17,
6, so FY2026 is the first year the residue hits 6: a newly-manifesting defect produced by correct
FY2026 data, not a long-standing wrong number.

Reachable and payable, confirmed by a live model run (HI, 2-person, categorically eligible):
snap_min_allotment [40.]snap_normal_allotment [40.]snap [40.]. Blast radius: Hawaii only,
1–2 person households whose computed allotment falls below the floor, effective 2025-10-01,
$1/month each. Not a blocker for this PR — file it (remediation in S11).


Validation Summary

Role CRITICAL SHOULD SUGGESTION Verdict
regulatory (program-reviewer) 0 3 6 Formula change correct; model becomes more compliant with 7 CFR 273.10(e)
references (reference-checker) 0 4 2 Both CFR designators verified verbatim against two independent sources; no wrong-subparagraph finding, rules not swapped
code (code-validator) 0 4 6 Vectorized, float-safe, ruff format clean; 10 of 10 clean categories PASS
tests (edge-case-checker) 1 7 7 Every changed baseline pin re-derived by hand and correct, except the stale ones in C1
PDF — issuance table 15/15 MATCH, zero differences of any size
PDF — rounding + FY2026 params 42/43 MATCH, 1 mismatch (Hawaii, pre-existing)
Phase 5C/5D verification 5 CONFIRMED, 1 REJECTED
Consolidated 1 13 15 REQUEST_CHANGES

Checks that passed cleanly and are worth recording:

  • No reinvented variable. The diff adds no new variable — it modifies exactly one and otherwise
    touches test YAML and a changelog fragment.
  • Order of operations correct. snap_net_income already implements 7 CFR 273.10(e)(1)(i)(A)–(I),
    and the new code inserts the (e)(1)(ii) rounding at exactly the right point — after net income is
    final, before the 30% multiply.
  • Vectorization. np.floor, np.round, np.ceil are ufuncs, elementwise and dtype-preserving.
    No Python if, no and/or/not, no .item(), no scalar conditional on an array.
  • Hard-coded values. 0.5 and 2 are numeric-method constants, not policy values, matching 13
    repo precedents for the half-up idiom. The only policy value (0.3) stays parameterised in
    gov.usda.snap.expected_contribution.
  • Entity levels and periods. SPMUnit/MONTH reading SPMUnit/MONTH at period; all 20 case periods
    are valid YYYY / YYYY-01 forms; no invented input variables anywhere in the new files.
  • Changelog. Correct name, correct top-level location, correct .fixed type (patch bump), both
    CFR subsections cited. Only the leading bullet (S2) is off.
  • Float-boundary safety. Half-up verified at 3.49→3, 3.50→4, 6.50→7, 10.50→11, 428.50→429;
    negatives unreachable via the upstream max_(0, …).
  • Formatting. uv run ruff format --check reports "1 file already formatted"; no line exceeds 88
    characters.

Review Severity

REQUEST_CHANGES

One CRITICAL is present: two baseline test pins (gov/usda/snap/integration.yaml:93,100 and
gov/usda/snap/snap.yaml:61) assert values the model no longer produces, surviving only because
absolute_error_margin: 5 and 0.3 respectively swallow the drift. Both were confirmed by running
the model against the PR head.

This is not a verdict on the substance of the change, which is correct. The formula implements
7 CFR 273.10(e)(2)(ii)(A)(1) exactly, matches the official FNS issuance table on all 15 new cases and
on boundary rows across six household sizes, and makes the model strictly more compliant than the
code it replaces. The blocking item is a two-value test-hygiene omission plus one process gate. If
the maintainer resolves the partner-approval question (A1) and accepts the two pin corrections, this
is otherwise a well-evidenced, well-tested fix.


Next Steps

Before merge (blocking):

  1. C1 — set policyengine_us/tests/policy/baseline/gov/usda/snap/integration.yaml:93 to 47 and
    :100 to 611, leaving the verbatim CBPP comment block (lines 46–99) untouched; and set
    policyengine_us/tests/policy/baseline/gov/usda/snap/snap.yaml:61 to 72 with
    snap.yaml:44 tightened from 0.3 to 0.01.
  2. A1 — the maintainer must consciously accept the partner contract change: confirm Ziming's
    approval exists, confirm the API partner was notified, and confirm the CLAUDE.md three-question
    gate was run before the 13 files under tests/policy/baseline/partners/** were edited. The review
    corroborated structure, direction and arithmetic on those files but cannot verify the approval
    or the notice.
  3. Rebase onto upstream/main (BEHIND=17) so CI's green result reflects the merge state.

Before merge (cheap and recommended):

  1. A2 — at minimum, qualify the equivalence comment at snap_expected_contribution.py:25-28 so
    it does not assert an equivalence the uprated projected years violate.
  2. A4 — add the #e_1_ii_A anchor as a third reference entry, one comment per URL; append the
    terminal (1) to the three bare (e)(2)(ii)(A) test comments.
  3. A5 — add one clause to each rounding comment noting (A)/(A)(1) are the state options
    PolicyEngine applies nationwide.
  4. A7 — add a comment on the net-$50 case at
    snap_normal_allotment_basis_of_issuance.yaml:278 marking it as the only float32 regression
    guard in the SNAP suite, and assert snap_expected_contribution explicitly in the net-$10 and
    net-$50 cases. Do not remove the np.round(..., 2) guard — the earlier suggestion to do so is
    retracted.
  5. A6 — pin the issuance-table citation to a Wayback capture and add the official FNS COLA URL
    alongside the Missouri mirror.
  6. S2 — drop the leading - from the changelog fragment.

File as follow-up issues (out of scope for this PR):

  1. Hawaii FY2026 minimum allotment $40 vs $41 — store the FNS-published per-region minimums, or the
    unrounded one-person TFP cost, as the derivation base (S11); add non-contiguous-region tests
    to snap_min_allotment.yaml.
  2. Round the uprated snap_max_allotment to a whole dollar, or floor snap_normal_allotment, so
    projected-year allotments are integers (A2).
  3. Hoist the (e)(1)(ii)(A) net-income rounding to the source so all three consumers agree (A3).
  4. Test coverage: FY-boundary case (A8), net-$0.50 boundary (A9), minimum-allotment boundary
    (A10), unasserted parameter facts (A12), and the breadth items in S9.
  5. Parameter hygiene: expected_contribution.yaml label/period (S14), FY2026 reference page
    anchors (S13), GU/VI poverty guidelines (S12), (e)(2)(iii) denial tracking issue (S1).
  6. Convention cleanups in the two new test files (A13).

Monitor: the two partner pins sitting 0.0002 from a rounding tie (A11) — il.yaml at
net = 540.4998 and federal.yaml LUA at net = 686.4998. A future FPG/SUA/LUA uprating of
+$0.0002 will flip them by $1–$3, partner-visibly.

hua7450 and others added 2 commits August 25, 2026 14:05
- Move the 7 CFR 273.10(e)(1)(ii)(A) half-up rounding into
  snap_net_income so the eligibility test, FPG ratio, and expected
  contribution all read the same rounded figure; the expected
  contribution keeps only its 273.10(e)(2)(ii)(A)(1) round-up.
- Compare BBCE net income against the same rounded-up whole-dollar
  standard as the federal net test in
  meets_tanf_non_cash_net_income_test; the previous raw ratio
  comparison denied households sitting exactly at the published
  standard once net income is rounded.
- Update the stale CBPP integration pins (47 / 611) and the North
  Carolina yearly pin (72) hidden behind wide error margins, and
  tighten those margins.
- Cite each rounding provision in its own variable, mark the net-$50
  case as the float32 cents-guard regression test, pin the issuance
  table to a Wayback capture, note the state-option elections, and
  drop the changelog fragment's leading bullet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…into fix/snap-expected-contribution-rounding
@hua7450

hua7450 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Response to the review (commit 949546a, merged with main at 22597f4):

C1 (blocking): Both stale pins fixed. integration.yaml CBPP case now pins snap_expected_contribution: 47 and snap_normal_allotment: 611, with "(Model note, not CBPP:…)" comments added below the untouched verbatim CBPP block explaining the ~$1 divergence. snap.yaml NC yearly case now pins snap: 72 (3 × $24 Oct–Dec minimum allotments, commented) with the margin tightened from 0.3 to 0.01.

A1 (partner gate): On record from the maintainer (@hua7450): the partner-contract edits were explicitly approved, and the API partner is informed — the partner's own rounding report is in fact what surfaced this issue and motivated this PR, so the changes here are the partner's requested behavior.

A3 (rounding at the source) — taken into this PR rather than deferred. snap_net_income now returns the half-up-rounded figure itself, so meets_snap_net_income_test, snap_net_income_fpg_ratio, and snap_expected_contribution all read the same rounded quantity per 273.10(e)(2)(i)(A); the expected contribution keeps only its (e)(2)(ii)(A)(1) round-up. This surfaced exactly one knife-edge: the partner case snap_ca_net_income_at_fy2026_net_limit_36447 (net 2,220.80 vs standard 2,220.83) lost Jan–Sep because the BBCE net test compared the raw ratio 2,221/2,220.83 > 1 while the federal test compares against the ceil'd whole-dollar standard (2,221 ≤ 2,221). Fixed the inconsistency at its source: meets_tanf_non_cash_net_income_test now uses the same ceil(round(limit × fpg, 4)) dollar standard as meets_snap_net_income_test, so a household at exactly the published standard passes and the partner pin is untouched. Its unit tests were rewritten against the dollar standard, including a case pinning the 2,220.83 → 2,221 knife-edge. Full partner folder re-run locally after the change: 630 passed, 0 failed.

A2 / A5: The equivalence comment is now qualified ("for whole-dollar maximum allotments…"), and both rounding comments note that (e)(1)(ii)(A) and (e)(2)(ii)(A)(1) are the state options PolicyEngine applies for all states (states may instead elect TANF rounding under (e)(1)(ii)(B)).

A4: Each provision is now cited in the variable that implements it — with one correction to the suggested fix: the #e_1_ii_A Cornell anchor does not exist (Cornell's HTML assigns printed paragraph (e)(1) the anchor ids e_8_*; verified against the live page). snap_net_income therefore cites (e)(1)(ii)(A) via an eCFR paragraph anchor (ecfr.gov/current/title-7/section-273.10#p-273.10(e)(1)(ii)(A)), and snap_expected_contribution retains only its own (e)(2)(ii)(A)(1) cite. The three bare (e)(2)(ii)(A) test comments now carry the terminal (1).

A6: The issuance-table citation is pinned to the Wayback capture of the Missouri DSS mirror (with a note that the live URL is replaced each October) plus the official FNS FY26 COLA URL.

A7: The np.round(…, 2) guard is kept per the codepath-5 verification. The net-$50 case is now commented as the only float32 cents-guard regression test in the SNAP suite (with what breaks if the guard is removed), and the net-$10 and net-$50 cases assert snap_expected_contribution explicitly (3 and 15).

S2 / S3: Changelog leading bullet dropped (and the sentence updated for the source-level rounding); both stale absolute_error_margin: 0.01 # Floating point issue. lines removed from snap_expected_contribution.yaml — the pins are now exact.

Rebase: Merged with current main (22597f42ba).

Deferred per the review's own triage (to be filed as follow-up issues): Hawaii FY2026 minimum allotment derivation base ($40 vs $41), whole-dollar allotments in uprated projected years, the (e)(2)(iii) zero-benefit denial tracking issue, GU/VI poverty guidelines, the additional coverage cases (A8–A10, A12, S9), and the test-file convention cleanups (A13).

🤖 Generated with Claude Code

@hua7450
hua7450 merged commit 5d88007 into main Aug 25, 2026
32 checks passed
@hua7450
hua7450 deleted the fix/snap-expected-contribution-rounding branch August 25, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SNAP expected contribution: round net income to nearest and 30% product up per 7 CFR 273.10(e)

3 participants