Skip to content

Release the 10/8 uk-data batch (15 PRs, relock to policyengine-uk 2.122.2) - #544

Merged
MaxGhenis merged 85 commits into
mainfrom
release/uk-data-2026-10-08
Oct 7, 2026
Merged

MaxGhenis merged 85 commits into
mainfrom
release/uk-data-2026-10-08

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Release the 10/8 uk-data batch as one data release, expected to be 1.58.0. This branch contains 15 member PRs at their reviewed heads, the planned merge conflict resolutions, the SPI UC pension-age integration (I1), and the dependency relock (R). It lands only on Max's go under d833.

The member PRs' impact estimates were measured separately, under different policyengine-uk versions or development heads. They do not add, and no full build of the combined release has run locally.

Member PRs and exact-head reviews

PR Change Reviewed head Independent review
#503 Exclude lodgers' rent payments from property income. 0812cd1b3c5952a2b4b259ae046baf44dfced8a3 approves this head
#509 Exclude property losses from income and count sub-let rent for every tenure; carries #503. cc937e0555d7ead58fc980b41b1959510709b1db approves this head
#490 Calibrate Housing Benefit to DWP's GB figures by age group. f4d8f95c512ad47b902c2a2bb9c12f8853cce7b8 approves this head (delta review over r4 at 954c225)
#510 Solve Pension Credit take-up over entitled units and use 37% take-up for newly entitled units; carries #490. 65656305498b1964ca000ecf1620097eec71c5ec approves this head
#516 Weight BRMA draws by census private-rented households. 29e1bd7a18a80942cc9014946d2dc8bea7e40b44 approves this head
#513 Use FRS benefit-unit capital for Pension Credit's capital test. a1690afaa72595b2d5a847ac6581b692be1fbf14 approves this head
#536 Fall back to committed OBR workbooks when obr.uk returns a non-workbook page. 7605e63efb57b1dce40df91ea459aa7da9485cc6 approves this head
#530 Replace OBR UC targets with one GB OBR UC total and reach the UC payment top band in calibration. 974fa8cbb90b636fdc06e21195a420a3e0fad7cf approves this head
#533 Restore HMRC salary-sacrifice relief targets. 0863a6aa540be49dac65cfd73c17ebe82b17eecd approves this head
#514 Remove unobserved benefit claims from SPI-synthetic rows. deb16e944e0ed00df43f6492344ebbc6b055657c approves this head
#517 Draw each cloned household's Output Area from its own FRS region. 1c5b20c02a2f0d3509fff90cc679f302b8225c0e approves this head
#524 Supply is_claimant_or_partner from the FRS adult table. 5e320ff2140bf2cb2a7ad0c01bc31d5fa71cb766 approves this head
#525 Set UC gainful self-employment from FRS main-job status. dc2abf7b4cd52b9e63c3631020af6e7e0c79c6da approves this head
#526 Map FRS EMPSTATI 11 to OTHER_INACTIVE. 1832adeb2296b53fe5a7d7194e770118aa34d1f8 approves this head
#529 Draw SPI incomes within earnings groups set by FRS employment status. d39371d508c33065e636fd9dcc5f15e750177fa2 approves this head

A4 merge conflict resolutions

#503, #509, #490, #510, #536, #533, and #517 merged without conflicts. No code choices beyond the A4 rules were needed.

I1: SPI donor UC pension-age exclusion

Apply #490's pension-age exclusion to #514's SPI donor redraw, so a benefit unit whose claimants are all over State Pension age cannot acquire would_claim_uc through that redraw.

apply_spi_donor_benefit_rules(dataset, donor_person=None, uc_pension_age_excluded=None) accepts an optional boolean array with one entry per benefit unit. After redrawing take-up flags, it applies would_claim_uc &= ~uc_pension_age_excluded when the array is supplied; with None, it excludes nothing. The rule function does not construct a Microsimulation.

The sole pipeline caller always computes the mask on the real target dataset with the existing _all_claimants_over_state_pension_age(target_dataset, year) helper, using the same computation as create_frs, and passes it explicitly. The helper loads a one-year UKMultiYearDataset container holding a copy of the target, so it evaluates that year's claimant and State Pension age variables without extending the dataset or uprating unrelated household inputs. Example and property tests compute the mask on _people_dataset(...) and pass it in; a caller-wiring test captures the keyword argument so omitting it fails. This keeps synthetic fixtures independent of household uprating inputs and avoids adding a simulation to every donor-rule Hypothesis example.

#490's exclusion therefore reaches both UC draws. #510's enhanced-dataset Pension Credit take-up step still supersedes #514's SPI Pension Credit redraw. On policyengine-uk 2.122.2, claimant_or_partner_variable selects is_claimant_or_partner, supplied by #524.

R: dependency relock

Set the dependency floors to policyengine-uk>=2.122.0 and policyengine-core>=3.32.13, retain one hypothesis>=6.168.3 requirement in dev, and keep build and towncrier>=24.8.0 on separate lines. Lock policyengine-uk exactly to 2.122.2, the version covered by the pin analysis, rather than advancing to a later release. #525 requires policyengine-uk 2.122.0 or later.

The expected lock changes are exactly four package records:

Package Main's lock Release lock
policyengine-uk 2.93.0 2.122.2
policyengine-core 3.31.1 3.32.13
hypothesis absent 6.168.5
policyengine-uk-data 1.56.16 1.57.4

R adds changelog.d/uk-data-batch-relock.changed.md. Together with the 15 member fragments, versioning should produce one 1.58.0 section containing 16 entries; #524's .added fragment drives the minor bump from 1.57.4.

Local validation

The four targeted files passed: 48 passed, 1 skipped (39 warnings) in 120.46 seconds, using the required shared lockf -k, two workers, and a fresh temporary directory removed afterward. The pension-age example, property, and caller-wiring tests passed; the original synthetic-fixture regression passed unchanged. The dataset-dependent pension-age check skipped because the built dataset was unavailable.

uv lock --check passed for 162 packages. Ruff formatting passed for 242 files and git diff --check passed. All 15 reviewed heads are included; only the seven expected storage paths differ, with no generated geography output.

What publishes after the go

The release branch push publishes no data. The release PR runs PR CI on the combined tree. On Max's go, one merge to main starts versioning and a docs redeploy; versioning commits 1.58.0, and push.yaml runs the full build and tests. The docs redeploy runs independently and can complete even if the later build tests fail. The hub cancels the duplicate push.yaml run on the merge commit at the old version.

If the full build and tests pass:

  • Private Hugging Face model repo policyengine/policyengine-uk-data-private: one commit writes seven root files: base FRS, enhanced FRS, tiny-base FRS, and tiny-enhanced FRS .h5 files; parliamentary_constituency_weights.h5; local_authority_weights.h5; and local_geography_weights.csv.gz. It also writes release_manifest.json at the root and at releases/1.58.0/, then creates and verifies HF tag 1.58.0.
  • GCS bucket policyengine-uk-data-private: overwrite those same seven objects in place, each with version metadata 1.58.0. Consumers fetching the HF root files or GCS objects by name receive the new release.
  • GitHub: main receives the release merge and the Update package version commit with version 1.58.0 and one CHANGELOG section. The publish workflow attempts a lightweight git tag 1.58.0, which the hub verifies afterward. The gh-pages docs redeploy comes from the merge commit because Weight FRS BRMA draws by census private-rented households #516 and Draw each cloned household's Output Area from its own FRS region #517 change docs/.

HF uploads before GCS. The installed-version manifest certifies policyengine-uk==2.122.2 and policyengine-core==3.32.13. Nothing is uploaded to PyPI or to the public HF repo, and no GitHub Release object is created. This batch changes no release-path files. The downstream microcosm#1087, PE-UK #2082, and policyengine.py release follow on their own gates; d778's policyengine.py release pins the then-latest policyengine-uk and core with the new data and certifies that bundle.

The release PR carries the member PRs' issue closures because GitHub's handling of their closing keywords when the member heads reach main has not been verified:

Fixes #515
Fixes #504

🤖 Generated with Claude Code

MaxGhenis and others added 30 commits September 30, 2026 07:55
Replace the OBR table 4.9 Housing Benefit target, which is GB DWP-funded
spending but was compared with UK-wide modelled Housing Benefit, with DWP
Spring 2026 targets for Housing Benefit spending and claims over Pension
Credit qualifying age in Great Britain. Targets can now name the countries
they cover.

The figures under Pension Credit qualifying age are kept but not
calibrated: the model pays working-age Housing Benefit to too few records
for a target to be met without concentrating it on a handful of them.

Stop drawing would_claim_uc for benefit units whose adults have all
reached State Pension age, keeping every other unit's draw unchanged.

Require policyengine-uk 2.102.5, which lets pension-age families make new
Housing Benefit claims (PolicyEngine/policyengine-uk#1901).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
State in the release note that modelled GB Housing Benefit falls to about
£8.0bn. Mark the mixed-age classification as an assumption, date the
benefit-group equality from 2024-25, and quote the calibrated build's
working-age claims. Add a test that the calibrated columns build on the
dataset, since the loss matrix skips a target whose column raises, and note
that would_claim_uc uses survey-year State Pension age.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FRS CVPAY is the rent a boarder or lodger pays the householder, after
deducting any state benefits to help with rent (FRS question CvPay, asked
about each person not related to the HRP in the second and later benefit
units of a conventional household). frs.py added it to the payer's own
property_income. Move the property income calculation into a tested
helper, frs_property_income, that keeps SUBRENT and ROYYR1 and drops CVPAY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #503: the seeded cases only draw non-negative amounts, so the
floor at zero was never active, and every fixture had a unique index
while create_frs stacks the adult and child tables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tenure

ROYYR1 holds a property loss as a positive amount flagged by RENTPROF = 2,
and the build counted it as income. A loss now counts as zero.

SUBRENT was counted only for owner-occupiers, although the FRS asks every
household about sub-letting. The tenure restriction is removed.

SUBRENT stays as reported whether SUBALLOW says it is before or after
allowable expenses; the FRS collects no expense amount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Negative FRS values are missing-value codes (-1 to -9), not amounts. Flooring
the sum let a missing ROYYR1 code cancel the household reference person's
SUBRENT, which now matters for renting households too. Floor each amount
instead. FRS 2024-25 has no negative codes in either field, so the build is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…flag

The create_frs smoke test's fake simulation did not answer the variables
the would_claim_uc rule now reads (person_benunit_id, is_adult, is_SP_age),
which failed CI with KeyError: 'person_benunit_id'.

Pick the claimant-and-partner flag the way the locked policyengine-uk does
for Housing Benefit's pension-age route: is_claimant_or_partner where the
release defines it, otherwise is_adult as in 2.102.5. Both the
would_claim_uc rule and the age-group targets use it. Under 2.102.5 the
base FRS is identical to the previous commit's in every column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TOTCAPB3 (DWP's derived benefit-unit total of savings and investments) for
every FRS benefit unit; -1 where missing or negative. Pension Credit counts
the claimant's and partner's capital (SPCA 2002 s.5); the WAS household
imputation gives FRS Pension Credit reporters a weighted median of 74,300
pounds against the survey's own 300 for those the model then finds not
entitled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second-stage QRF draws each SPI-donor person's benefit reports from
age, gender, region and incomes, with no view of the benefit unit or of
health. Those reports then act as existing claims in policyengine-uk. The
take-up anchors, receives_benefits_in_own_right and ssmg_reported were
also left as the FRS donor's.

On SPI-donor rows, this zeroes the income-related awards (UC, Pension
Credit, Housing Benefit, CTR, IS, tax credits, income-related ESA and
JSA, SSMG), the out-of-work benefits (contributory ESA and JSA,
incapacity benefit, SDA) and Child Benefit, whose only use is the
take-up anchor. It then rebuilds the anchors and
receives_benefits_in_own_right from the rows' own reports. The zeroed
columns stay in the QRF chain, so the values of the reports kept do not
change.

Also adds hypothesis as a dev extra, for the property tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FRS build drew each benefit unit's Broad Rental Market Area from rows
of lha_list_of_rents.csv.gz within region and LHA category. That file's
Scottish, Welsh and Northern Ireland lists are byte-identical copies of
English BRMAs' lists, each matched on the April 2019 rate, so those
nations' draws ignored their rental markets (#515).

Draw instead in proportion to census private-rented households by region,
BRMA and the bedroom band matching each LHA category (England and Wales
2021, Scotland 2022, Northern Ireland 2021; Northern Ireland has no
bedrooms question, so its weights cover every category). Move the draw to
datasets/brma.py, add property tests for it, document sources and
validation in storage/BRMA_DATA_SOURCES.md, and remove the list of rents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efit flag

Review of 96672af (subfleet 20261002-011737-spi-514-review):
- IIDB, AFCS and bereavement support follow an injury, service or a
  death, not income, and the QRF drew them at 6.2, 2.4 and 4.6 times the
  FRS rate by weight. SPI rows now take the donor's own values.
- The Child Benefit take-up flag keeps the donor's value: the award does
  not depend on the replaced incomes and the claim is for the same
  children. Only the UC and Pension Credit flags are redrawn.
- The comment no longer says the model's means test replaces the zeroed
  income-related reports: in policyengine-uk 2.93.0 housing benefit, CTR,
  IS, tax credits and income-related ESA and JSA need a report, so SPI
  rows no longer get them. Evidence is now cited by weight, and Child
  Benefit counts 16-19 qualifying young people.
- One assign_reported_takeup helper serves create_frs and the SPI rows,
  so the rate and the anchoring rule have one source. The SPI draws no
  longer depend on which columns are present.
- Tests pin the three rule sets, use shuffled, gapped ids and check the
  rebuilt flags against reports that are not zeroed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e year and the UC redraw

Round-2 review of eb14fd0 (subfleet 20261002-041419-spi-514-review-r2,
APPROVE): the FRS comparison for ESA (contributory) rested on under 10
records; the take-up rates' year and the stage-two UC redraw had no
direct test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An FRS benefit unit is one adult or a couple plus dependent children. The
adult table holds the head (UPERSON 1) and any partner (UPERSON 2); any
other adult forms and heads their own benefit unit. So adult-table
membership is policyengine-uk's is_claimant_or_partner (#1896), which the
model otherwise infers from ages and cannot get right for unflagged
large-age-gap households (policyengine-uk#2039).

- frs.py derives it next to is_parent and rejects a benefit unit without
  one or two adult records (none in FRS 2020-21, 2022-23, 2023-24 or 2024-25).
- SPI records are one-person benefit units, so each is its claimant.
- The transfer builder marks the tax-unit head and a joint filer's spouse;
  the checked-in enhanced_cps_2025.h5 gains the builder's column in place
  (every other column unchanged).
- stack_datasets refuses to stack a person table without the role onto one
  with it. Cloning, subsampling, uprating and the SPI and CGT donor copies
  carry whole person tables, so the column follows them.
- Tests: examples, Hypothesis properties over generated FRS-shaped
  households, stacking, cloning, SPI, transfer builder vs checked-in H5,
  and built-dataset invariants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
create_frs's smoke test stubs Microsimulation.calculate with plain arrays,
so take np.asarray rather than .values, and remove the stub of the
deleted lha_list_of_rents.csv.gz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/brma_households downloads every census and BRMA geography input,
checks each against a pinned sha256 (decoded-content hashes for the ONS
custom-API batches), and rebuilds
storage/brma_private_rented_households.csv byte for byte. Scotland's
census ward table is the one manual download, with instructions in the
manifest. Area overrides established from official lookups carry dated
evidence and are asserted against the polygons so a boundary change
fails loudly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Northern Ireland: build each BRMA's private-rented households from
  NISRA's postcode-district household counts and NI's private-rented
  share, dropping the ONS Postcode Directory Northern Ireland records,
  whose LPS end user licence does not clearly allow publishing derived
  figures. BRMA shares move by 1.5 points on average.
- Move the household pick into brma.pick_household_brmas and test it.
- Test that every category uses its bedroom band, with spot values; give
  each region's BRMAs distinct names in the property tests; make the
  fail-closed test remove a cell rather than skip.
- Decode enum regions; assert Scotland's bedroom headers exactly.
- Docs: Cardiff Bay and Balloch overrides, centroid wording, what the
  validation correlates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clone_and_assign sampled a household's OA by population from anywhere in
its country, then wrote the OA's region, LA and constituency onto the
household without touching `region`. In release 1.57.4, 88.5% of English
households (86% of English weight) carry an OA in a different region from
the one the FRS recorded, and the median English LA draws 93% of its
weight from households surveyed in another region.

policyengine.py groups households by `constituency_code_oa` / `la_code_oa`
for its constituency and local-authority impacts and for area-scoped
simulations, so every English area was reporting an England-wide mix.

assign_random_geography now takes the households' regions and samples
within the region (Wales and Scotland are one region each; a household
with no region below the country falls back to its country). Collision
avoidance draws from the same region. A region with no OAs in the
crosswalk, a region that contradicts the country, or an unrecognised
region value raises. Called without regions it draws exactly as before:
seed 42 reproduces the released `oa_code` column.

Adds Hypothesis to the dev extras for the property tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…K 2.107.0

- SPI-synthetic copies get pension_credit_reported_capital = -1: the FRS
  donor's capital does not belong with SPI-imputed incomes (24 of 152 SPI
  PC reporters were entitled in build E only because of it).
- uprating_factors.csv and uprating_growth_factors.csv get the column with
  the savings row (policyengine-uk uprates it with the same per-capita GDP
  index), so uprate_dataset keeps the two in step.
- policyengine-uk >= 2.107.0, the first release defining the variable
  (#2018); core 3.32.12 comes with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk#2081 adds the person input uc_is_in_gainful_self_employment
(UC Regs 2013 reg 64). Without it the model reads any self-employment income
other than zero as gainful self-employment and none as none, so a trader who
breaks even, or whose loss this build floors at zero, never gets the minimum
income floor, while an employee's small side trade does.

Set it from the FRS: true when the main job (EMPSTATI) is self-employment,
whatever its profit (ADM H4013, H4054, H4503), and when a side trade's profit
is above the person's employment income (ADM H4034). The SPI copy in
impute_income re-derives it from its own imputed incomes. Releases of
policyengine-uk without the variable skip the column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FRS employment_status mapping zipped range(12) with 11 statuses, so
EMPSTATI 11 ("Other inactive" in the UKDS FRS 2024-25 data dictionary) was
unmapped and the fillna fallback made it LONG_TERM_DISABLED. That also put
other-inactive working-age adults into the ESA health-condition and
support-group proxies.

Map the adult codes through an explicit table keyed to the data dictionary,
make child-table rows CHILD, and fail the build on any adult code the table
does not know rather than guessing a status. Every adult in the 2020-21,
2022-23, 2023-24 and 2024-25 releases carries a code from 1 to 11.

Tests cover codes 0-11 exhaustively, Hypothesis properties of the mapping and
the ESA proxies, create_frs end to end for every code and a child row, and
the built datasets. Adds hypothesis as a dev dependency (same pyproject and uv.lock
change as #522 and #525).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SPI income model drew every SPI-synthetic row's six incomes from age,
gender and region alone, while the row kept its FRS donor's employment
status. Children drew pay, the unemployed and retired drew earnings, and
self-employed rows rarely drew a profit.

Fit one QRF per earnings group (employee, self-employed, both, neither) on
the SPI records in that group, using PAY and the SPI self-employment
indicator SEINC_NUM, and draw each FRS row from the group its employment
status and recorded earnings put it in. Children keep their own incomes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same lines as #514 and #524, so whichever lands second merges cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hypothesis properties for the FRS and SPI group mappings, the training
sample allocation, the per-group model's draws and the donor values kept
for children, plus a check on a built enhanced FRS. The cache tests write
the per-group format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI build logs are public, so the error now names the unknown codes without
per-code counts and reports a total under 10 adults as "fewer than 10".
Raised by the microcosm port session; no change on current FRS releases,
where every adult has a code from 1 to 11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under pandas 3, Series.astype(str) keeps NaN as a float, so sorting a set
holding a blank code and another unknown code raised TypeError and hid the
codes. Format from the float codes instead ("0, 12, blank"), which behaves
the same under pandas 2 and 3. Found by the microcosm port session's
differential under pandas 3.0.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With SPI incomes drawn by earnings group, the SPI-synthetic rows no longer
give pay to children and people out of work. Calibration then met the HMRC
counts of income-tax payers with employment income, which are annual and
include part-year earners, by moving about 4m people's weight from out of
work to employees (32.2m against the LFS's 29.1m for 2024). Nothing tied
employment status to an official count.

Add national targets for the ONS LFS employee and self-employed levels
(MGRN, MGRQ annual averages, 2022-2025), counted from the FRS ILO main-job
status. Move the status groups to utils/employment_status.py so the income
imputation and the targets share them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A loaded runner tripped Hypothesis's too_slow health check while the
properties themselves held. Drop the deadline and that health check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HMRC's counts of income-tax payers with employment income by area are
annual, so they include people with pay for part of the year whose FRS
status at interview is out of work. Trained on, they held employees at
32m against the LFS target of 29.6m. The area amounts and the national
counts by income band still train; the counts stay in the calibration
logs as validation targets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 12 commits October 6, 2026 08:45
Conflict resolved per runbook A4 step 6: pyproject.toml takes policyengine-uk>=2.107.0 (R sets the final floor); the auto-merged uv.lock is left as merged and reset to main's in R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict resolved per runbook A4 step 8: targets/schema.py keeps #490's side, with carry_forward; #530's hunk is a verbatim copy of #490's earlier countries hunk without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved per runbook A4 step 10: datasets/frs.py keeps #514's assign_reported_takeup(...) calls for would_claim_pc and would_claim_uc, keeps #490/#510's comments, and ANDs #490's ~derive_all_claimants_over_state_pension_age(...) term onto the would_claim_uc draw (draw count unchanged); .gitignore takes the union of both sides; uv.lock reset to main's (b45c373), relocked in R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved per runbook A4 step 12: datasets/frs.py keeps both new functions, #490's derive_all_claimants_over_state_pension_age and #524's derive_is_claimant_or_partner_from_frs_microdata; uv.lock reset to main's (b45c373), relocked in R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict resolved per runbook A4 step 13: datasets/frs.py keeps all three new functions, #490's derive_all_claimants_over_state_pension_age, #524's derive_is_claimant_or_partner_from_frs_microdata and #525's derive_uc_is_in_gainful_self_employment. #525's uv.lock edits auto-merged and are reset to main's in R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved per runbook A4 step 14, both in tests/test_legacy_benefit_proxies.py: (a) the FakeMicrosimulation double keeps #490's person_benunit_id, is_adult and is_SP_age branches and #526's [66] * len(...) state_pension_age; (b) #526's create_single_adult_frs helper drops the lha_list_of_rents.csv.gz read_csv fake, because #516 deleted that file and its fake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict resolved per runbook A4 step 15: uv.lock reset to main's (b45c373), relocked in R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SPI-donor rules redraw would_claim_uc on SPI-synthetic rows, so a unit
whose claimant and any partner have all reached State Pension age could get
would_claim_uc there even though create_frs (#490) never gives it one.
apply_spi_donor_benefit_rules now takes an optional per-benefit-unit mask,
uc_pension_age_excluded, and clears would_claim_uc where it is set. Its one
pipeline caller always computes the mask on the real target dataset with the
same derivation as create_frs and passes it; a test checks that wiring.

Integration commit for the 10/8 uk-data release (d833).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#525 needs policyengine-uk 2.122.0 or later (first release with PE-UK
#2081). Floors become policyengine-uk>=2.122.0 and policyengine-core>=3.32.13;
the dev extras keep a single hypothesis>=6.168.3. The lock changes exactly
four packages: policyengine-uk 2.93.0 -> 2.122.2, policyengine-core 3.31.1 ->
3.32.13, hypothesis added (6.168.5), and the package's own entry 1.56.16 ->
1.57.4. uv lock --check passes.

Relock commit for the 10/8 uk-data release (d833).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ount (F1)

test_full_target_set_available_offline (from #536) asserted at least 30 OBR
targets offline. The release combines four member PRs that each replace OBR
targets with better sources, which no single PR's CI saw together:
#490 swaps obr/housing_benefit for DWP Housing Benefit targets, #510 swaps
obr/pension_credit for DWP Pension Credit spend and caseload, #533 swaps the
two OBR salary-sacrifice NI relief targets for HMRC's, and #530 merges the
two OBR UC targets into obr/universal_credit. That leaves 29 OBR targets (34
on main), so release CI failed with 29 >= 30.

The test now checks what it was for: offline, the committed workbooks give
exactly the OBR targets that the same workbooks give when served, plus the
receipts and NICs names. A broken fallback still fails it.

Release-only fix for the 10/8 uk-data release (d833).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 8936a56 into main Oct 7, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant