From 5b3b8f7d7053c3c54b0d0130e4e6a48cc22785b8 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 9 Jul 2026 11:04:45 -0400 Subject: [PATCH 1/2] Keep each spouse's own DE personal credit in their FS4 column Under Filing Status 4, de_personal_credit_indv allocated ALL personal credits - including each spouse's own $110 - to whichever column minimized tax. The PIT-RES line 27a instructions' example pins the rule: a no-dependent married couple enters '$110 in each column if Filing Status 4' - own credits are fixed per column; only dependent credits are splittable. The over-flexible allocation made FS4 spuriously beat joint filing, so the post-credit election picked FS4 and understated DE tax. With the fix, the taxsim #839 record elects joint and lands at -69.28, matching both the TaxAct return in the bundle (refund $68) and taxsimtest. Three tests that encoded the free-split behavior are corrected (the elderly FS4 integration case now correctly elects joint at 388.42 rather than free-split FS4 at 309). Fixes #8977. Follow-on to #7940. Co-Authored-By: Claude Opus 4.8 --- ...de-fs4-own-personal-credit-column.fixed.md | 1 + .../personal/de_personal_credit_indv.yaml | 9 +++- .../states/de/tax/income/de_income_tax.yaml | 33 ++++++++++++++ .../gov/states/de/tax/income/integration.yaml | 8 ++-- .../personal/de_personal_credit_indv.py | 45 +++++++++++-------- 5 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 changelog.d/de-fs4-own-personal-credit-column.fixed.md create mode 100644 policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml diff --git a/changelog.d/de-fs4-own-personal-credit-column.fixed.md b/changelog.d/de-fs4-own-personal-credit-column.fixed.md new file mode 100644 index 00000000000..5dc5953850e --- /dev/null +++ b/changelog.d/de-fs4-own-personal-credit-column.fixed.md @@ -0,0 +1 @@ +Keep each spouse's own Delaware personal credit in their own Filing Status 4 column per the PIT-RES line 27a example, allocating only dependent credits between columns, so the post-credit filing-status election no longer favors combined-separate filing through an impermissible credit shift. diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml index 1bc0bbd1089..406b0d0f8f9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml @@ -17,7 +17,10 @@ state_code: DE output: # 2 exemptions × $110 = $220, all to spouse (more capacity) - de_personal_credit_indv: [0, 220] + # Each spouse's own $110 stays in their column (PIT-RES line 27a + # example: "$110 in each column if Filing Status 4"); only dependent + # credits are splittable, and this couple has none. + de_personal_credit_indv: [110, 110] - name: all credits to higher-capacity column (head) period: 2025 @@ -44,7 +47,9 @@ state_code: DE output: # 4 exemptions × $110 = $440, all to head (more capacity) - de_personal_credit_indv: [440, 0, 0, 0] + # Own $110 per spouse is fixed; only the two dependents' credits are + # allocable, so the head column can receive at most 110 + 220 = 330. + de_personal_credit_indv: [330, 110, 0, 0] - name: tied capacity splits evenly period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml new file mode 100644 index 00000000000..4504221bebb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml @@ -0,0 +1,33 @@ + +- name: DE FS4 does not shift a spouse's own personal credit across columns (taxsim #839) + absolute_error_margin: 2 + period: 2025 + input: + people: + head: + age: 31 + employment_income: 5_490.84 + spouse: + age: 32 + employment_income: 20_000 + c1: + age: 8 + c2: + age: 15 + marital_units: + marital_unit: + members: [head, spouse] + tax_units: + tax_unit: + members: [head, spouse, c1, c2] + households: + household: + members: [head, spouse, c1, c2] + state_code: DE + output: + # Each spouse's own $110 credit stays in their column (PIT-RES line 27a + # example: "$110 in each column if Filing Status 4"), so legal FS4 + # (255.30) exceeds joint (~253) and the election lands on joint: + # tax 692.56 - 440 credits - 321.84 refundable EITC = -69.28, matching + # the TaxAct return (refund $68) and taxsimtest. + de_income_tax: -69.28 diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/integration.yaml index 88d65c123f6..aa2846f6c36 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/integration.yaml @@ -359,9 +359,11 @@ members: [person1, person2, child1, child2] state_fips: 10 output: - # Per-column: $550 of $660 pre-EITC credits used (line 27a+27b), - # then NR EITC (~$50) applied to higher-income column (line 30). - de_income_tax: 309 + # With each spouse's own personal credit fixed to their column + # (PIT-RES line 27a example), legal FS4 leaves column A's credits + # unusable (column tax 0) for a total of 468.34, so the couple + # elects joint filing: 388.42. + de_income_tax: 388.42 - name: FS4 young couple one dependent - per-column personal credit cap (issue #8710) # DE, MFJ, both age 20, one dependent, $21,790 taxable interest split 50/50, diff --git a/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py b/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py index 6f44b0ba150..6722f22b3c2 100644 --- a/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py +++ b/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py @@ -11,14 +11,20 @@ class de_personal_credit_indv(Variable): defined_for = StateCode.DE def formula(person, period, parameters): - # PIT-RES Line 27a: "split the total between Columns A and - # B in increments of $110." The form lets the taxpayer - # choose any split; we find the optimal split that maximises - # total credits used (i.e. minimises tax). + # PIT-RES Line 27a: "split the total between Columns A and B in + # increments of $110." The instructions' example pins down how: + # a married couple with no dependents enters "$110 in each column + # if Filing Status 4" - each spouse's own credit belongs to their + # own column, and only the DEPENDENT credits may be split between + # columns. We allocate the dependent increments to maximise total + # credits used (i.e. minimise tax); the taxpayer may choose any + # dependent split, so the optimal one is the tax-minimising one. p = parameters(period).gov.states.de.tax.income.credits is_head = person("is_tax_unit_head", period) is_spouse = person("is_tax_unit_spouse", period) + is_head_or_spouse = is_head | is_spouse + credit_per = p.personal_credits.personal person_tax = person("de_income_tax_before_non_refundable_credits_indv", period) head_tax = person.tax_unit.sum(is_head * person_tax) spouse_tax = person.tax_unit.sum(is_spouse * person_tax) @@ -26,36 +32,37 @@ def formula(person, period, parameters): fixed = person("de_aged_personal_credit_indv", period) + person( "de_cdcc_indv", period ) - head_fixed = person.tax_unit.sum(is_head * fixed) - spouse_fixed = person.tax_unit.sum(is_spouse * fixed) + # Each column's own $110 personal credit is fixed to that column. + head_fixed = person.tax_unit.sum(is_head * fixed) + credit_per + spouse_fixed = person.tax_unit.sum(is_spouse * fixed) + credit_per head_capacity = max_(head_tax - head_fixed, 0) spouse_capacity = max_(spouse_tax - spouse_fixed, 0) - credit_per = p.personal_credits.personal total_units = person.tax_unit("exemptions_count", period) - total = credit_per * total_units + # Dependent increments: exemptions beyond the two spouses. + dep_units = max_(total_units - 2, 0) - # Optimal split: try proportional floor and ceil, pick + # Optimal dependent split: try proportional floor and ceil, pick # whichever maximises effective credits (min of alloc vs - # capacity in each column). + # remaining capacity in each column). total_capacity = head_capacity + spouse_capacity ratio = where(total_capacity > 0, head_capacity / total_capacity, 0) - n_low = np.floor(total_units * ratio) + n_low = np.floor(dep_units * ratio) n_high = n_low + 1 - n_low = max_(min_(n_low, total_units), 0) - n_high = max_(min_(n_high, total_units), 0) + n_low = max_(min_(n_low, dep_units), 0) + n_high = max_(min_(n_high, dep_units), 0) eff_low = min_(n_low * credit_per, head_capacity) + min_( - (total_units - n_low) * credit_per, spouse_capacity + (dep_units - n_low) * credit_per, spouse_capacity ) eff_high = min_(n_high * credit_per, head_capacity) + min_( - (total_units - n_high) * credit_per, spouse_capacity + (dep_units - n_high) * credit_per, spouse_capacity ) - n_head = where(eff_high > eff_low, n_high, n_low) - head_alloc = n_head * credit_per - spouse_alloc = total - head_alloc + n_head_dep = where(eff_high > eff_low, n_high, n_low) + head_alloc = credit_per + n_head_dep * credit_per + spouse_alloc = credit_per + (dep_units - n_head_dep) * credit_per - return where(is_head, head_alloc, where(is_spouse, spouse_alloc, 0)) + return is_head_or_spouse * where(is_head, head_alloc, spouse_alloc) From 59fc9e41409bc72ed69ca352ae71d90ca6e9c2cc Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 16 Jul 2026 14:28:25 -0400 Subject: [PATCH 2/2] Remove leading blank line in de_income_tax.yaml Co-Authored-By: Claude Fable 5 --- .../policy/baseline/gov/states/de/tax/income/de_income_tax.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml index 4504221bebb..dd80c7a8568 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/de_income_tax.yaml @@ -1,4 +1,3 @@ - - name: DE FS4 does not shift a spouse's own personal credit across columns (taxsim #839) absolute_error_margin: 2 period: 2025