Conversation
housing_benefit_assessable_capital, housing_benefit_applicable_income and housing_benefit_tariff_income disregarded all income and capital whenever guarantee_credit was positive for a family with someone over State Pension age. guarantee_credit is computed whether or not the family claims Pension Credit (would_claim_pc) or can get it (is_pension_credit_eligible, false for mixed-age couples). So pensioners who do not take up Pension Credit, and mixed-age couples with a continuing Housing Benefit award, had their capital ignored and kept Housing Benefit above the £16,000 capital limit. SI 2006/214 reg 26 (NI SR 2006/406 reg 24) disregards the whole of the capital and income of "a claimant who is in receipt, or whose partner is in receipt, of a guarantee credit". The new in_receipt_of_guarantee_credit is true when the Pension Credit paid to the benefit unit includes a guarantee credit (pension_credit > 0 and guarantee_credit > 0). Under the Pension Credit freeze it keeps the baseline receipt, because the frozen award is the baseline award. The three Housing Benefit formulas now use it. The existing passport case in housing_benefit_capital.yaml set guarantee_credit 1 for a pensioner whose Pension Credit income exceeds the minimum guarantee, so no Pension Credit was paid; it now also sets is_pension_credit_eligible. Also documents the passport on the Housing Benefit docs page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Local Housing Allowance fix rewrites the means-test line of the docs page, so a note appended there would conflict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1909 (pensioner Council Tax Reduction) adds the same in_receipt_of_guarantee_credit file. Both branches now carry it byte-identical, so either can merge first without a conflict. Its formula, is_pension_credit_eligible & would_claim_pc & (guarantee_credit > 0), equals the previous (pension_credit > 0) & (guarantee_credit > 0) whenever pension_credit is computed: pension_credit is (guarantee_credit + savings_credit) x is_pension_credit_eligible x would_claim_pc, and savings_credit is never negative. The Enhanced FRS stores neither pension_credit nor guarantee_credit, and a re-run gives arrays identical to the previous run. The Housing Benefit formulas keep their SI 2006/214 reg 26 comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pensioner not claiming Pension Credit with capital of exactly £16,000 keeps Housing Benefit, since SI 2006/214 reg 43 and SSCBA 1992 s.134(1) bar entitlement only where capital "exceeds" the prescribed amount; at £16,000.01 there is no entitlement. Both fail on main, where the computed guarantee credit passports the capital. The rulespec-uk companion tests use the same two cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reviewed, and it looks right to me: approve with nits. I checked the law on legislation.gov.uk: SI 2006/214 regs 26, 2(5), 5(2) and 43; SR 2006/406 reg 24; SPCA 2002 s.4(1A); SSAA 1992 s.1(1); SPC Regs 2002 reg 13. I also hand-checked cases 1, 3, 6, 7 and 9. Locally, the HB and Pension Credit YAML tests pass (127), and so do the property tests (4). Merged together with #1926 there are no conflicts, and 136 pass.
The CI Test job was still pending when I looked. |
…4 saving) Doc-only; the formula is unchanged. Keeps the file byte-identical with #1927. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SI 2019/37 art 4 disapplies SPCA 2002 s.4(1A) for a mixed-age couple entitled to Pension Credit or Housing Benefit as that couple on 14 May 2019, and PolicyEngine gives Housing Benefit to a mixed-age couple only on a continuing award, which housing_benefit_eligible treats as standing in for that saving. is_pension_credit_eligible omits the saving, so it pays those couples no Pension Credit and the passport now drops for them, although in law they may be entitled to a guarantee credit. The mixed-age test cases, docs paragraph and changelog now say so instead of calling the change a legal correction. The two mixed-age cases no longer assert the computed guarantee credit, which #1896 makes 0 for a family not eligible for Pension Credit; their passport and Housing Benefit assertions stay. in_receipt_of_guarantee_credit takes the shared v2 text also carried by #1909: documentation and references only (SSAA 1992 s.1, SI 2019/37 art 4). The three Housing Benefit call sites now cite reg 26, NI reg 24 and reg 2(5), under which "in receipt" includes a person who would be but for SPC Regs 2002 reg 13, which PolicyEngine does not model. The tariff-income passport, redundant with the zero assessable capital, is kept for parallelism with reg 26's "capital and income" and says so. The capital test's comment calls 11,536.20 PolicyEngine's 2025 parameter, not the minimum guarantee. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Overlap note from #1896: that PR also edits |
Resolve the conflict in housing_benefit_assessable_capital: keep main's person-level capital for the claimant and partner (Lifetime ISA), and apply the Guarantee Credit receipt passport to the combined capital. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1909 added in_receipt_of_guarantee_credit.py with the same contents, so the file drops out of this PR's diff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, Vahid. All seven points are handled:
The branch has merged main (no force-push), and #1909's identical file has dropped out of the diff. An independent round-2 review is running. |
|
Cross-reference from #2000 (fixes #1993, the SI 2006/213 Sch 5 para 4 / Sch 6 para 5 passport for Income Support, income-based JSA and income-related ESA).
🤖 Generated with Claude Code |
Problem
housing_benefit_assessable_capital,housing_benefit_applicable_incomeandhousing_benefit_tariff_incomedisregarded all capital and income wheneverguarantee_credit > 0for a family with someone over State Pension age. Butguarantee_creditis computed whether or not the family claims Pension Credit (would_claim_pc, the dataset take-up draw or a household input), and whether or not PolicyEngine treats it as eligible (is_pension_credit_eligible, which is False for every mixed-age couple). So two groups were passported although PolicyEngine pays them no guarantee credit: pensioners who do not take up Pension Credit, and mixed-age couples with a continuing award. Their capital was ignored, and they kept Housing Benefit above the £16,000 capital limit.For non-claimants this PR follows the law: with no claim there is no award, so no guarantee credit is received. For mixed-age couples it follows the model, not the law. PolicyEngine gives a mixed-age couple Housing Benefit only on a continuing award, which
housing_benefit_eligibletreats as standing in for the SI 2019/37 art 4 saving. A saved couple is not excluded by SPCA 2002 s.4(1A), so it may be entitled to a guarantee credit in law, and if it receives one, reg 26 passports it. But PolicyEngine'sis_pension_credit_eligibleomits that saving, so saved couples may be entitled in law although PolicyEngine pays them nothing. Until the saving is modelled (Not in this PR, follow-up 1), removing their passport is a known move away from the law.is_pension_credit_eligibleFalse, because PolicyEngine omits the SI 2019/37 art 4 saving)The first three rows were rerun on
origin/main1c5b4d0 and on this branch on 2026-09-30.Law
housing_benefit_eligiblelets a mixed-age couple have Housing Benefit only on a continuing award, and its comment says "For mixed-age couples the reported award stands in for the SI 2019/37 art. 4 saving (reg 6A(5))".Reg 26 keys on receipt, not on a guarantee credit the family would get if it claimed. A guarantee credit is received only as part of an award of state pension credit, which needs entitlement under s.1 and a claim under SSAA s.1. PolicyEngine does not model the reg 13 minimum payment, so it pays every positive guarantee credit in an award, and reg 2(5) needs nothing more.
SPCA s.4(1A) excludes a mixed-age couple from state pension credit. Art 4 disapplies it for a member who was entitled to Pension Credit or Housing Benefit as that couple on 14 May 2019, until the saving ceases under art 4(2).
is_pension_credit_eligiblerequires every adult to be over State Pension age and has no such saving. So for saved couples, PolicyEngine's receipt is narrower than the law's. All texts were read from legislation.gov.uk CLML on 2026-09-30.Change
in_receipt_of_guarantee_credit(BenUnit, bool, invariables/gov/dwp/pension_credit/guarantee_credit/) isis_pension_credit_eligible & would_claim_pc & (guarantee_credit > 0).de434752…), and merged on 2026-10-01. This branch has merged main, so the file is no longer in this PR's diff; the HB call sites here read it from main.pension_creditis(guarantee_credit + savings_credit) × is_pension_credit_eligible × would_claim_pc(pension_credit_entitlementisdefined_foris_pension_credit_eligible), andsavings_creditis never negative. So the variable holds exactly whenpension_credit > 0andguarantee_credit > 0: the Pension Credit paid includes a guarantee credit. A property test checks this.pension_creditis supplied as an input rather than computed. The Enhanced FRS stores neitherpension_creditnorguarantee_credit.gov.contrib.freeze_pension_credit).pension_creditthen returns the baseline simulation's value. The variable likewise returns the baseline receipt, because the frozen award is the baseline award, guarantee credit included.housing_benefit_assessable_capital,housing_benefit_applicable_incomeandhousing_benefit_tariff_incomenow testany_over_SP_age & in_receipt_of_guarantee_credit, notany_over_SP_age & (guarantee_credit > 0).is_pension_credit_eligible). Theany_over_SP_ageguard is kept, so an input that sets receipt for a working-age family cannot passport it.housing_benefit_tariff_incomethe passport is redundant: tariff income is computed fromhousing_benefit_assessable_capital, which is already 0 for a passported family. It is kept for parallelism with reg 26's "capital and income", and a comment says so.pc_total_bnis unchanged.housing_benefit_capital.yamlsetguarantee_credit: 1for a pensioner whose Pension Credit income is £14,160 (£10,000 plus £4,160 deemed from £50,000 of capital). That is above PolicyEngine's 2025 parameter for the minimum guarantee (£221.85 × 52 = £11,536.20), so no Pension Credit is paid and the case failed. It now also setsis_pension_credit_eligible: true, and its expected values are unchanged.Invariants
These are property-tested in
test_housing_benefit_guarantee_credit_passport_properties.py:in_receipt_of_guarantee_creditholds exactly whenpension_credit > 0andguarantee_credit > 0. So it impliesis_pension_credit_eligibleandwould_claim_pc, and every adult in a receiving family is over State Pension age.in_receipt_of_guarantee_credit.is_pension_credit_eligibleomits, that difference is a known departure from the law until the saving is modelled.would_claim_pcfrom True to False never raises HB.Setup: four family shapes (single pensioner, pensioner couple, mixed-age couple, single working-age) across council, housing association, private and owner-occupier tenures. Hypothesis runs with
derandomize=Trueand 10 examples of up to 30 families each. EachSimulationbuilds its own tax-benefit system, so the old-formula swap in (3) cannot leak into the other tests.Tests
New
housing_benefit_guarantee_credit_passport.yaml, 10 cases. Values are hand-computed from the 2026 parameters: minimum guarantee £12,376 single and £18,889 couple; £1 a week per £500 (or part) over £10,000; aged allowance £13,312 single and £19,934.20 couple; taper 65%.is_pension_credit_eligibleis False because PolicyEngine omits the SI 2019/37 art 4 saving, so no Pension Credit is paid; capital £20,000; HB £0.I ran the file on
origin/main1c5b4d0, minus the new variable's lines. Cases 1, 3, 5, 8, 9 and 10 fail there and 2, 4, 6 and 7 pass. Case 9 fails on main only on its capital and tariff outputs; its HB is £6,240 either way.New
pension_credit/guarantee_credit/in_receipt_of_guarantee_credit.yaml, 6 unit cases:Robust to the other two HB means-test fixes, in either order. Every tenant rents from the council, so the LHA cap order cannot matter. The two cases whose HB depends on applicable income set
housing_benefit_applicable_income_disregardto 0, the value the earnings-disregard fix gives a family with no earnings. Cases 9 and 10 do not depend on it.I applied each sibling's committed diff on top of this branch (d8c24d9) in scratch copies. I then ran every HB and Pension Credit YAML, plus
uc_legacy_mutual_exclusion.yaml. Nothing failed:I also applied Replace generic child and adult flags with each programme's legal definitions #1896's
guarantee_credithunk at 16940d8. It addsdefined_for = "is_pension_credit_eligible", so a mixed-age couple's computed guarantee credit becomes 0. With it, the two new files give 16 passed.The independent review applied Split pension-age Housing Benefit allowances by when State Pension age was attained #1913 on top of Take Housing Benefit earnings disregards from net earnings only #1908 and Cap the eligible rent at the LHA before the Housing Benefit taper #1926 at 58ee875: 198 passed, 3 failed. All 3 failures are in Take Housing Benefit earnings disregards from net earnings only #1908's and Split pension-age Housing Benefit allowances by when State Pension age was attained #1913's own files, none in this PR's.
Mutation checks. Each mutated formula was run and the file restored. These ran at 2a507af, before cases 9 and 10 were added and before d8c24d9 removed the two mixed-age
guarantee_creditassertions, so the counts are for the tests at that commit.guarantee_credit > 0fails 4 of 4 properties and 6 YAML cases.guarantee_credit > 0 & would_claim_pcfails 3 properties and 2 cases.pension_credit > 0fails 2 cases (Savings Credit only).guarantee_credit > 0 & is_pension_credit_eligiblefails 4 cases.would_claim_pc & (guarantee_credit > 0)fails the 2 mixed-age cases and 2 of 4 properties.Runs on this branch.
uc_legacy_mutual_exclusion.yaml, at d8c24d9: 129 passed, 0 failed.policyengine-core test policyengine_uk/tests/policy -c policyengine_uk, at 58ee875: 1,292 passed, 0 failed. d8c24d9 removes two assertions and changes comments, documentation, docs and the changelog; it adds or removes no case. CI'sTestjob runs the full suites on the head.pytest policyengine_uk/tests -m "not microsimulation", at 8bad05c: 322 passed, 15 skipped, 30 deselected, 0 failed. Later commits change YAML files, comments, documentation, docs, the changelog and the property test file's docstring. That file was rerun above.ruff format --checkon the five changed Python files is clean.Dataset impact (Enhanced FRS 2024-25, current weights, real runs of main vs this branch)
Read the headline as a range: −£0.0014bn to −£0.423bn. Almost all of the −£0.423bn comes from one pension-age profile whose counted capital is mostly imputed
corporate_wealth. Withoutcorporate_wealthas HB capital, the fix moves HB by −£0.0014bn (see the caveats; #1837 raises the same question for UC). The change is a means-test correction. It is not progress on the gap between PolicyEngine's pension-age HB and DWP's outturn, which #1901 traced to the weights.I re-ran the branch after switching to the shared formula form. All 39 per-benefit-unit output arrays are identical to the earlier run. The formulas have not changed since: d8c24d9 changed comments, documentation and references only. The independent review re-ran it and got identical arrays.
There are no gainers. HB recipients with a passport fall from 1,538.9k to 1,433.4k (−105.5k):
is_pension_credit_eligibletreats as not eligible.Why the capital limit matters and the income side rarely does:
Caveats:
would_claim_pcFalse and a computed guarantee credit, and its counted capital is over £16,000.corporate_wealth.corporate_wealth.corporate_wealthstill carry it in that bucket. This dataset has noprivate_pension_wealthcolumn.corporate_wealthremoved from the HB capital sources (one parameter changed, Pension Credit untouched). The fix then moves HB by −£0.0014bn: 2 records, the mixed-age ESA profile, which is itself a move away from the law (next caveat).corporate_wealthas HB capital. What remains without it runs the wrong way until follow-up 2 lands. Exclude corporate_wealth from Universal Credit countable capital pending a pension split #1837 raises the same question for UC.housing_benefit_eligibletreats as standing in for the SI 2019/37 art 4 saving. Art 4(1)(b) saves a couple through Housing Benefit as well as through Pension Credit.is_pension_credit_eligibleomits the saving, so it pays these couples no Pension Credit. This PR follows the Pension Credit PolicyEngine pays, so it removes their passport.would_claim_pcis True, and counted capital is over £16,000, mostlycorporate_wealth. On main the computed guarantee credit passports its capital; on this branch it loses all its HB. If the couple is saved under art 4, as its reported Pension Credit and continuing award suggest, it can be awarded a guarantee credit, and reg 26 then disregards its capital, so main's award is the law's.would_claim_pcis the dataset's take-up draw. None of the pension-age losers reports Pension Credit.The scripts and outputs are in the research folder, not in this repo:
dataset_impact.pyandcompare_impact.py(shared),diag_gc.py,diag_gc_reported.py,trace_gc_records.pyandsensitivity_gc_corporate_wealth.py.Not in this PR
SI 2006/214 reg 27. Savings-credit-only awards use the DWP's income and capital assessment. This is not modelled; Savings Credit only families are means-tested as before.
Other readers of a computed guarantee credit as receipt.
parameters/gov/dfe/targeted_childcare_entitlement/qualifying_benefits.yamllistsguarantee_credit, andtargeted_childcare_entitlement_eligiblesums the list. So a computed, unpaid guarantee credit qualifies a family.in_receipt_of_guarantee_creditis available for a follow-up.Pensioner Council Tax Reduction. Disregard guarantee credit recipients' income and capital in pensioner council tax reduction #1909 applies the CTR passport (SI 2012/2885 Sch 1 para 13 and the Welsh and Scottish equivalents) with the same variable.
Follow-up 1: the SI 2019/37 art 4 saving in
is_pension_credit_eligible. Model the saving, for example for a mixed-age couple with a reported Pension Credit award or a continuing Housing Benefit award. That would restore the passport for saved couples such as the first mixed-age profile above, and it moves Pension Credit totals. Replace generic child and adult flags with each programme's legal definitions #1896 also notes that the saving is not modelled.Follow-up 2: the SI 2006/213 passport for Housing Benefit claimants on income support, income-based JSA or income-related ESA. (Disregard the whole income and capital of HB claimants on IS, income-based JSA or income-related ESA (HB Regs Sch 5 para 4, Sch 6 para 5) #1993). Sch 5 para 4 disregards the whole of their income and Sch 6 para 5 the whole of their capital (quoted below).
housing_benefit_applicable_incomecountsincome_support,jsa_incomeandesa_incomeas income, and nothing passports them.Which HB instrument applies. In the second mixed-age profile above, the younger partner receives income-related ESA.
Overlap with Replace generic child and adult flags with each programme's legal definitions #1896 (
remove-is-child-flags, 16940d8). It edits two files this PR also touches:housing_benefit_assessable_capital.py, the adult-share proxy, which ends two lines above the passport line changed here;housing_benefit_capital.yaml, a case name.It also adds
defined_for = "is_pension_credit_eligible"toguarantee_credit, which makes a mixed-age couple's computed guarantee credit 0. The two mixed-age cases here do not assert it, and they pass with that hunk (see Tests).git merge-treeof this branch with Replace generic child and adult flags with each programme's legal definitions #1896's head is clean.Overlap with Use the State Pension Credit qualifying age, and start-of-year status for Class 4 NI #1907 (
spc-qualifying-age-class-4-spa, draft, cae3131).any_over_SP_agetoany_over_qualifying_age(fromhas_attained_state_pension_credit_qualifying_age), and it still testsguarantee_credit > 0.git merge-treewith this branch conflicts in exactly those three files. The resolved line in each is below. Keep Use the State Pension Credit qualifying age, and start-of-year status for Class 4 NI #1907'sany_over_qualifying_agefor the tariff-income thresholds.is_pension_credit_eligiblerequires every adult to have attained the qualifying age. So receipt implies the age term, and the two forms agree whenever receipt is computed.in_receipt_of_guarantee_creditis supplied as an input for a family with no one at the qualifying age. There the age term keeps this PR's guard. It is also the scope of SI 2006/214 itself (reg 5(1): "these Regulations apply to a person who has attained the qualifying age for state pension credit").The other two HB means-test fixes (Take Housing Benefit earnings disregards from net earnings only #1908, earnings disregards; Cap the eligible rent at the LHA before the Housing Benefit taper #1926, the LHA cap before the taper) are separate PRs.
git merge-treeof this branch is clean with each of these:axiom: SI 2006/214 reg 26 — TheAxiomFoundation/rulespec-uk#380 queued
🤖 Generated with Claude Code