diff --git a/changelog.d/tx-rebate.added.md b/changelog.d/tx-rebate.added.md new file mode 100644 index 00000000000..95718daf4c8 --- /dev/null +++ b/changelog.d/tx-rebate.added.md @@ -0,0 +1 @@ +Texas $1,500 per-household rebate proposal as a contributed reform. diff --git a/policyengine_us/parameters/gov/contrib/states/tx/rebate/amount.yaml b/policyengine_us/parameters/gov/contrib/states/tx/rebate/amount.yaml new file mode 100644 index 00000000000..ad6d77710f7 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/tx/rebate/amount.yaml @@ -0,0 +1,14 @@ +description: Texas provides this rebate per household under the proposed Money in Your Pocket agenda (the campaign's "families" modeled as households; eligibility unspecified in the proposal). + +values: + 0000-01-01: 0 + 2027-01-01: 1_500 + 2028-01-01: 0 + +metadata: + unit: currency-USD + period: year + label: Texas rebate check amount + reference: + - title: Gina Hinojosa campaign - Money in Your Pocket economic agenda + href: https://ginafortexas.com/2026/07/recap-gina-hinojosa-launches-money-in-your-pocket-economic-agenda-across-houston-san-antonio-and-laredo/ diff --git a/policyengine_us/parameters/gov/contrib/states/tx/rebate/in_effect.yaml b/policyengine_us/parameters/gov/contrib/states/tx/rebate/in_effect.yaml new file mode 100644 index 00000000000..85dd28226d9 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/tx/rebate/in_effect.yaml @@ -0,0 +1,12 @@ +description: Texas provides the per-household rebate check if this is true, as part of the proposed Money in Your Pocket agenda. + +values: + 0000-01-01: false + +metadata: + unit: bool + period: year + label: Texas rebate check in effect + reference: + - title: Gina Hinojosa campaign - Money in Your Pocket economic agenda + href: https://ginafortexas.com/2026/07/recap-gina-hinojosa-launches-money-in-your-pocket-economic-agenda-across-houston-san-antonio-and-laredo/ diff --git a/policyengine_us/reforms/reforms.py b/policyengine_us/reforms/reforms.py index 840825735bf..fda21e9fbde 100644 --- a/policyengine_us/reforms/reforms.py +++ b/policyengine_us/reforms/reforms.py @@ -260,6 +260,9 @@ from .states.ct.hb5114 import ( create_ct_hb5114_reform, ) +from .states.tx.rebate import ( + create_tx_rebate_reform, +) from .congress.watca import ( create_watca_reform, ) @@ -529,6 +532,7 @@ def create_structural_reforms_from_parameters(parameters, period): ct_tax_rebate_2026 = create_ct_tax_rebate_2026_reform(parameters, period) ct_hb5009 = create_ct_hb5009_reform(parameters, period) ct_hb5114 = create_ct_hb5114_reform(parameters, period) + tx_rebate = create_tx_rebate_reform(parameters, period) al_hb527_overtime_deduction = create_al_hb527_overtime_deduction_reform( parameters, period ) @@ -674,6 +678,7 @@ def create_structural_reforms_from_parameters(parameters, period): ct_hb5114, ct_sb100, ct_tax_rebate_2026, + tx_rebate, al_hb527_overtime_deduction, ca_ab2591, ga_sb520, diff --git a/policyengine_us/reforms/states/tx/__init__.py b/policyengine_us/reforms/states/tx/__init__.py new file mode 100644 index 00000000000..bdaed3054c9 --- /dev/null +++ b/policyengine_us/reforms/states/tx/__init__.py @@ -0,0 +1,3 @@ +from .rebate import ( + create_tx_rebate_reform, +) diff --git a/policyengine_us/reforms/states/tx/rebate/__init__.py b/policyengine_us/reforms/states/tx/rebate/__init__.py new file mode 100644 index 00000000000..d85dcc0b45a --- /dev/null +++ b/policyengine_us/reforms/states/tx/rebate/__init__.py @@ -0,0 +1,3 @@ +from .tx_rebate import ( + create_tx_rebate_reform, +) diff --git a/policyengine_us/reforms/states/tx/rebate/tx_rebate.py b/policyengine_us/reforms/states/tx/rebate/tx_rebate.py new file mode 100644 index 00000000000..2a6d7f163f9 --- /dev/null +++ b/policyengine_us/reforms/states/tx/rebate/tx_rebate.py @@ -0,0 +1,158 @@ +"""Texas "Money in Your Pocket" one-time rebate reform. + +Proposal: Gina Hinojosa's "Money in Your Pocket" economic agenda proposes a +one-time $1,500 rebate check to Texas families, funded by an ~$17 billion draw +on the state's Economic Stabilization Fund. +Source: https://ginafortexas.com/2026/07/recap-gina-hinojosa-launches-money-in-your-pocket-economic-agenda-across-houston-san-antonio-and-laredo/ + +Modeling assumptions: the proposal does not specify the eligibility unit or an +income limit. PolicyEngine models one check per household with no income limit -- +the reading consistent with the campaign's stated ~$17 billion Economic +Stabilization Fund draw. The campaign's "families" are therefore operationalized +as households. The rebate is paid once (2027) and reverts to $0 from 2028. +""" + +from policyengine_us.model_api import * +from policyengine_core.periods import period as period_ +from policyengine_core.periods import instant + + +def create_tx_rebate() -> Reform: + class tx_rebate(Variable): + value_type = float + entity = Household + label = "Texas rebate check" + unit = USD + definition_period = YEAR + defined_for = StateCode.TX + reference = "https://ginafortexas.com/2026/07/recap-gina-hinojosa-launches-money-in-your-pocket-economic-agenda-across-houston-san-antonio-and-laredo/" + + def formula(household, period, parameters): + return parameters(period).gov.contrib.states.tx.rebate.amount + + def modify_parameters(parameters): + state_benefits = parameters.gov.household.household_state_benefits + current_benefits = state_benefits(instant("2027-01-01")) + if "tx_rebate" not in current_benefits: + new_benefits = list(current_benefits) + ["tx_rebate"] + state_benefits.update( + start=instant("2027-01-01"), + stop=instant("2027-12-31"), + value=new_benefits, + ) + return parameters + + class spm_unit_benefits(Variable): + value_type = float + entity = SPMUnit + label = "Benefits" + definition_period = YEAR + unit = USD + + def formula(spm_unit, period, parameters): + BENEFITS = [ + "social_security", + "ssi", + "in_ssp", + "ct_ssp", + "ga_ssp", + "al_ssp", + "ak_ssp", + "dc_ossp", # DC benefits + "id_aabd", # Idaho benefits + "ky_ssp", # Kentucky benefits + "de_ssp", # Delaware benefits + "fl_oss", + "ks_sspp", # Kansas benefits + "hi_oss", + "la_oss", # Louisiana benefits + "ma_state_supplement", # Massachusetts benefits + "md_paa", # Maryland benefits + "wa_ssp", # Washington benefits + "mi_ssp", # Michigan benefits + "me_ssp", # Maine benefits + "mo_ssp", # Missouri benefits + "mn_msa", # Minnesota benefits + "ne_aabd", # Nebraska benefits + # California programs. + "ca_cvrp", # California Clean Vehicle Rebate Project. + # Colorado programs. + "co_ccap_subsidy", + "co_state_supplement", + "co_oap", + # Washington programs. + "wa_child_care_subsidies", + # New Mexico programs. + "nm_ssi_state_supplement", + # South Carolina programs. + "sc_ssi_state_supplement", + # Texas programs. + "tx_ssi_state_supplement", + # West Virginia programs. + "wv_child_care_subsidies", + "snap", + "wic", + "free_school_meals", + "reduced_price_school_meals", + "child_support_received", + "workers_compensation", + "educational_assistance", + "financial_assistance", + "survivor_benefits", + "spm_unit_energy_subsidy", + "tanf", + # Washington (WA) cash-assistance programs. wa_sfa and wa_rca + # sit alongside the federal TANF aggregator entry; under default + # rules these three are mutually exclusive at the SPM-unit level + # so summing them does not double-count. + "wa_sfa", + "wa_rca", + "high_efficiency_electric_home_rebate", + "residential_efficiency_electrification_rebate", + "unemployment_compensation", + # One-time energy relief payments. + # Paid at the same time as the Alaska Permanent Fund Dividend, + # which is part of IRS gross income. + "ak_energy_relief", + # Contributed. + "basic_income", + "ny_drive_clean_rebate", + "tx_rebate", + ] + if parameters(period).gov.contrib.ubi_center.flat_tax.deduct_ptc: + BENEFITS.append("assigned_aca_ptc") + if not parameters(period).gov.hud.abolition: + BENEFITS.append("spm_unit_capped_housing_subsidy") + return add(spm_unit, period, BENEFITS) + + class reform(Reform): + def apply(self): + self.update_variable(tx_rebate) + self.update_variable(spm_unit_benefits) + self.modify_parameters(modify_parameters) + + return reform + + +def create_tx_rebate_reform(parameters, period, bypass: bool = False): + if bypass: + return create_tx_rebate() + + p = parameters.gov.contrib.states.tx.rebate + + reform_active = False + current_period = period_(period) + + for i in range(5): + if p(current_period).in_effect: + reform_active = True + break + current_period = current_period.offset(1, "year") + + if reform_active: + return create_tx_rebate() + else: + return None + + +tx_rebate = create_tx_rebate_reform(None, None, bypass=True) diff --git a/policyengine_us/tests/policy/contrib/states/tx/rebate/tx_rebate.yaml b/policyengine_us/tests/policy/contrib/states/tx/rebate/tx_rebate.yaml new file mode 100644 index 00000000000..b1e56bc9dde --- /dev/null +++ b/policyengine_us/tests/policy/contrib/states/tx/rebate/tx_rebate.yaml @@ -0,0 +1,183 @@ +# Texas $1,500 rebate check ("Money in Your Pocket" proposal) +# One-time $1,500 per household, no income limit, TX residents, 2027. +# Source: Hinojosa campaign press release, July 13, 2026. + +- name: Case 1, Texas household receives the rebate. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + tx_rebate: 1_500 + +- name: Case 2, non-Texas household gets no rebate. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: CA + output: + tx_rebate: 0 + +- name: Case 3, rebate flows into household benefits and net income. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + # High income so no other benefits fire; only the rebate remains. + employment_income: 500_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + # tx_rebate is appended to household_state_benefits, which household_benefits + # adds; household_net_income adds household_benefits, so net income rises by + # exactly 1,500. Assert the two benefit aggregates (robust to tax churn). + tx_rebate: 1_500 + household_state_benefits: 1_500 + household_benefits: 1_500 + +- name: Case 4, rebate is included in spm_unit_benefits. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + # High income so baseline SPM benefits are 0; only the rebate remains. + employment_income: 500_000 + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + tx_rebate: 1_500 + spm_unit_benefits: 1_500 + +- name: Case 5, zero-income Texas household still receives the rebate. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + employment_income: 0 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + tx_rebate: 1_500 + +- name: Case 6, high-income Texas household still receives the rebate. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + # $1M income confirms there is no income limit on the rebate. + employment_income: 1_000_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + tx_rebate: 1_500 + +- name: Case 7, before the 2027 effective date the rebate is zero. + period: 2026 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + # amount parameter is 0 before 2027-01-01, so the rebate is 0 in 2026 + # even with in_effect true. + tx_rebate: 0 + +- name: Case 8, after the one-time 2027 payment the rebate does not recur. + period: 2028 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.tx.rebate.tx_rebate.tx_rebate + input: + gov.contrib.states.tx.rebate.in_effect: true + people: + person1: + age: 35 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + # amount parameter reverts to 0 at 2028-01-01, so the one-time payment does + # not recur -- neither directly nor through the spm_unit_benefits path. + tx_rebate: 0 + spm_unit_benefits: 0