Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1f0cd1b
Make head_is_dependent_elsewhere consumers independent of the head label
MaxGhenis Oct 8, 2026
3ecc675
Keep tax units without a filer eligible where only claimed filers are…
MaxGhenis Oct 9, 2026
8da8312
Address review: Hawaii worksheet earnings, NM same-filer age, childle…
MaxGhenis Oct 9, 2026
e838623
Allow a cent of rounding in the Hawaii self-employed joint pair
MaxGhenis Oct 9, 2026
41fb4a5
Address review round 2: worksheet earnings, minors' own status, input…
MaxGhenis Oct 9, 2026
527d47f
Merge remote-tracking branch 'upstream/main' into head-dependent-else…
MaxGhenis Oct 9, 2026
436805e
Merge the Hawaii deductions references after syncing main
MaxGhenis Oct 9, 2026
23c6229
Apply the federal rules for a filer who can be claimed as a dependent
MaxGhenis Oct 9, 2026
31fd311
Apply Virginia's rules for a filer who can be claimed as a dependent
MaxGhenis Oct 9, 2026
4597c55
Address the federal claimable-filer review
MaxGhenis Oct 10, 2026
fa11328
Address the Virginia claimable-filer review
MaxGhenis Oct 10, 2026
32da880
Merge commit '4597c55a86' into claimable-filer-va
MaxGhenis Oct 10, 2026
e4b8f36
Separate dependency facts and fix Hawaii qualified-person counts
MaxGhenis Oct 10, 2026
c7f969f
Clarify claimability outside the current tax unit
MaxGhenis Oct 10, 2026
4c8ed81
Merge commit 'c7f969f872' into claimable-filer-federal
MaxGhenis Oct 10, 2026
3f88702
Read the split dependency inputs from #10024
MaxGhenis Oct 10, 2026
dcbf74e
Merge commit '3f8870261c' into claimable-filer-va
MaxGhenis Oct 10, 2026
6fdc911
Read the split dependency inputs from #10024 in Virginia
MaxGhenis Oct 10, 2026
b2771d2
Follow the corrected exemption count in three Hawaii credits
MaxGhenis Oct 10, 2026
eac0c63
Merge commit 'b2771d2f15' into claimable-filer-va
MaxGhenis Oct 10, 2026
163fcd4
Address the second federal claimable-filer review
MaxGhenis Oct 10, 2026
cdbaf6e
Merge commit '163fcd4cfa98345d5ec0c358d2d35c8516789334' into claimabl…
MaxGhenis Oct 10, 2026
f5f0b31
Keep the claimable-filer rules in Virginia's dependent exemption reform
MaxGhenis Oct 10, 2026
3d0864f
Address the third federal claimable-filer review
MaxGhenis Oct 10, 2026
35a2cbc
Give the spouse without an SSN a card type in the recovery rebate test
MaxGhenis Oct 10, 2026
7702243
Merge commit '35a2cbcbb971f6b43d35db009d9bda41b76386a5' into claimabl…
MaxGhenis Oct 10, 2026
50fb063
Merge main into claimable-filer-va after #10087
MaxGhenis Oct 16, 2026
c27d346
Merge main into claimable-filer-va
MaxGhenis Oct 18, 2026
0fdbf8f
Split the Virginia exemption reform tests to fit the reform-combo budget
MaxGhenis Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/claimable-filer-va.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Apply Virginia's rules for a filer who can be claimed as a dependent: the standard deduction is limited to the filers' earned income (Va. Code 58.1-322.03(1)(b)), personal exemptions follow those allowable federally (also under the dependent exemption reform), and the EITC alternatives are barred (58.1-339.8(B)(1)); the aged and blind exemption now goes only to aged or blind filers, while a blind or aged dependent still bars the low-income credit (58.1-339.8(D)(4)).
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ def formula(tax_unit, period, parameters):

person = tax_unit.members
age = person("age", period)
is_dependent = person("is_tax_unit_dependent", period)
# As in the baseline, a return on which the filer (or, if joint,
# either spouse) can be claimed as a dependent has no dependents
# (IRC 152(b)(1)), unless the would-be claimant meets the filing
# exception.
is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit(
"head_or_spouse_is_dependent_elsewhere_without_filing_exception", period
)

# Apply age limit if in effect
if p.age_limit.in_effect:
Expand Down Expand Up @@ -83,7 +89,13 @@ class va_older_dependents_count(Variable):

def formula(tax_unit, period, parameters):
person = tax_unit.members
is_dependent = person("is_tax_unit_dependent", period)
# As in the baseline, a return on which the filer (or, if joint,
# either spouse) can be claimed as a dependent has no dependents
# (IRC 152(b)(1)), unless the would-be claimant meets the filing
# exception.
is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit(
"head_or_spouse_is_dependent_elsewhere_without_filing_exception", period
)
total_dependents = tax_unit.sum(is_dependent)
eligible_dependent_exemptions = tax_unit(
"va_eligible_dependents_count", period
Expand All @@ -104,14 +116,14 @@ def formula(tax_unit, period, parameters):
# Calculate personal exemptions base amount (filer + spouse + older dependents)
filing_status = tax_unit("filing_status", period)
older_dependents = tax_unit("va_older_dependents_count", period)
personal_count = (
where(
filing_status == filing_status.possible_values.JOINT,
2,
1,
)
+ older_dependents
# As in the baseline, a filer whom another taxpayer can claim has
# no exemption of their own (IRC 151(d)(2)).
filers = where(
tax_unit("head_or_spouse_is_dependent_elsewhere", period),
tax_unit("head_spouse_count_not_dependent_elsewhere", period),
where(filing_status == filing_status.possible_values.JOINT, 2, 1),
)
personal_count = filers + older_dependents
personal_exemption_amount = personal_count * p_base

# Add dependent exemption (has its own phase-out logic)
Expand Down
70 changes: 70 additions & 0 deletions policyengine_us/tests/core/test_claimable_filer_va_invariants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Virginia rules for a filer who can be claimed as a dependent.

The standard deduction is limited to the filers' earned income, personal
exemptions follow those allowable federally, and the EITC alternatives are
barred (Va. Code 58.1-322.03, 58.1-339.8).

For couples with either, both or neither spouse claimed:

1. Swap invariance: each tax-unit output is the same under either head/spouse
labelling (Hypothesis batches and the seeded population).
2. Monotonicity: marking another filer as claimed never raises an output
(seeded population only).

The outputs are tax-unit totals; which spouse an exemption goes to is checked
by YAML cases on the person-level variables.
"""

import pytest
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st

from policyengine_us.tests.core.test_claimable_filer_federal_invariants import (
check_monotone,
check_swap,
)
from policyengine_us.tests.core.test_dependent_elsewhere_head_spouse_swap_invariance import (
CLAIM_PATTERNS,
_crafted_cases,
_seeded_couples,
adults,
dependents,
)

STATES = ["VA"]
YEARS = [2023, 2025, 2026]
OUTPUTS = [
"va_standard_deduction",
"va_personal_exemption",
"va_aged_blind_exemption",
"va_refundable_eitc_if_claimed",
"va_non_refundable_eitc_if_claimed",
]


@st.composite
def couples(draw):
return {
"state": draw(st.sampled_from(STATES)),
"adults": [draw(adults()), draw(adults())],
"dependents": draw(st.lists(dependents(), max_size=2)),
"claimed": draw(st.sampled_from(CLAIM_PATTERNS)),
}


@settings(
max_examples=2,
deadline=None,
derandomize=True,
suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large],
)
@given(st.sampled_from(YEARS), st.lists(couples(), min_size=6, max_size=12))
def test_va_claimable_filer_rules_are_label_free(year, units):
check_swap(units, year, OUTPUTS)


@pytest.mark.parametrize("year", YEARS)
def test_va_claimable_filer_rules_seeded_population(year):
units = _seeded_couples(STATES, per_state=3) + _crafted_cases(STATES)
check_swap(units, year, OUTPUTS)
check_monotone(units, year, OUTPUTS)
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,77 @@
output:
# 2025: 20% of federal EITC
va_non_refundable_eitc_if_claimed: 800


- name: 2025 joint return with one claimable spouse gets no nonrefundable Virginia EITC (the claimable spouse is the head)
period: 2025
input:
people:
person_a:
age: 30
claimed_as_dependent_on_another_return: true
is_tax_unit_head: true
is_tax_unit_spouse: false
person_b:
age: 30
is_tax_unit_head: false
is_tax_unit_spouse: true
tax_units:
tax_unit:
members: [person_a, person_b]
filing_status: JOINT
eitc: 1000
va_low_income_tax_credit: 0
households:
household:
members: [person_a, person_b]
state_code: VA
output:
va_non_refundable_eitc_if_claimed: 0

- name: 2025 joint return with one claimable spouse gets no nonrefundable Virginia EITC (the claimable spouse is the spouse)
period: 2025
input:
people:
person_b:
age: 30
is_tax_unit_head: true
is_tax_unit_spouse: false
person_a:
age: 30
claimed_as_dependent_on_another_return: true
is_tax_unit_head: false
is_tax_unit_spouse: true
tax_units:
tax_unit:
members: [person_b, person_a]
filing_status: JOINT
eitc: 1000
va_low_income_tax_credit: 0
households:
household:
members: [person_b, person_a]
state_code: VA
output:
va_non_refundable_eitc_if_claimed: 0

- name: 2025 joint return with neither spouse claimable gets the nonrefundable Virginia EITC
period: 2025
input:
people:
person_a:
age: 30
person_b:
age: 30
tax_units:
tax_unit:
members: [person_a, person_b]
filing_status: JOINT
eitc: 1000
va_low_income_tax_credit: 0
households:
household:
members: [person_a, person_b]
state_code: VA
output:
va_non_refundable_eitc_if_claimed: 200
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,76 @@
eitc: 5_000
output:
va_refundable_eitc_if_claimed: 0


- name: 2025 joint return with one claimable spouse gets no Virginia EITC (the claimable spouse is the head)
period: 2025
input:
people:
person_a:
age: 30
claimed_as_dependent_on_another_return: true
is_tax_unit_head: true
is_tax_unit_spouse: false
person_b:
age: 30
is_tax_unit_head: false
is_tax_unit_spouse: true
tax_units:
tax_unit:
members: [person_a, person_b]
filing_status: JOINT
eitc: 1000
households:
household:
members: [person_a, person_b]
state_code: VA
output:
# Va. Code 58.1-339.8(B)(1).
va_refundable_eitc_if_claimed: 0

- name: 2025 joint return with one claimable spouse gets no Virginia EITC (the claimable spouse is the spouse)
period: 2025
input:
people:
person_b:
age: 30
is_tax_unit_head: true
is_tax_unit_spouse: false
person_a:
age: 30
claimed_as_dependent_on_another_return: true
is_tax_unit_head: false
is_tax_unit_spouse: true
tax_units:
tax_unit:
members: [person_b, person_a]
filing_status: JOINT
eitc: 1000
households:
household:
members: [person_b, person_a]
state_code: VA
output:
va_refundable_eitc_if_claimed: 0

- name: 2025 joint return with neither spouse claimable gets the Virginia EITC
period: 2025
input:
people:
person_a:
age: 30
person_b:
age: 30
tax_units:
tax_unit:
members: [person_a, person_b]
filing_status: JOINT
eitc: 1000
households:
household:
members: [person_a, person_b]
state_code: VA
output:
# 20% x $1,000.
va_refundable_eitc_if_claimed: 200
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,80 @@
state_code: VA
output:
va_low_income_tax_credit_eligible: false

# Va. Code 58.1-339.8(D)(4): no credit when "any person claimed as a dependent"
# claims the additional exemption for blind or aged taxpayers on their own
# return. The model takes a blind or aged dependent to claim it.
- name: 2025 blind dependent bars the low income tax credit
period: 2025
input:
people:
parent:
age: 40
is_tax_unit_head: true
is_tax_unit_spouse: false
child:
age: 16
is_blind: true
is_tax_unit_head: false
is_tax_unit_spouse: false
tax_units:
tax_unit:
members: [parent, child]
households:
household:
members: [parent, child]
state_code: VA
output:
va_aged_blind_exemption: 0
va_low_income_tax_credit_eligible: false

- name: 2025 aged dependent bars the low income tax credit
period: 2025
input:
people:
person_a:
age: 40
is_tax_unit_head: true
is_tax_unit_spouse: false
person_b:
age: 40
is_tax_unit_head: false
is_tax_unit_spouse: true
parent:
age: 70
is_tax_unit_head: false
is_tax_unit_spouse: false
tax_units:
tax_unit:
members: [person_a, person_b, parent]
filing_status: JOINT
households:
household:
members: [person_a, person_b, parent]
state_code: VA
output:
va_aged_blind_exemption: 0
va_low_income_tax_credit_eligible: false

- name: 2025 dependent who is neither blind nor aged does not bar the low income tax credit
period: 2025
input:
people:
parent:
age: 40
is_tax_unit_head: true
is_tax_unit_spouse: false
child:
age: 16
is_tax_unit_head: false
is_tax_unit_spouse: false
tax_units:
tax_unit:
members: [parent, child]
households:
household:
members: [parent, child]
state_code: VA
output:
va_low_income_tax_credit_eligible: true
Loading
Loading