From dffb53b525146ad305b78b90f87187acbf0db92d Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Wed, 27 May 2026 11:37:49 +0200 Subject: [PATCH 1/7] Add UK pipeline alignment planning doc (#1621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1621 is an umbrella tracker. Persist it in-repo as a docs page so the queue of architectural fixes has a stable reference. Auditing the current state: - The three small bugs flagged in the issue body (tax_credits min_benefit unit, benunit_weekly_hours label, state_pension_type heuristic) have all been fixed since the issue was filed. Note them as done. - The state pension residual is documented separately in #1632 (now covered by docs/book/programs/gov/dwp/state-pension.md), so this page links there rather than restating. The remaining architectural gaps captured: - runtime _reported consultation in would_claim_housing_benefit, would_claim_IS, would_claim_WTC, would_claim_CTC — needs conversion to input-only matching the UC/PC pattern, with stochastic assignment in policyengine-uk-data; - take-up assignment in uk-data ignores reported data — needs port of the US assign_takeup_with_reported_anchors helper; - second-stage QRF gap on SPI donors (analogous to policyengine-us-data#589) so consequential FRS-only variables (rent, gift_aid, pension contributions, *_reported benefits) are consistent with imputed income; - residual aggregate gaps against OBR (income_support, esa_contrib, attendance_allowance, tax_free_childcare, state_pension). Ends with a candidate PR sequence so the receiving repos (this one + policyengine-uk-data) have a low-risk-first ordering. --- changelog.d/1621.md | 1 + .../assumptions/uk-pipeline-alignment-plan.md | 146 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 changelog.d/1621.md create mode 100644 docs/book/assumptions/uk-pipeline-alignment-plan.md diff --git a/changelog.d/1621.md b/changelog.d/1621.md new file mode 100644 index 0000000000..f495e4ee59 --- /dev/null +++ b/changelog.d/1621.md @@ -0,0 +1 @@ +- Add a planning docs page at `docs/book/assumptions/uk-pipeline-alignment-plan.md` capturing the architectural gaps between the UK model/data pipeline and the US `policyengine-us` / `policyengine-us-data` patterns — remaining `_reported`-at-runtime `would_claim_*` formulas, take-up anchoring, second-stage QRF for SPI-donor FRS-only variables — and the residual benefit-aggregate gaps against OBR after the recent BASIC/NEW state-pension fixes. diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md new file mode 100644 index 0000000000..6faf357880 --- /dev/null +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -0,0 +1,146 @@ +# UK model/data pipeline alignment plan + +```{note} +**Planning page** capturing the gaps between the UK model + enhanced FRS +pipeline and the US `policyengine-us` / `policyengine-us-data` patterns, +plus a sequence of scoped PRs that would close them. Tracked under +[#1621](https://github.com/PolicyEngine/policyengine-uk/issues/1621). +Most items here are **cross-repo** — they touch +`policyengine-uk-data` rather than this repo — but the model-side +contract is documented here so the receiving side has a stable target. +``` + +## Architectural gaps vs US + +### 1. `_reported` consulted at runtime in some `would_claim_*` formulas + +**US pattern**: `_reported` columns are consulted **once, in the +data-build**, to anchor stochastic take-up flags (reported recipient → +takeup = True with certainty; rest filled probabilistically to a target +rate). Runtime formulas only read the pre-computed +`takes_up_X_if_eligible` flag, so reforms that change eligibility flow +through cleanly to take-up. + +**UK pattern (partial)**: some UK `would_claim_*` variables already +match — `would_claim_uc` and `would_claim_pc` are input-only with +`default_value = True`, populated stochastically in +`policyengine-uk-data/datasets/frs.py`. But several still derive from +`_reported` at runtime: + +- `would_claim_housing_benefit` +- `would_claim_IS` +- `would_claim_WTC` +- `would_claim_CTC` + +The runtime pattern is `claims_all_entitled_benefits | (foo_reported > 0)`, +which means a reform expanding eligibility only reaches existing +`_reported > 0` claimants — non-claimants who newly qualify are +invisible unless the user manually sets +`claims_all_entitled_benefits = True` everywhere. + +**Fix**: convert each to input-only `default_value = True` (matching +`would_claim_uc`), populate stochastically in +`uk-data/datasets/frs.py`. Note: WTC and CTC ceased to pay new awards on +2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), +so their fix is primarily for back-cast simulations. + +### 2. Take-up assignment ignores reported data + +Today's UK take-up assignment is a pure random draw: + +```python +pe_benunit["would_claim_uc"] = generator.random(len(pe_benunit)) < universal_credit_rate +``` + +This discards information: respondents who reported receiving UC +clearly took it up, and should have `would_claim_uc = True` with +certainty. + +**Fix**: port `policyengine_us_data/utils/takeup.py::assign_takeup_with_reported_anchors` +into `policyengine-uk-data` and apply it to each take-up variable where +a `_reported` column exists. This tightens per-variable calibration +without changing the target rates. + +### 3. Second-stage imputation gap on SPI donors + +`policyengine_uk_data/datasets/imputations/income.py::impute_income` +trains a QRF that replaces only six income variables on SPI donor +rows; everything else (rent, wealth, pension contributions, gift aid, +benefits-reported flags) stays as whatever middle-income FRS donor +happened to be sampled. So a synthetic SPI donor ending up with £2M +imputed self-employment income still carries an unchanged middle-FRS- +donor's savings balance and zero gift aid. + +The US equivalent ([`policyengine-us-data#589`](https://github.com/PolicyEngine/policyengine-us-data/pull/589)) +fixed this by adding a second-stage QRF: train on CPS with predictors = +demographics + newly-imputed income vars, outputs = the ~60 CPS-only +variables; for PUF-clone prediction, substitute imputed PUF incomes as +predictors so CPS-only variables come out consistent with imputed +income. + +**Fix**: add the analogous second-stage QRF in +`policyengine_uk_data/datasets/imputations/`, training on FRS with +predictors = demographics + 6 income vars, outputs = a wide set of +"FRS-only" consequential variables including rent, +`mortgage_interest_repayment`, `gift_aid`, `covenanted_payments`, +`charitable_investment_gifts`, `other_deductions`, +`employee_pension_contributions*`, `employer_pension_contributions`, +total wealth components, `capital_allowances`, `deficiency_relief`, +and the full list of `*_reported` benefits. + +## Specific aggregate residuals + +After the formula-side fixes already merged (BASIC/NEW classification +in [PR #1618](https://github.com/PolicyEngine/policyengine-uk/pull/1618), +new State Pension pro-rating + Protected Payment in +[PR #1634](https://github.com/PolicyEngine/policyengine-uk/pull/1634)), +the residual benefit-aggregate gaps against OBR are: + +| Variable | Model | Target | Gap | Likely cause | +|----------|------:|------:|----:|--------------| +| `income_support` | ~£1.07 bn | ~£0.2 bn | **+£0.9 bn** | Legacy benefit near-fully migrated; same `reported > 0` retention issue as the (pre-fix) tax credits. | +| `esa_contrib` | ~£5.56 bn | ~£2.5 bn | **+£3.1 bn** | Large. Likely reported-based take-up plus no phase-out for migrated cases. | +| `attendance_allowance` | ~£8.76 bn | ~£7.0 bn | +£1.8 bn | Cap-rate or take-up issue. | +| `tax_free_childcare` | ~£0.41 bn | ~£0.9 bn | **-£0.5 bn** | Understated by about half — under-imputed eligible families. | +| `state_pension` | ~£127.5 bn | ~£140 bn | -£12 bn | Documented in [state-pension.md](../programs/gov/dwp/state-pension.md); data-side ASP under-imputation. | + +Each warrants a scoped PR pair (model + data) once the architectural +fixes above land. + +## Items already fixed since #1621 was filed + +The issue body flagged three small bugs; all are now resolved: + +- `tax_credits/min_benefit.yaml` unit was `currency-USD` — now `currency-GBP`. +- `benunit_weekly_hours` label said "Average" but the formula sums; label now reads "Total weekly hours worked by adults in the benefit unit". +- `state_pension_type` BASIC/NEW classification (the `is_SP_age` heuristic in the issue) was rewritten in PR #1618. + +This page reflects the *current* gap rather than the issue body, so the +next reviewer working through the queue doesn't waste time on +already-fixed items. + +## Candidate PR sequence + +1. **Convert remaining `would_claim_*` formulas to input-only** (model + side) + stochastic assignment in `policyengine-uk-data` + (`datasets/frs.py`). Low risk; matches the existing UC/PC pattern. +2. **Port `assign_takeup_with_reported_anchors`** into + `policyengine-uk-data`. Pure data-pipeline change; tightens + calibration. +3. **Add second-stage QRF for FRS-only variables on SPI donors** in + `policyengine-uk-data`. Biggest single ticket but directly addresses + the "high-income donor has zero gift aid / zero rent" failure. +4. **Add `gift_aid` to `IMPUTATIONS`** (one-line addition in + `policyengine_uk_data/datasets/imputations/income.py`). Independent + of (3); can land immediately. +5. **Residual benefit-aggregate follow-ups** — separate small PRs for + IS phase-out (analogous to the WTC/CTC reactive scheme close-out), + ESA contrib investigation, AA calibration, TFC under-imputation, + ASP data-side fix (the residual #1632 leg). + +## References + +- Tracking issue: [#1621](https://github.com/PolicyEngine/policyengine-uk/issues/1621). +- US pattern reference: [`policyengine-us-data#589`](https://github.com/PolicyEngine/policyengine-us-data/pull/589). +- Related state pension docs: [state-pension.md](../programs/gov/dwp/state-pension.md), [#1632](https://github.com/PolicyEngine/policyengine-uk/issues/1632). +- Related calibration scope: [non-uk-benefit-receipt-plan.md](./non-uk-benefit-receipt-plan.md) (#842) — separate from this pipeline alignment but interacts with the calibration target side. From eac18e19ab1ea6f20eb5a31804af07a37891bcc9 Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Thu, 8 Oct 2026 15:49:30 +0100 Subject: [PATCH 2/7] Refresh the page against current main and Microcosm UK; type the changelog fragment Co-Authored-By: Claude Opus 5.5 --- changelog.d/1621.added.md | 1 + changelog.d/1621.md | 1 - .../assumptions/uk-pipeline-alignment-plan.md | 67 +++++++++++++------ 3 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 changelog.d/1621.added.md delete mode 100644 changelog.d/1621.md diff --git a/changelog.d/1621.added.md b/changelog.d/1621.added.md new file mode 100644 index 0000000000..6c13eaceb8 --- /dev/null +++ b/changelog.d/1621.added.md @@ -0,0 +1 @@ +Added a UK pipeline alignment planning page (docs/book/assumptions/uk-pipeline-alignment-plan.md): the model-side take-up gap still open, and what Microcosm UK now covers (reported-anchored take-up, the SPI stage-2 fill). diff --git a/changelog.d/1621.md b/changelog.d/1621.md deleted file mode 100644 index f495e4ee59..0000000000 --- a/changelog.d/1621.md +++ /dev/null @@ -1 +0,0 @@ -- Add a planning docs page at `docs/book/assumptions/uk-pipeline-alignment-plan.md` capturing the architectural gaps between the UK model/data pipeline and the US `policyengine-us` / `policyengine-us-data` patterns — remaining `_reported`-at-runtime `would_claim_*` formulas, take-up anchoring, second-stage QRF for SPI-donor FRS-only variables — and the residual benefit-aggregate gaps against OBR after the recent BASIC/NEW state-pension fixes. diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index 6faf357880..05cee7adae 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -5,9 +5,20 @@ pipeline and the US `policyengine-us` / `policyengine-us-data` patterns, plus a sequence of scoped PRs that would close them. Tracked under [#1621](https://github.com/PolicyEngine/policyengine-uk/issues/1621). -Most items here are **cross-repo** — they touch -`policyengine-uk-data` rather than this repo — but the model-side -contract is documented here so the receiving side has a stable target. +Most items here are **cross-repo** — they touch the data pipeline +rather than this repo — but the model-side contract is documented here +so the receiving side has a stable target. +``` + +```{admonition} Status, October 2026 +The target data pipeline is now +[Microcosm UK](https://github.com/PolicyEngine/microcosm), which replaced +the Enhanced FRS build in `policyengine-uk-data`. Its first certified +national release (`microcosm_uk_2024_25`) was published on 4 October 2026. +Gaps 2 and 3 below, and PR 4 of the sequence, are implemented there; +gap 1 is still open on the model side. The aggregate residuals are the +May 2026 Enhanced FRS measurements and have not been re-measured on the +Microcosm release. ``` ## Architectural gaps vs US @@ -23,8 +34,8 @@ through cleanly to take-up. **UK pattern (partial)**: some UK `would_claim_*` variables already match — `would_claim_uc` and `would_claim_pc` are input-only with -`default_value = True`, populated stochastically in -`policyengine-uk-data/datasets/frs.py`. But several still derive from +`default_value = True`, populated by the data build (now Microcosm +UK's `frs_take_up` stage). But several still derive from `_reported` at runtime: - `would_claim_housing_benefit` @@ -39,14 +50,20 @@ invisible unless the user manually sets `claims_all_entitled_benefits = True` everywhere. **Fix**: convert each to input-only `default_value = True` (matching -`would_claim_uc`), populate stochastically in -`uk-data/datasets/frs.py`. Note: WTC and CTC ceased to pay new awards on +`would_claim_uc`), and add each to Microcosm UK's reported-anchored +take-up stages. Note: WTC and CTC ceased to pay new awards on 2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), so their fix is primarily for back-cast simulations. ### 2. Take-up assignment ignores reported data -Today's UK take-up assignment is a pure random draw: +**Done in Microcosm UK.** Its `frs_take_up`, `pension_credit_take_up` +and `child_benefit_take_up` stages keep every benefit unit that reports +receipt as a claimant and draw the rest to the target rate +([`source_stages.json` at `75167a6`](https://github.com/PolicyEngine/microcosm/blob/75167a68/packages/microcosm-build/src/microcosm/build/uk/source_stages.json)). +The description below is the Enhanced FRS behaviour this replaced. + +The Enhanced FRS take-up assignment was a pure random draw: ```python pe_benunit["would_claim_uc"] = generator.random(len(pe_benunit)) < universal_credit_rate @@ -63,6 +80,14 @@ without changing the target rates. ### 3. Second-stage imputation gap on SPI donors +**Done in Microcosm UK.** `hmrc_spi_income_spine` trains an FRS-only +second stage on the new SPI incomes (its outputs include `gift_aid` and +`charitable_investment_gifts`), `spi_housing_shell` redraws tenure, +dwelling and council tax band for the SPI households, and the wealth, +spending and VAT stages now run after the SPI block, so they are drawn +for the new incomes rather than inherited from the FRS parent. The +description below is the Enhanced FRS behaviour this replaced. + `policyengine_uk_data/datasets/imputations/income.py::impute_income` trains a QRF that replaces only six income variables on SPI donor rows; everything else (rent, wealth, pension contributions, gift aid, @@ -94,7 +119,8 @@ After the formula-side fixes already merged (BASIC/NEW classification in [PR #1618](https://github.com/PolicyEngine/policyengine-uk/pull/1618), new State Pension pro-rating + Protected Payment in [PR #1634](https://github.com/PolicyEngine/policyengine-uk/pull/1634)), -the residual benefit-aggregate gaps against OBR are: +the residual benefit-aggregate gaps against OBR on the Enhanced FRS +(measured May 2026; not yet re-measured on Microcosm UK) were: | Variable | Model | Target | Gap | Likely cause | |----------|------:|------:|----:|--------------| @@ -122,18 +148,17 @@ already-fixed items. ## Candidate PR sequence 1. **Convert remaining `would_claim_*` formulas to input-only** (model - side) + stochastic assignment in `policyengine-uk-data` - (`datasets/frs.py`). Low risk; matches the existing UC/PC pattern. -2. **Port `assign_takeup_with_reported_anchors`** into - `policyengine-uk-data`. Pure data-pipeline change; tightens - calibration. -3. **Add second-stage QRF for FRS-only variables on SPI donors** in - `policyengine-uk-data`. Biggest single ticket but directly addresses - the "high-income donor has zero gift aid / zero rent" failure. -4. **Add `gift_aid` to `IMPUTATIONS`** (one-line addition in - `policyengine_uk_data/datasets/imputations/income.py`). Independent - of (3); can land immediately. -5. **Residual benefit-aggregate follow-ups** — separate small PRs for + side) + reported-anchored assignment in Microcosm UK's `frs_take_up` + stage. Still open: the four formulas above read `_reported` at + runtime on `main`. +2. ~~Port `assign_takeup_with_reported_anchors`~~ — done in Microcosm UK + (gap 2). +3. ~~Add second-stage QRF for FRS-only variables on SPI donors~~ — done + in Microcosm UK (gap 3). +4. ~~Add `gift_aid` to `IMPUTATIONS`~~ — done in Microcosm UK, where + `gift_aid` is a stage-2 output. +5. **Residual benefit-aggregate follow-ups** — re-measure on the + Microcosm release first, then separate small PRs for IS phase-out (analogous to the WTC/CTC reactive scheme close-out), ESA contrib investigation, AA calibration, TFC under-imputation, ASP data-side fix (the residual #1632 leg). From 4e4e02db372f83a0b8b48c362c2802cde2795dce Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Mon, 12 Oct 2026 11:08:21 +0200 Subject: [PATCH 3/7] Bring the pipeline alignment page in line with main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the State Pension residual with the 2024-25 figures documented in docs/book/programs/gov/dwp/state-pension.md (£119.0bn against £131.3bn, -£12.2bn) and point to policyengine-uk-data#493. - Say that tax credits stopped paying all ongoing awards on 5 April 2025 (gov.dwp.tax_credits.active), not only new awards. - Drop the unverified Microcosm release date and "certified" claim. - Remove em dashes. Co-Authored-By: Claude Opus 5.5 --- .../assumptions/uk-pipeline-alignment-plan.md | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index 05cee7adae..34def30814 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -1,20 +1,20 @@ # UK model/data pipeline alignment plan ```{note} -**Planning page** capturing the gaps between the UK model + enhanced FRS +**Planning page** setting out the gaps between the UK model and data pipeline and the US `policyengine-us` / `policyengine-us-data` patterns, -plus a sequence of scoped PRs that would close them. Tracked under +plus a sequence of scoped PRs that would close them. Tracked in [#1621](https://github.com/PolicyEngine/policyengine-uk/issues/1621). -Most items here are **cross-repo** — they touch the data pipeline -rather than this repo — but the model-side contract is documented here -so the receiving side has a stable target. +Most items here are **cross-repo**: they touch the data pipeline rather +than this repo, but the model-side contract is documented here so the +receiving side has a stable target. ``` ```{admonition} Status, October 2026 The target data pipeline is now [Microcosm UK](https://github.com/PolicyEngine/microcosm), which replaced -the Enhanced FRS build in `policyengine-uk-data`. Its first certified -national release (`microcosm_uk_2024_25`) was published on 4 October 2026. +the Enhanced FRS build in `policyengine-uk-data`; its UK national line is +built on the Family Resources Survey 2024-25 (`microcosm_uk_2024_25`). Gaps 2 and 3 below, and PR 4 of the sequence, are implemented there; gap 1 is still open on the model side. The aggregate residuals are the May 2026 Enhanced FRS measurements and have not been re-measured on the @@ -33,7 +33,7 @@ rate). Runtime formulas only read the pre-computed through cleanly to take-up. **UK pattern (partial)**: some UK `would_claim_*` variables already -match — `would_claim_uc` and `would_claim_pc` are input-only with +match: `would_claim_uc` and `would_claim_pc` are input-only with `default_value = True`, populated by the data build (now Microcosm UK's `frs_take_up` stage). But several still derive from `_reported` at runtime: @@ -45,15 +45,16 @@ UK's `frs_take_up` stage). But several still derive from The runtime pattern is `claims_all_entitled_benefits | (foo_reported > 0)`, which means a reform expanding eligibility only reaches existing -`_reported > 0` claimants — non-claimants who newly qualify are +`_reported > 0` claimants. Non-claimants who newly qualify are invisible unless the user manually sets `claims_all_entitled_benefits = True` everywhere. **Fix**: convert each to input-only `default_value = True` (matching `would_claim_uc`), and add each to Microcosm UK's reported-anchored -take-up stages. Note: WTC and CTC ceased to pay new awards on -2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), -so their fix is primarily for back-cast simulations. +take-up stages. Tax credits stopped paying ongoing awards on 5 April +2025, and `gov.dwp.tax_credits.active` is false from 2025-04-06 (see +[tax-credits.md](../programs/gov/dwp/tax-credits.md)), so the WTC and CTC +fix matters only for simulations of earlier years. ### 2. Take-up assignment ignores reported data @@ -120,15 +121,16 @@ in [PR #1618](https://github.com/PolicyEngine/policyengine-uk/pull/1618), new State Pension pro-rating + Protected Payment in [PR #1634](https://github.com/PolicyEngine/policyengine-uk/pull/1634)), the residual benefit-aggregate gaps against OBR on the Enhanced FRS -(measured May 2026; not yet re-measured on Microcosm UK) were: +(measured May 2026, except the State Pension row, which is taken from +the State Pension page; none re-measured on Microcosm UK) were: | Variable | Model | Target | Gap | Likely cause | |----------|------:|------:|----:|--------------| | `income_support` | ~£1.07 bn | ~£0.2 bn | **+£0.9 bn** | Legacy benefit near-fully migrated; same `reported > 0` retention issue as the (pre-fix) tax credits. | | `esa_contrib` | ~£5.56 bn | ~£2.5 bn | **+£3.1 bn** | Large. Likely reported-based take-up plus no phase-out for migrated cases. | | `attendance_allowance` | ~£8.76 bn | ~£7.0 bn | +£1.8 bn | Cap-rate or take-up issue. | -| `tax_free_childcare` | ~£0.41 bn | ~£0.9 bn | **-£0.5 bn** | Understated by about half — under-imputed eligible families. | -| `state_pension` | ~£127.5 bn | ~£140 bn | -£12 bn | Documented in [state-pension.md](../programs/gov/dwp/state-pension.md); data-side ASP under-imputation. | +| `tax_free_childcare` | ~£0.41 bn | ~£0.9 bn | **-£0.5 bn** | Understated by about half: under-imputed eligible families. | +| `state_pension` | £119.0 bn | £131.3 bn | -£12.2 bn | 2024-25, against DWP's Spring Forecast 2026 less payments abroad, on policyengine-uk-data-private 1.57.4, as documented in [state-pension.md](../programs/gov/dwp/state-pension.md). The shortfall arises in building the Enhanced FRS ([policyengine-uk-data#493](https://github.com/PolicyEngine/policyengine-uk-data/issues/493)). | Each warrants a scoped PR pair (model + data) once the architectural fixes above land. @@ -137,7 +139,7 @@ fixes above land. The issue body flagged three small bugs; all are now resolved: -- `tax_credits/min_benefit.yaml` unit was `currency-USD` — now `currency-GBP`. +- `tax_credits/min_benefit.yaml` unit was `currency-USD`; it is now `currency-GBP`. - `benunit_weekly_hours` label said "Average" but the formula sums; label now reads "Total weekly hours worked by adults in the benefit unit". - `state_pension_type` BASIC/NEW classification (the `is_SP_age` heuristic in the issue) was rewritten in PR #1618. @@ -151,21 +153,21 @@ already-fixed items. side) + reported-anchored assignment in Microcosm UK's `frs_take_up` stage. Still open: the four formulas above read `_reported` at runtime on `main`. -2. ~~Port `assign_takeup_with_reported_anchors`~~ — done in Microcosm UK +2. ~~Port `assign_takeup_with_reported_anchors`~~: done in Microcosm UK (gap 2). -3. ~~Add second-stage QRF for FRS-only variables on SPI donors~~ — done +3. ~~Add second-stage QRF for FRS-only variables on SPI donors~~: done in Microcosm UK (gap 3). -4. ~~Add `gift_aid` to `IMPUTATIONS`~~ — done in Microcosm UK, where +4. ~~Add `gift_aid` to `IMPUTATIONS`~~: done in Microcosm UK, where `gift_aid` is a stage-2 output. -5. **Residual benefit-aggregate follow-ups** — re-measure on the +5. **Residual benefit-aggregate follow-ups**: re-measure on the Microcosm release first, then separate small PRs for IS phase-out (analogous to the WTC/CTC reactive scheme close-out), ESA contrib investigation, AA calibration, TFC under-imputation, - ASP data-side fix (the residual #1632 leg). + the State Pension data-side fix (#1632, policyengine-uk-data#493). ## References - Tracking issue: [#1621](https://github.com/PolicyEngine/policyengine-uk/issues/1621). - US pattern reference: [`policyengine-us-data#589`](https://github.com/PolicyEngine/policyengine-us-data/pull/589). - Related state pension docs: [state-pension.md](../programs/gov/dwp/state-pension.md), [#1632](https://github.com/PolicyEngine/policyengine-uk/issues/1632). -- Related calibration scope: [non-uk-benefit-receipt-plan.md](./non-uk-benefit-receipt-plan.md) (#842) — separate from this pipeline alignment but interacts with the calibration target side. +- Related calibration scope: [non-uk-benefit-receipt-plan.md](./non-uk-benefit-receipt-plan.md) (#842), separate from this pipeline alignment but interacting with it on the calibration target side. From 017a845328c3d7e8b3d99e46b55b5d9f3866f812 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 02:24:50 -0400 Subject: [PATCH 4/7] Correct the UK pipeline alignment plan against main, uk-data and #1621 - would_claim_council_tax_reduction also reads _reported (five, not four). - The runtime pattern differs by source: data-built simulations set claims_all_entitled_benefits to false and HB/CTR ignore it, and the CTC, WTC, IS and working-age HB eligibility tests also need a reported award, so setting the flag does not reach non-reporters; the fix changes take-up only for CTR and pension-age HB. - policyengine-uk-data#359 (take-up anchors), #347 (gift aid) and #362 (second-stage QRF, narrower outputs) landed in April 2026, before Microcosm; past-tense description of the old build. - In Microcosm, gift_aid is a stage-1 SPI output, not stage 2. - The non-State-Pension residuals are #1621's April 2026 figures on Enhanced FRS 1.50.3, not May 2026 measurements; most (not all) of the State Pension shortfall arises in building the Enhanced FRS. - #1621's third small item is the ~-on-bool DeprecationWarning, with no fix tracked; state_pension_type was a separate fix (#1618). - The IS phase-out landed in #1631. - Replaced the released .added fragment with a .changed fragment. Co-Authored-By: Claude Opus 5.5 --- ...line-alignment-page-corrections.changed.md | 1 + changelog.d/1621.added.md | 1 - .../assumptions/uk-pipeline-alignment-plan.md | 105 +++++++++++------- 3 files changed, 68 insertions(+), 39 deletions(-) create mode 100644 changelog.d/1621-pipeline-alignment-page-corrections.changed.md delete mode 100644 changelog.d/1621.added.md diff --git a/changelog.d/1621-pipeline-alignment-page-corrections.changed.md b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md new file mode 100644 index 0000000000..097a3bb288 --- /dev/null +++ b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md @@ -0,0 +1 @@ +Corrected the UK pipeline alignment planning page (`docs/book/assumptions/uk-pipeline-alignment-plan.md`): five `would_claim_*` formulas read reported receipt (Council Tax Reduction was missing), data-built simulations ignore `claims_all_entitled_benefits` and legacy benefit eligibility needs a reported award, policyengine-uk-data already anchored take-up and imputed gift aid and a second stage in April 2026, the residual aggregates come from #1621 in April 2026, the Income Support phase-out landed in #1631, and the third small item in #1621 was a `~`-on-bool warning, not `state_pension_type`. diff --git a/changelog.d/1621.added.md b/changelog.d/1621.added.md deleted file mode 100644 index 6c13eaceb8..0000000000 --- a/changelog.d/1621.added.md +++ /dev/null @@ -1 +0,0 @@ -Added a UK pipeline alignment planning page (docs/book/assumptions/uk-pipeline-alignment-plan.md): the model-side take-up gap still open, and what Microcosm UK now covers (reported-anchored take-up, the SPI stage-2 fill). diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index 34def30814..940c45b272 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -15,10 +15,11 @@ The target data pipeline is now [Microcosm UK](https://github.com/PolicyEngine/microcosm), which replaced the Enhanced FRS build in `policyengine-uk-data`; its UK national line is built on the Family Resources Survey 2024-25 (`microcosm_uk_2024_25`). -Gaps 2 and 3 below, and PR 4 of the sequence, are implemented there; -gap 1 is still open on the model side. The aggregate residuals are the -May 2026 Enhanced FRS measurements and have not been re-measured on the -Microcosm release. +Gaps 2 and 3 below, and PR 4 of the sequence, are implemented there (and +were in policyengine-uk-data in April 2026, gap 3 in narrower form); +gap 1 is still open on the model side. The aggregate residuals other than +State Pension are the figures #1621 reported for 2025 on Enhanced FRS 1.50.3 +in April 2026, and none has been re-measured on the Microcosm release. ``` ## Architectural gaps vs US @@ -39,19 +40,29 @@ UK's `frs_take_up` stage). But several still derive from `_reported` at runtime: - `would_claim_housing_benefit` +- `would_claim_council_tax_reduction` - `would_claim_IS` - `would_claim_WTC` - `would_claim_CTC` -The runtime pattern is `claims_all_entitled_benefits | (foo_reported > 0)`, -which means a reform expanding eligibility only reaches existing -`_reported > 0` claimants. Non-claimants who newly qualify are -invisible unless the user manually sets -`claims_all_entitled_benefits = True` everywhere. +In hand-entered situations each is `claims_all_entitled_benefits | (foo_reported > 0)` +(for Income Support only the claimant's or partner's report counts), and +`claims_all_entitled_benefits` is true for a benefit unit that reports under +£1 across seven means-tested benefits. In data-built simulations +`claims_all_entitled_benefits` is false for every benefit unit, and Housing +Benefit and Council Tax Reduction ignore it, so a reform expanding +eligibility only reaches existing `_reported > 0` claimants. Setting +`claims_all_entitled_benefits` to true does not change this for Child Tax +Credit, Working Tax Credit, Income Support or working-age Housing Benefit, +because their eligibility tests (`is_CTC_eligible`, `is_WTC_eligible`, +`income_support_eligible`, `housing_benefit_eligible`) also require a +reported award. **Fix**: convert each to input-only `default_value = True` (matching `would_claim_uc`), and add each to Microcosm UK's reported-anchored -take-up stages. Tax credits stopped paying ongoing awards on 5 April +take-up stages. Because of those eligibility tests, this changes take-up for +non-reporters only for Council Tax Reduction and pension-age Housing +Benefit. Tax credits stopped paying ongoing awards on 5 April 2025, and `gov.dwp.tax_credits.active` is false from 2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), so the WTC and CTC fix matters only for simulations of earlier years. @@ -62,9 +73,11 @@ fix matters only for simulations of earlier years. and `child_benefit_take_up` stages keep every benefit unit that reports receipt as a claimant and draw the rest to the target rate ([`source_stages.json` at `75167a6`](https://github.com/PolicyEngine/microcosm/blob/75167a68/packages/microcosm-build/src/microcosm/build/uk/source_stages.json)). -The description below is the Enhanced FRS behaviour this replaced. +The description below is the Enhanced FRS behaviour before +[policyengine-uk-data#359](https://github.com/PolicyEngine/policyengine-uk-data/pull/359) +(April 2026), which anchored the same three flags on reported receipt. -The Enhanced FRS take-up assignment was a pure random draw: +Until then the Enhanced FRS take-up assignment was a pure random draw: ```python pe_benunit["would_claim_uc"] = generator.random(len(pe_benunit)) < universal_credit_rate @@ -74,27 +87,36 @@ This discards information: respondents who reported receiving UC clearly took it up, and should have `would_claim_uc = True` with certainty. -**Fix**: port `policyengine_us_data/utils/takeup.py::assign_takeup_with_reported_anchors` +**Fix** (done in policyengine-uk-data#359): port +`policyengine_us_data/utils/takeup.py::assign_takeup_with_reported_anchors` into `policyengine-uk-data` and apply it to each take-up variable where -a `_reported` column exists. This tightens per-variable calibration +a `_reported` column exists (`would_claim_uc`, `would_claim_pc`, +`would_claim_child_benefit`). This tightens per-variable calibration without changing the target rates. ### 3. Second-stage imputation gap on SPI donors -**Done in Microcosm UK.** `hmrc_spi_income_spine` trains an FRS-only -second stage on the new SPI incomes (its outputs include `gift_aid` and -`charitable_investment_gifts`), `spi_housing_shell` redraws tenure, +**Done in Microcosm UK.** `hmrc_spi_income_spine` draws `gift_aid` and +`charitable_investment_gifts` from the SPI with the new incomes, and trains +an FRS-only second stage on FRS rows that it predicts from those incomes +(pension contributions, tax-free savings income and the `*_reported` +benefits), `spi_housing_shell` redraws tenure, dwelling and council tax band for the SPI households, and the wealth, spending and VAT stages now run after the SPI block, so they are drawn for the new incomes rather than inherited from the FRS parent. The -description below is the Enhanced FRS behaviour this replaced. +description below is the Enhanced FRS behaviour before April 2026: +policyengine-uk-data#347 then added `gift_aid` and +`charitable_investment_gifts` to the SPI imputation, and #362 added a +second-stage QRF (`datasets/imputations/frs_only.py`) for pension +contributions, tax-free savings income and the `*_reported` benefits; rent +and wealth on SPI rows still come from the FRS parent there. `policyengine_uk_data/datasets/imputations/income.py::impute_income` -trains a QRF that replaces only six income variables on SPI donor +trained a QRF that replaced only six income variables on SPI donor rows; everything else (rent, wealth, pension contributions, gift aid, -benefits-reported flags) stays as whatever middle-income FRS donor +benefits-reported flags) stayed as whatever middle-income FRS donor happened to be sampled. So a synthetic SPI donor ending up with £2M -imputed self-employment income still carries an unchanged middle-FRS- +imputed self-employment income still carried an unchanged middle-FRS- donor's savings balance and zero gift aid. The US equivalent ([`policyengine-us-data#589`](https://github.com/PolicyEngine/policyengine-us-data/pull/589)) @@ -112,7 +134,9 @@ predictors = demographics + 6 income vars, outputs = a wide set of `charitable_investment_gifts`, `other_deductions`, `employee_pension_contributions*`, `employer_pension_contributions`, total wealth components, `capital_allowances`, `deficiency_relief`, -and the full list of `*_reported` benefits. +and the full list of `*_reported` benefits. policyengine-uk-data#362 (April +2026) added this stage with a narrower output list: pension contributions, +tax-free savings income and the `*_reported` benefits. ## Specific aggregate residuals @@ -121,7 +145,8 @@ in [PR #1618](https://github.com/PolicyEngine/policyengine-uk/pull/1618), new State Pension pro-rating + Protected Payment in [PR #1634](https://github.com/PolicyEngine/policyengine-uk/pull/1634)), the residual benefit-aggregate gaps against OBR on the Enhanced FRS -(measured May 2026, except the State Pension row, which is taken from +(for the rows other than State Pension, the figures #1621 reported for 2025 +on Enhanced FRS 1.50.3 in April 2026; the State Pension row is taken from the State Pension page; none re-measured on Microcosm UK) were: | Variable | Model | Target | Gap | Likely cause | @@ -130,18 +155,20 @@ the State Pension page; none re-measured on Microcosm UK) were: | `esa_contrib` | ~£5.56 bn | ~£2.5 bn | **+£3.1 bn** | Large. Likely reported-based take-up plus no phase-out for migrated cases. | | `attendance_allowance` | ~£8.76 bn | ~£7.0 bn | +£1.8 bn | Cap-rate or take-up issue. | | `tax_free_childcare` | ~£0.41 bn | ~£0.9 bn | **-£0.5 bn** | Understated by about half: under-imputed eligible families. | -| `state_pension` | £119.0 bn | £131.3 bn | -£12.2 bn | 2024-25, against DWP's Spring Forecast 2026 less payments abroad, on policyengine-uk-data-private 1.57.4, as documented in [state-pension.md](../programs/gov/dwp/state-pension.md). The shortfall arises in building the Enhanced FRS ([policyengine-uk-data#493](https://github.com/PolicyEngine/policyengine-uk-data/issues/493)). | +| `state_pension` | £119.0 bn | £131.3 bn | -£12.2 bn | 2024-25, against DWP's Spring Forecast 2026 less payments abroad, on policyengine-uk-data-private 1.57.4, as documented in [state-pension.md](../programs/gov/dwp/state-pension.md). Most of the shortfall arises in building the Enhanced FRS ([policyengine-uk-data#493](https://github.com/PolicyEngine/policyengine-uk-data/issues/493)). | Each warrants a scoped PR pair (model + data) once the architectural fixes above land. ## Items already fixed since #1621 was filed -The issue body flagged three small bugs; all are now resolved: +The issue body flagged three small items; two are resolved: -- `tax_credits/min_benefit.yaml` unit was `currency-USD`; it is now `currency-GBP`. -- `benunit_weekly_hours` label said "Average" but the formula sums; label now reads "Total weekly hours worked by adults in the benefit unit". -- `state_pension_type` BASIC/NEW classification (the `is_SP_age` heuristic in the issue) was rewritten in PR #1618. +- `tax_credits/min_benefit.yaml` unit was `currency-USD`; it is now `currency-GBP` (#1627). +- `benunit_weekly_hours` label said "Average" but the formula sums; label now reads "Total weekly hours worked by adults in the benefit unit" (#1627). +- Some `~` operations on Python bools raised `DeprecationWarning: Bitwise inversion '~' on bool`, which becomes an error in Python 3.16; no fix is tracked. + +Separately, the `state_pension_type` BASIC/NEW classification was rewritten in PR #1618. This page reflects the *current* gap rather than the issue body, so the next reviewer working through the queue doesn't waste time on @@ -151,19 +178,21 @@ already-fixed items. 1. **Convert remaining `would_claim_*` formulas to input-only** (model side) + reported-anchored assignment in Microcosm UK's `frs_take_up` - stage. Still open: the four formulas above read `_reported` at + stage. Still open: the five formulas above read `_reported` at runtime on `main`. -2. ~~Port `assign_takeup_with_reported_anchors`~~: done in Microcosm UK - (gap 2). +2. ~~Port `assign_takeup_with_reported_anchors`~~: done in + policyengine-uk-data#359 and in Microcosm UK (gap 2). 3. ~~Add second-stage QRF for FRS-only variables on SPI donors~~: done - in Microcosm UK (gap 3). -4. ~~Add `gift_aid` to `IMPUTATIONS`~~: done in Microcosm UK, where - `gift_aid` is a stage-2 output. + in policyengine-uk-data#362 (narrower outputs) and in Microcosm UK + (gap 3). +4. ~~Add `gift_aid` to `IMPUTATIONS`~~: done in policyengine-uk-data#347 + and in Microcosm UK, where `gift_aid` is a stage-1 SPI output. 5. **Residual benefit-aggregate follow-ups**: re-measure on the - Microcosm release first, then separate small PRs for - IS phase-out (analogous to the WTC/CTC reactive scheme close-out), - ESA contrib investigation, AA calibration, TFC under-imputation, - the State Pension data-side fix (#1632, policyengine-uk-data#493). + Microcosm release first, then separate small PRs for ESA contrib + investigation, AA calibration, TFC under-imputation, the State Pension + data-side fix (#1632, policyengine-uk-data#493). The IS phase-out + landed in #1631 (`gov.dwp.income_support.active` is false from + 2026-04-01). ## References From 995d4d864d7bb59c3f1c2c6c05367f09f0524eeb Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 03:14:30 -0400 Subject: [PATCH 5/7] Describe the claims flag by simulation type and the ungated CTC element From the independent review of the corrections: - On data-built simulations the flag reaches no new claimant of any of the five (HB and CTR ignore it; the others need a reported award), but in tax-credit years it turns on would_claim_CTC, and CTC_child_element is not gated on is_CTC_eligible, so tax_credits income can enter HB and CTR means tests for families that get no CTC. - The conversion's effect is on awards (CTR and pension-age HB), not on take-up flags, and needs that CTC gate first. - Microcosm's second stage is trained on FRS rows and predicts for SPI rows, with the same outputs as uk-data#362; named the three anchored flags; Enhanced FRS 1.50.3 predates #347, #359 and #362; renamed the small-items heading, which now lists an open item. - Fragment no longer says data-built simulations ignore the flag. Co-Authored-By: Claude Opus 5.5 --- ...line-alignment-page-corrections.changed.md | 2 +- .../assumptions/uk-pipeline-alignment-plan.md | 57 +++++++++++++------ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/changelog.d/1621-pipeline-alignment-page-corrections.changed.md b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md index 097a3bb288..eb1c307e5d 100644 --- a/changelog.d/1621-pipeline-alignment-page-corrections.changed.md +++ b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md @@ -1 +1 @@ -Corrected the UK pipeline alignment planning page (`docs/book/assumptions/uk-pipeline-alignment-plan.md`): five `would_claim_*` formulas read reported receipt (Council Tax Reduction was missing), data-built simulations ignore `claims_all_entitled_benefits` and legacy benefit eligibility needs a reported award, policyengine-uk-data already anchored take-up and imputed gift aid and a second stage in April 2026, the residual aggregates come from #1621 in April 2026, the Income Support phase-out landed in #1631, and the third small item in #1621 was a `~`-on-bool warning, not `state_pension_type`. +Corrected the UK pipeline alignment planning page (`docs/book/assumptions/uk-pipeline-alignment-plan.md`): five `would_claim_*` formulas read reported receipt (Council Tax Reduction was missing); in data-built simulations `claims_all_entitled_benefits` is false and Housing Benefit and Council Tax Reduction ignore it, and eligibility for tax credits, Income Support and working-age Housing Benefit needs a reported award; policyengine-uk-data anchored take-up (#359), imputed gift aid (#347) and added a second-stage imputation (#362) in April 2026; the residual aggregates are #1621's April 2026 figures; the Income Support phase-out landed in #1631; and the third small item in #1621 was a `~`-on-bool warning, not `state_pension_type`. diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index 940c45b272..fe125cdb93 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -52,17 +52,32 @@ In hand-entered situations each is `claims_all_entitled_benefits | (foo_reported `claims_all_entitled_benefits` is false for every benefit unit, and Housing Benefit and Council Tax Reduction ignore it, so a reform expanding eligibility only reaches existing `_reported > 0` claimants. Setting -`claims_all_entitled_benefits` to true does not change this for Child Tax -Credit, Working Tax Credit, Income Support or working-age Housing Benefit, -because their eligibility tests (`is_CTC_eligible`, `is_WTC_eligible`, -`income_support_eligible`, `housing_benefit_eligible`) also require a -reported award. +`claims_all_entitled_benefits` to true on a data-built simulation does not +change this: Housing Benefit and Council Tax Reduction ignore it there, and +the eligibility tests for Child Tax Credit, Working Tax Credit, Income +Support and working-age Housing Benefit (`is_CTC_eligible`, +`is_WTC_eligible`, `income_support_eligible`, `housing_benefit_eligible`) +also require a reported award. In years when tax credits are active it does +turn on `would_claim_CTC` for families that report no Child Tax Credit; +their `tax_credits` then includes the Child Tax Credit child element, which +`CTC_child_element` computes without checking `is_CTC_eligible`, and that +amount enters their Housing Benefit and Council Tax Reduction means tests. +In hand-entered situations the flag is already true for families that +report none of the seven benefits, and the same eligibility tests mean that, +of these five benefits, such a family can receive only Council Tax +Reduction and pension-age Housing Benefit. **Fix**: convert each to input-only `default_value = True` (matching `would_claim_uc`), and add each to Microcosm UK's reported-anchored -take-up stages. Because of those eligibility tests, this changes take-up for -non-reporters only for Council Tax Reduction and pension-age Housing -Benefit. Tax credits stopped paying ongoing awards on 5 April +take-up stages. Because of those eligibility tests, the only new awards +this gives families that report none of these benefits are Council Tax +Reduction and pension-age Housing Benefit (the model's pension-age route, +which includes mixed-age couples that can still claim on it). For years when +tax credits are active, `CTC_child_element` would first need an +`is_CTC_eligible` gate: without one, a non-reporting family with children +drawn as a Child Tax Credit claimant gets `tax_credits` but no Child Tax +Credit, and that amount cuts its Housing Benefit and Council Tax Reduction. +Tax credits stopped paying ongoing awards on 5 April 2025, and `gov.dwp.tax_credits.active` is false from 2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), so the WTC and CTC fix matters only for simulations of earlier years. @@ -75,7 +90,9 @@ receipt as a claimant and draw the rest to the target rate ([`source_stages.json` at `75167a6`](https://github.com/PolicyEngine/microcosm/blob/75167a68/packages/microcosm-build/src/microcosm/build/uk/source_stages.json)). The description below is the Enhanced FRS behaviour before [policyengine-uk-data#359](https://github.com/PolicyEngine/policyengine-uk-data/pull/359) -(April 2026), which anchored the same three flags on reported receipt. +(April 2026), which anchored `would_claim_uc`, `would_claim_pc` and +`would_claim_child_benefit` on reported receipt, the same three flags +Microcosm UK anchors. Until then the Enhanced FRS take-up assignment was a pure random draw: @@ -97,10 +114,10 @@ without changing the target rates. ### 3. Second-stage imputation gap on SPI donors **Done in Microcosm UK.** `hmrc_spi_income_spine` draws `gift_aid` and -`charitable_investment_gifts` from the SPI with the new incomes, and trains -an FRS-only second stage on FRS rows that it predicts from those incomes -(pension contributions, tax-free savings income and the `*_reported` -benefits), `spi_housing_shell` redraws tenure, +`charitable_investment_gifts` from the SPI jointly with the new incomes, and +an FRS-only second stage, trained on the FRS rows, predicts pension +contributions, tax-free savings income and the `*_reported` benefits for the +SPI rows from those incomes. `spi_housing_shell` redraws tenure, dwelling and council tax band for the SPI households, and the wealth, spending and VAT stages now run after the SPI block, so they are drawn for the new incomes rather than inherited from the FRS parent. The @@ -136,7 +153,8 @@ predictors = demographics + 6 income vars, outputs = a wide set of total wealth components, `capital_allowances`, `deficiency_relief`, and the full list of `*_reported` benefits. policyengine-uk-data#362 (April 2026) added this stage with a narrower output list: pension contributions, -tax-free savings income and the `*_reported` benefits. +tax-free savings income and the `*_reported` benefits. Microcosm UK's second +stage has the same outputs. ## Specific aggregate residuals @@ -146,7 +164,8 @@ new State Pension pro-rating + Protected Payment in [PR #1634](https://github.com/PolicyEngine/policyengine-uk/pull/1634)), the residual benefit-aggregate gaps against OBR on the Enhanced FRS (for the rows other than State Pension, the figures #1621 reported for 2025 -on Enhanced FRS 1.50.3 in April 2026; the State Pension row is taken from +on Enhanced FRS 1.50.3 in April 2026, which predates policyengine-uk-data#347, +#359 and #362; the State Pension row is taken from the State Pension page; none re-measured on Microcosm UK) were: | Variable | Model | Target | Gap | Likely cause | @@ -160,7 +179,7 @@ the State Pension page; none re-measured on Microcosm UK) were: Each warrants a scoped PR pair (model + data) once the architectural fixes above land. -## Items already fixed since #1621 was filed +## Small items from #1621 The issue body flagged three small items; two are resolved: @@ -183,8 +202,10 @@ already-fixed items. 2. ~~Port `assign_takeup_with_reported_anchors`~~: done in policyengine-uk-data#359 and in Microcosm UK (gap 2). 3. ~~Add second-stage QRF for FRS-only variables on SPI donors~~: done - in policyengine-uk-data#362 (narrower outputs) and in Microcosm UK - (gap 3). + in policyengine-uk-data#362 and in Microcosm UK (gap 3), both with a + narrower output list than proposed above; Microcosm UK also redraws + housing and runs the wealth, spending and VAT stages after the SPI + block. 4. ~~Add `gift_aid` to `IMPUTATIONS`~~: done in policyengine-uk-data#347 and in Microcosm UK, where `gift_aid` is a stage-1 SPI output. 5. **Residual benefit-aggregate follow-ups**: re-measure on the From 2b47e700c0d5b9356e4210ce626e7001c45a83ca Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 03:50:18 -0400 Subject: [PATCH 6/7] Say the ungated CTC child element already affects main Second-round review (household runs on main): in tax-credit years a hand-entered family with children that reports nothing already claims CTC by default, and CTC_child_element is not gated on is_CTC_eligible, so its tax_credits enters the HB and CTR means tests and the tax credit award the 2024 winter payment tests read (utils/winter_heating.py); families that report CTC but have moved to UC are affected in both kinds of simulation. Microcosm's stage 2 lacks two of #362's reported-benefit outputs. Co-Authored-By: Claude Opus 5.5 --- ...line-alignment-page-corrections.changed.md | 2 +- .../assumptions/uk-pipeline-alignment-plan.md | 36 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/changelog.d/1621-pipeline-alignment-page-corrections.changed.md b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md index eb1c307e5d..80deba2632 100644 --- a/changelog.d/1621-pipeline-alignment-page-corrections.changed.md +++ b/changelog.d/1621-pipeline-alignment-page-corrections.changed.md @@ -1 +1 @@ -Corrected the UK pipeline alignment planning page (`docs/book/assumptions/uk-pipeline-alignment-plan.md`): five `would_claim_*` formulas read reported receipt (Council Tax Reduction was missing); in data-built simulations `claims_all_entitled_benefits` is false and Housing Benefit and Council Tax Reduction ignore it, and eligibility for tax credits, Income Support and working-age Housing Benefit needs a reported award; policyengine-uk-data anchored take-up (#359), imputed gift aid (#347) and added a second-stage imputation (#362) in April 2026; the residual aggregates are #1621's April 2026 figures; the Income Support phase-out landed in #1631; and the third small item in #1621 was a `~`-on-bool warning, not `state_pension_type`. +Corrected the UK pipeline alignment planning page (`docs/book/assumptions/uk-pipeline-alignment-plan.md`): five `would_claim_*` formulas read reported receipt (Council Tax Reduction was missing); in data-built simulations `claims_all_entitled_benefits` is false and Housing Benefit and Council Tax Reduction ignore it, and eligibility for tax credits, Income Support and working-age Housing Benefit needs a reported award; policyengine-uk-data anchored take-up (#359), imputed gift aid (#347) and added a second-stage imputation (#362) in April 2026; the residual aggregates are #1621's April 2026 figures; the Income Support phase-out landed in #1631; `CTC_child_element` is not gated on `is_CTC_eligible`, so in tax-credit years an ineligible Child Tax Credit claimant's `tax_credits` enters its Housing Benefit and Council Tax Reduction means tests; and the third small item in #1621 was a `~`-on-bool warning, not `state_pension_type`. diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index fe125cdb93..dbf81d464d 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -58,14 +58,22 @@ the eligibility tests for Child Tax Credit, Working Tax Credit, Income Support and working-age Housing Benefit (`is_CTC_eligible`, `is_WTC_eligible`, `income_support_eligible`, `housing_benefit_eligible`) also require a reported award. In years when tax credits are active it does -turn on `would_claim_CTC` for families that report no Child Tax Credit; -their `tax_credits` then includes the Child Tax Credit child element, which -`CTC_child_element` computes without checking `is_CTC_eligible`, and that -amount enters their Housing Benefit and Council Tax Reduction means tests. -In hand-entered situations the flag is already true for families that -report none of the seven benefits, and the same eligibility tests mean that, -of these five benefits, such a family can receive only Council Tax -Reduction and pension-age Housing Benefit. +turn on `would_claim_CTC` for families that report no Child Tax Credit. +Unlike the family and disabled child elements, `CTC_child_element` does not +check `is_CTC_eligible`, so a family with children that claims Child Tax +Credit without being eligible has `tax_credits` (the child element, less the +income taper) but no `child_tax_credit`. That amount enters its Housing +Benefit and Council Tax Reduction means tests, and the tax credit award that +the 2024 Winter Fuel Payment and Pension Age Winter Heating Payment tests +read. This already happens on `main` in tax-credit years with nothing +overridden. In hand-entered situations the flag is true for every family +that reports none of the seven benefits, so each such family with children +claims Child Tax Credit. In both kinds of simulation, a family that reports +Child Tax Credit but claims Universal Credit, or whose legacy benefits have +closed, still claims it although ineligible. Otherwise, the same eligibility +tests mean that a hand-entered family reporting none of the seven benefits +can receive, of these five benefits, only Council Tax Reduction and +pension-age Housing Benefit. **Fix**: convert each to input-only `default_value = True` (matching `would_claim_uc`), and add each to Microcosm UK's reported-anchored @@ -73,10 +81,11 @@ take-up stages. Because of those eligibility tests, the only new awards this gives families that report none of these benefits are Council Tax Reduction and pension-age Housing Benefit (the model's pension-age route, which includes mixed-age couples that can still claim on it). For years when -tax credits are active, `CTC_child_element` would first need an -`is_CTC_eligible` gate: without one, a non-reporting family with children -drawn as a Child Tax Credit claimant gets `tax_credits` but no Child Tax -Credit, and that amount cuts its Housing Benefit and Council Tax Reduction. +tax credits are active, `CTC_child_element` needs an `is_CTC_eligible` gate. +The fix would make the gap wider but does not create it: without the gate, +every family with children drawn as a Child Tax Credit claimant without a +report gets `tax_credits` but no Child Tax Credit, and that amount cuts its +Council Tax Reduction and any Housing Benefit. Tax credits stopped paying ongoing awards on 5 April 2025, and `gov.dwp.tax_credits.active` is false from 2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), so the WTC and CTC @@ -154,7 +163,8 @@ total wealth components, `capital_allowances`, `deficiency_relief`, and the full list of `*_reported` benefits. policyengine-uk-data#362 (April 2026) added this stage with a narrower output list: pension contributions, tax-free savings income and the `*_reported` benefits. Microcosm UK's second -stage has the same outputs. +stage has the same outputs, less the incapacity benefit and maternity +allowance reports, which its base data lacks. ## Specific aggregate residuals From 4b08daa739c7adede799bdeb55c5375a819c6390 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 04:07:42 -0400 Subject: [PATCH 7/7] Narrow the legacy-closure and "every family" claims; date Microcosm's stage 2 Third-round review (household runs on main): legacy closures on main come only after tax credits end, so those families' tax_credits is zero unless a reform keeps tax credits active; the ungated amount is the child element less the taper and counts as income in CTR and HB means tests rather than always cutting them; Microcosm's stage 2 matched #362 at 75167a6 and has since added eight outputs (54b514f). Co-Authored-By: Claude Opus 5.5 --- .../assumptions/uk-pipeline-alignment-plan.md | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/book/assumptions/uk-pipeline-alignment-plan.md b/docs/book/assumptions/uk-pipeline-alignment-plan.md index dbf81d464d..50d2f87982 100644 --- a/docs/book/assumptions/uk-pipeline-alignment-plan.md +++ b/docs/book/assumptions/uk-pipeline-alignment-plan.md @@ -69,8 +69,11 @@ read. This already happens on `main` in tax-credit years with nothing overridden. In hand-entered situations the flag is true for every family that reports none of the seven benefits, so each such family with children claims Child Tax Credit. In both kinds of simulation, a family that reports -Child Tax Credit but claims Universal Credit, or whose legacy benefits have -closed, still claims it although ineligible. Otherwise, the same eligibility +Child Tax Credit but claims Universal Credit still claims it although +ineligible. So does a family whose legacy benefits have closed, but on +`main` that happens only once tax credits are no longer active, so its +`tax_credits` is zero unless a reform leaves tax credits active after a +legacy benefit has closed. Otherwise, the same eligibility tests mean that a hand-entered family reporting none of the seven benefits can receive, of these five benefits, only Council Tax Reduction and pension-age Housing Benefit. @@ -82,10 +85,11 @@ this gives families that report none of these benefits are Council Tax Reduction and pension-age Housing Benefit (the model's pension-age route, which includes mixed-age couples that can still claim on it). For years when tax credits are active, `CTC_child_element` needs an `is_CTC_eligible` gate. -The fix would make the gap wider but does not create it: without the gate, -every family with children drawn as a Child Tax Credit claimant without a -report gets `tax_credits` but no Child Tax Credit, and that amount cuts its -Council Tax Reduction and any Housing Benefit. +The fix would spread the problem but does not create it: without the gate, +a family with children drawn as a Child Tax Credit claimant without a report +gets the child element, less the income taper, as `tax_credits` but no Child +Tax Credit, and that amount counts as income in its Council Tax Reduction +and any Housing Benefit means test. Tax credits stopped paying ongoing awards on 5 April 2025, and `gov.dwp.tax_credits.active` is false from 2025-04-06 (see [tax-credits.md](../programs/gov/dwp/tax-credits.md)), so the WTC and CTC @@ -163,8 +167,11 @@ total wealth components, `capital_allowances`, `deficiency_relief`, and the full list of `*_reported` benefits. policyengine-uk-data#362 (April 2026) added this stage with a narrower output list: pension contributions, tax-free savings income and the `*_reported` benefits. Microcosm UK's second -stage has the same outputs, less the incapacity benefit and maternity -allowance reports, which its base data lacks. +stage had the same outputs at `75167a6`, less the incapacity benefit and +maternity allowance reports, which its base data lacks; it has since added +trading losses, six owner-manager company variables and the War Widow's +Pension report +([`source_stages.json` at `54b514f`](https://github.com/PolicyEngine/microcosm/blob/54b514f9/packages/microcosm-build/src/microcosm/build/uk/source_stages.json)). ## Specific aggregate residuals