Skip to content

Conversation

@MaxGhenis
Copy link
Collaborator

@MaxGhenis MaxGhenis commented Jan 17, 2026

Summary

Adopt the PolicyEngine US reform pattern and remove all gov.contrib references from gov/ variables. This provides a clean separation between core law (gov/) and policy proposals (contrib/).

Pattern Changes

The new pattern uses:

  1. A create_*() function that returns the Reform class directly
  2. A create_*_reform() wrapper that checks parameters and calls the creator
  3. A bypass parameter that allows skipping parameter checks for programmatic use

New Reform Files Created

Reform Purpose
policyengine/abolish_council_tax.py Removes council_tax from gov_tax/household_tax
policyengine/freeze_pension_credit.py Returns baseline pension credit values
policyengine/employer_ni_pension_exemption.py Exempts employer pension contributions from NI
policyengine/salary_sacrifice_haircut.py Applies broad-base employment income haircut
policyengine/two_child_limit_age_exemption.py Handles CTC/UC child limit age exemptions
ubi_center/basic_income_interactions.py Handles UBI means-testing, taxation, and CB withdrawal

Base Variables Updated

The following variables had gov.contrib conditional logic removed:

  • gov_tax.py, household_tax.py (abolish_council_tax)
  • child_benefit_respective_amount.py (withdraw_cb)
  • adjusted_net_income.py (include_in_taxable_income)
  • tax_credits_applicable_income.py, pension_credit_income.py, income_support_applicable_income.py, housing_benefit_applicable_income.py, uc_mif_capped_earned_income.py (include_in_means_tests)
  • pension_credit.py (freeze_pension_credit)
  • uc_individual_child_element.py, is_CTC_child_limit_exempt.py (two_child_limit age exemption)
  • ni_class_1_employer.py (employer_ni pension exemption)
  • salary_sacrifice_broad_base_haircut.py (haircut rate)

Example Usage

# Direct import (no parameter checks)
from policyengine_uk.reforms import abolish_council_tax_reform

# Create with bypass
from policyengine_uk.reforms import create_abolish_council_tax_reform
reform = create_abolish_council_tax_reform(parameters, period, bypass=True)

# Standard parameter-driven creation
reform = create_abolish_council_tax_reform(parameters, period)  # Only returns if contrib param is set

Test Plan

  • All 51 tests pass (excluding pre-existing UC taper rate failure)
  • All reform imports work correctly
  • bypass=True returns reforms without parameter checks
  • Pre-instantiated reforms available for direct import
  • No gov.contrib references remain in gov/ variables

🤖 Generated with Claude Code

MaxGhenis and others added 9 commits January 17, 2026 15:39
Adopt the PolicyEngine US reform pattern where:
1. A `create_*()` function returns the Reform class directly
2. A `create_*_reform()` wrapper checks parameters and calls the creator
3. A `bypass` parameter allows skipping parameter checks for programmatic use

This enables:
- Cleaner separation between core law (gov/) and policy proposals (contrib/)
- Programmatic reform application without parameter checks
- Pre-instantiated reform objects for direct import

All reform files updated:
- conservatives/household_based_hitc.py
- cps/marriage_tax_reforms.py
- policyengine/disable_simulated_benefits.py
- policyengine/adjust_budgets.py
- scotland/scottish_child_payment_reform.py (already had this pattern)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All contrib parameter behavior now handled via reforms:
- abolish_council_tax: removes council_tax from gov_tax/household_tax
- basic_income_interactions: handles UBI means-testing and taxation
- two_child_limit_age_exemption: handles CTC/UC age exemptions
- freeze_pension_credit: returns baseline pension credit values
- employer_ni_pension_exemption: exempts employer pension contributions
- salary_sacrifice_haircut: applies broad-base employment income haircut

New reform files created:
- policyengine/abolish_council_tax.py
- policyengine/freeze_pension_credit.py
- policyengine/employer_ni_pension_exemption.py
- policyengine/salary_sacrifice_haircut.py
- policyengine/two_child_limit_age_exemption.py
- ubi_center/basic_income_interactions.py

Base variables updated to remove conditional contrib logic:
- gov_tax.py, household_tax.py
- child_benefit_respective_amount.py
- adjusted_net_income.py
- tax_credits_applicable_income.py
- pension_credit_income.py, pension_credit.py
- income_support_applicable_income.py
- housing_benefit_applicable_income.py
- uc_mif_capped_earned_income.py
- uc_individual_child_element.py
- is_CTC_child_limit_exempt.py
- ni_class_1_employer.py
- salary_sacrifice_broad_base_haircut.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Following the US pattern, contrib parameters are now checked INSIDE
the reform variable formulas rather than applying all changes
unconditionally. This ensures each interaction (CB withdrawal,
taxable income, means tests) only activates when its specific
parameter is enabled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Key changes:
1. Apply structural reforms in CountryTaxBenefitSystem.__init__ so YAML
   tests (which use policyengine_core's test runner) also get reforms applied
2. Reform factories now always return reforms - formulas check params at
   calculation time, enabling dynamic parameter setting in tests
3. Removed unused period_ import from basic_income_interactions.py

This matches the policyengine-us architecture where structural reforms are
applied in both TaxBenefitSystem and Simulation initialization.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove basic_income from gov_spending.py adds list
- Remove basic_income deduction from housing_benefit_applicable_income.py
- Add gov_spending override to basic_income_interactions reform

This completes the separation of gov/ (actual law) from contrib/ concepts.
basic_income is now only added to gov_spending via the reform.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Created contrib_aggregates reform to add:
- wealth_tax, non_primary_residence_wealth_tax, LVT, carbon_tax to gov_tax/household_tax
- other_public_spending_budget_change to gov_spending

These contrib (policy proposal) variables are now only included via
structural reforms, keeping gov/ variables limited to actual law.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants