From 7e3a699351880f6f637261057f75c9da9cc301ed Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 6 Sep 2026 00:10:12 -0400 Subject: [PATCH 1/3] Default tenant_pays_utilities to true and derive utilities_included_in_rent The two inputs encoded the same utilities-responsibility fact as near-inverses with contradictory False defaults. tenant_pays_utilities becomes the canonical household-level input, defaulting to the common lease arrangement, and utilities_included_in_rent is derived as its inverse (still settable directly) for the Maine property tax fairness credit and the Michigan home heating credit. Co-Authored-By: Claude Fable 5.1 --- .../tenant-pays-utilities-default.changed.md | 1 + .../gov/hud/hud_utility_allowance.yaml | 10 ++++++++ .../policy/baseline/gov/hud/integration.yaml | 25 +++++++++++++++++++ .../housing/utilities_included_in_rent.yaml | 20 +++++++++++++++ ...erty_tax_fairness_credit_countable_rent.py | 8 +++--- .../expense/housing/tenant_pays_utilities.py | 3 +++ .../housing/utilities_included_in_rent.py | 5 ++++ 7 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 changelog.d/tenant-pays-utilities-default.changed.md create mode 100644 policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml diff --git a/changelog.d/tenant-pays-utilities-default.changed.md b/changelog.d/tenant-pays-utilities-default.changed.md new file mode 100644 index 00000000000..c698f7aa295 --- /dev/null +++ b/changelog.d/tenant-pays-utilities-default.changed.md @@ -0,0 +1 @@ +`tenant_pays_utilities` now defaults to true, so households in counties with an encoded HUD utility allowance schedule receive the allowance unless the input is set to false, and `utilities_included_in_rent` is derived as its inverse (still settable directly) so the Maine property tax fairness credit and the Michigan home heating credit read one utilities-responsibility fact. diff --git a/policyengine_us/tests/policy/baseline/gov/hud/hud_utility_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/hud/hud_utility_allowance.yaml index ac7da9bf2f8..ef6e018119f 100644 --- a/policyengine_us/tests/policy/baseline/gov/hud/hud_utility_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hud/hud_utility_allowance.yaml @@ -218,3 +218,13 @@ output: # 24 CFR 982.604(b): 0.75 * $210 (Wichita 0 BR) = $157.50/month. hud_utility_allowance: 157.5 * 12 + +- name: The allowance applies by default because tenants are assumed to pay utilities + period: 2023 + absolute_error_margin: 0.01 + input: + county_fips: "06037" + is_sro: false + bedrooms: 0 + output: + hud_utility_allowance: 227 * 12 diff --git a/policyengine_us/tests/policy/baseline/gov/hud/integration.yaml b/policyengine_us/tests/policy/baseline/gov/hud/integration.yaml index d82634ff5b2..76b6aa659f1 100644 --- a/policyengine_us/tests/policy/baseline/gov/hud/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hud/integration.yaml @@ -148,3 +148,28 @@ # the $3,576 allowance (11,976 - 3,576 = 8,400), confirming the flow-through. hud_hap: 8_400 housing_assistance: 8_400 + +- name: Outside LA, the utility allowance flows into HAP by default when tenant_pays_utilities is not set (Denton County, TX). + period: 2026 + absolute_error_margin: 1 + input: + people: + person1: + age: 40 + employment_income: 12_000 + pre_subsidy_rent: 12_000 + households: + household: + county_fips: "48121" + bedrooms: 2 + members: [person1] + spm_units: + spm_unit: + members: [person1] + receives_housing_assistance: true + output: + # Same household as the tenant_pays_utilities: true case above. + hud_utility_allowance: 298 * 12 + hud_gross_rent: 12_000 + 298 * 12 + hud_hap: 11_976 + housing_assistance: 11_976 diff --git a/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml b/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml new file mode 100644 index 00000000000..a76ac280226 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml @@ -0,0 +1,20 @@ +- name: Case 1, by default the tenant pays utilities, so none are included in rent. + period: 2026 + output: + tenant_pays_utilities: true + utilities_included_in_rent: false + +- name: Case 2, a tenant who does not pay utilities has them included in rent. + period: 2026 + input: + tenant_pays_utilities: false + output: + utilities_included_in_rent: true + +- name: Case 3, a direct value overrides the derivation. + period: 2026 + input: + tenant_pays_utilities: false + utilities_included_in_rent: false + output: + utilities_included_in_rent: false diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py index 198cc00be18..4e29c1b1c3f 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py @@ -13,10 +13,10 @@ class me_property_tax_fairness_credit_countable_rent(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.me.tax.income.credits.fairness.property_tax rent = add(tax_unit, period, ["rent"]) - # utilities_included_in_rent is a TaxUnit boolean (it lives in the - # household/ folder but is defined on the TaxUnit entity), used here as a - # proxy for Schedule PTFC/STFC line 5b (does rent paid include heat, - # utilities, furniture, or similar items?). + # utilities_included_in_rent is a TaxUnit boolean derived from the + # household-level tenant_pays_utilities input (and still settable + # directly), used here as a proxy for Schedule PTFC/STFC line 5b (does + # rent paid include heat, utilities, furniture, or similar items?). # Related sibling flag: heat_expense_included_in_rent (an SPMUnit bool used # by MA/IL LIHEAP) overlaps line 5b, but it is NOT OR'd into this gate. A # heat-only household (heat_expense_included_in_rent: true / diff --git a/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py b/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py index 049da05d224..ef8f4768a92 100644 --- a/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py +++ b/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py @@ -5,4 +5,7 @@ class tenant_pays_utilities(Variable): value_type = bool entity = Household label = "Whether the tenant is responsible for utility payments" + documentation = "Whether the household pays its own utilities rather than having them bundled into rent. Defaults to true, the common lease arrangement; set it to false when utilities are included in rent. The HUD utility allowance is available only when the tenant pays utilities, and utilities_included_in_rent is derived as the inverse of this input." definition_period = YEAR + default_value = True + reference = "https://www.law.cornell.edu/cfr/text/24/982.517" diff --git a/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py b/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py index 817184109a4..cc9d84eeada 100644 --- a/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py +++ b/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py @@ -5,4 +5,9 @@ class utilities_included_in_rent(Variable): value_type = bool entity = TaxUnit label = "Whether heat, utilities, furniture, or similar items are included in rent payments" + documentation = "Derived as the inverse of the household-level tenant_pays_utilities input, projected to the tax unit; it can still be set directly. Read by the Maine property tax fairness credit (Schedule PTFC line 5b) and, as implying heat is included, by the Michigan home heating credit." definition_period = YEAR + reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_pstfc_ff.pdf#page=2" + + def formula(tax_unit, period, parameters): + return ~tax_unit.household("tenant_pays_utilities", period) From 629afc253c70dc4892f227f0e6751a0c18f416cc Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 12 Sep 2026 21:25:19 -0400 Subject: [PATCH 2/3] Keep the SPM integration tests on the no-utility-allowance household The two Los Angeles County cases that exercise SPM accounting never set tenant_pays_utilities; with the new default they picked up the county's HUD utility allowance in housing assistance. Set the flag to false so they keep testing the cash leaves and the housing cap as written. Co-Authored-By: Claude Fable 5.1 --- .../household/income/spm_unit/spm_unit_net_income.yaml | 3 +++ policyengine_us/tests/unit/test_spm_integration_contract.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_net_income.yaml b/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_net_income.yaml index 1fb105a5c2d..392fbbe01ea 100644 --- a/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_net_income.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/spm_unit/spm_unit_net_income.yaml @@ -24,6 +24,9 @@ input: age: 30 county_fips: "06037" + # Los Angeles County has an encoded HUD utility allowance; keep it out + # of housing assistance so only the cash support leaves are summed. + tenant_pays_utilities: false spm_unit_market_income: 0 child_support_received: 1 workers_compensation: 2 diff --git a/policyengine_us/tests/unit/test_spm_integration_contract.py b/policyengine_us/tests/unit/test_spm_integration_contract.py index e03e00f18a1..3648c22528c 100644 --- a/policyengine_us/tests/unit/test_spm_integration_contract.py +++ b/policyengine_us/tests/unit/test_spm_integration_contract.py @@ -32,6 +32,9 @@ def household(*, people=None, county=None, earnings=0, rent=36_000): "members": members, "state_code": {YEAR: "CA"}, "pha_payment_standard": {YEAR: 36_000}, + # Los Angeles County has an encoded HUD utility allowance; keep gross + # rent equal to the rent so the cap comparisons hold as written. + "tenant_pays_utilities": {YEAR: False}, } if county is not None: location["county_fips"] = {YEAR: county} From 5d4a6d607e1c55f5589e79f64a500b62e53e4046 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 14 Sep 2026 15:17:47 -0400 Subject: [PATCH 3/3] Address review items on the tenant_pays_utilities default flip - Add a Maine countable-rent case that reaches the utilities-in-rent branch through tenant_pays_utilities alone, and an utilities_included_in_rent case overriding the default derivation to true; note that a direct input takes precedence in either direction. - Cite the 2025 Schedule PTFC/STFC (line 5b on page 1) in the two variables this PR touches instead of the 2022 form. - Wrap the tenant_pays_utilities documentation, note the household broadcast in the derivation, fix the stale Case 14 comment, and add the Maine coupling to the changelog. Co-Authored-By: Claude Fable 5.1 --- .../tenant-pays-utilities-default.changed.md | 2 +- ...rty_tax_fairness_credit_countable_rent.yaml | 18 ++++++++++++++++-- .../housing/utilities_included_in_rent.yaml | 12 +++++++++++- ...perty_tax_fairness_credit_countable_rent.py | 2 +- .../expense/housing/tenant_pays_utilities.py | 8 +++++++- .../housing/utilities_included_in_rent.py | 5 ++++- 6 files changed, 40 insertions(+), 7 deletions(-) diff --git a/changelog.d/tenant-pays-utilities-default.changed.md b/changelog.d/tenant-pays-utilities-default.changed.md index 19e747890fd..969d3ac69ce 100644 --- a/changelog.d/tenant-pays-utilities-default.changed.md +++ b/changelog.d/tenant-pays-utilities-default.changed.md @@ -1 +1 @@ -`tenant_pays_utilities` now defaults to true, so households in counties with an encoded HUD utility allowance schedule receive the allowance unless the input is set to false, and `utilities_included_in_rent` is derived as its inverse (still settable directly) so the Maine property tax fairness credit reads the same utilities-responsibility fact. +`tenant_pays_utilities` now defaults to true, so households in counties with an encoded HUD utility allowance schedule receive the allowance unless the input is set to false, and `utilities_included_in_rent` is derived as its inverse (still settable directly) so the Maine property tax fairness credit reads the same utilities-responsibility fact; a caller that sets `tenant_pays_utilities` to false without setting `utilities_included_in_rent` now gets the Maine utilities-in-rent treatment automatically. diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.yaml index 273d7b8d613..f03f49bddd4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.yaml @@ -193,8 +193,9 @@ rent: 1_000 output: me_property_tax_fairness_credit_countable_rent: 150 - # utilities_included_in_rent defaults to false -> utility portion 0; - # 1000 * 0.15 = 150. This is the path the microdata population takes. + # utilities_included_in_rent derives to false from the default + # tenant_pays_utilities (true) -> utility portion 0; 1000 * 0.15 = 150. + # This is the path the microdata population takes. absolute_error_margin: 0.01 - name: Case 15, unknown branch at the 2014 program start pins the start date. @@ -221,3 +222,16 @@ me_property_tax_fairness_credit_countable_rent: 127.5 # estimate = 15% * 1000 = 150; (1000 - 150) * 0.15 = 127.5. absolute_error_margin: 0.01 + +- name: Case 17, a tenant who does not pay utilities reaches the unknown-amount branch without setting the flag directly. + period: 2022 + input: + state_code: ME + rent: 1_000 + tenant_pays_utilities: false + output: + utilities_included_in_rent: true + me_property_tax_fairness_credit_countable_rent: 127.5 + # utilities_included_in_rent derives to true from the household input; + # estimate = 15% * 1000 = 150; (1000 - 150) * 0.15 = 127.5. + absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml b/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml index a76ac280226..2bd2592a59e 100644 --- a/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml +++ b/policyengine_us/tests/policy/baseline/household/expense/housing/utilities_included_in_rent.yaml @@ -11,10 +11,20 @@ output: utilities_included_in_rent: true -- name: Case 3, a direct value overrides the derivation. +# A direct input silently takes precedence over the derived value in either +# direction; the two inputs are not checked for consistency. +- name: Case 3, a direct false overrides the derivation when the tenant does not pay utilities. period: 2026 input: tenant_pays_utilities: false utilities_included_in_rent: false output: utilities_included_in_rent: false + +- name: Case 4, a direct true overrides the default derivation when the tenant pays utilities. + period: 2026 + input: + utilities_included_in_rent: true + output: + tenant_pays_utilities: true + utilities_included_in_rent: true diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py index 4e29c1b1c3f..413cd6e76e2 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/property_tax_fairness_credit/me_property_tax_fairness_credit_countable_rent.py @@ -8,7 +8,7 @@ class me_property_tax_fairness_credit_countable_rent(Variable): label = "Countable rent for Maine property tax fairness credit" definition_period = YEAR defined_for = StateCode.ME - reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_pstfc_ff.pdf#page=2" + reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_ptfc_fillable.pdf#page=1" def formula(tax_unit, period, parameters): p = parameters(period).gov.states.me.tax.income.credits.fairness.property_tax diff --git a/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py b/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py index ef8f4768a92..c1730ab2170 100644 --- a/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py +++ b/policyengine_us/variables/household/expense/housing/tenant_pays_utilities.py @@ -5,7 +5,13 @@ class tenant_pays_utilities(Variable): value_type = bool entity = Household label = "Whether the tenant is responsible for utility payments" - documentation = "Whether the household pays its own utilities rather than having them bundled into rent. Defaults to true, the common lease arrangement; set it to false when utilities are included in rent. The HUD utility allowance is available only when the tenant pays utilities, and utilities_included_in_rent is derived as the inverse of this input." + documentation = ( + "Whether the household pays its own utilities rather than having them " + "bundled into rent. Defaults to true, the common lease arrangement; set " + "it to false when utilities are included in rent. The HUD utility " + "allowance is available only when the tenant pays utilities, and " + "utilities_included_in_rent is derived as the inverse of this input." + ) definition_period = YEAR default_value = True reference = "https://www.law.cornell.edu/cfr/text/24/982.517" diff --git a/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py b/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py index 00413fb3332..bfbd29d6551 100644 --- a/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py +++ b/policyengine_us/variables/household/expense/housing/utilities_included_in_rent.py @@ -15,7 +15,10 @@ class utilities_included_in_rent(Variable): "instead." ) definition_period = YEAR - reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/22_1040me_sched_pstfc_ff.pdf#page=2" + reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_ptfc_fillable.pdf#page=1" def formula(tax_unit, period, parameters): + # The household value broadcasts to every tax unit in the household; + # a direct input on this variable is the only way to differentiate + # tax units that share a household. return ~tax_unit.household("tenant_pays_utilities", period)