Separate the dependent portion of 11 states' personal exemptions#8696
Separate the dependent portion of 11 states' personal exemptions#8696DTrim99 wants to merge 5 commits into
Conversation
Adds Child Poverty Impact Dashboard contrib reforms that split the dependent
portion of each state's per-person personal exemption (or credit) into its own
variable + parameters, mirroring the existing RI/DE/OR/VA reforms. Each defaults
to a no-op (in_effect=false; default amount reproduces current law) and lets the
per-dependent amount be adjusted or zeroed independently of the head/spouse
exemption.
States and how they separate:
- Bundled per-person exemption split into head/spouse + dependent: HI (preserves
the aged add-on), IN (base; the additional per-child exemption is untouched),
MI, NE, OK, VT, WI, WV.
- AGI-stepped per-person amount (MD, OH): the dependent amount defaults to the
baseline income-stepped schedule via a negative-amount sentinel, with an
optional flat override and income phase-out group.
- AR: redirects the existing person-level dependent slice of the personal credit
to a contributed amount (its base is otherwise shared with head/spouse). No
age limit, since it is summed via `adds`.
Each reform exposes in_effect + amount (+ age_limit for the exemption states;
+ phaseout for MD/OH). Indiana's module dir is the Python keyword `in`, so it is
imported dynamically in reforms.py and uses getattr for parameter access.
Parameters under gov.contrib.states.{st}.dependent_exemption (AR:
dependent_credit). Registered in reforms.py. Each state has YAML tests covering
the default no-op, amount reduction/elimination, childless-filer no-op, and
age-limit fallback; all pass. System smoke-imports cleanly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…coverage - Remove accidentally committed SEP_MSG.tmp scratch file from repo root - HI dependent-exemption reform: use `reference` field instead of `documentation` - Add MD/OH flat-amount and phaseout test cases (previously untested paths) - Add joint-filing test cases (MD, OH, MI, AR, HI, IN) covering the head/spouse-vs-dependent count split in 2-adult units - Strengthen MD/OH no-op case documentation 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 — Separate the dependent portion of 11 states' personal exemptions
Scope: Child Poverty Impact Dashboard contrib reforms, 11 states (AR, HI, IN, MD, MI, NE, OH, OK, VT, WI, WV) · three separation patterns (bundled split; AGI-stepped sentinel + phaseout; shared credit-base redirect)
A large but disciplined PR. All four review dimensions cleared the implementation: every state defaults to a verified no-op, cross-state isolation holds (no copy-paste/wrong-state references — the main risk in a multi-state PR), variable overrides match their baselines exactly, and all 55 YAML cases pass. Requesting changes to tighten the test suite before merge — the gaps are in what the tests prove, not in the code.
Requested changes
-
Add an end-to-end tax/net-income assertion (systemic — affects all 11 states). Every test currently asserts the reform's own exemption/credit-total variable (e.g.
md_total_personal_exemptions,hi_regular_exemptions,ar_personal_credits) — one level above the per-dependent variable, which does prove the head/spouse split arithmetic. But no test asserts state taxable income, state tax, or net income, so the exemption→tax wiring is unproven end-to-end. AR is most exposed (it asserts only a credit total, never a resulting AR tax/net-income figure). Please add at least one downstream-outcome case — ideally for AR plus one bundled-split state and MD/OH. -
Add the joint 2-adult case to NE, OK, VT, WI, WV. These five states have 4 cases and omit the joint two-adult + dependents case that HI/IN/MI/MD/OH/AR include. That case is precisely what catches off-by-one / sign-flip errors in the
personal_count = size − dependents + over-age dependentsformula — the risk the PR description itself flags. Add a joint 2-adult + 2-dependent case to each, mirroring HI/IN/MI.
Suggestions (non-blocking)
- Re-run the canceled "Quick Feedback (Selective Tests + Coverage)" job — it was canceled by an infra runner-shutdown, not a real failure (the
Full Suite - Contrib (states-shard-*)jobs that run these tests all passed). - Several count variables are typed
float/unit = USD(should be int/count); unusedinstantimport in ~7 reform files. - Most contrib params omit
reference— consistent with the merged DE/RI/VA/OR siblings, so optional; a one-line provenance comment on eachamount.yamlnoting it mirrors the state baseline would make the derivation explicit. - Minor consistency: inconsistent state-level
__init__.pypresence (works via namespace packages); WI/WV test values lack numeric underscores; effective-date style varies (0000-01-01vs2025-01-01) for inert defaults.
Validation Summary
| Check | Result |
|---|---|
| Regulatory Accuracy | ✅ No-op fidelity verified for all 11; bundled-split arithmetic, MD/OH sentinel, and state-specific preservations (HI aged add-on, IN per-child exemption, AR credit base, MI stillborn, OK extras, WV size==0) all correct; isolation confirmed |
| Reference Quality | ✅ All 11 default amounts trace to state baselines; metadata complete & consistent with siblings |
| Code Patterns | ✅ All update_variable overrides match baseline name/entity/period/value_type; non-negative fallback counts; IN dynamic import correct; 5-year gating loop references each state's own parameter — no cross-state leakage |
| Test Coverage | |
| CI Status |
Review Severity: REQUEST_CHANGES (approve-leaning — gaps are test-completeness only)
To auto-fix: /fix-pr 8696
🤖 Generated with Claude Code
Resolves PavelMakarchuk review on PR PolicyEngine#8696: - Add downstream-tax/net-income assertions (AR, MI, MD, OH) so tests no longer stop at intermediate exemption/credit variables - Add married-filing-joint 2-adult cases for NE, OK, VT, WI, WV - Add age-17 (eligible) / age-18 (excluded) boundary cases for all 10 age-limit states - Enrich AR dependent credit reference (AR1000F Line 7A + statute subsections); fix WI/WV test literal underscores - Remove unused `instant` imports (7 reform modules); add ne/wi re-export __init__.py; type dependent-count helpers as int (drop USD) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes Applied (full scope)Addresses PavelMakarchuk's review. CI: the prior "Full Suite - Rest" failure was a runner cancellation, not a test failure. Blocking review items
Suggestions
Verification
|
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Thanks for the updates — the joint 2-adult cases for NE/OK/VT/WI/WV (plus the age-limit boundary cases) and the downstream taxable_income assertions for MI and OH are exactly what was needed, and all 11 suites pass.
One remaining item from the downstream-assertion request: AR still asserts only ar_personal_credits (a credit total) — no resulting AR income-tax / net-income figure. AR is the one state using the shared-credit-base-redirect pattern (the credit is consumed via adds), so it's the most exposed to a wiring issue between the separated dependent credit and the actual tax — and it's the only pattern with no end-to-end assertion.
Requested change: add one downstream AR case asserting ar_income_tax (or net income) so the dependent-credit → tax wiring is proven end-to-end, mirroring the MI/OH taxable_income cases. A no-op default case plus an amount: 0 case showing AR tax rises would be ideal.
Everything else is good to go — this is the last blocker from my side.
Note: the failing "Quick Feedback" check is a transient runner-shutdown (infra), not a real failure; recommend re-running. 🤖
AR uses the shared-credit-base-redirect pattern (credit consumed via adds), so it was the one state lacking a downstream tax assertion. Add a default vs amount:0 pair on ar_income_tax proving the dependent credit is wired through to the actual tax: removing the dependent portion raises AR income tax by exactly 58 (1346.67 -> 1404.67), matching the credit drop from 87 to 29. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AR end-to-end income-tax assertion addedGood call — AR was the one state (shared-credit-base-redirect pattern, credit consumed via
The tax rises by exactly $58 when the dependent portion is removed — matching the credit drop from 87 → 29, proving the dependent-credit → tax wiring end-to-end. All 9 AR cases pass locally ( On the "Quick Feedback" check — agreed, that was a runner-shutdown/cancellation (infra), not a real test failure; it should go green on re-run. |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Review: Separate dependent portion of 11 states' personal exemptions
Thanks for this — the separation approach is sound and faithfully mirrors the RI/VA/DE precedent. The split math (head/spouse slice + dependent slice = original per-person total) checks out algebraically and against the no-op tests for all 11 states, and the reform correctly defaults OFF. Requesting changes on a few items before merge, the first being a genuine correctness gap.
Must address
-
MI & NE: the
in_effect=truedefault stops reproducing current law outside 2025.
mi/dependent_exemption/amount.yamlandne/dependent_exemption/amount.yamlfreeze the per-dependent amount at the single 2025 value (5_800/171), but the baseline per-person amounts (mi/.../exemptions/personal.yaml,ne/.../exemptions/amount.yaml) carryuprating: gov.irs.uprating. So when the reform is on, the personal slice uses the live uprated baseline while the dependent slice stays frozen — the "default = no-op" invariant silently breaks for 2026+ and pre-2025. A dashboard sweeping years will hit this. Fix by mirroring VT's uprating on the contrib param (vt/dependent_exemption/amount.yaml), or by adopting the MD/OH-1sentinel. (AR/HI/IN/OK/WI/WV are safe — flat, non-uprated baselines.) -
Add references to the 8 value params that lack them.
hi/in/mi/ne/ok/vt/wi/wv .../amount.yamlhave noreferencefield. Each value exactly matches a baseline parameter that already carries a full authoritative reference — please copy those over so the new file is independently traceable to current law. -
Cover the reform-activation branch + add downstream tax assertions.
Tests apply each reform via thebypass=Trueobject, so the non-bypassreform_active = True / return create_…()path never executes (the per-testin_effect: trueinput is inert — formulas only readp.age_limit.in_effect). Add a parametrized pure-Python test calling eachcreate_<st>_…_reform_fn(parameters, period)within_effecttrue and asserting aReform(notNone) is returned. Separately, only AR has an end-to-end income-tax assertion; HI/IN/NE/OK/VT/WI/WV assert the reform variable in isolation. Add a downstream state-tax assertion for those states so the separated dependent portion is shown to reach the tax base.
Should address
- Missing
__init__.pyin thear, md, ne, oh, wi, wvreform dirs — works via namespace packages, but the precedent and the other states ship explicit ones; add for consistency. - MD/OH
amount.yamldescriptions are two sentences (sentinel explanation) — split to one sentence per the parameter style rule. - Inconsistent
value-fromdates — age threshold uses2025-01-01in 8 states but0000-01-01in MD/OH. Harmless (gated byin_effect=false) but worth normalizing.
Suggestions
- Adopt the MD/OH
where(amount < 0, baseline_per, amount)sentinel across all flat-amount states — it auto-tracks the baseline and removes the MI/NE bug class entirely. - Consider a boolean toggle instead of the
-1currency sentinel (like the existingage_limit/in_effect), since-1overloads acurrency-USDparam. - No-op tests assert a hand-computed constant; a baseline-vs-reform equality assertion would be more robust.
- Factor out the repeated
range(5)lookahead loop (appears 11×). - AR statute href uses commercial casetext.com; an official source is preferable.
CI note (not blocking)
The failing Quick Feedback (Selective Tests + Coverage) check was an infrastructure runner-shutdown cancel at 10m01s while the selective suite was still passing (only ~27 of 90 tests had run, all green; the coverage step never started and runs --fail-under=0). Re-run the job. If it recurs, the suite is genuinely slow — each structural reform forces a full system rebuild per YAML test — and would benefit from an explicit timeout-minutes or sharding.
🤖 Review generated with Claude Code
What this does
Adds Child Poverty Impact Dashboard contrib reforms that separate the dependent portion of each state's per-person personal exemption (or credit) into its own variable + parameters, for 11 states: AR, HI, IN, MD, MI, NE, OH, OK, VT, WI, WV. This mirrors the existing RI/DE/OR/VA reforms and lets the per-dependent amount be adjusted or eliminated independently of the head/spouse exemption (which most of these states bundle together).
Each reform:
in_effect=false, and when on, the default amount reproduces current law.gov.contrib.states.{st}.dependent_exemption.{in_effect, amount, age_limit/*}(AR usesdependent_credit; MD/OH also get aphaseoutgroup).How each separates
{st}_dependent_exemption. HI preserves its aged add-on; IN leaves the existing per-child additional exemption untouched.baseis otherwise shared with head/spouse). No age limit — it's summed viaadds, which only reflects uniform per-dependent amounts.Indiana's module directory is the Python keyword
in, so it is imported dynamically inreforms.pyand usesgetattrfor parameter access.Tests
Each state has YAML tests (
tests/policy/contrib/states/{st}/) covering: default no-op for a family with children, amount reduction / elimination (amount: 0), childless-filer no-op, and (exemption states) age-limit fallback. All pass. The system smoke-imports cleanly with all 11 registered increate_structural_reforms_from_parameters.Follow-up
Once released, the dashboard flips these states to amount-editable (separate PR). Companion: #8691 (SC dependent-exemption parameter).
🤖 Generated with Claude Code