Add South Carolina fully refundable EITC contrib reform#8683
Conversation
Adds a Child Poverty Impact Dashboard contrib reform that converts South Carolina's nonrefundable EITC (125% of the federal EITC, capped per filer at $200 from 2026) to fully refundable for all filers, activated by gov.contrib.states.sc.child_poverty_impact_dashboard.eitc.in_effect. Mirrors the corrected Utah/Missouri/Ohio refundability reforms (PolicyEngine#8645/PolicyEngine#8642/ PolicyEngine#8657): pays sc_eitc_potential (uncapped at liability) so zero-liability filers receive the credit, rebuilds sc_non_refundable_credits via the ordered cap walk with sc_eitc filtered out (no add() path-string bug), and clears the inherited adds/subtracts on sc_refundable_credits before giving it a formula. The statutory per-filer cap (gov.states.sc.tax.income.credits.eitc.max) is preserved and remains separately adjustable. Tested: 4 YAML cases (zero-liability refund, no double-count with liability, $200 cap respected, SC-only) plus an end-to-end check that the in_effect param activates the reform and raises a zero-liability filer's net income. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…llowlist The fully refundable SC EITC reform filters sc_eitc out of the ordered non-refundable list by name, which the applied-credit downstream-consumer code-health guardrail flags as a new consumer. Add the reviewed entry, as done for the UT/OH refundability reforms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Program Review — Add South Carolina fully refundable EITC contrib reform
Scope: Contrib reform (Child Poverty Impact Dashboard), SC EITC, 2026 · CI: ✅ 25/25 passing
The implementation is correct and faithfully mirrors the corrected OH/MO refundability pattern (filtered ordered-cap re-walk; adds=None cleared before the formula; no double-counting), avoiding the older UT baseline − ut_eitc bug. The statute is verified and references are sound. Requesting changes to tighten test coverage and fix misleading cap terminology before merge.
Requested changes
-
Clarify "per-filer" → "per-tax-unit" terminology. The docstring, PR description, and test header describe a "per-filer $200 cap," but the implementation (
sc_eitc_potential.py,max.yaml) applies one flat $200 clamp per tax unit — a married-joint couple gets $200, not $400. The statute (SC Code 12-6-3632 / H.4216 §7) says "not to exceed two hundred dollars" with no per-filer multiplier, so the model is correct — but the wording is misleading and should say "per return / per tax unit." (This cap is pre-existing baseline behavior, not introduced here.) -
Add missing edge-case tests. The 4 YAML cases all use 2026 + the always-on
reforms:bypass and pin liability to 0 or a fully-absorbing value. Please add:- Partial liability — liability between 0 and the credit, confirming the full potential is paid (not the residual).
household_net_incomeassertion — every current case checks credit variables only; none confirms the refund flows end-to-end to net income. This is the highest-value missing test.- Married (MFJ) — pin the per-tax-unit cap behavior for 2 filers.
in_effect = falsegating — the 5-year activation look-ahead is currently untested (all cases use the bypass).- Lower priority: a pre-2026 (infinite-cap) case and a case with another SC non-refundable credit present, to exercise the re-walk the docstring says prevents overstatement.
Suggestions (non-blocking)
- Inline aggregate-override variables omit
referencefields (consistent with baseline SC aggregates / UT sibling — cosmetic). for i in range(5)→for _ in range(5)(unused loop var).
Validation Summary
| Check | Result |
|---|---|
| Regulatory Accuracy | ✅ Statute verified (125%, nonrefundable, $200 from 2026); reform logic correct, no double-counting |
| Reference Quality | ✅ Toggle param matches 12 sibling conventions; statutory claims trace to H.4216 §7 |
| Code Patterns | ✅ Mirrors corrected OH/MO pattern; adds=None cleared; no hard-coding; changelog + registration present |
| Test Coverage | |
| CI Status | ✅ Passing (25/25) |
To auto-fix: /fix-pr 8683
🤖 Generated with Claude Code
- Fix misleading "per-filer" wording in reform docstring/comments and tests: the $200 refundable cap is per return / per tax unit (MFJ also $200, not $400) - Add edge-case tests: end-to-end refund flow through net income, partial-liability split, MFJ cap, and in_effect=false no-op gating - Add reference to in_effect.yaml; use range(_) for the unused loop index Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes AppliedAddressed the review (no critical issues; CI was green throughout). Should-address
Suggestions
Verification
|
CI note: "Full Suite - Rest" failure was infrastructure, not a test failureThe failed Full Suite - Rest job did not contain any test failures. The logs show:
That is a runner shutdown / cancellation (CI infrastructure), unrelated to this PR's changes. The job is now re-running and should go green. |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Re-reviewed after "Address review: clarify per-tax-unit cap wording, add edge-case tests" — all requested changes addressed:
- Wording: docstring + comments now say "per return / per tax unit" (statute caps the credit at $200 per return, not per filer).
- Edge-case tests added: end-to-end net SC tax refund (
sc_income_tax: -125at zero liability), partial liability (capped $200 credit leaves $100 owed on $300 liability), MFJ capped at $200 per tax unit (not $400), andin_effect=falsegating no-op.
Verified all 8 YAML cases pass locally. The failing "Full Suite - Rest" check is a transient runner shutdown (infra), not a test failure — recommend re-running. Approving. 🤖
What this does
Adds a Child Poverty Impact Dashboard contrib reform that converts South Carolina's nonrefundable EITC to fully refundable for all filers, activated by
gov.contrib.states.sc.child_poverty_impact_dashboard.eitc.in_effect. This is the SC half of #8682.SC's EITC is 125% of the federal EITC, capped per filer at $200 from 2026, and is nonrefundable by default (applied only up to remaining state income tax liability), so zero-liability filers receive nothing today.
Approach
Mirrors the corrected Utah/Missouri/Ohio refundability reforms (#8645 / #8642 / #8657) — same principles, no reintroduced bugs:
sc_fully_refundable_eitcreturnssc_eitc_potential(uncapped at liability), so the full credit is paid as a refund.sc_eitcis capped at liability and zeroes out for exactly the low-liability filers refundability helps.sc_non_refundable_creditsre-runsordered_capped_state_non_refundable_creditsover the ordered list withsc_eitcfiltered out — noadd("<param-path string>")bug, and nosum − sc_eitcoverstatement when the bucket binds.addsbefore adding a formula.sc_refundable_creditssetsadds = None; subtracts = None(the baseline computes viaadds) and returns the standard refundable credits plussc_fully_refundable_eitc.The statutory per-filer cap (
gov.states.sc.tax.income.credits.eitc.max) is preserved and remains separately adjustable — the dashboard exposes it as its own lever.New files
policyengine_us/reforms/states/sc/child_poverty_eitc/sc_fully_refundable_eitc_reform.py(+__init__.py)policyengine_us/parameters/gov/contrib/states/sc/child_poverty_impact_dashboard/eitc/in_effect.yamlpolicyengine_us/tests/policy/reform/sc_fully_refundable_eitc.yamlpolicyengine_us/reforms/reforms.py; changelog fragmentVerification
…child_poverty_impact_dashboard.eitc.in_effect = trueon a 2026 SC filer with a federal EITC of $100 and zero SC liability yieldssc_fully_refundable_eitc = $125,sc_refundable_credits = $125, and +$125 household net income (baseline pays $0).Part of #8682.