Skip to content

Fix EITC eligibility for married separate filers - #10307

Merged
hua7450 merged 10 commits into
mainfrom
eitc-mfs-separation-conditions
Nov 3, 2026
Merged

hua7450 merged 10 commits into
mainfrom
eitc-mfs-separation-conditions

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Married separate filers currently receive the federal EITC from 2021 without meeting the separated-spouse exception, and CalEITC omits the marital check entirely. This change requires a qualifying child who lived with the filer for more than half the year, plus either different principal residences for the last six months or a qualifying separation instrument with separate households at year end (IRC 32(d)(2)(B)). YCTC inherits the corrected CalEITC eligibility, including its zero-earned-income route.

Separation facts and their defaults

  • eitc_spouses_lived_apart_last_six_months (IRC 32(d)(2)(B)(iii)(I)) defaults to true when the tax unit head or spouse has is_separated, and stays settable as an input. is_separated (documented against 26 U.S.C. 7703) is the model's existing living-apart indicator: head_of_household_eligible, filing_status and cdcc_treated_as_unmarried (IRC 21(e)(4)) already read it for the same fact. A separated dependent does not count.
  • eitc_legal_separation_and_apart_at_year_end (IRC 32(d)(2)(B)(iii)(II)) defaults to false. is_separated does not feed it: that route needs a decree or written agreement plus separate households at year end.
  • eitc_child_lived_with_taxpayer_more_than_half_year defaults to true, preserving the existing assumption for qualifying dependents.

The shared marital predicate checks is_eitc_qualifying_child before SSN filtering, so a child without a valid SSN can establish the marital exception. California retains its own age and identification rules. The existing separate_filer parameter now enables the conditional exception; pre-2021 joint-return requirements remain.

Separated head-of-household filers

From 2021, IRC 32(d)(2)(A) determines marriage under section 7703(a) only, so a separated filer whom 7703(b) treats as unmarried for filing status is still married for the EITC. PE-US produces such units: head_of_household_eligible gives head-of-household status to a head with is_separated, a qualifying child and no spouse in the unit. The exception test now also applies to a head-of-household unit whose head or spouse has is_separated, wherever the exception switch is on (federal from 2021, and states that follow it). With the defaults above, it changes results only when the child fails the EITC qualifying-child test. A living-apart or residence input set false takes the filer out of head-of-household status (IRC 7703(b)), so the unit files separately. Before 2021 these filers were unmarried for the EITC and are unaffected.

head_of_household_eligible now reads both residence inputs for the separated route: IRC 7703(b)(1) needs a child in the home for more than half the year (eitc_child_lived_with_taxpayer_more_than_half_year), and 7703(b)(3) needs the spouse out of the household for the last six months (eitc_spouses_lived_apart_last_six_months). Neither input depends on filing_status, so this adds no cycle. Witness (2025, WA): head aged 35, $10,000 wages, is_separated, a child aged 5, living-apart input false. The filer now files separately, the federal EITC is $0, and Washington's state-only path pays $660, as main does; before this change the filer stayed head of household and got $0.

Washington

calculate_eitc_demographic_eligibility, used only by the Washington Working Families Tax Credit, applied the childless 25-64 age test to any member of the tax unit. A childless filer under 25 could therefore qualify through a dependent's age. With the PR's separate-filer change, a separate filer in that position lost the credit while a single filer kept it ($335 on main in 2025 for both; $0 and $335 before this fix). The helper now tests only the filer and spouse, as the federal eitc_demographic_eligible does under IRC 32(c)(1)(A)(ii), so both get $0. A childless separate filer in the age band keeps the state-only credit ($335).

Decree of divorce or separate maintenance

IRC 7703(a)(2) treats an individual "legally separated from his spouse under a decree of divorce or of separate maintenance" as not married. Through IRC 32(d)(2)(A), such a filer is unmarried for the EITC, so IRC 32(d)(1) does not reach the unit and its marital test needs no qualifying child. The new Person input is_separated_under_decree (default false) records that decree. When the head or spouse has it, the shared predicate in tools/state_eitc_helpers.py drops the exception requirement in both the separate-return and the separated head-of-household branches. Survey data (CPS marital code 6) cannot tell a decree from an informal separation, so the input defaults to false and is_separated alone keeps the married reading. The existing combined route eitc_legal_separation_and_apart_at_year_end (decree or written agreement, plus separate households at year end) is unchanged.

The input reaches every reader of the predicate when the exception switch is on: eitc_marital_eligible (and through it eitc_eligible, CA, NM, New Jersey's age fallback and the NY reform), DC's childless credit, calculate_eitc_like_amount (DC's with-child ITIN credit, CO, IL), and Washington's WFTC, whose frozen 2022 law has the switch on. With the switch off (Oklahoma's frozen 2020 law, and every year before 2021), the predicate still returns "not a separate return" and never reads the input, so Oklahoma's results do not change. With the input false, the predicate returns what it did before; with it true, the predicate can only turn from false to true.

Witness (2025): head aged 35, $10,000 wages, dependent mother aged 70, head of household, is_separated and both separation routes true, no EITC qualifying child. With the decree input the EITC is $649 (the zero-child maximum: 0.0765 × 10,000 = 765, capped at 649, below the 10,620 phase-out start). Without it, $0.

New Jersey

P.L.2021, c.130 lets a childless filer who fails the federal EITC only on age claim New Jersey's credit, while keeping the other federal qualifications. Because eitc_eligible now includes the marital condition for separated heads of household, federal ineligibility for marital reasons was reaching that fallback, which excluded only the SEPARATE status. nj_childless_eitc_age_eligible now requires eitc_marital_eligible, so a unit that fails the federal marital test gets no fallback, whatever its filing status.

New Jersey adds its own bar. N.J.S.A. 54A:4-7a.(3), as P.L.2021, c.130 amended it, and N.J.A.C. 18:35-4.3(g)(3) require a married claimant to file a joint return, except one who files as a head of household or surviving spouse. Neither has an exception for a separate filer with a qualifying child. The 2025 NJ-1040 line 58 instructions do allow that filer the credit with a qualifying child resident for more than half the year and six months apart from the spouse. The code follows the statute and the regulation against the instructions, and source_conflicts/candidates.yaml records the conflict as us-nj:eitc-married-separate-filer. The new nj_eitc_separate_filer_eligible passes a unit that doesn't file separately, or whose head or spouse is separated under a decree (not married under IRC 7703(a)(2)). Both the federal match in nj_eitc and the age fallback read it, so every married separate filer without a decree gets $0 from New Jersey.

Witnesses, all SEPARATE in New Jersey:

Filer Main This PR
2026, 35, $30,000, child 5, six months apart 1,380.25 (0.4 × 3,450.62) 0
2026, 35, $20,000, child 5, separation-instrument route only 1,770.80 (0.4 × 4,427) 0
2025, 22, $8,000, child 5 without an SSN, either route 0 0
2025, 35, $20,000, child 5, decree, both routes false n/a 1,731.20 (0.4 × 4,328)
2025, 22, $8,000, no child, decree n/a 259.60 (0.4 × 649)

Witness for the fallback (2025): married separated head of household aged 55, $12,000 wages, a supported adult child aged 30, six months apart, no EITC qualifying child. Federal EITC falls from $543.43 to $0 (IRC 32(d)), and New Jersey's EITC is now $0 (it was $259.60 = 0.4 × 649 under the fallback). An unmarried 22-year-old with $8,000 of earnings and no qualifying child still gets $259.60.

Property tests

test_eitc_marital_eligibility_properties.py calls the production formulas on generated units with the real parameters and states these invariants, each of which can fail on its own:

  • Joint and single filers always pass the federal marital test: IRC 32(d) reaches only married filers who don't file jointly.
  • A separate filer with no resident qualifying child and no decree fails: 32(d)(2)(B)(i) needs the child.
  • A decree held by a dependent changes nothing: IRC 7703(a)(2) reaches the taxpayer only.
  • Turning on either separation route, or the head's decree, never removes eligibility: each only relieves the test.
  • Before 2021, the result is exactly "not a separate return": pre-ARPA 32(d) barred every separate return.
  • From 2021, a separate filer with neither route and no decree fails, and one with a resident qualifying child and either route passes, identified or not: 32(c)(3)(D) limits the credit schedule, not 32(d)(2)(B).
  • From 2021, a head with a decree always passes (7703(a)(2)), and a separated head of household gets the separate filer's result (32(d)(2)(A) uses 7703(a) only).
  • A state's frozen exception switch set off bars every separate return, and set on gives the current federal test in any year.
  • New Jersey pays $0 to a unit that fails the federal marital test, and $0 to a separate filer without a decree (54A:4-7a.(3)); neither rule can raise the credit.

Partner contract cases (approved by the partner-test owner)

The partner-test owner approved these six values on this PR on 2026-11-02. Under IRC 32(d)(1), a married individual gets the credit only on a joint return, unless the IRC 32(d)(2)(B) exception applies; neither of its conditions holds in these cases.

File Case Output Before After
partners/.../tax_credits/eitc/federal.yaml eitc_married_filing_separately_eligible_post_arpa, renamed eitc_married_filing_separately_same_household_ineligible eitc 4,427 0
eitc_eligible true false
income_tax -6,127 -1,700
partners/.../tax_credits/cdcc/federal.yaml cdcc_separate_filer_ineligible eitc 3,450.62 0
eitc_eligible true false
income_tax -4,230.62 -780

The first case's comment now explains that ARPA section 9623 covers only "certain separated spouses"; the second's notes that the same facts deny the EITC. No other partner file changes. The separated-parent case (cdcc_separated_parent_treated_as_unmarried) passes with its existing expectations.

Validation

See the hand-off comment for the test counts. CI is the evidence for the full suites. No microsimulation was run here.

impact: the hub will measure the impact. Default microdata may contain separate filers without the new separation inputs. They lose the EITC unless the decree input marks a head or spouse, or they have a resident qualifying child and is_separated marks a head or spouse. This PR also lowers New Jersey's EITC for married separate filers: without a decree they now get $0, through the match and the age fallback.

axiom: TheAxiomFoundation/rulespec-us#1403 queued; TheAxiomFoundation/rulespec-us#1988 queued; TheAxiomFoundation/rulespec-us#1554 queued; TheAxiomFoundation/rulespec-us#1989 queued


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Bring in current main (including the source-conflict guard fix) before fixing CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ption to separated HoH

IRC 32(d)(2)(B)(iii)(I) asks whether the spouses lived apart for the last six
months, the same fact is_separated already carries for the head of household,
filing status and IRC 21(e)(4) tests. Default eitc_spouses_lived_apart_last_six_months
from a separated head or spouse, keeping it overridable, so separated parents
filing separately keep the EITC. The legal-separation route stays separate.

From 2021, IRC 32(d)(2)(A) determines marriage under 7703(a) only, so a
separated filer whom 7703(b) lets file as head of household is still married
for the EITC. Apply the same exception test to that unit.

calculate_eitc_demographic_eligibility, used only by Washington, tested any
member's age, so a childless filer under 25 qualified through a dependent's
age. Restrict it to the filer and spouse, as the federal variable does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 46c538eaa033aaf6f344f94e3b487988fa5a1fa1. This is a fast-forward from 478e8de3cc: the merge commit 92db72879f (current main) plus 46c538eaa0. No new commits had appeared on the branch before this work.

What the item asked, and how it was resolved

  1. Living-apart default. eitc_spouses_lived_apart_last_six_months now has a formula: true when a tax unit head or spouse has is_separated. It stays settable as an input, and the docstring says so (policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_spouses_lived_apart_last_six_months.py:10-27). is_separated does not feed the legal-separation route (eitc_legal_separation_and_apart_at_year_end is unchanged). The resident qualifying-child requirement is kept (policyengine_us/tools/state_eitc_helpers.py:63-72).
  2. CDCC partner file (read-only): 15 of 16 pass. cdcc_separated_parent_treated_as_unmarried passes again with its existing expectations: EITC 3,450.62 = 4,427 − 0.1598 × (30,000 − 23,890), CDCC 1,260, income tax −5,150.62. The other failing case is cdcc_separate_filer_ineligible (item 3).
  3. Partner cases needing the maintainer (not edited). Both are married separate filers with a qualifying child and no separation facts. IRC 32(d)(1) allows the credit to a married individual only on a joint return, unless the 32(d)(2)(B) exception applies. Neither condition is supplied (no is_separated, no separation instrument), so the statute gives 0.
    • partners/analytics_coverage/edge_cases/federal/tax_credits/eitc/federal.yaml, eitc_married_filing_separately_eligible_post_arpa (2026): eitc 4,427 → 0; eitc_eligible true → false; income_tax −6,127 → −1,700. The other outputs match: ctc 2,200, ctc_value 2,090, refundable_ctc 1,700, cdcc 0, AGI 20,000.
    • partners/analytics_coverage/edge_cases/federal/tax_credits/cdcc/federal.yaml, cdcc_separate_filer_ineligible (2026): eitc 3,450.62 → 0; eitc_eligible true → false; income_tax −4,230.62 → −780. The other outputs match: ctc 2,200, ctc_value 2,200, refundable_ctc 780, cdcc_potential 0, cdcc 0, AGI 30,000. The brief described this case as failing "the same way" as the separated-parent case, but it has no separation facts, so the default doesn't restore it.
  4. Non-partner regressions (policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_marital_eligible.yaml):
    • is_separated + qualifying child → eligible.
    • The input override (false) beats the default.
    • is_separated without a child → ineligible.
    • A full 2026 household: separated MFS parent → EITC 3,450.62.
    • MFS with no separation facts → EITC 0.
    • Legal separation + apart at year end → EITC 3,450.62.
    • A separated dependent doesn't count.
    • The PR's case "General separation indicators do not establish either statutory route" asserted the opposite of the hub's decision and is replaced.
  5. Body updated: the default, the HoH extension, the Washington fix, the partner table with the statute, and axiom: needed. The old line was the forbidden axiom: pending: hub files parity.
  6. Hub's parity checks.
    • Washington: reproduced and fixed. Case: 2025, a childless filer aged 22 with $10,000 earnings and a 30-year-old dependent. On main the WFTC is $335 both as SINGLE and as SEPARATE. At the PR head before this fix: SEPARATE $0, SINGLE $335. The cause is calculate_eitc_demographic_eligibility (WA is its only caller), which tested any member's age. IRC 32(c)(1)(A)(ii) applies the age test to the filer or spouse, and the federal eitc_demographic_eligible already excludes dependents, so the right answer is $0 for both. The helper now tests only non-dependents (policyengine_us/tools/state_eitc_helpers.py:92-98). New cases in wa_working_families_tax_credit.yaml: SEPARATE 0, SINGLE 0, and a childless separate filer aged 30 keeps the state-only credit at $335 (earnings below the phase-out start).
    • Married head of household: it can happen, so the test now applies. filing_status returns HEAD_OF_HOUSEHOLD through head_of_household_eligible's separated branch: a qualifying child, is_separated on the head or spouse, and no spouse in the unit. That unit is married under 7703(a) and treated as unmarried only by 7703(b). eitc_filing_status_eligible now applies the same (d)(2)(B) test to a HEAD_OF_HOUSEHOLD unit whose head or spouse has is_separated, whenever the exception switch is on (policyengine_us/tools/state_eitc_helpers.py:51-72). Before 2021 (switch off) these filers are unaffected. Under the default, results change only if the living-apart input is set false or the child fails the EITC child or residence tests. Cases: separated HoH meets the exception by default; with the input false, eitc_marital_eligible false and EITC 0 (2025); the same unit is eligible in 2020; an unmarried HoH needs no facts. FTB 3514 Step 1(d) was not read here (unverified).

Tests (one file per process)

  • YAML: 66 files, 579 passed, 2 failed. The two failures are the partner cases above. Files covered: every file under gov/irs/credits/earned_income and gov/irs/credits/cdcc; CalEITC (ca/.../earned_income/*, 9 files) and YCTC (ca/.../young_child/ca_yctc.yaml); NM and OK EITC; CO, DC, IL and IN EITC; NY WFTC (contrib); WA WFTC (3 files, including the WA partner file, read-only); and the two federal partner files (read-only).
  • Python: tests/test_eitc_marital_eligibility_properties.py 3 passed. Its entity stub needed HEAD_OF_HOUSEHOLD, is_tax_unit_head_or_spouse and is_separated for the new helper branch; nobody in the stub is separated.
  • Mutants, each in its own process with the code restored after:
    • living-apart default forced false → 4 failures in eitc_marital_eligible.yaml;
    • HoH gate removed → 1 failure;
    • WA helper back to any-member age → 1 failure in wa_working_families_tax_credit.yaml.
  • ruff format/ruff check and git diff --check are clean.

Hand computations

2026, one child: maximum 4,427, phase-out start 23,890, rate 0.1598. At $30,000: 4,427 − 0.1598 × 6,110 = 3,450.62.

CI

Pull request run 41409143439 at this head: completed, conclusion failure. Every job passed except Household API Partners, whose log shows exactly the two cases above (cdcc_separate_filer_ineligible, eitc_married_filing_separately_eligible_post_arpa: eitc@2026 0 vs 3,450.62 and 4,427). The passing jobs include all Baseline, Contrib and Rest Python shards, Microsimulation, Quick Feedback, Package and PR validation. Until the partner maintainer decides, this PR stays red on that job only.

Judgment calls

  • The WA fix also zeroes the SINGLE case, a pre-existing main behaviour, not only the PR-introduced SEPARATE loss. That follows the federal rule the WFTC incorporates. The RCW text wasn't read here; the WA variable's own comment says it pins the frozen 2022 federal rules.
  • HoH units are gated on is_separated because it is the model's only "married but apart" signal.
  • axiom: needed is used because no Axiom work was possible from this session.

Proposed follow-up

  • Partner expectations for separate filers without separation facts. The partner maintainer to decide on the two cases above (EITC 0 under IRC 32(d)). Alternatively, the partner fixtures could add is_separated: true if the intended household is separated.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

@hua7450, this PR needs your go on two partner cases. "Household API Partners" fails on these two and no others (run 41409143439).

Both cases are married separate filers with a qualifying child and no separation facts. IRC 32(d)(1) allows the EITC to a married individual only on a joint return. The exception in 32(d)(2)(B) needs either six months living apart or a separation instrument with separate households at year end. Neither case supplies those facts, so the statute gives $0.

Both files sit under tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/:

File Case Output Expected now Statute
eitc/federal.yaml eitc_married_filing_separately_eligible_post_arpa eitc 4,427 0
eitc_eligible true false
income_tax -6,127 -1,700
cdcc/federal.yaml cdcc_separate_filer_ineligible eitc 3,450.62 0
eitc_eligible true false
income_tax -4,230.62 -780

The income_tax change equals the lost EITC in each case. Every other output in both cases already matches.

The second case can't take is_separated: true instead: that treats the filer as unmarried for the CDCC, and this case tests CDCC ineligibility. cdcc_separated_parent_treated_as_unmarried already covers that household, and it passes with its existing expectations.

If you agree, reply here and the hub will commit these six values to this branch. If you meant either case as a separated household, tell us which and what you'd like the fixture to say.

🤖 Generated with Claude Code

@hua7450

hua7450 commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Partner cases. Yes to the six values. IRC 32(d)(1) allows the EITC to a married individual "only if a joint return is filed", and neither fixture meets the (d)(2)(B) exception: in the first the spouse shares the household, and the second's own comment says "without living apart". Pub 596, Rule 3 reads the same way. Yes to the three partner-test questions.

Please also:

  • In eitc/federal.yaml, rename eitc_married_filing_separately_eligible_post_arpa (for example, eitc_married_filing_separately_same_household_ineligible) and rewrite its comment, which says separate filers "ARE eligible from 2021 on" and derives 4,427. ARPA section 9623 covers only "certain separated spouses" (H.R. 1319, p. 150).
  • Add a line to the cdcc_separate_filer_ineligible comment saying the EITC is denied on the same facts.
  • Rerun Household API Partners after the edit. Run 41409143439 stopped after edge_cases/federal (Makefile:304), so the state, signature and other partner batches (310 of 623 cases) haven't run at this head.

I'll review once that's green.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Thanks, @hua7450. A hub session will make your edits in one push: the six values, the rename of eitc_married_filing_separately_eligible_post_arpa with a rewritten comment, and the added line on cdcc_separate_filer_ineligible.

The same push fixes two findings from an independent review at 46c538eaa0:

  • A filer legally separated under a decree of separate maintenance is unmarried under IRC 7703(a)(2), so the 32(d) test doesn't apply. A new input, false by default, carries that fact.
  • New Jersey's age fallback will keep the federal marital test, so a married head of household who fails 32(d) gets no NJ credit.

When that push's Household API Partners job finishes, the hub will post its full result here, with all 623 cases.

🤖 Generated with Claude Code

…e fallback within the federal marital test

IRC 7703(a)(2) makes a filer legally separated under a decree of separate
maintenance unmarried, so IRC 32(d)(1) does not reach the unit. The new
is_separated_under_decree input (default false, since survey data cannot
identify a decree) lifts the exception requirement in both the separate
and the separated head-of-household branches of the shared predicate.

New Jersey's childless fallback waives only the federal age limits, so it
now requires eitc_marital_eligible instead of excluding only separate
returns; a married separated head of household with no qualifying child
no longer reaches it.

Updates the two partner cases whose new values the partner-test owner
approved on the PR, and adds YAML witnesses and Hypothesis invariants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6GNhVT4m1wugwntS23
@MaxGhenis

MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 354e4dddb1099b45c8bb9f6662c7cc943da8fb88 (one commit on top of 46c538eaa033aaf6f344f94e3b487988fa5a1fa1; nobody else had pushed since 07:04 UTC). The PR is still ready, not a draft.

Items

  1. Input. policyengine_us/variables/household/demographic/marital_unit/is_separated_under_decree.py: a boolean Person input in the same folder as is_separated, default_value = False. Its documentation says what it means, that it defaults to false because survey data can't tell a decree from an informal separation, and cites 26/7703#a_2.

  2. Predicate. policyengine_us/tools/state_eitc_helpers.py:63-72: needs_exception = (separate | separated_head_of_household) & ~separated_under_decree, where the decree comes from the head or spouse only. The comment quotes 7703(a)(2) using the brief's text. eitc_legal_separation_and_apart_at_year_end and eitc_spouses_lived_apart_last_six_months are unchanged.
    Readers. The input reaches every reader whenever the exception switch is on:

    • eitc_marital_eligible, and through it eitc_eligible, CA (ca_eitc_eligible), NM (nm_eitc_eligible) and the NY WFTC reform (both formulas);
    • DC's childless credit (dc_eitc_without_qualifying_child);
    • calculate_eitc_like_amount, used by DC's with-child ITIN credit, CO (both paths) and IL;
    • WA WFTC. It passes the switch from its frozen 2022 law, which is true, so the input reaches it.

    Oklahoma's frozen path. OK passes the 2020 switch, which is false. The predicate then returns ~separate before it reads the input, so Oklahoma's results are unchanged. That covers every year before 2021 too.
    Washington's frozen path. With the input false, Washington's results are unchanged. With it true, they can only rise.

  3. New Jersey. variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py:21-24,41: the fallback now requires tax_unit("eitc_marital_eligible", period) in place of ~separate. I put the fix in the variable nj_eitc.py:40 reads for the fallback, not in nj_eitc.py itself. Before 2021, and for every SEPARATE unit without a decree, this gives the same result as ~separate. Unverified: pub.njleg.state.nj.us refused the connection, so I couldn't fetch P.L.2021, c.130. The comment cites it and paraphrases its rule with no quote. The variable's existing reference block already quoted the act before this PR; I didn't check that quote.

  4. Partner tests. Only these edits under partners/, matching the values approved on 2026-11-02:

    • .../tax_credits/eitc/federal.yaml:872: the case is renamed eitc_married_filing_separately_same_household_ineligible. eitc 4,427 → 0, eitc_eligible true → false, income_tax -6,127 → -1,700. The comment is rewritten: ARPA 9623 covers only "certain separated spouses", and this couple meets neither 32(d)(2)(B) condition. That ARPA quote comes from the brief.
    • .../tax_credits/cdcc/federal.yaml:850: eitc 3,450.62 → 0, eitc_eligible true → false, income_tax -4,230.62 → -780. One comment line now says the same facts deny the EITC.
    • No other partner value moved: CI's Household API Partners job passed 623 of 623.
  5. YAML.

    • gov/irs/credits/earned_income/eitc_marital_eligible.yaml:
      • :302, the P2-1 witness with the input true: EITC 649 = min(0.0765 × 10,000 = 765, 649); 10,000 < 10,620 phase-out start.
      • :337, the same household with the input false: 0.
      • :368, SEPARATE with the input true, a resident child and both routes false (2026): 3,450.62 = 4,427 − 0.1598 × (30,000 − 23,890).
    • gov/states/nj/tax/income/credits/eitc/nj_eitc.yaml:
      • :142, the P2-2 witness: federal 0, NJ 0. Before this change it was 0.4 × 649 = 259.60.
      • :174, an unmarried NJ filer aged 22 with $8,000 and no qualifying child (2025): match 0.4 (nj/.../eitc/match.yaml, 2020 on) × zero-child maximum 649 (irs/credits/eitc/max.yaml, 2025) = 259.60.
  6. Hypothesis. All in tests/test_eitc_marital_eligibility_properties.py.

    • test_decree_of_separate_maintenance_invariants (:305). Units cross status (SEPARATE/HoH/JOINT/SINGLE), head is_separated, both routes and the decree holder (nobody/head/dependent) with generated children. It checks five things:

      • with the input false, the result equals head_rule_without_decree (:276), a statute-level restatement of the rule at 46c538e;
      • a decree on a dependent changes nothing;
      • the input never lowers eligibility;
      • before 2021 the input is ignored;
      • with the switch frozen off, only separate returns are barred.

      Why it can't pass trivially: it asserts that every generated case has some unit that fails without the decree and passes with it.

    • test_nj_eitc_requires_federal_marital_eligibility (:445). It runs the production nj_childless_eitc_age_eligible and nj_eitc formulas on real parameters. Units that fail the marital test get NJ 0.

      Why it can't pass trivially: the same units, with the marital test passed, must get a positive fallback whenever they're 18 or older, have no children, and pass the income tests.

    • The existing proxy now supplies is_separated_under_decree.

  7. Mutants. Each ran in its own process, and git checkout restored the file after each one; the tree was clean afterwards.

    • (a) Input ignored in the predicate: killed. eitc_marital_eligible.yaml failed 2 of 21 and the decree invariant failed.
    • (b) Input applied only to the SEPARATE branch: killed. eitc_marital_eligible.yaml failed 1 of 21 (the HoH witness) and the decree invariant failed.
    • (c) NJ marital condition removed: killed. nj_eitc.yaml failed 1 of 11 (the P2-2 witness) and the NJ invariant failed.
  8. Tests run at this head, one file per process. All passed: 179 YAML cases and 45 Python tests.

    File Passed
    eitc_marital_eligible.yaml 21
    ca_eitc_mfs.yaml 13
    wa_working_families_tax_credit.yaml 53
    partner eitc/federal.yaml 15
    partner cdcc/federal.yaml 16
    nj_eitc.yaml 11
    nj_childless_eitc_age_eligible.yaml 8
    nj_eitc_income_eligible.yaml 3
    nj_eitc_investment_income_baskets.yaml 6
    nj_income_tax.yaml 7
    ok_federal_eitc_eligible.yaml 3
    ok_eitc.yaml 11
    dc_eitc_without_qualifying_children.yaml 7
    dc_eitc_with_qualifying_child.yaml 5
    test_eitc_marital_eligibility_properties.py 5
    tests/unit/test_nj_eitc_investment_income_properties.py 40

    No full suites and no microsimulation were run here.

  9. Body. Updated through the REST PATCH and read back. It adds the decree reading and input, the reader list, the New Jersey fix and its witnesses, and the partner edits with the owner's go. The Impact: and axiom: lines are unchanged. The changelog fragment now mentions the input and the NJ fix.

  10. CI. Pull request run https://github.com/PolicyEngine/policyengine-us/actions/runs/41480725111 has completed with conclusion success: all 35 jobs passed. This line was updated after the run finished; at hand-off one Contrib shard was still running.

    • Household API Partners: success, all 8 batches plus the follow-on batches ran. 623 passed, 0 failed, so the stop after edge_cases/federal seen in run 41409143439 did not recur.
    • Partner API impact notice: success.

Judgement calls

  • The input sits beside is_separated (household/demographic/marital_unit), since 7703(a)(2) isn't specific to the EITC. Its documentation follows the statute and mentions a decree of divorce too.
  • A unit counts as decree-separated when its head or spouse has the input; a dependent's value is ignored, matching the is_separated handling.
  • I replaced NJ's ~separate with the federal marital variable rather than adding to it. So a SEPARATE filer with the decree input, which is an odd combination, can reach NJ's fallback, consistent with the federal reading.
  • The decree input isn't routed into the frozen switch-off path, so Oklahoma's results are unchanged.
  • I didn't add a source-conflict marker: this is a statutory reading, not a conflict between sources.

Proposed follow-up

  • Title: Document is_separated_under_decree for API and data users.
    The input defaults to false because CPS can't identify a decree. Partners who know the filer's legal status should be told to set it, since it changes EITC results for separated heads of household.

Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

@hua7450, here is the full partner result for the push. Your edits are in 354e4dddb1 with the decree input and the New Jersey fix.

  • Household API Partners passed all 623 cases, with 0 failures. All 18 batches exited 0, in run 41480725111, which concluded success.
  • Partner files touched: only edge_cases/federal/tax_credits/eitc/federal.yaml and .../cdcc/federal.yaml, with your six values, the rename and the comment lines. No other partner value moved.

An independent review of the new commit comes next, then the population impact check, before the PR lands.

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
354e4dd
METHODOLOGY_CHOICE: yes

Round 2 independent review of 46c538ea..354e4dddb1 (one commit). The head has not moved. I found no wrong result at this head. The change closes both round-1 findings. One claim in the hand-off is false, and the findings below are P3 (tests and docs). The hub can fold them into the landing push or a follow-up.

Findings

P3-1. The NJ change is not a no-op for separate filers without a decree; the hand-off says it is, and no test pins the new path

policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py:24,41. The hand-off says: "Before 2021, and for every SEPARATE unit without a decree, this gives the same result as ~separate." That is false. eitc_marital_eligible counts qualifying children before the SSN filter (state_eitc_helpers.py:73-76). no_qualifying_children uses eitc_child_count, which counts SSN-valid children only. So a separate filer whose only qualifying child lacks an SSN now passes the marital test and reaches the age fallback. ~separate used to block that filer.

Witness (2025, NJ): SEPARATE, head aged 22 with $8,000 of wages, a resident child aged 5 with ssn_card_type: NONE, and eitc_spouses_lived_apart_last_six_months: true.

eitc_marital_eligible eitc_child_count eitc nj_childless_eitc_age_eligible nj_eitc
this head true 0 0 true 259.60
~separate gate (round-1 head and main) – 0 0 false 0
same filer, apart false, this head false 0 0 false 0

On main, the same filer as HEAD_OF_HOUSEHOLD or SINGLE already gets nj_eitc 259.60. So the new result treats a separate filer who meets 32(d)(2)(B) like an unmarried one. That fits the code's own quote of P.L.2021, c.130 §1a(4) ("shall meet all qualifications, except for the minimum or maximum age"); I could not fetch the act itself. I don't ask for a code change. The equal-treatment reading follows the standing rulings. The 259.60 versus 0.4 × 612 = 244.80 question comes from main's federal age test on a no-SSN-child filer, the subject of the separate us:eitc-qualifying-child-without-ssn-age-test fix. It is not this PR's.

Pin the case in nj_eitc.yaml:

- name: Separate filer who meets the separated-spouse exception through a child without an SSN gets the NJ age fallback
  # The child counts for IRC 32(d)(2)(B) but not for the federal schedule
  # (32(c)(3)(D)), so eitc_child_count is 0. The filer is 22, below the
  # federal minimum age, and meets the marital test: NJ pays 0.4 x 649 = 259.60.
  period: 2025
  absolute_error_margin: 0.01
  input:
    people:
      head:
        age: 22
        employment_income: 8_000
      child:
        age: 5
        ssn_card_type: NONE
    tax_units:
      tax_unit:
        members: [head, child]
        filing_status: SEPARATE
        eitc_spouses_lived_apart_last_six_months: true
    households:
      household:
        members: [head, child]
        state_code: NJ
  output:
    eitc_marital_eligible: true
    eitc_child_count: 0
    nj_childless_eitc_age_eligible: true
    nj_eitc: 259.60

The hub's impact pair should also expect nj_eitc to move only for NJ separate filers aged under 25 or over 64 whose only qualifying children lack an SSN and who meet a separation route (see Population).

P3-2. No test puts the decree on a spouse, so a head-only reading survives in substance

policyengine_us/tools/state_eitc_helpers.py:68-71 reads the decree from the head or the spouse. That is right, and I confirmed the spouse case end to end. A 2026 SEPARATE unit with head ($30,000), spouse, a child aged 5, both routes false, and is_separated_under_decree on the spouse gets eitc 3,450.62. Without the decree it gets 0. But no YAML case sets the decree on a spouse, and DecreeUnits puts it only on the head or a dependent.

Mutant (e) swaps is_tax_unit_head_or_spouse for is_tax_unit_head. It passes eitc_marital_eligible.yaml (21/21) and nj_eitc.yaml (11/11). It "fails" three property tests only because the proxies have no is_tax_unit_head key (KeyError), not because any assertion catches it. Add the spouse case above to eitc_marital_eligible.yaml (output eitc_marital_eligible: true, eitc: 3_450.62). Or add a "spouse" holder to DECREE_HOLDERS, with a second head-or-spouse member.

P3-3. The input's documentation reads as general, but only the EITC predicate reads it

policyengine_us/variables/household/demographic/marital_unit/is_separated_under_decree.py:9-15 says "IRC 7703(a)(2) treats such a person as not married". The variable sits beside is_separated. But its only reader is eitc_filing_status_eligible. filing_status, head_of_household_eligible, cdcc_treated_as_unmarried, is_tax_unit_spouse and the other is_separated readers ignore it. So a decree-separated filer keeps SEPARATE status, with its standard deduction and CTC. An API user who sets the input may expect more than it does. Replace the last sentence with:

    Defaults to false because survey data cannot tell a decree from an
    informal separation, so is_separated alone keeps the married reading.
    Only the EITC marital test (IRC 32(d)) reads this input; filing status
    and other credits do not yet apply IRC 7703(a)(2).

The label says "decree of separate maintenance", but the text and the statute also cover a decree of divorce. That's fine, but the label could read "Legally separated under a decree of divorce or separate maintenance". Proposed follow-up: Apply is_separated_under_decree to filing status and the CDCC (IRC 7703(a)(2) also governs filing_status, head_of_household_eligible and 21(e)). A decree-separated filer can't file SEPARATE at all.

P3-4. Body wording

  • The body still spells the impact line Impact:, not the literal lowercase impact: the hub checks for (round 1 flagged this too). The axiom: line exists.
  • The body's impact sentence says separate filers "lose the EITC unless a head or spouse is flagged is_separated". is_separated alone isn't enough. The unit also needs a resident qualifying child (or, from this commit, a decree). Replace it with: "They lose the EITC unless they have a resident qualifying child and a head or spouse is flagged is_separated (or the decree input is set)."

Checked and sound

Sources. I couldn't fetch any statute from this session. curl and web fetch both failed for law.cornell.edu, govinfo.gov, uscode.house.gov, ecfr.gov, pub.njleg.state.nj.us, app.leg.wa.gov, law.justia.com, irs.gov, congress.gov and nj.gov: proxy 403, or DNS not allowed. So I quote no law here. The code's 7703(a)(2) comment matches the quote in the hub's ruling text, and I couldn't check it against the statute. I couldn't read RCW 82.08.0206 or N.J.S.A. 54A:4-7 either. The Washington and New Jersey conclusions below rest on the code.

The defect reproduces on main (origin/main 4bfc548). A SEPARATE filer with a child aged 5, $30,000 of wages and no separation facts gets eitc 1,943.30 in 2021 and 3,450.62 in 2026, and 0 in 2020. CalEITC is 90.37 in 2026. At this head the same 2026 filer gets 0.

P2-1, the decree input. It is a plain boolean Person input with default_value = False and no formula, so YAML and a dataset can both set it. The predicate reads only the head's or spouse's value. Mutant (d), which reads every member, is killed by test_decree_of_separate_maintenance_invariants ("a decree on a dependent changes nothing"); no YAML case kills it. With the mother as the only holder, the P2-1 witness still gets 0. & ~separated_under_decree applies to separate | separated_head_of_household, so it takes the unit out of both branches. Full-model witness (2025, TX, head 35, $10,000, dependent mother 70, HEAD_OF_HOUSEHOLD, is_separated, both routes true): 649 with the decree and 0 without. By hand: 0.0765 × 10,000 = 765, capped at the 649 maximum, and 10,000 is below the 10,620 phase-out start. I read those parameters at head: zero-child max 649, rate 0.0765, start 10,620. A 2020 SEPARATE filer with the decree still gets 0, because the switch is off and the function returns ~separate before reading the input.

Readers, P2-1 witness at 2025 in each state, without and then with the decree:

Reader No decree Decree
federal eitc 0 649
CA ca_eitc (through eitc_marital_eligible) 0 209.49
NM nm_eitc 0 162.25
DC dc_eitc_without_qualifying_child 0 649
CO co_eitc 0 324.50
IL il_eitc 0 129.80
WA WFTC 0 335
OK ok_eitc 22.27 22.27
NJ, P2-2 witness (55, $12,000) eitc 0, NJ 0 eitc 543.43, NJ 217.37
  • The NY WFTC reform reads eitc_marital_eligible at lines 198 and 387, so the input reaches it when the reform is on; I didn't run the reform.
  • DC's with-child ITIN path, CO's ITIN and under-25 paths, and IL go through calculate_eitc_like_amount with the current-year switch, so they inherit the input.
  • Washington. eitc_eligible ANDs the predicate with the frozen 2022 switch, which is true. The final eligible is eitc_eligible | state_only_eitc_eligible | age_expansion_eligible. The decree can only turn the predicate from false to true, so WA can only rise, as the 0 → 335 row shows.
  • Oklahoma. It passes the frozen 2020 switch (false), so the predicate returns ~separate before reading the input. A 2025 OK SEPARATE filer with the decree and a child gets ok_eitc 0.

P2-2, New Jersey. The fallback now ANDs eitc_marital_eligible. The P2-2 witness gives federal 0 and NJ 0 (it was 0.4 × 649 = 259.60). Without the federal phase-out the federal amount would be 649 − 0.0765 × (12,000 − 10,620) = 543.43. The 22-year-old, $8,000 case gives 0.4 × 649 = 259.60, with match 0.4 and NJ minimum age 18 read from parameters. A SEPARATE 22-year-old with the decree and no child reaches the fallback (259.60). Without the decree they get 0. That follows the hub's reading that the decree makes the filer unmarried for the EITC. Open #10525 quotes §1a(4) paying the match on "the federal maximum amount for taxpayers with no qualifying child", which supports using 649 rather than the phased-in 612. I could not verify that quote or any separate-filer rule of New Jersey's own.

Partner values.

  • eitc_married_filing_separately_same_household_ineligible: spouse in the household, no separation facts, so 32(d)(1) bars the credit. EITC 0. 20,000 − 16,100 = 3,900 taxable gives 390 of tax, absorbed by the CTC, so income_tax = −1,700, the refundable CTC.
  • cdcc_separate_filer_ineligible: EITC 0 and income_tax = −780, the refundable CTC on 30,000.
  • The separated-parent case stays at 4,427 − 0.1598 × (30,000 − 23,890) = 3,450.62, with the 2026 parameters read at head.
  • Both partner files pass locally (15 and 16 cases). Under partners/, the PR's file list shows only edge_cases/federal/tax_credits/eitc/federal.yaml and .../cdcc/federal.yaml. The rename and comment rewrites match the owner's requests. Nothing under .github/ changed.

Tests at head, one file per process, all passing:

File Result
eitc_marital_eligible.yaml 21 passed
nj_eitc.yaml 11 passed
nj_childless_eitc_age_eligible.yaml 8 passed
ca_eitc_mfs.yaml 13 passed
wa_working_families_tax_credit.yaml 53 passed
partner eitc/federal.yaml 15 passed
partner cdcc/federal.yaml 16 passed
test_eitc_marital_eligibility_properties.py 5 passed

Hypothesis tests.

  • head_rule_without_decree restates the standing reading rather than calling the formula: a status mask, a separated-HoH mask, OR of the two routes, and a resident child computed from the generated tuples. It mirrors the formula's structure, as any restatement of the same rule would.
  • The "some unit flips" assertion can't pass trivially. Every case includes a SEPARATE unit with no route and the decree on the head, and that unit must gain, so ignoring the input fails it (mutant a).
  • In the NJ test, the federal-route half holds by construction, because the proxy builds eitc_eligible = other & marital. Only the fallback half tests the code, and its @example guarantees at least one positive fallback row.
  • The generators reach no age or income boundaries (all-boolean member facts; NJ ages 15–80 are drawn, not pinned at 18, 24, 25, 64 or 65). The YAML endpoints cover those.

Mutants, each in its own process on a scratch worktree, restored afterwards; git status is clean:

Mutant Result Killed by
(a) input ignored killed eitc_marital_eligible.yaml 2/21 failed (by elimination, the only two cases that set the decree true, :302 and :368); decree property
(b) input only on the SEPARATE branch killed eitc_marital_eligible.yaml 1/21 (:302, the HoH witness); decree property
(c) NJ marital condition removed killed nj_eitc.yaml 1/11 (:142, the P2-2 witness); NJ property
(c2) NJ reverted to ~separate killed nj_eitc.yaml :142; NJ property
(d) decree read from every member killed decree property only
(e) decree read from the head only survives in substance see P3-2

Register. No register entry or # Source conflict marker is needed. The decree reading applies the text of 7703(a)(2) and 32(d)(2)(A), and no form or instruction conflicts with it. The ruling records it as a statute reading with nothing to register. The protocol places markers only for register entries, and only after landing.

Population. The policyengine-us-data 1.115.4 wheel from PyPI sets is_separated = A_MARITL == 6 (datasets/cps/cps.py:1116). Nothing in it mentions a decree, so the input stays false in the microdata. With the input false, the federal predicate and every state reader except NJ return what they did at round 1's head. The only data-reachable change in this commit is NJ:

  • (i) a separated head of household with no EITC qualifying child loses the fallback;
  • (ii) a SEPARATE filer who meets a route through a qualifying child without an SSN, and whose filer is outside 25–64, gains it (P3-1).

Round 1 reported that every calculated separated-HoH unit passes the gate and every calculated SEPARATE unit lacks an EITC qualifying child. If so, both sets are empty. The impact pair against round 1's head should then show zero change in every output. Any nonzero nj_eitc change should trace to (i) or (ii). I did not verify those counts.

CI. Run 41480725111 at 354e4dd concluded success, and all 35 jobs concluded success: 11 Baseline, 9 Contrib, 11 Python (including Microsimulation), Household API Partners, PR validation, Package and compatibility, and Quick Feedback. The partner job's log shows 18 batches summing to 623 passed and 0 failed. Partner API impact notice (run 41480720489) also concluded success.

Overlap. Open drafts #10043 and #10525 both change nj_childless_eitc_age_eligible.py. git merge-tree merges that file cleanly with both, and the merged trees keep marital_eligible. Both conflict with this PR in nj_eitc.yaml, and #10043 also in ok_eitc.yaml. Whichever lands second resolves those test files. By my reading of its diff (not run), #10525's recomputed fallback still pays 259.60 for the 22-year-old case, since min(612, 649) > 0 meets its income test.

What I couldn't check

  • Every statute and source: 26 U.S.C. 32 and 7703, ARPA §9623, P.L.2021, c.130, N.J.S.A. 54A:4-7, RCW 82.08.0206 and the NJ-1040 instructions. No legal host was reachable (proxy 403 or DNS not allowed). In particular, whether New Jersey has a separate-filer rule of its own that would bar the decree or no-SSN-child separate filer from the fallback.
  • Population counts and the impact pair: I ran no microsimulation, and the hub's data isn't reachable here.
  • Whether the queued rulespec-us issues on the axiom: line cover 7703(a)(2) and NJ §1a(4). That repository is outside this session's access.
  • The NY WFTC reform with the decree, which I didn't run.

Generated by Claude Code

Main now carries the former section 32(c)(1)(F) identification bar in
eitc_eligible. Keep both: build eligibility from this PR's five
conditions, eitc_marital_eligible included, apply main's 32(c)(1)(F)
block to it, and keep both references. Washington's test file keeps
this PR's childless cases and main's earned-income reduction case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6KcYmGRGLN8zAqaGY3
N.J.S.A. 54A:4-7a.(3) and N.J.A.C. 18:35-4.3(g)(3) require a married
claimant to file jointly, except a head of household or surviving
spouse. The NJ-1040 line 58 instructions add an exception for a
separate filer with a child and six months apart; the statute governs.
nj_eitc_separate_filer_eligible passes a filer who doesn't file
separately or is separated under a decree (IRC 7703(a)(2)), and both
the federal match and the age fallback now read it. Record the
conflict in candidates.yaml, pin each route in nj_eitc.yaml, and cut
the property file to invariants that can fail on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6KcYmGRGLN8zAqaGY3
IRC 7703(b)(1) needs the child in the home for more than half the year
and 7703(b)(3) needs the spouse out of the household for the last six
months, so head_of_household_eligible now reads both inputs. A
separated filer who fails either files separately, which keeps
Washington's state-only credit ($660 in the witness). The decree and
marital documentation now name every reader, and the changelog states
New Jersey's tightening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6KcYmGRGLN8zAqaGY3
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 0262579ff1af875370d4cff3b09b671c741c2473

Three commits on b235d3f71c: a merge of main, the New Jersey change, and the head-of-household and documentation fixes. I haven't replied to the owner's review or ruling; the hub handles that.

What the item asked and how I resolved it

Item Resolution
0. Merge main 8bcf438ba0. Main's history no longer contains 4cf0a98, the main commit this PR merged on 11/02. Main now runs from 97b6d5135c (11/02 04:55Z) straight to 11/03 commits, but every file the 20 old-main commits touched matches main, apart from 6 that later main commits changed again. So CHANGELOG.md, pyproject.toml and uv.lock conflicted on version numbers alone; the PR never changed them, so I took main's side. eitc_eligible.py: eligible holds this PR's five conditions, eitc_marital_eligible included (lines 30-36); main's former-32(c)(1)(F) block applies to it (37-51), and the formula returns eligible (52). eitc = parameters... stays (line 20), and both references stay (32(d), and main's former-32(c)(1)(F) link with its comment, line 15). wa_working_families_tax_credit.yaml also conflicted: I kept this PR's three childless cases and main's Case 39.
1. New variable, statute variant nj_eitc_separate_filer_eligible.py returns ~separate | under_decree (line 31), with no lived_apart. Its reference holds the statute and regulation URLs. nj_eitc.py:41 and nj_childless_eitc_age_eligible.py:26 read it. The property file builds units through the formula, not a hard-coded True.
2. YAML in nj_eitc.yaml Instrument route 2026 (line 228): eitc 4,427, nj_eitc 0. Instrument route 2025, SSN-less child (256): eitc_marital_eligible true, fallback false, 0. Decree with child 2025 (314): eitc 4,328, nj_eitc 1,731.20. Six months apart 2026 (284), named for the line 58 conflict: eitc 3,450.62, nj_eitc 0. The old 259.60 case (197) now expects fallback false and 0, under the requested name. Decree without child 2025 (344): fallback true, 259.60.
3. Register entry source_conflicts/candidates.yaml:6090, before us-nj:liheap-income-ceiling, with follows: statute and state: ruled. It lists three sources: statute, regulation (the kind the file's other 164 regulation sources use) and instructions. Each readings entry says what its text says, and a note covers the decree. No # Source conflict marker.
4. P2-2 head of household head_of_household_eligible.py:45-61, as written in the review. The two eitc_marital_eligible.yaml updates are at lines 234 (now SEPARATE, renamed) and 257 (living-apart input dropped). The Washington witness at wa_working_families_tax_credit.yaml:1759 passes: filing_status: SEPARATE, eitc: 0, wa_working_families_tax_credit: 660. The body sentence is replaced.
5. P3-2, statute reading The changelog says New Jersey's EITC and fallback follow the federal marital condition and bar every married separate filer unless a decree applies. There's no widening clause or witness. The body's New Jersey section states (a)(3) and (g)(3) and says the code follows them against the line 58 instructions (see judgment 1).
6. P3-3 to P3-6 P3-3: is_separated_under_decree.py:13-17, with the "If P2-1 lands" wording. P3-4: eitc_marital_eligible.yaml:398-402. P3-5: eitc_marital_eligible.py:21-23. P3-6: the body's reader list and heading, and the changelog's "decree of divorce or separate maintenance".
7. Cleanup asks The property file drops from 459 to 323 lines and 5 to 4 tests. I dropped head_rule_without_decree and the NJ fallback expression, which restated formulas. Nine invariants remain, each with its reason in the code and in the body's new "Property tests" section. I found no exact duplicates. Across the PR's four non-partner YAML files (138 cases, main's included), no two cases share the same period and input, let alone output, so I removed none.
8. Body impact: and axiom: keep their lines. impact: adds "This PR also lowers New Jersey's EITC for married separate filers: without a decree they now get $0, through the match and the age fallback."

Tests (one file per process, at the head's code)

  • nj_eitc.yaml: 17 passed (12 existing, 1 rewritten among them, plus 5 new).
  • eitc_marital_eligible.yaml: 22 passed.
  • wa_working_families_tax_credit.yaml: 55 passed.
  • head_of_household_eligible.yaml: 15 passed.
  • filing_status.yaml: 14 passed.
  • eitc_eligible.yaml, main's file with the 32(c)(1)(F) bar cases and the only test file that sets that parameter: 15 passed.
  • test_eitc_marital_eligibility_properties.py: 4 passed in 12.7 s. It builds no model.
  • test_source_conflicts.py: 103 passed.

Mutants (each in its own process, restored after)

Mutant Fails
| lived_apart added to the new variable's return nj_eitc.yaml: "...through a child without an SSN gets no NJ age fallback" and "Separate filer with a child and six months apart gets no NJ EITC...". Property: test_nj_eitc_marital_invariants.
Term dropped from the age fallback nj_eitc.yaml: "...through a child without an SSN gets no NJ age fallback" and "...separation-instrument route with a child without an SSN gets no NJ age fallback". Property: test_nj_eitc_marital_invariants.
Head of household read without lived_apart eitc_marital_eligible.yaml: "Separated filer who did not live apart for six months files separately and fails from 2021". wa_working_families_tax_credit.yaml: the new Washington witness.

Hand computations

  • 2026 instrument route: 0.34 × 20,000 = 6,800 caps at 4,427; 20,000 < 23,890, so federal 4,427 and NJ 0.
  • 2026 six months apart: 4,427 − 0.1598 × (30,000 − 23,890) = 3,450.62 federal; the instructions' reading would pay 0.4 × 3,450.62 = 1,380.25; the model pays 0.
  • 2025 decree with child: 0.34 × 20,000 caps at 4,328; NJ 0.4 × 4,328 = 1,731.20.
  • 2025 decree, no child, 22: 0.4 × 649 = 259.60.
  • 2025 Washington witness: one-child amount 660, with no phase-out at 10,000.

CI

The Pull request run at this head, 41707568662, concluded success with all 36 jobs successful. Partner API impact notice, 41707565636, also concluded success. No partner test file changed in this round.

Judgment calls

  1. Sources. This session's network refused pub.njleg.state.nj.us, law.cornell.edu, nj.gov and web.archive.org. So I couldn't fetch the statute, the regulation or the NJ-1040 instructions. The brief says to fetch a source before quoting it, so the body states what (a)(3) and (g)(3) require and links them rather than quoting them. The register entry's readings paraphrase as well. The entry keeps the review's #page=44 for the instructions, which I couldn't confirm. The hub can swap in quotes from its own reads.
  2. Property file scope. I added two invariants the old file lacked. One: a separated head of household gets the separate filer's result from 2021. Two: a state switch set on equals the current federal test in any year. I dropped the old "decree on head equals rule before input" comparison, because it needed a restated formula. The non-vacuity checks went too; the YAML pins the positive amounts.
  3. Changelog. I added a sentence on the head-of-household residence requirement. The fix changes filing_status when either input is false, so it's user-visible.
  4. New Jersey comment in nj_eitc.py:35-36. It says (a)(3) bars a married separate filer "without the federal separated-spouse exception". It adds no # Source conflict marker, which the hub adds after landing.
  5. Let a qualifying child without an SSN establish EITC eligibility from 2021 #10583 is still open. Under this reading the old 259.60 pin is now 0 whatever Let a qualifying child without an SSN establish EITC eligibility from 2021 #10583 does, so the 244.80 move the review noted no longer applies.
  6. Main's history. Main was rewritten after this PR's 11/02 merge. I merged rather than rebased, as the brief asks. The PR's diff against main covers only its own files, so the 20 old-main commits add nothing to it.

For the hub's follow-up list (not fixed)

Proposed follow-up issue: "Apply IRC 7703(a)(2) decree status outside the EITC". is_separated_under_decree is read only by the EITC marital test and New Jersey's rule. Filing status, dependent rules and the tip and overtime deductions still treat a decree-separated filer as married.


Generated by Claude Code

@hua7450 hua7450 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.

Approved at 0262579ff1. The New Jersey rule, the head-of-household fix, the merge and the register entry all check out, and CI passed on this head. The register's readings match the sources, and #page=44 is the right page for line 58.

@hua7450
hua7450 merged commit a61a874 into main Nov 3, 2026
37 checks passed
@hua7450
hua7450 deleted the eitc-mfs-separation-conditions branch November 3, 2026 23:46
MaxGhenis pushed a commit that referenced this pull request Nov 5, 2026
…e child lacks an SSN

Main's case from #10307 expected eitc_eligible false for a 2025 separated
parent aged 20, since the parent is under the childless minimum age. On
this branch the child without an SSN establishes federal eligibility from
2021 (IRC 32(c)(1)(A)(i)) while staying out of the amount, so the parent
gets the zero-child 649. The CalEITC outputs do not move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HSDrJsaVhwknS3Czs
MaxGhenis added a commit that referenced this pull request Nov 5, 2026
… 2021 (#10583)

* Let a qualifying child without an SSN establish EITC eligibility from 2021

IRC 32(c)(1)(A)(i) makes an individual with a qualifying child an
eligible individual; 32(c)(3)(D) and (m) leave a child without a valid
SSN out only of the amount under subsection (b). ARPA section 9622 struck
former 32(c)(1)(F) from 2021. The model based eligibility on the
SSN-filtered count, so such a parent under 25 failed the childless age
test and got nothing. Eligibility now also counts the identification-
independent qualifying child from 2021; the amount still uses the
filtered count, giving the zero-child schedule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give the under-25 DC case the federal self-only credit and DC's match

From 2021 a filer whose only qualifying child lacks a TIN is an eligible
individual under IRC 32(c)(1)(A)(i), so the federal credit is the
zero-child $459. D.C. Code 47-1806.04(f)(4) keeps the child a qualifying
child, and (f)(1)(B-1) allows 70% of that credit: $321.30. Update the
with-child formula's comment to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge remote-tracking branch 'origin/main' into eitc-no-ssn-child-age-test

* Pin both NJ EITC routes, make the no-SSN property test search, and fix a page range

The property test now leaves qualifying-child status to the formulas and
compares each filer with an age-40 childless twin, so it fails if the
no-SSN child stops establishing eligibility or if the head counts as a
qualifying child. The pre-2021 property becomes an upper bound that holds
whether or not former 32(c)(1)(F) is modeled. Two NJ cases pin 54A:4-7(a)(1)
for the federally eligible parent and (a)(4) for the childless filer under
the federal age floor. 32(c)(3)(D) is on file page 3 of the 2021 Code PDF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Search the EITC property at its age boundaries and with a TIN

Round 2's mutants found that random ages rarely reach 64 and that no property case had a child with has_tin false. The New Jersey parent case also pinned nj_childless_eitc_age_eligible at a value the ruling rejects; nj_eitc alone kills the same mutant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge main: keep both sides' NJ EITC cases and pin the federal credit beside them

nj_eitc.yaml conflicted because both sides appended cases; both are kept,
main's first. With this branch, a child without an SSN makes main's two
NJ separate filers federally eligible (zero-child 612 in 2025), so those
cases now say so and pin eitc 612; nj_eitc stays 0 under N.J.S.A.
54A:4-7a.(3). Adds a 2021 married separate case to the federal witness
file so the #10307 marital exception and this branch's demographic rule
are tested together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Expect the federal zero-child credit for the separated CA parent whose child lacks an SSN

Main's case from #10307 expected eitc_eligible false for a 2025 separated
parent aged 20, since the parent is under the childless minimum age. On
this branch the child without an SSN establishes federal eligibility from
2021 (IRC 32(c)(1)(A)(i)) while staying out of the amount, so the parent
gets the zero-child 649. The CalEITC outputs do not move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read main's EITC identification bar instead of a second parameter

#10297's `qualifying_child_without_identification_bar` already encodes ARPA
section 9622, so `eitc_demographic_eligible` reads it and this PR's complement
goes. Move the federal cases to a file named for the variable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge main into EITC child without SSN

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants