From 4789532d76e3632d8b0ec52a6bf9854d1a931e03 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 23:05:00 -0400 Subject: [PATCH 1/9] Follow state statutes on NM, VT, NY/NYC and PR income tax items - New Mexico, NMSA 7-2-5.9(A): the senior medical exemption needs expenses that "exceed" $28,000; the PIT-ADJ instructions say "$28,000 or more". - Vermont, 32 V.S.A. 5828c (through 2021): AGI "less than" $30,000/$40,000; the 2021 Schedule IN-112 says "or less". - New York, Tax Law 606(b), and New York City, 1310(d): a married couple's household credit counts the exemptions it is entitled to; IT-201 lines 40 and 48 deny the credit when either spouse can be claimed. - Puerto Rico, section 1052.01(i): the earned income credit is indexed; add Hacienda's 2024 (BI RI 24-04) and 2025 (BI RI 26-01) schedules, and pin the 2023-value cases to 2023. Co-Authored-By: Claude Opus 5.5 --- .../statute-first-state-income-tax.fixed.md | 1 + .../min_expenses.yaml | 2 +- .../credits/earned_income/max_amount.yaml | 15 +++- .../phase_out/threshold/joint.yaml | 15 +++- .../phase_out/threshold/single.yaml | 15 +++- ...st_medical_expense_premium_conservation.py | 5 +- ...c_children_medical_exemption_invariants.py | 7 +- ...st_state_medical_expense_input_deletion.py | 9 ++- .../household/nyc_household_credit.yaml | 11 ++- .../nm_medical_expense_exemption.yaml | 30 +++++-- .../income/credits/ny_household_credit.yaml | 13 +-- .../cdcc/vt_low_income_cdcc_eligible.yaml | 30 +++++-- .../credits/earned_income/integration.yaml | 4 +- .../pr_earned_income_credit.yaml | 79 +++++++++++++++++-- .../credits/household/nyc_household_credit.py | 23 ++++-- .../nm_medical_expense_exemption.py | 6 +- .../credits/ny_household_credit_potential.py | 26 ++++-- .../cdcc/vt_low_income_cdcc_eligible.py | 9 ++- 18 files changed, 239 insertions(+), 61 deletions(-) create mode 100644 changelog.d/statute-first-state-income-tax.fixed.md diff --git a/changelog.d/statute-first-state-income-tax.fixed.md b/changelog.d/statute-first-state-income-tax.fixed.md new file mode 100644 index 00000000000..c88c93ad797 --- /dev/null +++ b/changelog.d/statute-first-state-income-tax.fixed.md @@ -0,0 +1 @@ +Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; New York and New York City household credits count a joint return's federal exemptions rather than denying the credit when one spouse can be claimed; and Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules. diff --git a/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/unreimbursed_medical_care_expense/min_expenses.yaml b/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/unreimbursed_medical_care_expense/min_expenses.yaml index 5aaca843301..d02feaad554 100644 --- a/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/unreimbursed_medical_care_expense/min_expenses.yaml +++ b/policyengine_us/parameters/gov/states/nm/tax/income/exemptions/unreimbursed_medical_care_expense/min_expenses.yaml @@ -1,4 +1,4 @@ -description: New Mexico provides the unreimbursed medical care expense exemption for filers with expenses at or above this amount. +description: New Mexico provides the unreimbursed medical care expense exemption for filers with expenses above this amount. values: 2005-01-01: 28_000 metadata: diff --git a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/max_amount.yaml b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/max_amount.yaml index ce5d03dd662..324c3e19ee4 100644 --- a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/max_amount.yaml +++ b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/max_amount.yaml @@ -4,18 +4,26 @@ brackets: 2023-01-01: 0 amount: 2023-01-01: 1_656 + 2024-01-01: 1_745 + 2025-01-01: 1_794 - threshold: 2023-01-01: 1 amount: 2023-01-01: 3_864 + 2024-01-01: 4_071 + 2025-01-01: 4_186 - threshold: 2023-01-01: 2 amount: 2023-01-01: 6_072 + 2024-01-01: 6_400 + 2025-01-01: 6_576 - threshold: 2023-01-01: 3 amount: 2023-01-01: 7_173 + 2024-01-01: 7_563 + 2025-01-01: 7_774 metadata: amount_unit: currency-USD threshold_unit: child @@ -23,7 +31,12 @@ metadata: label: Puerto Rico earned income credit max amount period: year reference: - # discrepancy: legal statute max amount doesn't correspond with tax form values + - title: "Hacienda Boletín Informativo de Rentas Internas 24-04, inflation adjustment to the earned income credit for 2024 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2024/12/bi_ri_24-04_-_credito_por_trabajo_-_ajuste_por_inflacion_2024_firmada_23-dic-2024.pdf#page=1 + - title: "Hacienda Boletín Informativo de Rentas Internas 26-01, inflation adjustment to the earned income credit for 2025 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2026/02/bi_ri_26-01_ajuste_por_inflacion_al_credito_por_trabajo_para_el_ano_2025_signed.pdf#page=1 + - title: P.R. Internal Revenue Code § 1052.01(i), 13 L.P.R.A. § 30211(i) (inflation adjustment as adjusted by the federal IRS) + href: https://docs.pr.gov/files/OCIF/LEYES-REGLAMENTOS-CARTAS-CIRCULARES/Leyes-delegan-Facultad-OCIF/Ley%20N%C3%BAm.%201-2011%20-%20C%C3%B3digo%20de%20Rentas%20Internas%20para%20un%20Nuevo%20Puerto%20Rico.pdf#page=300 - title: P.R. Laws tit. 13, § 30211 (1)-(6) href: https://law.justia.com/codes/puerto-rico/title-thirteen/subtitle-17/part-ii/chapter-1007/subchapter-b/30211/ - title: Schedule CT Individual, Earned Income Credit, 2023 diff --git a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/joint.yaml b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/joint.yaml index b8742b1404a..4d136070e9a 100644 --- a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/joint.yaml +++ b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/joint.yaml @@ -5,18 +5,26 @@ brackets: 2023-01-01: 0 amount: 2023-01-01: 19_870 + 2024-01-01: 20_940 + 2025-01-01: 21_530 - threshold: 2023-01-01: 1 amount: 2023-01-01: 24_280 + 2024-01-01: 25_590 + 2025-01-01: 26_310 - threshold: 2023-01-01: 2 amount: 2023-01-01: 27_590 + 2024-01-01: 29_080 + 2025-01-01: 29_900 - threshold: 2023-01-01: 3 amount: 2023-01-01: 27_590 + 2024-01-01: 29_080 + 2025-01-01: 29_900 metadata: amount_unit: currency-USD threshold_unit: child @@ -24,8 +32,13 @@ metadata: label: Puerto Rico earned income credit phase out threshold period: year reference: + - title: "Hacienda Boletín Informativo de Rentas Internas 24-04, inflation adjustment to the earned income credit for 2024 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2024/12/bi_ri_24-04_-_credito_por_trabajo_-_ajuste_por_inflacion_2024_firmada_23-dic-2024.pdf#page=1 + - title: "Hacienda Boletín Informativo de Rentas Internas 26-01, inflation adjustment to the earned income credit for 2025 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2026/02/bi_ri_26-01_ajuste_por_inflacion_al_credito_por_trabajo_para_el_ano_2025_signed.pdf#page=1 + - title: P.R. Internal Revenue Code § 1052.01(i), 13 L.P.R.A. § 30211(i) (inflation adjustment as adjusted by the federal IRS) + href: https://docs.pr.gov/files/OCIF/LEYES-REGLAMENTOS-CARTAS-CIRCULARES/Leyes-delegan-Facultad-OCIF/Ley%20N%C3%BAm.%201-2011%20-%20C%C3%B3digo%20de%20Rentas%20Internas%20para%20un%20Nuevo%20Puerto%20Rico.pdf#page=300 - title: Schedule CT Individual, Earned Income Credit, 2023 href: https://hacienda.pr.gov/sites/default/files/schedule_ct_rev._jul_5_23_informative_-_instructions.pdf#page=2 - # discrepancy: legal statute phase out thresholds do not correspond with tax form values - title: P.R. Laws tit. 13, § 30211 (1) - (6) href: https://law.justia.com/codes/puerto-rico/title-thirteen/subtitle-17/part-ii/chapter-1007/subchapter-b/30211/ diff --git a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/single.yaml b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/single.yaml index 3f25534d252..0446792fce9 100644 --- a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/single.yaml +++ b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/single.yaml @@ -5,18 +5,26 @@ brackets: 2023-01-01: 0 amount: 2023-01-01: 17_660 + 2024-01-01: 18_610 + 2025-01-01: 19_130 - threshold: 2023-01-01: 1 amount: 2023-01-01: 19_870 + 2024-01-01: 20_940 + 2025-01-01: 21_530 - threshold: 2023-01-01: 2 amount: 2023-01-01: 23_180 + 2024-01-01: 24_430 + 2025-01-01: 25_110 - threshold: 2023-01-01: 3 amount: 2023-01-01: 23_180 + 2024-01-01: 24_430 + 2025-01-01: 25_110 metadata: amount_unit: currency-USD threshold_unit: child @@ -24,8 +32,13 @@ metadata: label: Puerto Rico earned income credit phase out threshold period: year reference: + - title: "Hacienda Boletín Informativo de Rentas Internas 24-04, inflation adjustment to the earned income credit for 2024 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2024/12/bi_ri_24-04_-_credito_por_trabajo_-_ajuste_por_inflacion_2024_firmada_23-dic-2024.pdf#page=1 + - title: "Hacienda Boletín Informativo de Rentas Internas 26-01, inflation adjustment to the earned income credit for 2025 #page 1-3" + href: https://hacienda.pr.gov/sites/default/files/publicaciones/2026/02/bi_ri_26-01_ajuste_por_inflacion_al_credito_por_trabajo_para_el_ano_2025_signed.pdf#page=1 + - title: P.R. Internal Revenue Code § 1052.01(i), 13 L.P.R.A. § 30211(i) (inflation adjustment as adjusted by the federal IRS) + href: https://docs.pr.gov/files/OCIF/LEYES-REGLAMENTOS-CARTAS-CIRCULARES/Leyes-delegan-Facultad-OCIF/Ley%20N%C3%BAm.%201-2011%20-%20C%C3%B3digo%20de%20Rentas%20Internas%20para%20un%20Nuevo%20Puerto%20Rico.pdf#page=300 - title: Schedule CT Individual, Earned Income Credit, 2023 href: https://hacienda.pr.gov/sites/default/files/schedule_ct_rev._jul_5_23_informative_-_instructions.pdf#page=2 - # discrepancy: legal statute phase out thresholds do not correspond with tax form values - title: P.R. Laws tit. 13, § 30211 (1) - (6) href: https://law.justia.com/codes/puerto-rico/title-thirteen/subtitle-17/part-ii/chapter-1007/subchapter-b/30211/ diff --git a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py index 5734523889c..1618a6dc584 100644 --- a/policyengine_us/tests/core/test_medical_expense_premium_conservation.py +++ b/policyengine_us/tests/core/test_medical_expense_premium_conservation.py @@ -395,7 +395,10 @@ def _state_simulation(cases, reference_system, with_se_deduction): # deduction. The oracle preserves the supplied amount exactly as main # does; only the computed path restores an applied federal exclusion. main_base = case["subtotal"] if supplied else gross + # The credit (NMSA 7-2-18.13) takes expenses of $28,000 or more; the + # exemption (NMSA 7-2-5.9(A)) takes expenses that "exceed" $28,000. senior_eligible = case["age"] >= 65 and main_base >= 28_000 + senior_exemption_eligible = case["age"] >= 65 and main_base > 28_000 denominator = 2 if case["filing_status"] == "SEPARATE" else 1 for state in ("ND", "NM"): @@ -464,7 +467,7 @@ def _state_simulation(cases, reference_system, with_se_deduction): ) # NMSA 7-2-5.9 and PIT-ADJ line 18 allow the full exemption. expectations["nm_medical_expense_exemption"].append( - 3_000 if state == "NM" and senior_eligible else 0 + 3_000 if state == "NM" and senior_exemption_eligible else 0 ) simulation = Simulation( tax_benefit_system=reference_system, diff --git a/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py b/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py index 96154868af8..1a71feb44d5 100644 --- a/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py +++ b/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py @@ -182,7 +182,7 @@ def unit(adults, dependents, expenses=0.0): unit([adult(25), adult(20)], [dependent(22, student=True)]), # The only 65+ person is a dependent parent. unit([adult(40)], [elderly_parent], expenses=30_000.0), - # A 65+ filer with exactly $28,000 of expenses. + # A 65+ filer with exactly $28,000 of expenses (not over $28,000). unit([adult(70)], [], expenses=28_000.0), ] @@ -268,7 +268,8 @@ def _reference_exemption(unit): if unit["state"] != "NM": return 0 filer_65 = any(a["age"] >= 65 for a in unit["adults"]) - return EXEMPTION * (filer_65 and unit["medical_expenses"] >= MIN_EXPENSES) + # NMSA 7-2-5.9(A): expenses that "exceed" $28,000. + return EXEMPTION * (filer_65 and unit["medical_expenses"] > MIN_EXPENSES) def _describe(unit): @@ -336,7 +337,7 @@ def _assert_coverage(drawn_units, credit, exemption): u["state"] == "NM" and all(a["age"] < 65 for a in u["adults"]) and any(d["age"] >= 65 for d in u["dependents"]) - and u["medical_expenses"] >= MIN_EXPENSES + and u["medical_expenses"] > MIN_EXPENSES for u in drawn_units ] ) diff --git a/policyengine_us/tests/core/test_state_medical_expense_input_deletion.py b/policyengine_us/tests/core/test_state_medical_expense_input_deletion.py index 8f700ae0cae..c48a55b7835 100644 --- a/policyengine_us/tests/core/test_state_medical_expense_input_deletion.py +++ b/policyengine_us/tests/core/test_state_medical_expense_input_deletion.py @@ -69,13 +69,14 @@ def test_nd_paid_cost_base_after_medical_subtotal_deletion(delete_subtotal): @pytest.mark.parametrize("delete_subtotal", SUBTOTAL_DELETION_CASES) def test_nm_senior_benefits_after_medical_subtotal_deletion(delete_subtotal): - simulation = _simulation("NM", other_expenses=22_000) + simulation = _simulation("NM", other_expenses=22_001) if delete_subtotal: simulation.delete_arrays("itemized_medical_expenses", PERIOD) - # The recomputed federal subtotal is 27,000, but restoring its 1,000 - # exclusion meets NM's 28,000 paid-cost threshold. Calculate both before - # asserting to preserve evidence. + # The recomputed federal subtotal is 27,001, but restoring its 1,000 + # exclusion gives 28,001 of paid costs, which meets the credit's $28,000 + # threshold and exceeds the exemption's. Calculate both before asserting + # to preserve evidence. benefits = tuple( simulation.calculate(variable, PERIOD).item() for variable in ( diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index 43eeabb49b8..039a0ed9eda 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -273,7 +273,7 @@ output: nyc_household_credit: 15 -- name: 2025 joint return with one claimable spouse gets no NYC household credit (the claimable spouse is the head) +- name: 2025 joint return with one claimable spouse counts the other spouse's exemption for the NYC credit (the claimable spouse is the head) period: 2025 input: people: @@ -298,9 +298,12 @@ state_code: NY in_nyc: true output: - nyc_household_credit: 0 + # Tax Law 1310(d) counts the exemptions the couple is entitled to: one. + # $30 x 1. IT-201 line 48 would deny the credit; the model follows the + # statute. + nyc_household_credit: 30 -- name: 2025 joint return with one claimable spouse gets no NYC household credit (the claimable spouse is the spouse) +- name: 2025 joint return with one claimable spouse counts the other spouse's exemption for the NYC credit (the claimable spouse is the spouse) period: 2025 input: people: @@ -325,7 +328,7 @@ state_code: NY in_nyc: true output: - nyc_household_credit: 0 + nyc_household_credit: 30 - name: 2025 joint return with neither spouse claimable gets the NYC household credit period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.yaml index fbc36a3d88e..c0abdd891c8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.yaml @@ -15,10 +15,12 @@ age: 65 medicare_enrolled: false health_insurance_premiums: 6_000 - other_medical_expenses: 22_000 + other_medical_expenses: 22_001 self_employed_health_insurance_ald: 1_000 output: - itemized_medical_expenses: 27_000 + # Gross paid: 6,000 + 22,001 = 28,001, over $28,000; the federal + # subtotal (27,001) excludes the 1,000 deducted premium. + itemized_medical_expenses: 27_001 nm_medical_expense_exemption: 3_000 - name: New Mexico senior exemption preserves caller-supplied medical expenses @@ -27,7 +29,7 @@ state_code: NM age: 65 medicare_enrolled: false - itemized_medical_expenses: 28_000 + itemized_medical_expenses: 28_001 output: nm_medical_expense_exemption: 3_000 @@ -58,7 +60,7 @@ input: state_code: NM age: 65 - other_medical_expenses: 28_000 + other_medical_expenses: 28_001 filing_status: JOINT output: nm_medical_expense_exemption: 3_000 @@ -68,7 +70,7 @@ input: state_code: NM age: 65 - other_medical_expenses: 28_000 + other_medical_expenses: 28_001 filing_status: SEPARATE output: # Neither NMSA 7-2-5.9 nor the PIT-ADJ line 18 instructions halve the @@ -87,7 +89,7 @@ # PIT-ADJ line 18: "enter $3,000 on line 18 to claim the exemption". nm_medical_expense_exemption: 3_000 -- name: 2025 expenses of exactly $28,000 qualify under the PIT-ADJ instructions +- name: 2025 expenses of exactly $28,000 do not qualify under the statute period: 2025 input: state_code: NM @@ -96,8 +98,20 @@ other_medical_expenses: 28_000 filing_status: SINGLE output: - # PIT-ADJ line 18: expenses "of $28,000 or more". NMSA 7-2-5.9(A) says - # expenses that "exceed" $28,000, which would give 0 here. + # NMSA 7-2-5.9(A): expenses that "exceed twenty-eight thousand dollars". + # The PIT-ADJ line 18 instructions say "of $28,000 or more", which would + # give 3,000; the model follows the statute. + nm_medical_expense_exemption: 0 + +- name: 2025 expenses one dollar over $28,000 qualify + period: 2025 + input: + state_code: NM + age: 70 + medicare_enrolled: false + other_medical_expenses: 28_001 + filing_status: SINGLE + output: nm_medical_expense_exemption: 3_000 - name: 2025 expenses one dollar under $28,000 do not qualify diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml index 75ecd16039e..4f0b9b35809 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml @@ -127,7 +127,7 @@ output: ny_household_credit_potential: 45 -- name: 2025 joint return with one claimable spouse gets no household credit (the claimable spouse is the head) +- name: 2025 joint return with one claimable spouse counts the other spouse's exemption (the claimable spouse is the head) period: 2025 input: people: @@ -151,10 +151,13 @@ members: [person_a, person_b] state_code: NY output: - # Item C is one box per return; either spouse marks it. - ny_household_credit_potential: 0 + # Tax Law 606(b) counts the exemptions the couple is entitled to: one, + # for the spouse who cannot be claimed. $60 base at $10,000 of AGI plus + # $15 x (1 - 1). IT-201 line 40 would deny the credit because item C is + # marked; the model follows the statute. + ny_household_credit_potential: 60 -- name: 2025 joint return with one claimable spouse gets no household credit (the claimable spouse is the spouse) +- name: 2025 joint return with one claimable spouse counts the other spouse's exemption (the claimable spouse is the spouse) period: 2025 input: people: @@ -178,7 +181,7 @@ members: [person_b, person_a] state_code: NY output: - ny_household_credit_potential: 0 + ny_household_credit_potential: 60 - name: 2025 joint return with neither spouse claimable gets the household credit period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.yaml index 2bd8ce741df..0dc4cac1263 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.yaml @@ -1,3 +1,8 @@ +# 32 V.S.A. 5828c as in effect for 2021: federal adjusted gross income "less +# than $30,000.00 (or $40,000.00 for married, filing jointly)". The 2021 +# Schedule IN-112 says "$30,000 (or $40,000 for Married Filing Jointly) or +# less"; the model follows the statute, so income at the threshold does not +# qualify. - name: Vermont low-income CDCC single filer not eligible period: 2021 input: @@ -7,12 +12,21 @@ output: vt_low_income_cdcc_eligible: false -- name: Vermont low-income CDCC single filer eligible +- name: Vermont low-income CDCC single filer at the threshold not eligible period: 2021 input: state_code: VT adjusted_gross_income: 30_000 filing_status: SINGLE + output: + vt_low_income_cdcc_eligible: false + +- name: Vermont low-income CDCC single filer below the threshold eligible + period: 2021 + input: + state_code: VT + adjusted_gross_income: 29_999 + filing_status: SINGLE output: vt_low_income_cdcc_eligible: true @@ -25,14 +39,20 @@ output: vt_low_income_cdcc_eligible: false -- name: Vermont low-income CDCC joint filer eligible +- name: Vermont low-income CDCC joint filer at the threshold not eligible period: 2021 input: state_code: VT adjusted_gross_income: 40_000 filing_status: JOINT output: - vt_low_income_cdcc_eligible: true + vt_low_income_cdcc_eligible: false - - +- name: Vermont low-income CDCC joint filer below the threshold eligible + period: 2021 + input: + state_code: VT + adjusted_gross_income: 39_999 + filing_status: JOINT + output: + vt_low_income_cdcc_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/integration.yaml index 218cfb9da86..90853e2571d 100644 --- a/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/integration.yaml @@ -1,5 +1,5 @@ - name: integration test 1, a married couple with 2 dependents - period: 2024 + period: 2023 input: people: person1: @@ -29,7 +29,7 @@ pr_earned_income_credit: 6_072 - name: integration test 2, individual with 4 dependents - period: 2024 + period: 2023 input: people: person1: diff --git a/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/pr_earned_income_credit.yaml b/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/pr_earned_income_credit.yaml index 78abde891bf..92fbdcf688d 100644 --- a/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/pr_earned_income_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/territories/pr/tax/income/credits/earned_income/pr_earned_income_credit.yaml @@ -25,7 +25,7 @@ pr_earned_income_credit: 0 - name: Eligible person, 0 children with max credit - period: 2024 + period: 2023 input: people: person1: @@ -45,7 +45,7 @@ pr_earned_income_credit: 1_656 - name: Eligible person, 1 child and higher bracket - period: 2024 + period: 2023 input: people: person1: @@ -68,7 +68,7 @@ pr_earned_income_credit: 2_483.004 - name: Eligible person, 0 child and filing jointly - period: 2024 + period: 2023 input: people: person1: @@ -91,7 +91,7 @@ pr_earned_income_credit: 1_656 - name: Eligible person, 1 child, filing jointly, higher bracket - period: 2024 + period: 2023 input: people: person1: @@ -152,7 +152,7 @@ pr_earned_income_credit: 0 - name: Eligible HEAD_OF_HOUSEHOLD filer gets the individual phase-out threshold - period: 2024 + period: 2023 input: people: person1: @@ -176,7 +176,7 @@ pr_earned_income_credit: 1_656 - name: Eligible SURVIVING_SPOUSE filer gets the joint phase-out threshold - period: 2024 + period: 2023 input: people: person1: @@ -198,3 +198,70 @@ # (joint threshold = 19_870), so the full max credit applies. output: pr_earned_income_credit: 1_656 + +# Section 1052.01(i) indexes the credit "según ajustado por el Servicio de +# Rentas Internas Federal"; Hacienda publishes the adjusted amounts in BI RI +# 24-04 (2024) and BI RI 26-01 (2025). +- name: 2024 three or more children, single, phase-out under the 2024 threshold + period: 2024 + input: + people: + person1: + pr_gross_income_person: 25_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + eitc_child_count: 3 + pr_earned_income_credit_eligible: true + households: + household: + members: [person1] + state_code: PR + output: + # 2024 maximum $7,563 less 34.21% of ($25,000 - $24,430) = $194.997. + pr_earned_income_credit: 7_368.003 + +- name: 2025 three or more children, single, under the 2025 threshold + period: 2025 + input: + people: + person1: + pr_gross_income_person: 25_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + eitc_child_count: 3 + pr_earned_income_credit_eligible: true + households: + household: + members: [person1] + state_code: PR + output: + # $25,000 is under the $25,110 threshold, so the $7,774 maximum applies + # (the 2023 schedule would give $7,173 - 34.21% x $1,820 = $6,550.378). + pr_earned_income_credit: 7_774 + +- name: 2025 one child, joint, phase-out over the 2025 threshold + period: 2025 + input: + people: + person1: + pr_gross_income_person: 30_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + members: [person1] + filing_status: JOINT + eitc_child_count: 1 + pr_earned_income_credit_eligible: true + households: + household: + members: [person1] + state_code: PR + output: + # $4,186 less 26.92% of ($30,000 - $26,310) = $993.348. + pr_earned_income_credit: 3_192.652 diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index c919d1626d0..8fd375430f3 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -8,6 +8,10 @@ class nyc_household_credit(Variable): unit = USD definition_period = YEAR defined_for = "in_nyc" + reference = ( + "https://www.nysenate.gov/legislation/laws/TAX/1310", + "https://www.tax.ny.gov/pdf/2025/inc/it201i_2025.pdf#page=13", + ) def formula(tax_unit, period, parameters): # First get their federal AGI. @@ -25,10 +29,15 @@ def formula(tax_unit, period, parameters): filing_statuses = filing_status.possible_values - # Tax Law 1310(d) excludes an individual whom another taxpayer can - # claim as a dependent; IT-201 line 48: "If you marked the Yes box at - # item C ... you do not qualify for this credit". + # Tax Law 1310(d)(3)(B) excludes, among unmarried individuals, one whom + # another taxpayer can claim as a dependent; for a married couple, + # 1310(d)(2)(B) counts the exemptions the couple is entitled to. + # IT-201 line 48 says "If you marked the Yes box at item C ... you do + # not qualify for this credit"; the model follows the statute on a + # joint return and keeps the exclusion for other returns. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + joint = filing_status == filing_statuses.JOINT + exemptions = tax_unit("exemptions_count", period) amount = select( [ filing_status == filing_statuses.SINGLE, @@ -42,7 +51,9 @@ def formula(tax_unit, period, parameters): p.separate_per_dependent.calc(federal_agi, right=True) * tax_unit_size, ], # Joint, head of household, and surviving spouse filers have a different - # amount per person, varying with AGI. - default=p.other_per_dependent.calc(federal_agi, right=True) * tax_unit_size, + # amount per person, varying with AGI; a joint return counts its + # federal exemptions. + default=p.other_per_dependent.calc(federal_agi, right=True) + * where(joint, exemptions, tax_unit_size), ) - return where(dependent_filer, 0, amount) + return where(dependent_filer & ~joint, 0, amount) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py index 1efcc1a8ac3..0e6a54aac29 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py @@ -51,9 +51,9 @@ def formula(tax_unit, period, parameters): age_eligible = tax_unit.any(filer & (age >= p.age_eligibility)) # NMSA 7-2-5.9(A) requires expenses that "exceed twenty-eight thousand # dollars ($28,000)"; PIT-ADJ line 18 instructions allow expenses "of - # $28,000 or more". This keeps the instructions' reading, under which - # expenses of exactly $28,000 qualify, until the conflict is resolved. - expense_eligible = medical_expense >= p.min_expenses + # $28,000 or more". The model follows the statute, so expenses of + # exactly $28,000 do not qualify. + expense_eligible = medical_expense > p.min_expenses eligible = age_eligible & expense_eligible # Neither 7-2-5.9 nor the PIT-ADJ line 18 instructions halve the # exemption for married individuals filing separately. Sections that diff --git a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py index b71524d37c8..b163f59696f 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py @@ -33,19 +33,29 @@ def formula(tax_unit, period, parameters): ["tax_unit_size", "spouse_separate_tax_unit_size"], ) separate = filing_status == filing_statuses.SEPARATE + joint = filing_status == filing_statuses.JOINT + # Tax Law 606(b)(2)(B) and (3)(B) count "the number of exemptions for + # which the taxpayer (or in the case of a husband and wife, taxpayers) + # is entitled to a deduction" for federal purposes. On a joint return + # that excludes a spouse whom another taxpayer can claim + # (exemptions_count). Other statuses keep the tax unit size. + size = where(joint, tax_unit("exemptions_count", period), size) non_single_base = p.non_single.base.calc(agi, right=True) non_single_additional = p.non_single.additional.calc(agi, right=True) - total_amount_if_not_single = non_single_base + non_single_additional * ( - size - 1 + total_amount_if_not_single = non_single_base + non_single_additional * max_( + size - 1, 0 ) amount_if_not_single = np.ceil( total_amount_if_not_single / where(separate, 2, 1) ) non_single_amount = amount_if_not_single * ~single - # Tax Law 606(b)(3)(B) excludes an individual for whom a deduction - # under IRC 151(c) "is allowable to another taxpayer", and IT-201 line - # 40 says "If you marked the Yes box at item C ... you do not qualify - # for this credit". Item C is one box per return; this formula reads - # it as marked when either spouse of a joint return can be claimed. + # Tax Law 606(b)(3)(B) excludes, among unmarried individuals, one for + # whom a deduction under IRC 151(c) "is allowable to another + # taxpayer"; a married couple is a household in its own right, and its + # credit counts only the exemptions the couple is entitled to. IT-201 + # line 40 says "If you marked the Yes box at item C ... you do not + # qualify for this credit", which denies a joint return with one + # claimable spouse; the model follows the statute and keeps the + # exclusion for other returns. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) - return where(dependent_filer, 0, single_amount + non_single_amount) + return where(dependent_filer & ~joint, 0, single_amount + non_single_amount) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py index 5299936f491..f62072dfe6f 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py @@ -9,6 +9,8 @@ class vt_low_income_cdcc_eligible(Variable): reference = ( "https://tax.vermont.gov/sites/tax/files/documents/IN-112-2021.pdf#page=2", "https://law.justia.com/codes/vermont/2021/title-32/chapter-151/section-5828c/", + # 2022 Act 138 section 3 reproduces the pre-2022 text it amended. + "https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=3", ) defined_for = StateCode.VT @@ -17,5 +19,8 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) federal_agi_threshold = p.income_threshold[filing_status] federal_agi = tax_unit("adjusted_gross_income", period) - # Law says "less than" but tax form says "$x or less". We apply form rules. - return federal_agi <= federal_agi_threshold + # 32 V.S.A. 5828c (as in effect through 2021) requires federal adjusted + # gross income "less than $30,000.00 (or $40,000.00 for married, + # filing jointly)"; the 2021 Schedule IN-112 says "$30,000 (or $40,000 + # for Married Filing Jointly) or less". The model follows the statute. + return federal_agi < federal_agi_threshold From af34291657b12572dabf8fdcf3e1d1b8f874af9d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 23:25:00 -0400 Subject: [PATCH 2/9] Apply NY/NYC household definitions to every married and head status (review r1) Review r1: Tax Law 606(b)(3)(B) and 1310(d) make a husband and wife, a head of household and a surviving spouse households in their own right; only an unmarried individual is excluded for being claimable. Every non-single return now counts its federal exemptions (a separate return adds the other spouse's unit at its size) and keeps the credit; the statute's "one less than the number of exemptions" is applied literally when there are none, with the credit floored at zero. Also fixes stale NM, VT and PR comments. Co-Authored-By: Claude Opus 5.5 --- .../credits/cdcc/income_limit_in_effect.yaml | 2 +- .../earned_income/unearned_income/limit.yaml | 4 +- ...c_children_medical_exemption_invariants.py | 9 ++-- .../household/nyc_household_credit.yaml | 47 +++++++++++++++++ .../income/credits/ny_household_credit.yaml | 50 ++++++++++++++++++ .../credits/household/nyc_household_credit.py | 31 +++++------ .../credits/ny_household_credit_potential.py | 51 ++++++++++--------- .../vt/tax/income/credits/cdcc/vt_cdcc.py | 8 +-- 8 files changed, 149 insertions(+), 53 deletions(-) diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/cdcc/income_limit_in_effect.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/cdcc/income_limit_in_effect.yaml index 3e736d244b6..3e7124523e0 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/cdcc/income_limit_in_effect.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/cdcc/income_limit_in_effect.yaml @@ -1,4 +1,4 @@ -description: Vermont limits the Child and Dependent Care Credit to filers with adjusted gross income at or below the low-income threshold when this is in effect. +description: Vermont limits the Child and Dependent Care Credit to filers with adjusted gross income below the low-income threshold when this is in effect. values: # Through 2021 the Vermont credit under 32 V.S.A. § 5828c was the low-income # child and dependent care credit, limited to filers with AGI below the diff --git a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml index 6ed10b22989..c173e04814c 100644 --- a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml +++ b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml @@ -6,7 +6,9 @@ metadata: label: Puerto Rico earned income credit unearned income limit period: year reference: - # discrepancy: legal statute lists unearned income as 2_200 + # Section 1052.01(e) keeps older $2,200 wording but adds a proviso for + # taxable years after 2020 allowing other income, including exempt + # unearned income, equal to or below $10,000. - title: P.R. Laws tit. 13, § 30211 (6)(e) href: https://law.justia.com/codes/puerto-rico/title-thirteen/subtitle-17/part-ii/chapter-1007/subchapter-b/30211/ - title: Schedule CT Individual, Earned Income Credit, 2023 diff --git a/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py b/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py index 1a71feb44d5..c778364bfcb 100644 --- a/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py +++ b/policyengine_us/tests/core/test_nm_ctc_children_medical_exemption_invariants.py @@ -4,8 +4,9 @@ by IRC 152(c), which has no taxpayer identification number requirement, so `nm_ctc_qualifying_children` counts 152(c) children whatever their Social Security number status. NMSA 7-2-5.9 gives $3,000 to a filer or spouse 65 or -older with at least $28,000 of medical care expenses (the PIT-ADJ reading of -the threshold), with no halving for married filing separately. +older with more than $28,000 of medical care expenses (NMSA 7-2-5.9(A); the +PIT-ADJ instructions' "$28,000 or more" is not followed), with no halving for +married filing separately. For households drawn by Hypothesis and a seeded population, in 2023-2026: @@ -25,8 +26,8 @@ 5. Separate filing: a single adult filing separately gets half of `nm_ctc` (7-2-18.34(G)) and the whole `nm_medical_expense_exemption`. 6. Reference exemption: `nm_medical_expense_exemption` is $3,000 exactly when - a filer (not a dependent) is 65 or older and medical expenses are at least - $28,000, and 0 otherwise. + a filer (not a dependent) is 65 or older and medical expenses are more than + $28,000 (NMSA 7-2-5.9(A)), and 0 otherwise. """ import numpy as np diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index 039a0ed9eda..351bbe2c710 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -352,3 +352,50 @@ output: # $30 x 2. nyc_household_credit: 60 + +# Tax Law 1310(d): only an unmarried individual is excluded for being +# claimable; other households count the exemptions they are entitled to. +- name: 2025 separate filer who can be claimed has no exemption for the NYC credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + filing_status: SEPARATE + households: + household: + members: [person_a] + state_code: NY + in_nyc: true + output: + # The separate amount per exemption times zero exemptions. + nyc_household_credit: 0 + +- name: 2025 joint return where both spouses can be claimed has no exemptions for the NYC credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NY + in_nyc: true + output: + nyc_household_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml index 4f0b9b35809..e21fae0a614 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml @@ -204,3 +204,53 @@ output: # $60 + $15 x (2 - 1). ny_household_credit_potential: 75 + +# Tax Law 606(b)(3)(B): a husband and wife, a head of household and a +# surviving spouse are households in their own right; only an unmarried +# individual is excluded for being claimable. IT-201 line 40 denies every +# return with item C marked; the model follows the statute. +- name: 2025 separate filer who can be claimed counts the couple's exemptions + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + filing_status: SEPARATE + households: + household: + members: [person_a] + state_code: NY + output: + # Combined AGI $10,000 (the spouse's separate return is assumed alike); + # exemptions: 0 for the claimable filer plus 1 for the spouse's unit. + # $60 base + $15 x (1 - 1) = $60, halved for a separate return. + ny_household_credit_potential: 30 + +- name: 2025 joint return where both spouses can be claimed reduces the base by one additional amount + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + person_b: + age: 22 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NY + output: + # No exemptions: $60 base + $15 x (0 - 1) = $45. + ny_household_credit_potential: 45 diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index 8fd375430f3..f019722035b 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -24,19 +24,16 @@ def formula(tax_unit, period, parameters): # Then get the household credit part of the parameter tree. p = parameters(period).gov.local.ny.nyc.tax.income.credits.household - # Then get their number of people. - tax_unit_size = tax_unit("tax_unit_size", period) - filing_statuses = filing_status.possible_values - # Tax Law 1310(d)(3)(B) excludes, among unmarried individuals, one whom - # another taxpayer can claim as a dependent; for a married couple, - # 1310(d)(2)(B) counts the exemptions the couple is entitled to. - # IT-201 line 48 says "If you marked the Yes box at item C ... you do - # not qualify for this credit"; the model follows the statute on a - # joint return and keeps the exclusion for other returns. + # Tax Law 1310(d) counts the exemptions each household is entitled to + # and excludes, among unmarried individuals, one whom another taxpayer + # can claim; a husband and wife, a head of household and a surviving + # spouse are households in their own right. IT-201 line 48 says "If + # you marked the Yes box at item C ... you do not qualify for this + # credit" for every return; the model follows the statute. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) - joint = filing_status == filing_statuses.JOINT + single = filing_status == filing_statuses.SINGLE exemptions = tax_unit("exemptions_count", period) amount = select( [ @@ -46,14 +43,12 @@ def formula(tax_unit, period, parameters): [ # Single filers get a flat amount. p.flat_amount.calc(federal_agi, right=True), - # Separate filers get an amount for each person in the tax - # unit, varying with AGI. - p.separate_per_dependent.calc(federal_agi, right=True) * tax_unit_size, + # Separate filers get an amount for each exemption, varying + # with AGI. + p.separate_per_dependent.calc(federal_agi, right=True) * exemptions, ], # Joint, head of household, and surviving spouse filers have a different - # amount per person, varying with AGI; a joint return counts its - # federal exemptions. - default=p.other_per_dependent.calc(federal_agi, right=True) - * where(joint, exemptions, tax_unit_size), + # amount per exemption, varying with AGI. + default=p.other_per_dependent.calc(federal_agi, right=True) * exemptions, ) - return where(dependent_filer & ~joint, 0, amount) + return where(dependent_filer & single, 0, amount) diff --git a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py index b163f59696f..a5045e0f2df 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py @@ -25,37 +25,38 @@ def formula(tax_unit, period, parameters): # Use `right=True` to reflect "over...but not over". amount_if_single = p.single.calc(agi, right=True) single_amount = single * amount_if_single - # All others are based on AGI & size. - # In the case of married filing separate, based on the combined AGI and size. - size = add( - tax_unit, - period, - ["tax_unit_size", "spouse_separate_tax_unit_size"], - ) + # All others are based on AGI and the number of exemptions. Tax Law + # 606(b)(2)(B) and (3)(B) count "the number of exemptions for which + # the taxpayer (or in the case of a husband and wife, taxpayers) is + # entitled to a deduction" for federal purposes, which leaves out a + # filer whom another taxpayer can claim (exemptions_count). A married + # couple filing separately uses the couple's combined AGI and + # exemptions; the model has no record of the other spouse's + # claimability, so the spouse's separate unit counts at its size. separate = filing_status == filing_statuses.SEPARATE - joint = filing_status == filing_statuses.JOINT - # Tax Law 606(b)(2)(B) and (3)(B) count "the number of exemptions for - # which the taxpayer (or in the case of a husband and wife, taxpayers) - # is entitled to a deduction" for federal purposes. On a joint return - # that excludes a spouse whom another taxpayer can claim - # (exemptions_count). Other statuses keep the tax unit size. - size = where(joint, tax_unit("exemptions_count", period), size) + size = tax_unit("exemptions_count", period) + tax_unit( + "spouse_separate_tax_unit_size", period + ) non_single_base = p.non_single.base.calc(agi, right=True) non_single_additional = p.non_single.additional.calc(agi, right=True) - total_amount_if_not_single = non_single_base + non_single_additional * max_( - size - 1, 0 + # The statute adds the additional amount for each exemption beyond one. + # With no exemptions (both spouses of a refund-only joint return can + # be claimed) that is one less than none, so the base is reduced; + # the credit is not negative. + total_amount_if_not_single = max_( + non_single_base + non_single_additional * (size - 1), 0 ) amount_if_not_single = np.ceil( total_amount_if_not_single / where(separate, 2, 1) ) non_single_amount = amount_if_not_single * ~single - # Tax Law 606(b)(3)(B) excludes, among unmarried individuals, one for - # whom a deduction under IRC 151(c) "is allowable to another - # taxpayer"; a married couple is a household in its own right, and its - # credit counts only the exemptions the couple is entitled to. IT-201 - # line 40 says "If you marked the Yes box at item C ... you do not - # qualify for this credit", which denies a joint return with one - # claimable spouse; the model follows the statute and keeps the - # exclusion for other returns. + # Tax Law 606(b)(3)(B) defines a household as a husband and wife, a + # head of household, a surviving spouse, or an unmarried individual + # for whom a deduction under IRC 151(c) is not "allowable to another + # taxpayer". Only a single filer is excluded for being claimable; the + # other households count only the exemptions they are entitled to. + # IT-201 line 40 says "If you marked the Yes box at item C ... you do + # not qualify for this credit" for every return; the model follows the + # statute. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) - return where(dependent_filer & ~joint, 0, single_amount + non_single_amount) + return where(dependent_filer & single, 0, single_amount + non_single_amount) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py index 6b613164326..a3da549d124 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py @@ -27,10 +27,10 @@ def formula(tax_unit, period, parameters): federal_cdcc = tax_unit("pre_obbba_capped_cdcc", period) else: federal_cdcc = tax_unit("capped_cdcc", period) - # 2022 Act 138 raised the 5828c match to 72 percent but retained the - # low-income adjusted gross income cap and the certified-facility - # requirement for 2022; 2023 Act 72 removed them effective 2023. We - # do not track facility certification or in-state care at the moment. + # 2022 Act 138 raised the 5828c match to 72 percent from 2022, and the + # 2022 Schedule IN-112 applies it with no income test (see + # income_limit_in_effect). We do not track facility certification or + # in-state care at the moment. if p.income_limit_in_effect: income_eligible = tax_unit("vt_low_income_cdcc_eligible", period) return income_eligible * federal_cdcc * p.rate From ef2a69ff36dc21dce6f93f589363e8b21881f3eb Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 23:32:20 -0400 Subject: [PATCH 3/9] Use the couple's AGI and exemptions for NYC separate filers (review r2) Review r2: Tax Law 1310(d) aggregates a husband and wife's federal AGIs "irrespective of whether joint or separate city income tax returns are filed" and gives each spouse half the household credit; IT-201 Table 6 keys on AGI and dependents "from both returns". The NYC credit for a separate return now uses own plus spouse AGI and exemptions (the spouse's return assumed alike, as for New York State); the older separate cases are restated on household AGI. Adds head-of-household cases for NY and NYC and widens the changelog. Co-Authored-By: Claude Opus 5.5 --- .../statute-first-state-income-tax.fixed.md | 2 +- .../household/nyc_household_credit.yaml | 89 +++++++++++++++---- .../income/credits/ny_household_credit.yaml | 25 ++++++ .../credits/household/nyc_household_credit.py | 21 ++++- 4 files changed, 117 insertions(+), 20 deletions(-) diff --git a/changelog.d/statute-first-state-income-tax.fixed.md b/changelog.d/statute-first-state-income-tax.fixed.md index c88c93ad797..854195d277d 100644 --- a/changelog.d/statute-first-state-income-tax.fixed.md +++ b/changelog.d/statute-first-state-income-tax.fixed.md @@ -1 +1 @@ -Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; New York and New York City household credits count a joint return's federal exemptions rather than denying the credit when one spouse can be claimed; and Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules. +Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; the New York and New York City household credits count the federal exemptions of married, head-of-household and surviving-spouse filers instead of denying the credit when a filer can be claimed (Tax Law 606(b), 1310(d)), and the New York City credit for a married couple filing separately uses the couple's AGI and exemptions; and Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules. diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index 351bbe2c710..ac66461bdcc 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -186,52 +186,81 @@ output: nyc_household_credit: 0 -- name: Separate filers making $15k or less get $15 per tax unit dependent (size=2). +# Tax Law 1310(d) and IT-201 Table 6: a married couple filing separately is +# one household, keyed on federal AGI and dependents "from both returns" plus +# one for each spouse, and each spouse gets half the household amount. +- name: Separate filers with household AGI of $15k or less get $15 per exemption of the couple. period: 2022 absolute_error_margin: 0 input: state_code: NY in_nyc: true - adjusted_gross_income: 15_000 + adjusted_gross_income: 7_500 + spouse_separate_adjusted_gross_income: 7_500 filing_status: SEPARATE tax_unit_size: 2 + spouse_separate_tax_unit_size: 2 output: - nyc_household_credit: 30 + # Household AGI $15,000; 2 + 2 exemptions x $15. + nyc_household_credit: 60 -- name: Separate filers making $18k get $8 per tax unit dependent (size=4). +- name: Separate filers with household AGI of $18k get $8 per exemption of the couple. period: 2022 - absolute_error_margin: 3 # Table has some rounding. + absolute_error_margin: 0 input: state_code: NY in_nyc: true - adjusted_gross_income: 18_000 + adjusted_gross_income: 9_000 + spouse_separate_adjusted_gross_income: 9_000 filing_status: SEPARATE tax_unit_size: 4 + spouse_separate_tax_unit_size: 4 output: - nyc_household_credit: 32 + # Household AGI $18,000; 4 + 4 exemptions x $8. + nyc_household_credit: 64 -- name: Separate filers making $21k get $5 per tax unit dependent. +- name: Separate filers with household AGI of $21k get $5 per exemption of the couple. period: 2022 - absolute_error_margin: 3 # Table has some rounding. + absolute_error_margin: 0 input: state_code: NY in_nyc: true - adjusted_gross_income: 21_000 + adjusted_gross_income: 10_500 + spouse_separate_adjusted_gross_income: 10_500 filing_status: SEPARATE tax_unit_size: 6 + spouse_separate_tax_unit_size: 6 output: - nyc_household_credit: 30 + # Household AGI $21,000; 6 + 6 exemptions x $5. + nyc_household_credit: 60 -- name: Separate filers making over $22.5k gets no credit. +- name: Separate filers with household AGI over $22.5k get no credit. period: 2022 absolute_error_margin: 0 input: state_code: NY in_nyc: true - adjusted_gross_income: 22_501 + adjusted_gross_income: 11_251 + spouse_separate_adjusted_gross_income: 11_251 filing_status: SEPARATE tax_unit_size: 4 + spouse_separate_tax_unit_size: 4 + output: + nyc_household_credit: 0 + +- name: A separate filer's own AGI alone does not set the bracket. + period: 2022 + absolute_error_margin: 0 + input: + state_code: NY + in_nyc: true + adjusted_gross_income: 15_000 + spouse_separate_adjusted_gross_income: 15_000 + filing_status: SEPARATE + tax_unit_size: 2 + spouse_separate_tax_unit_size: 2 output: + # Household AGI $30,000 is over $22,500. nyc_household_credit: 0 @@ -355,7 +384,7 @@ # Tax Law 1310(d): only an unmarried individual is excluded for being # claimable; other households count the exemptions they are entitled to. -- name: 2025 separate filer who can be claimed has no exemption for the NYC credit +- name: 2025 separate filer who can be claimed gets half the household credit for the spouse's exemption period: 2025 input: people: @@ -373,8 +402,10 @@ state_code: NY in_nyc: true output: - # The separate amount per exemption times zero exemptions. - nyc_household_credit: 0 + # Household AGI $10,000 (the spouse's return is assumed alike); the + # couple's exemptions: 0 for the claimable filer plus 1 for the spouse. + # $15 x 1. + nyc_household_credit: 15 - name: 2025 joint return where both spouses can be claimed has no exemptions for the NYC credit period: 2025 @@ -399,3 +430,29 @@ in_nyc: true output: nyc_household_credit: 0 + +- name: 2025 head of household who can be claimed counts the dependent's exemption + period: 2025 + input: + people: + parent: + age: 22 + employment_income: 10_000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: HEAD_OF_HOUSEHOLD + # The would-be claimant need not file, so the parent may still claim + # the child (Publication 501, Dependent Taxpayer Test). + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + households: + household: + members: [parent, child] + state_code: NY + in_nyc: true + output: + # One exemption (the child); $30 x 1. + nyc_household_credit: 30 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml index e21fae0a614..f73505ce3ad 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml @@ -254,3 +254,28 @@ output: # No exemptions: $60 base + $15 x (0 - 1) = $45. ny_household_credit_potential: 45 + +- name: 2025 head of household who can be claimed counts the dependent's exemption + period: 2025 + input: + people: + parent: + age: 22 + employment_income: 10_000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: HEAD_OF_HOUSEHOLD + # The would-be claimant need not file, so the parent may still claim + # the child (Publication 501, Dependent Taxpayer Test). + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + households: + household: + members: [parent, child] + state_code: NY + output: + # One exemption: $60 base + $15 x (1 - 1). + ny_household_credit_potential: 60 diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index f019722035b..7713cb7bca4 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -35,6 +35,20 @@ def formula(tax_unit, period, parameters): dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) single = filing_status == filing_statuses.SINGLE exemptions = tax_unit("exemptions_count", period) + # A husband and wife filing separately are one household: 1310(d) + # aggregates "their federal adjusted gross incomes ... irrespective + # of whether joint or separate city income tax returns are filed", + # and each spouse gets "one-half the credit otherwise allowed his or + # her household" (IT-201 Table 6: AGI and dependents "from both + # returns", plus one for each spouse). The model has no record of the + # other spouse's return, so it is assumed alike, as for New York + # State: its AGI and its unit's size are added. + separate_agi = federal_agi + tax_unit( + "spouse_separate_adjusted_gross_income", period + ) + separate_exemptions = exemptions + tax_unit( + "spouse_separate_tax_unit_size", period + ) amount = select( [ filing_status == filing_statuses.SINGLE, @@ -43,9 +57,10 @@ def formula(tax_unit, period, parameters): [ # Single filers get a flat amount. p.flat_amount.calc(federal_agi, right=True), - # Separate filers get an amount for each exemption, varying - # with AGI. - p.separate_per_dependent.calc(federal_agi, right=True) * exemptions, + # Separate filers get half the household amount for each of the + # couple's exemptions, varying with the couple's AGI. + p.separate_per_dependent.calc(separate_agi, right=True) + * separate_exemptions, ], # Joint, head of household, and surviving spouse filers have a different # amount per exemption, varying with AGI. From 1bc6af0c093368a54a6019afbfae99e5568038e6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 23:41:23 -0400 Subject: [PATCH 4/9] Halve the NYC household credit for separate filers after the household amount (review r3) Review r3: Tax Law 1310(d) gives a separate filer half the household's credit. The model multiplied IT-201 Table 6's rounded per-exemption half amount by the couple's exemptions ($8 x 8 = $64 at $18,000); it now takes the married amount per exemption times the couple's exemptions, halved ($15 x 8 / 2 = $60). Table 6's separate amounts are kept as a parameter for reference but no longer used. Co-Authored-By: Claude Opus 5.5 --- .../household/separate_per_dependent.yaml | 6 +++++- .../credits/household/nyc_household_credit.yaml | 17 ++++++++++------- .../credits/household/nyc_household_credit.py | 12 ++++++++---- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml index 93b9f764442..5f86166d977 100644 --- a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml +++ b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml @@ -1,4 +1,8 @@ -description: NYC provides a household credit of this amount per dependent for separate filers. +description: NYC provides a household credit of this amount per dependent for separate filers under IT-201 Table 6. +# Not used by nyc_household_credit: Tax Law 1310(d) gives a separate filer +# "one-half the credit otherwise allowed his or her household", which the +# model computes from the married amounts (other_per_dependent). Table 6 +# rounds the half amount per exemption, which can differ by a dollar or more. metadata: type: single_amount threshold_unit: currency-USD diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index ac66461bdcc..11b8c7c5e50 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -189,7 +189,7 @@ # Tax Law 1310(d) and IT-201 Table 6: a married couple filing separately is # one household, keyed on federal AGI and dependents "from both returns" plus # one for each spouse, and each spouse gets half the household amount. -- name: Separate filers with household AGI of $15k or less get $15 per exemption of the couple. +- name: Separate filers with household AGI of $15k or less get half of $30 per exemption of the couple. period: 2022 absolute_error_margin: 0 input: @@ -201,10 +201,10 @@ tax_unit_size: 2 spouse_separate_tax_unit_size: 2 output: - # Household AGI $15,000; 2 + 2 exemptions x $15. + # Household AGI $15,000: $30 x (2 + 2) = $120, halved. nyc_household_credit: 60 -- name: Separate filers with household AGI of $18k get $8 per exemption of the couple. +- name: Separate filers with household AGI of $18k get half of $15 per exemption of the couple. period: 2022 absolute_error_margin: 0 input: @@ -216,10 +216,13 @@ tax_unit_size: 4 spouse_separate_tax_unit_size: 4 output: - # Household AGI $18,000; 4 + 4 exemptions x $8. - nyc_household_credit: 64 + # Household AGI $18,000: the married amount is $15 per exemption, so the + # household credit is $15 x 8 = $120 and each spouse gets half, $60. + # IT-201 Table 6 rounds the half amount to $8 per exemption ($53 + $8 = + # $61 with its over-seven note); the model follows Tax Law 1310(d). + nyc_household_credit: 60 -- name: Separate filers with household AGI of $21k get $5 per exemption of the couple. +- name: Separate filers with household AGI of $21k get half of $10 per exemption of the couple. period: 2022 absolute_error_margin: 0 input: @@ -231,7 +234,7 @@ tax_unit_size: 6 spouse_separate_tax_unit_size: 6 output: - # Household AGI $21,000; 6 + 6 exemptions x $5. + # Household AGI $21,000: $10 x (6 + 6) = $120, halved. nyc_household_credit: 60 - name: Separate filers with household AGI over $22.5k get no credit. diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index 7713cb7bca4..0ae037ce399 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -57,10 +57,14 @@ def formula(tax_unit, period, parameters): [ # Single filers get a flat amount. p.flat_amount.calc(federal_agi, right=True), - # Separate filers get half the household amount for each of the - # couple's exemptions, varying with the couple's AGI. - p.separate_per_dependent.calc(separate_agi, right=True) - * separate_exemptions, + # Separate filers get "one-half the credit otherwise allowed his + # or her household": the married amount per exemption at the + # couple's AGI, times the couple's exemptions, halved. IT-201 + # Table 6 instead rounds a per-exemption half amount, which can + # differ by a dollar or more; the model follows the statute. + p.other_per_dependent.calc(separate_agi, right=True) + * separate_exemptions + / 2, ], # Joint, head of household, and surviving spouse filers have a different # amount per exemption, varying with AGI. From 396044ce482e4b4ab868ac08fbcdc49e1c1cdeea Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 23:44:16 -0400 Subject: [PATCH 5/9] Correct an NYC test comment and the Table 6 parameter description (review r4) Co-Authored-By: Claude Opus 5.5 --- .../tax/income/credits/household/separate_per_dependent.yaml | 2 +- .../nyc/tax/income/credits/household/nyc_household_credit.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml index 5f86166d977..00bfeece18e 100644 --- a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml +++ b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml @@ -1,4 +1,4 @@ -description: NYC provides a household credit of this amount per dependent for separate filers under IT-201 Table 6. +description: IT-201 Table 6 lists this NYC household credit amount per dependent for separate filers. # Not used by nyc_household_credit: Tax Law 1310(d) gives a separate filer # "one-half the credit otherwise allowed his or her household", which the # model computes from the married amounts (other_per_dependent). Table 6 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index 11b8c7c5e50..35d0c19d297 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -407,7 +407,7 @@ output: # Household AGI $10,000 (the spouse's return is assumed alike); the # couple's exemptions: 0 for the claimable filer plus 1 for the spouse. - # $15 x 1. + # $30 x 1, halved. nyc_household_credit: 15 - name: 2025 joint return where both spouses can be claimed has no exemptions for the NYC credit From d4e2e85906f6bc3c9c06547b274187b778868e4e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 21:47:29 +0000 Subject: [PATCH 6/9] Record the NM, VT and NY source conflicts and fix the ratchet after merging main Merged with main, the #10287 ratchet failed: this PR removed the four Puerto Rico "discrepancy" comments (never real conflicts: the bulletins index the statute's base amounts), so their KNOWN_UNMARKED lines go. The NM, VT and NY/NYC changes are real statute-versus-form conflicts, so their deciding-line comments now read as conflicts to the ratchet (one line each) and carry KNOWN_UNMARKED counts of 1 until the register entries and markers land. Vermont's line stays at 1 for that reason. In candidates.yaml the three entries now say follows: statute, list #10300, and replace the notes that said a model change was still needed (VT is limited to 2021, the only year the strict test governed, and cites Act 138). The us-pr:eitc-unearned-income-limit entry is deleted: Section 1052.01(e)'s proviso sets the $10,000 limit from 2021, so there is no conflict. Co-Authored-By: Claude Opus 5.5 --- .../code_health/test_source_conflicts.py | 7 +- .../credits/household/nyc_household_credit.py | 7 +- .../nm_medical_expense_exemption.py | 6 +- .../credits/ny_household_credit_potential.py | 6 +- .../cdcc/vt_low_income_cdcc_eligible.py | 5 +- source_conflicts/candidates.yaml | 77 ++++++++----------- 6 files changed, 47 insertions(+), 61 deletions(-) diff --git a/policyengine_us/tests/code_health/test_source_conflicts.py b/policyengine_us/tests/code_health/test_source_conflicts.py index 4a9b7f803db..301329747d1 100644 --- a/policyengine_us/tests/code_health/test_source_conflicts.py +++ b/policyengine_us/tests/code_health/test_source_conflicts.py @@ -141,12 +141,8 @@ "policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/disqualified_loss_sources.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/eligibility/min.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/eligibility/separate_filer.yaml": 1, - "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/max_amount.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_in_rate.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/rate.yaml": 1, - "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/joint.yaml": 1, - "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/phase_out/threshold/single.yaml": 1, - "policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/gradual_adjustment/exemption_rate.yaml": 1, "policyengine_us/parameters/gov/territories/pr/tax/income/gradual_adjustment/threshold.yaml": 1, "policyengine_us/parameters/gov/usda/snap/eligibility/waiting_period/subject_immigration_statuses.yaml": 1, @@ -158,6 +154,7 @@ "policyengine_us/variables/gov/irs/tax_unit_is_required_to_file.py": 1, "policyengine_us/variables/gov/local/ca/cc/ga/ca_cc_general_assistance_maximum_grant.py": 1, "policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible.py": 1, + "policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py": 1, "policyengine_us/variables/gov/states/ar/ade/oec/sra/rates/ar_sra_daily_copay.py": 1, "policyengine_us/variables/gov/states/ca/hbex/premium_subsidy/ca_premium_subsidy_applicable_percentage.py": 1, "policyengine_us/variables/gov/states/ct/tax/income/rebate/ct_child_tax_rebate.py": 1, @@ -171,6 +168,8 @@ "policyengine_us/variables/gov/states/mn/dcyf/mfip/income/earned/mn_mfip_dependent_care_deduction.py": 1, "policyengine_us/variables/gov/states/mn/tax/income/deductions/mn_itemized_deductions.py": 1, "policyengine_us/variables/gov/states/mo/dese/ccs/eligibility/mo_ccs_income_eligible.py": 1, + "policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py": 1, + "policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py": 1, "policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py": 1, "policyengine_us/variables/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_categorical_eligible.py": 1, "policyengine_us/variables/gov/territories/pr/tax/income/pr_agi.py": 1, diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index 40444ea1025..c45988694f4 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -29,9 +29,10 @@ def formula(tax_unit, period, parameters): # Tax Law 1310(d) counts the exemptions each household is entitled to # and excludes, among unmarried individuals, one whom another taxpayer # can claim; a husband and wife, a head of household and a surviving - # spouse are households in their own right. IT-201 line 48 says "If - # you marked the Yes box at item C ... you do not qualify for this - # credit" for every return; the model follows the statute. + # spouse are households in their own right. At line 48 the IT-201 + # instructions say "If you marked the Yes box at item C ... you do not + # qualify for this credit" for every return; the model follows the + # statute. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) single = filing_status == filing_statuses.SINGLE exemptions = tax_unit("exemptions_count", period) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py index 0e6a54aac29..7b7d8c53b41 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py @@ -50,9 +50,9 @@ def formula(tax_unit, period, parameters): filer = person("is_tax_unit_head_or_spouse", period) age_eligible = tax_unit.any(filer & (age >= p.age_eligibility)) # NMSA 7-2-5.9(A) requires expenses that "exceed twenty-eight thousand - # dollars ($28,000)"; PIT-ADJ line 18 instructions allow expenses "of - # $28,000 or more". The model follows the statute, so expenses of - # exactly $28,000 do not qualify. + # dollars ($28,000)", but the PIT-ADJ line 18 instructions say expenses + # "of $28,000 or more" qualify. The model follows the statute, so + # expenses of exactly $28,000 do not qualify. expense_eligible = medical_expense > p.min_expenses eligible = age_eligible & expense_eligible # Neither 7-2-5.9 nor the PIT-ADJ line 18 instructions halve the diff --git a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py index 808877a8ab0..c71b11d6a75 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py @@ -55,8 +55,8 @@ def formula(tax_unit, period, parameters): # for whom a deduction under IRC 151(c) is not "allowable to another # taxpayer". Only a single filer is excluded for being claimable; the # other households count only the exemptions they are entitled to. - # IT-201 line 40 says "If you marked the Yes box at item C ... you do - # not qualify for this credit" for every return; the model follows the - # statute. + # The IT-201 instructions say at line 40, "If you marked the Yes box at + # item C ... you do not qualify for this credit", for every return; the + # model follows the statute. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) return where(dependent_filer & single, 0, single_amount + non_single_amount) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py index f62072dfe6f..574eab62fa0 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py @@ -21,6 +21,7 @@ def formula(tax_unit, period, parameters): federal_agi = tax_unit("adjusted_gross_income", period) # 32 V.S.A. 5828c (as in effect through 2021) requires federal adjusted # gross income "less than $30,000.00 (or $40,000.00 for married, - # filing jointly)"; the 2021 Schedule IN-112 says "$30,000 (or $40,000 - # for Married Filing Jointly) or less". The model follows the statute. + # filing jointly)", but the 2021 Schedule IN-112 form says "$30,000 (or + # $40,000 for Married Filing Jointly) or less". The model follows the + # statute. return federal_agi < federal_agi_threshold diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 6b1dbcbedc4..151ef8fc013 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -6440,8 +6440,8 @@ candidates: question: Must medical expenses exceed $28,000, or does exactly $28,000 qualify for the exemption? sources: - kind: statute - citation: NMSA 7-2-5.9(A) - url: null + citation: NMSA 7-2-5.9(A) (TRD Income Tax Act compilation, rev. July 2023, PDF p. 41) + url: https://klvg4oyd4j.execute-api.us-west-2.amazonaws.com/prod/PublicFiles/34821a9573ca43e7b06dfad20f5183fd/856ebf4b-3814-49dd-8631-ebe579d6a42b/Personal%20Income%20Tax.pdf#page=41 - kind: instructions citation: 2025 PIT-ADJ line 18 instructions url: https://realfile.tax.newmexico.gov/2025pit-adj-ins.pdf#page=5 @@ -6453,16 +6453,18 @@ candidates: other: Expenses of $28,000 or more qualify. example: At exactly $28,000 of expenses, the statute gives no exemption and the instructions give a $3,000 exemption. pe_us: - follows: conflicting-source + follows: statute code: - policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py prs: - https://github.com/PolicyEngine/policyengine-us/pull/10047 - https://github.com/PolicyEngine/policyengine-us/pull/10088 + - https://github.com/PolicyEngine/policyengine-us/pull/10300 state: ruled - note: 'PE-US follows the instructions: expenses of exactly $28,000 qualify, and the formula''s comment records - the statute''s "exceed" wording (nm_medical_expense_exemption.py). An October 2026 ruling accepted the instructions; - the later statute-first rule requires a strict greater-than threshold.' + note: 'An October 2026 ruling accepted the instructions; under the later statute-first rule PE-US follows the statute + (#10300): expenses must exceed $28,000, so exactly $28,000 gives no exemption (nm_medical_expense_exemption.py). + The companion 65+ medical care credit (7-2-18.13) keeps its own "$28,000 or more" test. TRD processes returns + under the instructions, so results differ from the agency''s at exactly $28,000.' - id: us-nm:mixed-spouse-dependent-credit-tests program: income-tax years: @@ -6693,22 +6695,25 @@ candidates: citation: IT-201 instructions, line 40 (household credit) and item C url: https://www.tax.ny.gov/pdf/2025/inc/it201i_2025.pdf#page=12 readings: - statute: An exemption-count or whole-household reading preserves some or all of the credit. + statute: Only an unmarried individual whom another taxpayer can claim is excluded from the household definition; + a husband and wife, a head of household and a surviving spouse count the exemptions they are entitled to, and + a spouse filing separately gets one-half the household's credit on the couple's combined AGI. other: The instructions deny the credit when either spouse can be claimed. - example: The test household receives $0 under the instructions, $60 state/$30 NYC by exemption count, or $75 state/$60 - NYC by whole household. + example: A joint return with one claimable spouse, AGI $10,000 and no dependents receives $0 under the instructions + and $60 state / $30 NYC by exemption count. pe_us: - follows: conflicting-source + follows: statute code: - policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py - policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py prs: - https://github.com/PolicyEngine/policyengine-us/pull/10090 + - https://github.com/PolicyEngine/policyengine-us/pull/10300 state: ruled - note: 'PE-US follows the instructions: the New York and New York City household credits are zero when either spouse - can be claimed, reading item C as marked for the whole return (ny_household_credit_potential.py, nyc_household_credit.py). - An October 2026 ruling accepted the instructions; the statute-first rule requires a model change after resolving - the statutory allocation.' + note: 'An October 2026 ruling accepted the instructions; under the later statute-first rule PE-US follows Tax Law + 606(b)(3)(B) and 1310(d)(3)(B) (#10300): only claimable single filers are zeroed, other households count their + federal exemptions (exemptions_count), and NYC separate filers get half the married credit on the couple''s AGI + and exemptions. DTF processes returns under item C, so results differ from the agency''s for these households.' - id: us-ny:real-property-credit-claimable-spouse program: income-tax years: @@ -7990,31 +7995,6 @@ candidates: prs: [] state: unreviewed note: Verify whether statutory silence permits the filing-status restriction; no individual policy ruling is supplied. -- id: us-pr:eitc-unearned-income-limit - program: income-tax - years: - from: 2023 - type: tax - question: Is Puerto Rico's EITC unearned-income limit the statute's $2,200 or Schedule CT's $10,000? - sources: - - kind: statute - citation: P.R. Laws tit. 13, § 30211(6)(e) - url: https://law.justia.com/codes/puerto-rico/title-thirteen/subtitle-17/part-ii/chapter-1007/subchapter-b/30211/ - - kind: instructions - citation: Schedule CT Individual, Earned Income Credit, 2023, page 4 - url: https://hacienda.pr.gov/sites/default/files/schedule_ct_rev._jul_5_23_informative_-_instructions.pdf#page=4 - readings: - statute: Filers with unearned income at or above $2,200 lose the credit. - other: The limit is $10,000. - example: A filer with $5,000 unearned income passes the form's limit but fails the supplied statutory limit. - pe_us: - follows: conflicting-source - code: - - policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml - prs: [] - state: unreviewed - note: The statute-first rule requires changing the $10,000 form-based limit to the statutory limit; confirm applicable - years and the boundary test. - id: us-ri:liheap-income-floor-vs-smi-limit program: liheap years: @@ -9742,13 +9722,15 @@ candidates: program: income-tax years: from: 2021 + to: 2021 type: tax question: Does AGI exactly equal to the low-income child-care-credit threshold qualify under the form despite the statute requiring income below it? sources: - kind: statute - citation: 32 V.S.A. § 5828c - url: https://law.justia.com/codes/vermont/2021/title-32/chapter-151/section-5828c/ + citation: 32 V.S.A. § 5828c as in effect through 2021 (text struck by 2022 Act 138, Sec. 3; Sec. 13(b) makes the + change retroactive to January 1, 2022) + url: https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=3 - kind: form citation: Vermont Schedule IN-112, 2021, page 2 url: https://tax.vermont.gov/sites/tax/files/documents/IN-112-2021.pdf#page=2 @@ -9756,13 +9738,16 @@ candidates: statute: AGI must be strictly below the threshold. other: AGI equal to the threshold qualifies. pe_us: - follows: conflicting-source + follows: statute code: - policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py - prs: [] - state: unreviewed - note: The statute-first rule requires a strict below-threshold test; verify form years before changing the inclusive - comparison. + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10300 + state: ruled + note: Under the statute-first rule PE-US applies the strict below-threshold test (#10300). Act 138 struck the income + test from 2022, so 2021 is the only affected year in the model; a 2021 filer at exactly the threshold takes the + 24% nonrefundable credit instead. Vermont processed 2021 returns under the form, so results differ from the + agency's at exactly $30,000 or $40,000. - id: us-vt:medical-deduction-without-federal-itemizing program: income-tax years: From 9eb75a3f9e929bb3028c33317a1ec86f3fbcba2e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 21:54:19 +0000 Subject: [PATCH 7/9] NYC household credit: cover zero and negative AGI; clarify VT and NYC notes; add edge cases - The three NYC household credit scales started at a threshold of -1, so with right=True any AGI at or below -1 got $0, though the 1310(d) tables' first rows are "not over" amounts with no lower bound. Start them at -.inf, as NY State's household credit scales do. - other_per_dependent is now also the separate filers' amount before halving; fix its description and label (and the "suriving" typo). - vt_cdcc: cite Act 138 Sec. 3 and Sec. 13(b) for 2022's lack of an income test, rather than the 2022 form. - Tests: VT 2021 single at $30,000 and joint at $40,000 take the 24% nonrefundable credit; NY ($60) and NYC ($30) claimable surviving spouse with one exemption; an NYC separate return pinned to exact halving ($37.50); NYC single and joint returns at negative AGI ($15, $60). Co-Authored-By: Claude Opus 5.5 --- .../income/credits/household/flat_amount.yaml | 2 +- .../household/other_per_dependent.yaml | 6 +- .../household/separate_per_dependent.yaml | 2 +- .../household/nyc_household_credit.yaml | 67 +++++++++++++++++++ .../income/credits/ny_household_credit.yaml | 24 +++++++ .../credits/cdcc/vt_nonrefundable_cdcc.yaml | 26 +++++++ .../vt/tax/income/credits/cdcc/vt_cdcc.py | 5 +- 7 files changed, 125 insertions(+), 7 deletions(-) diff --git a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/flat_amount.yaml b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/flat_amount.yaml index 8ff0f54f145..b66bbc6aa4b 100644 --- a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/flat_amount.yaml +++ b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/flat_amount.yaml @@ -15,7 +15,7 @@ metadata: href: https://www.tax.ny.gov/pdf/current_forms/it/it201i.pdf#page=20 brackets: - threshold: - 2022-01-01: -1 + 2022-01-01: -.inf amount: 2022-01-01: 15 - threshold: diff --git a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/other_per_dependent.yaml b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/other_per_dependent.yaml index ede9c2334d5..3a7af369bbf 100644 --- a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/other_per_dependent.yaml +++ b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/other_per_dependent.yaml @@ -1,9 +1,9 @@ -description: NYC provides a household credit of this amount per dependent for joint, suriving spouse, and head of household filers. +description: NYC provides a household credit of this amount per exemption for joint, surviving spouse, head of household and, before halving, separate filers. metadata: type: single_amount threshold_unit: currency-USD amount_unit: currency-USD - label: NYC Household Credit for joint, suriving spouse, and head of household filers. + label: NYC Household Credit per exemption for non-single filers reference: - title: Instructions for Form IT-201, 2022 href: https://www.tax.ny.gov/pdf/2022/printable-pdfs/inc/it201i-2022.pdf#page=17 @@ -15,7 +15,7 @@ metadata: href: https://www.tax.ny.gov/pdf/current_forms/it/it201i.pdf#page=20 brackets: - threshold: - 2022-01-01: -1 + 2022-01-01: -.inf amount: 2022-01-01: 30 - threshold: diff --git a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml index 00bfeece18e..8d4a2739796 100644 --- a/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml +++ b/policyengine_us/parameters/gov/local/ny/nyc/tax/income/credits/household/separate_per_dependent.yaml @@ -19,7 +19,7 @@ metadata: href: https://www.tax.ny.gov/pdf/current_forms/it/it201i.pdf#page=20 brackets: - threshold: - 2022-01-01: -1 + 2022-01-01: -.inf amount: 2022-01-01: 15 - threshold: diff --git a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml index a9d8fb25317..7ff3c1be8e3 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.yaml @@ -481,3 +481,70 @@ # federal AGI (IT-201 line 19a) is 12,600, over it. adjusted_gross_income: 12_300 nyc_household_credit: 0 + +- name: 2025 surviving spouse who can be claimed counts the dependent's exemption + # Tax Law 1310(d)(3)(B): a surviving spouse is a household in its own right. + period: 2025 + input: + people: + parent: + age: 22 + employment_income: 10_000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: SURVIVING_SPOUSE + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + households: + household: + members: [parent, child] + state_code: NY + in_nyc: true + output: + # One exemption (the child); $30 x 1. + nyc_household_credit: 30 + +- name: Separate filers get exactly half the household credit, not Table 6's rounded amount + period: 2022 + absolute_error_margin: 0 + input: + state_code: NY + in_nyc: true + adjusted_gross_income: 8_000 + spouse_separate_adjusted_gross_income: 8_000 + filing_status: SEPARATE + tax_unit_size: 1 + spouse_separate_tax_unit_size: 2 + output: + # Household AGI $16,000 is over $15,000 and not over $17,500: $25 per + # exemption. $25 x (1 + 2) = $75, halved: $37.50 (Table 6 prints $38). + nyc_household_credit: 37.5 + +- name: A single filer with negative AGI gets the lowest-bracket NYC credit + # The 1310(d) table's first row is "Not over $10,000", which includes + # zero and negative AGI. + period: 2025 + absolute_error_margin: 0 + input: + state_code: NY + in_nyc: true + adjusted_gross_income: -5_000 + filing_status: SINGLE + output: + nyc_household_credit: 15 + +- name: A joint return with negative AGI gets the lowest-bracket NYC credit per exemption + period: 2025 + absolute_error_margin: 0 + input: + state_code: NY + in_nyc: true + adjusted_gross_income: -5_000 + filing_status: JOINT + tax_unit_size: 2 + output: + # $30 x 2. + nyc_household_credit: 60 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml index f73505ce3ad..1054cb4c75b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml @@ -279,3 +279,27 @@ output: # One exemption: $60 base + $15 x (1 - 1). ny_household_credit_potential: 60 + +- name: 2025 surviving spouse who can be claimed counts the dependent's exemption + # Tax Law 606(b)(3)(B): a surviving spouse is a household in its own right. + period: 2025 + input: + people: + parent: + age: 22 + employment_income: 10_000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: SURVIVING_SPOUSE + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + households: + household: + members: [parent, child] + state_code: NY + output: + # One exemption (the child): $60 base + $15 x (1 - 1). + ny_household_credit_potential: 60 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_nonrefundable_cdcc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_nonrefundable_cdcc.yaml index 9d3ba433591..d49b377f41d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_nonrefundable_cdcc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/cdcc/vt_nonrefundable_cdcc.yaml @@ -26,3 +26,29 @@ state_code: VT output: vt_nonrefundable_cdcc: 0 + +- name: Vermont 2021 single filer at exactly $30,000 takes the nonrefundable credit + # The pre-2022 5828c test is AGI "less than $30,000.00", so a filer at the + # threshold is not low-income eligible and takes 24% of the federal credit. + period: 2021 + input: + capped_cdcc: 1_000 + adjusted_gross_income: 30_000 + filing_status: SINGLE + state_code: VT + output: + vt_low_income_cdcc_eligible: false + vt_low_income_cdcc: 0 + vt_nonrefundable_cdcc: 240 # 1,000 * 0.24 + +- name: Vermont 2021 joint filer at exactly $40,000 takes the nonrefundable credit + period: 2021 + input: + capped_cdcc: 1_000 + adjusted_gross_income: 40_000 + filing_status: JOINT + state_code: VT + output: + vt_low_income_cdcc_eligible: false + vt_low_income_cdcc: 0 + vt_nonrefundable_cdcc: 240 # 1,000 * 0.24 diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py index a3da549d124..cf92b44d838 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_cdcc.py @@ -27,8 +27,9 @@ def formula(tax_unit, period, parameters): federal_cdcc = tax_unit("pre_obbba_capped_cdcc", period) else: federal_cdcc = tax_unit("capped_cdcc", period) - # 2022 Act 138 raised the 5828c match to 72 percent from 2022, and the - # 2022 Schedule IN-112 applies it with no income test (see + # 2022 Act 138, Sec. 3 raised the 5828c match to 72 percent and struck + # the low-income AGI cap, retroactive to January 1, 2022 under Sec. + # 13(b), so from 2022 there is no income test (see # income_limit_in_effect). We do not track facility certification or # in-state care at the moment. if p.income_limit_in_effect: From c56d748f4bb5fdd9bc18766920cbaceb36b23a32 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 21:55:22 +0000 Subject: [PATCH 8/9] Changelog: note the NYC household credit's negative-AGI fix Co-Authored-By: Claude Opus 5.5 --- changelog.d/statute-first-state-income-tax.fixed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/statute-first-state-income-tax.fixed.md b/changelog.d/statute-first-state-income-tax.fixed.md index 854195d277d..f7a790b9ac8 100644 --- a/changelog.d/statute-first-state-income-tax.fixed.md +++ b/changelog.d/statute-first-state-income-tax.fixed.md @@ -1 +1 @@ -Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; the New York and New York City household credits count the federal exemptions of married, head-of-household and surviving-spouse filers instead of denying the credit when a filer can be claimed (Tax Law 606(b), 1310(d)), and the New York City credit for a married couple filing separately uses the couple's AGI and exemptions; and Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules. +Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; the New York and New York City household credits count the federal exemptions of married, head-of-household and surviving-spouse filers instead of denying the credit when a filer can be claimed (Tax Law 606(b), 1310(d)), and the New York City credit for a married couple filing separately uses the couple's AGI and exemptions; Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules; and the New York City household credit gives negative AGI the lowest bracket's amount. From bad474cf4f4d2468a375e177d615797d5ebbd80a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 12:58:22 +0000 Subject: [PATCH 9/9] Halve the NY household credit exactly for separate filers; record the conflict Tax Law 606(b)(3)(C) and 1310(d)(3)(C) give a spouse filing separately "one-half the credit otherwise allowed his or her household", with no rounding. New York State rounded the half up as IT-201 Table 3 does, while New York City already halved exactly. Both now follow the statute, and the choice is recorded as us-ny:household-credit-separate-return-halving. The NYC comment is reworded so the ratchet counts it; both files rise to 2. Also from the round 6 review: describe what the forms direct instead of asserting agency processing in three candidate notes, start the NM candidate at 2021 (the model's first year for the exemption), and say "at or below" in the Puerto Rico unearned income limit, matching the <= in pr_earned_income_credit_eligible_person. Co-Authored-By: Claude Opus 5.5 --- .../statute-first-state-income-tax.fixed.md | 2 +- .../earned_income/unearned_income/limit.yaml | 2 +- .../code_health/test_source_conflicts.py | 4 +- .../income/credits/ny_household_credit.yaml | 33 ++++++++++++-- .../credits/household/nyc_household_credit.py | 12 ++--- .../credits/ny_household_credit_potential.py | 9 ++-- source_conflicts/candidates.yaml | 45 ++++++++++++++++--- 7 files changed, 86 insertions(+), 21 deletions(-) diff --git a/changelog.d/statute-first-state-income-tax.fixed.md b/changelog.d/statute-first-state-income-tax.fixed.md index f7a790b9ac8..5b2a9861eec 100644 --- a/changelog.d/statute-first-state-income-tax.fixed.md +++ b/changelog.d/statute-first-state-income-tax.fixed.md @@ -1 +1 @@ -Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; the New York and New York City household credits count the federal exemptions of married, head-of-household and surviving-spouse filers instead of denying the credit when a filer can be claimed (Tax Law 606(b), 1310(d)), and the New York City credit for a married couple filing separately uses the couple's AGI and exemptions; Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules; and the New York City household credit gives negative AGI the lowest bracket's amount. +Follow the statute where state forms differ: New Mexico's senior medical exemption needs expenses over $28,000; Vermont's 2021 low-income child care credit needs AGI under the threshold; the New York and New York City household credits count the federal exemptions of married, head-of-household and surviving-spouse filers instead of denying the credit when a filer can be claimed (Tax Law 606(b), 1310(d)), and the New York City credit for a married couple filing separately uses the couple's AGI and exemptions, and separate filers get exactly one-half of both household credits (Tax Law 606(b)(3)(C), 1310(d)(3)(C)) rather than the tables' rounded halves; Puerto Rico's earned income credit uses Hacienda's 2024 and 2025 inflation-adjusted schedules; and the New York City household credit gives negative AGI the lowest bracket's amount. diff --git a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml index c173e04814c..d2151145b09 100644 --- a/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml +++ b/policyengine_us/parameters/gov/territories/pr/tax/income/credits/earned_income/unearned_income/limit.yaml @@ -1,4 +1,4 @@ -description: Puerto Rico limits the earned income credit to filers with unearned income below this threshold. +description: Puerto Rico limits the earned income credit to filers with unearned income at or below this threshold. values: 2023-01-01: 10_000 metadata: diff --git a/policyengine_us/tests/code_health/test_source_conflicts.py b/policyengine_us/tests/code_health/test_source_conflicts.py index 301329747d1..4e92c1a9fd6 100644 --- a/policyengine_us/tests/code_health/test_source_conflicts.py +++ b/policyengine_us/tests/code_health/test_source_conflicts.py @@ -154,7 +154,7 @@ "policyengine_us/variables/gov/irs/tax_unit_is_required_to_file.py": 1, "policyengine_us/variables/gov/local/ca/cc/ga/ca_cc_general_assistance_maximum_grant.py": 1, "policyengine_us/variables/gov/local/ca/cc/ga/eligibility/ca_cc_general_assistance_eligible.py": 1, - "policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py": 1, + "policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py": 2, "policyengine_us/variables/gov/states/ar/ade/oec/sra/rates/ar_sra_daily_copay.py": 1, "policyengine_us/variables/gov/states/ca/hbex/premium_subsidy/ca_premium_subsidy_applicable_percentage.py": 1, "policyengine_us/variables/gov/states/ct/tax/income/rebate/ct_child_tax_rebate.py": 1, @@ -169,7 +169,7 @@ "policyengine_us/variables/gov/states/mn/tax/income/deductions/mn_itemized_deductions.py": 1, "policyengine_us/variables/gov/states/mo/dese/ccs/eligibility/mo_ccs_income_eligible.py": 1, "policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_medical_expense_exemption.py": 1, - "policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py": 1, + "policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py": 2, "policyengine_us/variables/gov/states/vt/tax/income/credits/cdcc/vt_low_income_cdcc_eligible.py": 1, "policyengine_us/variables/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_categorical_eligible.py": 1, "policyengine_us/variables/gov/territories/pr/tax/income/pr_agi.py": 1, diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml index 1054cb4c75b..4258a8850b0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_credit.yaml @@ -31,9 +31,10 @@ ny_income_tax_before_credits: 1000000 output: ny_household_credit: 35 -- name: 2-person married filing separately tax unit with $16,000 income receives half - of a 4-person's with $32,000 income (above), rounded up. +- name: 2-person married filing separately tax unit with $16,000 income receives exactly + half of a 4-person's with $32,000 income (above). period: 2021 + absolute_error_margin: 0 input: state_code: NY adjusted_gross_income: 16000 @@ -43,7 +44,10 @@ spouse_separate_tax_unit_size: 2 ny_income_tax_before_credits: 1000000 output: - ny_household_credit: 18 + # Combined AGI $32,000 is "not over $32,000": $20 base + $5 x (4 - 1) = + # $35. Tax Law 606(b)(3)(C) gives one-half, $17.50; IT-201 Table 3 rounds + # it up to $18. The model follows the statute. + ny_household_credit: 17.5 - name: Simple ny_household_credit test absolute_error_margin: 0.01 period: 2021 @@ -303,3 +307,26 @@ output: # One exemption (the child): $60 base + $15 x (1 - 1). ny_household_credit_potential: 60 + +- name: 2025 separate filer gets exactly one-half of the household credit, not Table 3's rounded amount + period: 2025 + absolute_error_margin: 0 + input: + people: + person_a: + age: 40 + employment_income: 2_500 + tax_units: + tax_unit: + members: [person_a] + filing_status: SEPARATE + households: + household: + members: [person_a] + state_code: NY + output: + # Combined AGI $2,500 + $2,500 = $5,000 (the spouse's separate return is + # assumed alike), which is "not over $5,000"; exemptions 1 + 1 = 2. + # Household amount: $90 base + $15 x (2 - 1) = $105. Tax Law 606(b)(3)(C) + # gives one-half, $52.50; IT-201 Table 3 prints $53 for two exemptions. + ny_household_credit_potential: 52.5 diff --git a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py index c45988694f4..253f1f58895 100644 --- a/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py +++ b/policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py @@ -58,11 +58,13 @@ def formula(tax_unit, period, parameters): [ # Single filers get a flat amount. p.flat_amount.calc(federal_agi, right=True), - # Separate filers get "one-half the credit otherwise allowed his - # or her household": the married amount per exemption at the - # couple's AGI, times the couple's exemptions, halved. IT-201 - # Table 6 instead rounds a per-exemption half amount, which can - # differ by a dollar or more; the model follows the statute. + # Tax Law 1310(d)(3)(C) gives a separate filer "one-half the + # credit otherwise allowed his or her household": the married + # amount per exemption at the couple's AGI, times the couple's + # exemptions, halved. The IT-201 instructions say under Table 6 + # that "The credit amounts have been rounded", and the table's + # half amounts can differ by a dollar or more; the model + # follows the statute. p.other_per_dependent.calc(separate_agi, right=True) * separate_exemptions / 2, diff --git a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py index c71b11d6a75..b722c9b0339 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py @@ -46,9 +46,12 @@ def formula(tax_unit, period, parameters): total_amount_if_not_single = max_( non_single_base + non_single_additional * (size - 1), 0 ) - amount_if_not_single = np.ceil( - total_amount_if_not_single / where(separate, 2, 1) - ) + # Tax Law 606(b)(3)(C) permits a husband or wife filing a separate + # return "one-half the credit otherwise allowed his or her household". + # The IT-201 instructions say under Table 3 that "The credit amounts + # have been rounded", and the table rounds each half up to a whole + # dollar; the model follows the statute. + amount_if_not_single = total_amount_if_not_single / where(separate, 2, 1) non_single_amount = amount_if_not_single * ~single # Tax Law 606(b)(3)(B) defines a household as a husband and wife, a # head of household, a surviving spouse, or an unmarried individual diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index aebb0757d37..28a59bf0b26 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -6468,7 +6468,7 @@ candidates: - id: us-nm:medical-expense-exemption-28000-boundary program: income-tax years: - from: 2023 + from: 2021 type: tax question: Must medical expenses exceed $28,000, or does exactly $28,000 qualify for the exemption? sources: @@ -6496,8 +6496,10 @@ candidates: state: ruled note: 'An October 2026 ruling accepted the instructions; under the later statute-first rule PE-US follows the statute (#10300): expenses must exceed $28,000, so exactly $28,000 gives no exemption (nm_medical_expense_exemption.py). - The companion 65+ medical care credit (7-2-18.13) keeps its own "$28,000 or more" test. TRD processes returns - under the instructions, so results differ from the agency''s at exactly $28,000.' + The companion 65+ medical care credit (7-2-18.13) keeps its own "$28,000 or more" test. The instructions direct + filers to claim the exemption at $28,000 of expenses or more, so a return filed as instructed differs from the + model at exactly $28,000. The years start at 2021, the first year the model computes nm_medical_expense_exemption; the + statute''s text dates from Laws 2005, ch. 104, § 6.' - id: us-nm:mixed-spouse-dependent-credit-tests program: income-tax years: @@ -6746,7 +6748,38 @@ candidates: note: 'An October 2026 ruling accepted the instructions; under the later statute-first rule PE-US follows Tax Law 606(b)(3)(B) and 1310(d)(3)(B) (#10300): only claimable single filers are zeroed, other households count their federal exemptions (exemptions_count), and NYC separate filers get half the married credit on the couple''s AGI - and exemptions. DTF processes returns under item C, so results differ from the agency''s for these households.' + and exemptions. The instructions direct filers who mark item C Yes not to claim the credit, so a return filed as + instructed differs from the model for these households.' +- id: us-ny:household-credit-separate-return-halving + program: income-tax + years: + from: 2015 + type: tax + question: Does a married individual filing a separate return get exactly one-half of the household credit, or the + half amounts that IT-201 Tables 3 and 6 round up to whole dollars? + sources: + - kind: statute + citation: N.Y. Tax Law 606(b)(3)(C); 1310(d)(3)(C) + url: https://www.nysenate.gov/legislation/laws/TAX/606 + - kind: instructions + citation: IT-201 instructions, 2025, household credit Tables 3 and 6 and their Note 5 ("The credit amounts have + been rounded"), under lines 40 and 48 + url: https://www.tax.ny.gov/forms/html-instructions/2025/it/it201i-2025.htm#line-40 + readings: + statute: Each spouse gets one-half the credit otherwise allowed the household, unrounded. + other: Tables 3 and 6 round each half amount up to a whole dollar. + example: New York City, combined AGI $16,000 and three exemptions, gives $37.50 by the statute and $38 by Table 6; + New York State, combined AGI $5,000 and two exemptions, gives $52.50 by the statute and $53 by Table 3. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_credit_potential.py + - policyengine_us/variables/gov/local/ny/nyc/tax/income/credits/household/nyc_household_credit.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10300 + state: ruled + note: Both credits halve exactly, as the statute says; the tables round for the form. Section 606 authorizes + nearest-dollar tables only for the real property tax credit. - id: us-ny:real-property-credit-claimable-spouse program: income-tax years: @@ -9779,8 +9812,8 @@ candidates: state: ruled note: Under the statute-first rule PE-US applies the strict below-threshold test (#10300). Act 138 struck the income test from 2022, so 2021 is the only affected year in the model; a 2021 filer at exactly the threshold takes the - 24% nonrefundable credit instead. Vermont processed 2021 returns under the form, so results differ from the - agency's at exactly $30,000 or $40,000. + 24% nonrefundable credit instead. The 2021 form directs filers whose AGI equals the threshold to treat it as + qualifying, so a return filed as instructed differs from the model at exactly $30,000 or $40,000. - id: us-vt:medical-deduction-without-federal-itemizing program: income-tax years: