Repository navigation
Conversation
programs.yaml now flags each term of household_tax and household_benefits with `budget`, an optional `group`, and the council tax abolition and benefit uprating rules. Terms that no programme entry carries sit in a new `budget_terms` section with a `kind`, and `budget_order` fixes the summation order. policyengine_uk.programs reads the file, and budget_programs() returns each term with its label, sign and group. HOUSEHOLD_TAX_VARIABLES and HOUSEHOLD_BENEFIT_VARIABLES keep their modules, contents and order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6Kyd2hZt9Nh1cPJ8mH
policyengine_uk.programs.budget_programs() is reachable after `import policyengine_uk` without relying on the variable modules having imported the submodule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6Kyd2hZt9Nh1cPJ8mH
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles. Checks failing on both base and head (1)
Counted-once checkEach of 92 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
|
Ready for hub review Head Items
Tests run
I ran the 549-test batch before commit Mutation and revert checks (scratch worktree)
Aggregate impact report
The check failing on both sides is Skipped
Judgment calls
Pre-Budget lists
Generated by Claude Code |
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
Addresses #2702
Reproducer: n/a: this changes no result
axiom: n/a: this changes how the package lists programmes and changes no rule or parameter value
What the code does now
policyengine_uk/programs.yamldefines the budget totals as well as model coverage.programsentry whosevariablea total adds carriesbudget: taxorbudget: benefit, and an optionalgroupfor terms that move together (for example the five childcare entitlements, or ESA's two parts).budget_termssection. Each has akind:programme_measure(the amount counted for a listed programme, such asesa_incomefor ESA, withprogramnaming it),programme(a programme the coverage list does not carry yet),reform_measure(not a programme: a hypothetical tax or benefit, or a change from baseline such asvat_changeand the employer NI incidence terms) orpublic_service(public services in kind). These entries stay out ofprograms, so the coverage page andmodelled_policiesnever list them.budget_dropped_when: gov.contrib.abolish_council_taxsits on council tax, the High Value Council Tax Surcharge and Council Tax Reduction.budget_upratingsits on the State Pension ([all]) and basic income ([]); every other benefit term takes bothgov.contrib.benefit_upratingrates.budget_ordergives each total's summation order. The loader refuses a registry whose flags and order disagree.policyengine_uk/programs.pyreads the YAML with no import from the package's top level.budget_programs()returns oneBudgetProgramper term (variable, label,taxorbenefit, group, kind, programme id, dropped-when parameter, uprating rates), taxes first, each in summation order.household_taxandhousehold_benefitsbuild their lists from it, andHOUSEHOLD_TAX_VARIABLESandHOUSEHOLD_BENEFIT_VARIABLESstay in their modules with the same contents in the same order.build_metadata.DATA_BUILD_SURFACEnow includesprograms.py.No law changes, so this cites none.
Invariants
HOUSEHOLD_TAX_VARIABLESandHOUSEHOLD_BENEFIT_VARIABLESequalmain's lists, element by element.budget_dropped_whenparameter is true;household_taxreads them inbudget_order.budget_programs()variable exists in the system and appears once, and its sign agrees with the list it came from.budget_orderand refuses one whose order drops or repeats a flagged term.household_tax,household_benefitsandhousehold_net_incomeequalmain's values exactly: the totals add the same float32 terms in the same order.Tests
New tests:
tests/test_budget_programs.py(16 tests): drift between the registry and what each total's formula reads (traced, at current law and with council tax abolished); the differential test againstmain's two lists, frozen as literals in order, and its council tax and uprating rules;budget_programs()from a fresh interpreter, importingpolicyengine_uk.programs, thehousehold_taxmodule or the package first; and Hypothesis properties on the real registry and on generated registries.tests/test_budget_totals_pinned.py(17 tests):household_tax,household_benefitsandhousehold_net_incomefor 12 hand-built households in 2023, 2025, 2026 and 2028 under four reforms (current law;abolish_council_tax; both uprating rates; both together with a flat basic income), 192 cells, with exact equality. The values come frommainat 2da212a, and the file passes onmain's code too.test_budget_programs.pytest_budget_totals_pinned.pyon this branch, and onmain's codetest_program_registry.py,test_fiscal_partition.py,test_household_benefits_properties.py,test_gov_spending_properties.py,test_uc_statutory_pay_properties.py,tests/aggregate_impact,test_build_metadata.pypolicyengine_bundles)tests/code_healthformat --check,check,check --select F811,F821;git diff --checkuv buildpolicyengine_uk/programs.pyandpolicyengine_uk/programs.yamlMutation checks on the two new test files (each change made in a scratch copy):
income_taxandnational_insuranceinbudget_order.taxbudget_uprating: [all]budget_dropped_whenfrom the High Value Council Tax Surchargehousehold_taxignorebudget_dropped_whenbudget: benefitfrom Scottish Child Payment, order unchangedbudget_order.benefit and the entries flagged budget: benefit differ: only ordered ['scottish_child_payment']main's two formula modulesGenerated by Claude Code