Add Universal Credit deductions with Fair Repayment Rate cap - #1815
Conversation
Assigns latent deduction demand to UC households from DWP deductions statistics (March-May 2025, the last months under the 25% cap): deterministic id-hashed draws set incidence (region-adjusted), the rate distribution in bands of the standard allowance, and the type combination (advances, third party, government debt). The statutory cap is a dated parameter (40% to Oct 2019, 30%, 25%, 15% from 30 April 2025), applied to latent demand at runtime; last resort deductions above 25% are exempt. Reform levers: change the cap or abolish deduction types. Validation on enhanced FRS 2023/24 against DWP deductions statistics: incidence 45.8% (DWP: 46-47%); pre-FRR at-cap share 13.1% (13-14%); mean monthly deduction 67 GBP pre-FRR / 53 GBP post (67-68 / 51-54); mean annual FRR gain 421 GBP (DWP: 420 GBP). Aggregate levels scale with the model's UC caseload. Fixes #1814 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The Test job failure is environmental, not from this branch: every error in both runs is the same Hugging Face 401 Unauthorized downloading the private dataset ( |
Limits benefit cap reductions plus deductions to keep the award at or above floor x standard allowance (zero under current law), the reform shape of JRF's protected minimum floor: under the Fair Repayment Rate, its remaining bite comes mostly through benefit cap interactions, which the deductions cap alone cannot express. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Statutory parameters (cap, protected floor, abolition switches) stay under gov.dwp.universal_credit.deductions; the calibrated distributions (latent rate bands, type combinations, regional incidence, mean amounts by type) move to gov.simulation.uc_deductions - they describe the world, not the law, and are slated to migrate into the dataset build as imputed inputs, at which point the in-model fallback formulas retire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nal rebuild - Protected floor now caps combined reductions at (1 - floor) x standard allowance, matching JRF's worked example (reduction limited to 15% of the standard allowance), instead of flooring the payment; inactive at zero with arithmetic identical to the pre-floor formula. - Latent demand above 25% splits into a cappable portion and a last resort excess that sits on top of the cap (Schedule 6, SI 2013/380), instead of exempting the household's whole deduction. - Deductions leave one penny per assessment period payable (Schedule 6). - Regional incidence factors rebuilt from all 633 constituency rows (Table 6 labels regions only on their first row; the earlier parse kept one constituency per region): North West 0.95 -> 1.09, East of England 1.06 -> 0.93, and the rest re-derived. - Draw defaults move to 1.0 so high-incidence regions get no deductions in single-household simulations; splitmix output uses the top 53 bits so draws are strictly below 1. - Zero-mean reform guard in type-share division; AT_15/AT_25 shares corrected to the true three-month averages (.047/.133); statutory references added (SI 2013/380 Schedule 6, Budget 2018/2020) and observation-period provenance documented on estimation parameters. Validation after fixes: incidence 47.0-47.1% (DWP 46-47%), pre-FRR at-cap 13.4% (13-14%), above-cap 1.8% (2%), FRR mean annual gain 421 GBP (DWP: 420 GBP), 1.01m households better off (DWP: 1.2m). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Adversarial review round (GPT-5.6 sol, PolicyEngine review-program workflow) returned seven confirmed findings, all fixed in 9164354:
Documented rather than changed: the 1e6-weight microdata heuristic and float32 draw storage (noted limitations), and the 15%-regime at-cap gap (model 26.4% vs observed 21% — the latent-demand model holds demand fixed where reality attrits under the lower cap; stated in the body). Post-fix validation improved on five of seven headline statistics (table in the PR body updated). |
…ance - One-penny safeguard becomes gov.dwp.universal_credit.deductions .minimum_payable (0.01 GBP per assessment period, Schedule 6) instead of a hard-coded 12p; last-resort boundary becomes gov.simulation.uc_deductions.calibration_cap (0.25, the cap in force during the March-May 2025 calibration window). - Cap provenance corrected: Schedule 6 paragraph 4(1) sets a 40% statutory ceiling; the 30%/25%/15% caps are operational rates set administratively (the Fair Repayment Rate required no legislative change, per the EM to SI 2025/387); the 25% cap was announced for October 2021 at Budget 2020 and brought forward to April 2021 at Budget 2021. References re-pointed accordingly with file-page anchors. - Splitmix draws clamp below the largest float32 under 1, closing the storage-rounding endpoint hole. - Stale draw/deduction docstrings updated; period metadata added to dimensionless parameters; trailing-zero formatting normalized. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Verification round (sol, round 2) confirmed all seven round-1 fixes mechanically — including independently reproducing the regional derivation from the official ODS to 3e-16 and probe-testing the floor/split/penny across edge cases — and surfaced a second tier, fixed in the latest commit: the one-penny amount and last-resort boundary are now parameters ( |
DWP guidance treats last resort deductions (housing and fuel arrears hardship) and child maintenance as separate categories; both may exceed the operational cap (EM to SI 2025/387, paras 5.10-5.15, now cited with its page anchor). Wording aligned across the cap, calibration and distribution parameters and the rate formula; the excess is named above_cap_excess. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Reviewed for methodology, data provenance, and validity. The core design — latent demand calibrated to the last 25%-cap window, statutory cap applied at runtime, last-resort excess exempt — is sound, and the validation table (pre-FRR pileup and both regimes' means reproducing from one latent distribution without separate fitting) is convincing at the per-household level. Specific points:
Correctness/semantics
- Protected floor folds the above-cap excess in.
universal_creditlimitsbenefit_cap_reduction + uc_deductionsjointly, anduc_deductionsincludes the last-resort/child-maintenance excess. Under current law those categories can exceed the cap; whether JRF's floor is intended to bind on them too isn't obvious from their briefing (their worked example only involves cappable deductions and the benefit cap). Worth a one-line note onprotected_floor.yamlthat the modeled floor binds on all deductions including last-resort — and, if JRF's design exempts them, a follow-up. - Rate/incidence share one draw.
uc_latent_deduction_ratereusesuc_deduction_random_drawrescaled by regional incidence — conditional on assignment the quantile is uniform, so this is correct, but it hard-couples region to the rate quantile: in high-incidence regions the marginal households assigned by the regional uplift all land in the top of the rate distribution... actually they land uniformly since draw/incidence is uniform on [0,1) — fine. But note the rate distribution itself is national; regions plausibly differ in composition (more advances in high-incidence regions). Documented constancy assumption would help. uc_deduction_combinationzero-total branch returnsnp.full(..., dtype=object)while the normal branch returns aselectover Enum values — check this degrades gracefully to an EnumArray; a test reforming all seven shares to zero would cover it.- Historic years inherit the 2025 latent distribution (values from 2015-04-01) while the cap parameter is 40%/30% back then — so 2019–2024 simulations produce deduction levels from a 2025-shaped demand distribution truncated at higher caps. Defensible forward, not calibrated backward; the descriptions say "constancy assumption" but a sentence in the PR body on which years are considered validated (2024–2025 only) would prevent misquoting.
Validity for the target use cases
- Aggregates are the binding limitation for JRF-style claims. ~1.9m deducting households vs 3.3m admin means poverty counts and £ costs of a floor reform will run ~40% low regardless of per-household validation. Since the requesting users (WPI, charities) will want exactly those aggregates, I'd promote this from a limitations bullet to a prominent caveat in the changelog/docs, with a pointer to the caseload calibration issue in policyengine-uk-data.
- 15%-regime at-cap gap (26.4% vs 21%): holding latent demand fixed under the lower cap is the right first-order choice, and the direction of bias is stated. Implication worth adding: reforms that lower the cap further will overstate the number of constrained households and thus overstate gains; reforms raising the cap are on firmer ground since the latent distribution was observed under 25%.
- Annualization inflates annual amounts for short spells (advance recovery ≤24 months). Monthly means validate, so cross-sectional statistics are fine; annual per-household gains are upper bounds for spell-limited types.
Smaller items
retained(kept, total)uses Pythonmax(total, 1e-9)on what are parameter scalars — fine as written sincem.*are scalars, just noting it would break if these ever became arrays.- The 1e6-weight heuristic distinguishing microdata from calculators is pragmatic but silent: a dataset with unweighted benunits would get calculator behavior. The documented escape hatch (setting draws explicitly) covers it; the planned move of imputation to dataset build retires the whole issue — good end-state.
- Tests are genuinely strong: the cap-restoration reform test and the floor binding/non-binding pair cover the reform levers practitioners will actually pull.
Overall: methodology is publishable with the aggregate-caseload caveat made prominent; per-household statistics are the quotable layer, matching how the validation is framed.
Documents the limitations raised in review rather than tuning them away, and adds regression tests for the two behaviours that were implicit. - Aggregates: per-household statistics are the validated layer; weighted aggregates scale with the model's UC caseload shortfall. Promoted from a PR-body bullet to the changelog entry, the uc_deductions documentation and a new validation page. - Validated years: only 2024 (25% cap) and 2025 (15% cap) are checked against published statistics; earlier years apply the 2025-shaped latent distribution under the 40% and 30% caps then in force. - Cap direction: demand is held fixed as the cap moves, so cuts below 15% overstate the constrained population while increases toward 25% stay inside the observed window. - Protected floor: it binds on the above-cap excess (last resort and child maintenance) that current law exempts from the deductions cap. JRF's briefing does not settle this, so the choice is documented on the parameter, in universal_credit, and pinned by a test. - uc_deduction_combination: the all-shares-zero branch now uses filled_array, keeping it on the same encoding path as the select branch, with a test asserting it still yields an EnumArray. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
policyengine-core passes a token to hf_hub_download only when the repo reports private=True. policyengine-uk-data-private has been public and gated (manual approval) since 31 July 2026, so private is False, core sends token=None, the download goes out anonymously and the gate returns 401 GatedRepoError. Replacing the HUGGING_FACE_TOKEN secret could not fix this: no token was being sent at all. huggingface_hub falls back to the HF_TOKEN environment variable when the explicit token is None (get_token_to_send -> get_token), so exporting the same secret under that name restores authentication. This is a workaround. The durable fix belongs in policyengine-core, which should pass the token whenever one is available, or check gated alongside private. Refs #1816. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every dataset-backed job on main and on PRs has failed with 401 since 31 July 2026. The cause is not the HUGGING_FACE_TOKEN secret: no token is being sent at all. policyengine-core's download_huggingface_dataset passes a token to hf_hub_download only when the repo reports private=True (tools/hugging_face.py:81-90). policyengine-uk-data-private has been public + gated (manual approval) since 31 July, so private is False, core passes token=None, the download goes out anonymously and the gate returns 401 GatedRepoError. Replacing the secret on 10 August could not have fixed this, and the out-of-band validation of that token passed because it supplied the token explicitly - the step core skips. huggingface_hub falls back to the HF_TOKEN environment variable when the explicit token is None (get_token_to_send -> get_token), so exporting the same secret under that name restores authentication. Verified green on #1815, where the gated dataset downloads and the microsimulation suite passes (177 passed, 1 skipped). This is a workaround. The durable fix belongs in policyengine-core, which should pass the token whenever one is available, or test gated alongside private. Refs #1816. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for the review — all seven points are addressed in #1821: floor-scope note on |
… bias direction (#1821) * Document review findings on UC deductions scope and bias direction From the post-merge methodology review on #1815: the protected floor binds on all deduction categories including last resort and child maintenance (JRF's briefing does not state their intent for those); the rate distribution is national and validated for 2024-2025, with earlier years an uncalibrated extrapolation under higher caps; cap-lowering reforms overstate newly constrained households while cap raises rest on the observed 25%-cap distribution; annual amounts are upper bounds for spell-limited types. Adds a graceful-degradation test for zeroed combination shares and promotes the aggregate-caseload caveat to the changelog with pointers to policyengine-uk-data#452 and #450. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix YAML colon in latent distribution description Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Trim duplicated notes; correct validated-years wording Main already carried a fuller floor-binding note and a stronger zero-shares test; drop the redundant additions. State validation correctly: model years 2024-2025 are validated against the March 2025 to February 2026 observations (which begin March 2025). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Keep only the additions main lacks Main gained fuller validated-years and bias-direction notes in parallel; this branch now adds only the national-composition sentence, the annualization upper-bound note, and the promoted changelog caveat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Restore untouched lockfile Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Fixes #1814.
Adds Universal Credit deductions — the money DWP takes off awards to repay advances, government debt (benefit and tax credit overpayments) and third party debts — with the Fair Repayment Rate cap and reform levers. Requested by external model users building deduction reform analysis for charities in the poverty space.
Important
Per-household statistics are the validated layer. Weighted aggregates are not. Counts and costs scale with the model's UC caseload, which runs ~40% below the administrative count (PolicyEngine/policyengine-uk-data#452), so headcounts and exchequer costs of deduction and protected-floor reforms come out low in roughly that proportion. Users replicating published floor estimates should compare per-household gains and distributional shape, not totals. Full limitations: docs/book/validation/uc-deductions.md.
Design: latent demand, statutory cap
Other models assign the observed (post-cap) deduction rates directly, which bakes the cap into the data: raising the cap back to 25% becomes inexpressible. This implementation separates the two:
uc_latent_deduction_rateassigns deduction demand from the distribution DWP observed in March–May 2025 — the last full months under the 25% cap — in bands of the standard allowance (point masses at institutional rates: 5%, 10%, 15%, 20%, 25%).gov.dwp.universal_credit.deductions.capis a dated parameter (40% to Oct 2019 → 30% → 25% → 15% from 30 April 2025, landing on the repo's April-30 fiscal-year sampling instant) applied at runtime. Latent demand above 25% is last resort deductions (e.g. child maintenance), which the law exempts from the cap.uc_deduction_combinationassigns which of the three deduction types the household repays, from DWP's published seven-way combination distribution, so type-specific reforms operate household by household.Assignment uses deterministic splitmix64 hashes of
benunit_id— reproducible across runs and machines, dispersed like uniform draws, and overridable by datasets or situations (uc_deduction_random_draw,uc_deduction_type_random_draw). Single-household simulations get no deductions unless set explicitly, matching the repo's determinism conventions.Law vs estimation: statutory parameters (cap, protected floor, abolition switches) live under
gov.dwp.universal_credit.deductions; the calibrated distributions live undergov.simulation.uc_deductions— they describe the world, not the law. The assignment formulas are an explicit fallback: the end state imputes these attributes at dataset build (tracked in policyengine-uk-data), at which point the engine consumes them as plain inputs and the fallback retires. Raw-FRS users keep working through the fallback until then.Reform levers
A cap of
1 − xis equivalent to a protected minimum floor atxof the standard allowance for capped deduction types. For floor policies that also bind on the benefit cap — the shape of JRF's protected minimum floor, whose post-FRR effect comes mostly through benefit-cap interactions — a dedicated lever limits the combined reductions:The floor limits the combined reductions themselves to
(1 − floor) ×standard allowance — JRF's design (their worked example caps the reduction at 15% of the standard allowance) — and is zero (inactive, reductions unlimited) under current law.Validation (enhanced FRS 2023/24 v1.40.3, vs DWP deductions statistics)
The pre-FRR at-cap pileup and both regimes' mean amounts reproduce without being separately fitted — the cap parameter generates them from the same latent distribution. Two known gaps, stated rather than tuned away:
Other limitations
Tests
uc_deduction_combinationstill yielding anEnumArray.Documentation
New page docs/book/validation/uc-deductions.md: the validation table, every limitation above with its practical consequence for reform estimates, and the reform levers.
🤖 Generated with Claude Code