From 518fbea31094ec5af87a06e95118c1188690e330 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 5 Nov 2026 10:16:19 -0500 Subject: [PATCH 1/2] Give a separated Pennsylvania filer the unmarried tax forgiveness base pa_tax_forgiveness_rate doubled the $6,500 base for every SEPARATE unit. PE gives SEPARATE to an is_separated head alone in the tax unit, so that person got the $13,000 married base on their own income. Under 72 P.S. 7304(d)(1) a separate filer who lives apart from the spouse is not considered married (Table 1). A SEPARATE unit now gets the doubled base only when it holds a spouse (Table 2, joint eligibility income). Fixes #10511 Co-Authored-By: Claude Opus 5.5 --- ...11-pa-forgiveness-separated-filer.fixed.md | 1 + .../forgiveness/pa_tax_forgiveness_rate.yaml | 98 +++++++++++++++++++ .../forgiveness/pa_tax_forgiveness_rate.py | 14 ++- 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md diff --git a/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md b/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md new file mode 100644 index 00000000000..a9edfcbc18d --- /dev/null +++ b/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md @@ -0,0 +1 @@ +Give a Pennsylvania separate filer who is separated and alone in the tax unit the unmarried $6,500 tax forgiveness base instead of the married $13,000 base, which applies only to joint eligibility income of both spouses (72 P.S. 7304(d)(1)). diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml index 11feb60665d..5e6c00f4104 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml @@ -84,3 +84,101 @@ state_code: PA output: pa_tax_forgiveness_rate: 0.1 + +- name: Separated head alone in a separate unit gets the unmarried base + # 72 P.S. 7304(d)(1): a separate filer who lives apart from the spouse is + # not considered married, so Eligibility Income Table 1 applies. + # Base $6,500; ($10,000 - $6,500) / $250 = 14 steps; 1 - 14 * 10% < 0. + # Tax before forgiveness: 3.07% * $10,000 = $307, none forgiven. + period: 2025 + input: + people: + head: + age: 37 + employment_income: 10_000 + is_separated: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: PA + output: + filing_status: SEPARATE + pa_eligibility_income: 10_000 + pa_tax_forgiveness_rate: 0 + pa_income_tax_before_forgiveness: 307 + pa_income_tax_after_forgiveness: 307 + +- name: Separated head alone in a separate unit just above the unmarried base + # Base $6,500; ($7,000 - $6,500) / $250 = 2 steps; 1 - 2 * 10% = 80%. + period: 2025 + input: + people: + head: + age: 37 + employment_income: 7_000 + is_separated: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: PA + output: + filing_status: SEPARATE + pa_tax_forgiveness_rate: 0.8 + +- name: Married couple in one separate unit combines incomes on the married base + # Eligibility Income Table 2 ("Married Claimants, even if filing separately"): + # joint eligibility income $10,000 + $3,000 = $13,000, base $13,000, 0 steps. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 10_000 + spouse: + age: 38 + employment_income: 3_000 + tax_units: + tax_unit: + members: [head, spouse] + filing_status: SEPARATE + households: + household: + members: [head, spouse] + state_code: PA + output: + tax_unit_married: true + pa_eligibility_income: 13_000 + pa_tax_forgiveness_rate: 1 + pa_income_tax_after_forgiveness: 0 + +- name: Joint filers keep the married base + # Base $13,000; ($14,000 - $13,000) / $250 = 4 steps; 1 - 4 * 10% = 60%. + # Tax before forgiveness: 3.07% * $14,000 = $429.80; after: 40% = $171.92. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 11_000 + spouse: + age: 38 + employment_income: 3_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: PA + output: + filing_status: JOINT + pa_eligibility_income: 14_000 + pa_tax_forgiveness_rate: 0.6 + pa_income_tax_before_forgiveness: 429.8 + pa_income_tax_after_forgiveness: 171.92 diff --git a/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py b/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py index 8eec409edbe..949a0717c99 100644 --- a/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py +++ b/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py @@ -15,13 +15,17 @@ def formula(tax_unit, period, parameters): person = tax_unit.members is_child_dependent = person("is_qualifying_child_dependent", period) child_dependents = tax_unit.sum(is_child_dependent) - # filing status affects the base, where it doubles for married claimants + # The base doubles for a married claimant, whose eligibility income + # includes the spouse's. A separate filer who lives apart from the + # spouse for the last six months of the year (or under a written + # separation agreement) is not considered married, so a separate + # filer gets the doubled base only when the tax unit holds a spouse. filing_status = tax_unit("filing_status", period) filing_statuses = filing_status.possible_values - joint_separate = (filing_status == filing_statuses.JOINT) | ( - filing_status == filing_statuses.SEPARATE - ) - base_multiplier = where(joint_separate, 2, 1) + joint = filing_status == filing_statuses.JOINT + separate = filing_status == filing_statuses.SEPARATE + married_separate = separate & tax_unit("tax_unit_married", period) + base_multiplier = where(joint | married_separate, 2, 1) p = parameters(period).gov.states.pa.tax.income.forgiveness base = p.base * base_multiplier rate_per_dependent = p.dependent_rate From aae7f98fcb3d56fdd1b9a36c14831649f745a246 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Fri, 6 Nov 2026 10:46:50 -0500 Subject: [PATCH 2/2] Gate the PA unmarried forgiveness base on living apart Address review on #10666. 72 P.S. 7304(d)(1)(i)-(ii) treats a separate filer as unmarried only if the spouses also live apart for the last six months of the year (or under a written separation agreement). The previous commit gave Table 1 to every SEPARATE unit without a spouse, which in PE is every married-filing-separately return, including cohabiting couples modelled as two tax units. - Table 1 now needs a SEPARATE return, no spouse in the unit and eitc_spouses_lived_apart_last_six_months (defaults to is_separated). - A married separate filer whose spouse files in another tax unit uses joint eligibility income, summed over the marital unit (Table 2). - Cite 72 P.S. 7304(d)(1) and the 2025 PA-40 instructions; update the base.yaml description and the changelog fragment. - Add cases for cohabiting MFS couples, separated spouses living apart, and the lived-apart override. Co-Authored-By: Claude Opus 5.5 --- ...11-pa-forgiveness-separated-filer.fixed.md | 2 +- .../pa/tax/income/forgiveness/base.yaml | 2 +- .../forgiveness/pa_tax_forgiveness_rate.yaml | 171 ++++++++++++++++++ .../forgiveness/pa_tax_forgiveness_rate.py | 38 +++- 4 files changed, 202 insertions(+), 11 deletions(-) diff --git a/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md b/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md index a9edfcbc18d..351b57adc6e 100644 --- a/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md +++ b/changelog.d/fix-10511-pa-forgiveness-separated-filer.fixed.md @@ -1 +1 @@ -Give a Pennsylvania separate filer who is separated and alone in the tax unit the unmarried $6,500 tax forgiveness base instead of the married $13,000 base, which applies only to joint eligibility income of both spouses (72 P.S. 7304(d)(1)). +Give a Pennsylvania separate filer who lives apart from their spouse for the last six months of the year the unmarried $6,500 tax forgiveness base on their own income, and give a married separate filer who does not live apart the $13,000 base on the joint eligibility income of both spouses, including a spouse in another tax unit (72 P.S. 7304(d)(1)). diff --git a/policyengine_us/parameters/gov/states/pa/tax/income/forgiveness/base.yaml b/policyengine_us/parameters/gov/states/pa/tax/income/forgiveness/base.yaml index 4bfdf2d9c19..47b454ea66b 100644 --- a/policyengine_us/parameters/gov/states/pa/tax/income/forgiveness/base.yaml +++ b/policyengine_us/parameters/gov/states/pa/tax/income/forgiveness/base.yaml @@ -1,4 +1,4 @@ -description: Pennsylvania starts reducing its tax forgiveness when eligibility income exceeds this amount, or double for joint filers. +description: Pennsylvania starts reducing its tax forgiveness when eligibility income exceeds this amount, or double for married claimants, including separate filers who do not live apart. values: 2004-01-01: 6_500 metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml index 5e6c00f4104..745a60b8280 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.yaml @@ -182,3 +182,174 @@ pa_tax_forgiveness_rate: 0.6 pa_income_tax_before_forgiveness: 429.8 pa_income_tax_after_forgiveness: 171.92 + +- name: Cohabiting married couple filing separately keeps the married base + # 72 P.S. 7304(d)(1)(ii): spouses who file separately but live together are + # still married, so Eligibility Income Table 2 applies to both returns. + # Joint eligibility income $10,000 + $0 = $10,000 <= $13,000 base: 100%. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 10_000 + spouse: + age: 38 + tax_units: + head_unit: + members: [head] + filing_status: SEPARATE + cohabitating_spouses: true + spouse_unit: + members: [spouse] + filing_status: SEPARATE + cohabitating_spouses: true + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: PA + output: + eitc_spouses_lived_apart_last_six_months: [false, false] + pa_eligibility_income: [10_000, 0] + pa_tax_forgiveness_rate: [1, 1] + pa_income_tax_before_forgiveness: [307, 0] + pa_income_tax_after_forgiveness: [0, 0] + +- name: Cohabiting married couple filing separately combines incomes above the ceiling + # Joint eligibility income $10,000 + $9,000 = $19,000; base $13,000; + # ($19,000 - $13,000) / $250 = 24 steps; 1 - 24 * 10% < 0, so 0% + # (Table 2, 0 children, top column $15,250). + # Tax: 3.07% * $10,000 = $307 and 3.07% * $9,000 = $276.30, none forgiven. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 10_000 + spouse: + age: 38 + employment_income: 9_000 + tax_units: + head_unit: + members: [head] + filing_status: SEPARATE + spouse_unit: + members: [spouse] + filing_status: SEPARATE + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: PA + output: + pa_tax_forgiveness_rate: [0, 0] + pa_income_tax_after_forgiveness: [307, 276.3] + +- name: Cohabiting married couple filing separately in the phase-down + # Joint eligibility income $10,000 + $3,500 = $13,500; base $13,000; + # ($13,500 - $13,000) / $250 = 2 steps; 1 - 2 * 10% = 80% on both returns + # (Table 2 $13,500 column). + # Head: 3.07% * $10,000 = $307; after 20% = $61.40. + # Spouse: 3.07% * $3,500 = $107.45; after 20% = $21.49. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 10_000 + spouse: + age: 38 + employment_income: 3_500 + tax_units: + head_unit: + members: [head] + filing_status: SEPARATE + spouse_unit: + members: [spouse] + filing_status: SEPARATE + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: PA + output: + pa_tax_forgiveness_rate: [0.8, 0.8] + pa_income_tax_after_forgiveness: [61.4, 21.49] + +- name: Separated spouses living apart each use the unmarried base on own income + # 72 P.S. 7304(d)(1)(i)-(ii): separate returns and living apart, so neither + # is considered married and each uses Table 1 on their own income. + # Head: ($7,000 - $6,500) / $250 = 2 steps; 80%. + # Spouse: $1,000 <= $6,500; 100%. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 7_000 + is_separated: true + spouse: + age: 38 + employment_income: 1_000 + is_separated: true + tax_units: + head_unit: + members: [head] + spouse_unit: + members: [spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + head_household: + members: [head] + state_code: PA + spouse_household: + members: [spouse] + state_code: PA + output: + filing_status: [SEPARATE, SEPARATE] + eitc_spouses_lived_apart_last_six_months: [true, true] + pa_eligibility_income: [7_000, 1_000] + pa_tax_forgiveness_rate: [0.8, 1] + +- name: Separated spouses who did not live apart for six months stay married + # The 2025 PA-40 instructions count as married a claimant "Separated and + # lived apart, but for less than the last six months". Same couple as + # above, but the living-apart input is false: joint eligibility income + # $7,000 + $1,000 = $8,000 <= $13,000 base, so Table 2 gives 100%. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 7_000 + is_separated: true + spouse: + age: 38 + employment_income: 1_000 + is_separated: true + tax_units: + head_unit: + members: [head] + eitc_spouses_lived_apart_last_six_months: false + spouse_unit: + members: [spouse] + eitc_spouses_lived_apart_last_six_months: false + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: PA + output: + filing_status: [SEPARATE, SEPARATE] + pa_tax_forgiveness_rate: [1, 1] diff --git a/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py b/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py index 949a0717c99..8a13de6b969 100644 --- a/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py +++ b/policyengine_us/variables/gov/states/pa/tax/income/forgiveness/pa_tax_forgiveness_rate.py @@ -7,25 +7,45 @@ class pa_tax_forgiveness_rate(Variable): label = "PA tax forgiveness on eligibility income" unit = "/1" definition_period = YEAR - reference = "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2021/2021_pa-40in.pdf#page=39" + reference = ( + "https://www.palegis.us/statutes/unconsolidated/law-information/view-statute?txtType=PDF&SessYr=1971&SessInd=0&ActNum=0002.&chpt=003.&subchpt=000.&sctn=004.&subsctn=000.", # 72 P.S. 7304(d)(1) + "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2025/2025_pa-40in.pdf#page=36", + "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2021/2021_pa-40in.pdf#page=39", + ) defined_for = StateCode.PA def formula(tax_unit, period, parameters): - eligibility_income = tax_unit("pa_eligibility_income", period) + own_eligibility_income = tax_unit("pa_eligibility_income", period) person = tax_unit.members is_child_dependent = person("is_qualifying_child_dependent", period) child_dependents = tax_unit.sum(is_child_dependent) - # The base doubles for a married claimant, whose eligibility income - # includes the spouse's. A separate filer who lives apart from the - # spouse for the last six months of the year (or under a written - # separation agreement) is not considered married, so a separate - # filer gets the doubled base only when the tax unit holds a spouse. + # 72 P.S. 7304(d)(1)(i)-(ii): a claimant who files a separate return + # and lives apart from the spouse for all of the last six months of + # the year (or under a written separation agreement) is not + # considered married, and uses the single base on their own income + # (Eligibility Income Table 1). Every other married claimant, joint + # or separate, uses the doubled base on the joint eligibility income + # of both spouses (Table 2). filing_status = tax_unit("filing_status", period) filing_statuses = filing_status.possible_values joint = filing_status == filing_statuses.JOINT separate = filing_status == filing_statuses.SEPARATE - married_separate = separate & tax_unit("tax_unit_married", period) - base_multiplier = where(joint | married_separate, 2, 1) + spouse_in_unit = tax_unit("tax_unit_married", period) + lived_apart = tax_unit("eitc_spouses_lived_apart_last_six_months", period) + unmarried_separate = separate & ~spouse_in_unit & lived_apart + married_claimant = (joint | separate) & ~unmarried_separate + # A married separate filer whose spouse files in another tax unit + # adds that unit's eligibility income, found through the marital unit. + is_head = person("is_tax_unit_head", period) + head_unit_income = person.tax_unit("pa_eligibility_income", period) * is_head + couple_income = person.marital_unit.sum(head_unit_income) + joint_eligibility_income = tax_unit.sum(is_head * couple_income) + eligibility_income = where( + married_claimant & ~spouse_in_unit, + joint_eligibility_income, + own_eligibility_income, + ) + base_multiplier = where(married_claimant, 2, 1) p = parameters(period).gov.states.pa.tax.income.forgiveness base = p.base * base_multiplier rate_per_dependent = p.dependent_rate