Repository navigation
Uprate three more columns, and guard the gap that hid them - #1863
Conversation
|
Changes requested: clear the formatting failure and register the changelog fragment. Reviewed head: The uprating additions remain useful. Main
Acceptance commands from the repaired checkout: uv run pytest policyengine_uk/tests/code_health/test_uprating_declarations.py -q
uv run ruff format --check policyengine_uk/tests/code_health/test_uprating_declarations.py
uv run ruff check policyengine_uk/tests/code_health/test_uprating_declarations.py
uv run towncrier build --draft --version 0.0.0Require all three tests and both Ruff checks to pass, verify that Towncrier includes the fragment, and obtain green CI on the updated head. Keep this repair scoped to formatting and the fragment filename; changing the economic indices or removing exceptions requires separate evidence. Validation performed: exact-snapshot guard tests 3 passed in 0.91s; formatter failure and ignored fragment independently reproduced. Execution used the existing Python 3.13.14/core 3.32.9 environment. The map was compared with current main and existing variable declarations. The guard confirms consistency with those declarations; it does not independently establish the economic choice of indices. No private-data projection or rebased integration run was performed, and the proposed repair has not been applied or tested. Live posting check (2026-10-02T15:11:38.782329+00:00): same reviewed commit; GitHub reports |
747a1d1 to
8300b0a
Compare
juaristi22
left a comment
There was a problem hiding this comment.
Re-review of 8300b0a: the two requested blockers are addressed in the diff. The guard test is formatted, the changelog fragment now has the .fixed.md type, and the added mappings are placed under the variables’ declared indices. Lint and the four install/smoke jobs pass; the current Test job is still running, so this is a comment rather than an approval.
Should address: test_every_known_exception_still_declares_uprating checks only that an exception variable exists and still declares uprating. It does not check that a KNOWN_UNUPRATED entry remains absent from the YAML or that a KNOWN_INDEX_OVERRIDES entry still has an index mismatch. If an exception becomes stale, it can mask a later gap. Please check that each exception’s reason still holds, or remove it when resolved. This is a guard-coverage concern; I found no demonstrated current mapping error.
I could not independently run the guard, Towncrier draft, or private-dataset projections in this workspace. Please wait for the Test job before treating this re-review as complete.
Review of #1863 — Uprate three more columns, and guard the gap that hid themBase repository: PolicyEngine/policyengine-uk Source DocumentsNo source documents registered; see scope and validation. CriticalNone. Should AddressNone. SuggestionsNone. Evidence Gaps
Notes
Validation SummaryReused exact-head validation (not rerun in this batch): 12 passed, 1 warning in 6.35s; snapshot import confirmed; raw command/paths in pr-1863-code-tests-config.json and pr-1863-code-tests.log; Diagnostic: ten-column uprating script ( Reused exact-head validation (not rerun in this batch): 0 official originals; static source/policy review; no duplicate model execution. Manifest: pr-1863-review-sources.json; bundled check_source_manifest.py passed under existing Python3.13; cache result pr-1863-review-source-cache-check.json CI at 155f50b: Install + smoke-import (py3.11)=SUCCESS, Install + smoke-import (py3.12)=SUCCESS, Install + smoke-import (py3.13)=SUCCESS, Install + smoke-import (py3.14)=SUCCESS, Lint=SUCCESS, Test=SUCCESS. Detailed code, policy, source and reproduction artifacts were retained locally. Timingelapsed seconds: 1151.70s; setup seconds: 271.12s Review SeverityCOMMENT. Open findings: 0 critical, 0 should address, 0 suggestions. |
Follow-up to #1860, closing most of #1862. Three variables declared an uprating index but were missing from uprating_indices.yaml, so they were never uprated. On enhanced_frs_2024_25, 2024 -> 2027: water_and_sewerage_charges 375.51 -> 522.12 (+39.0%) housing_service_charges 91.81 -> 105.06 (+14.4%) pension_contributions_via_salary_sacrifice 418.69 -> 466.81 (+11.5%) Each matches its index compounded over the same years exactly: ofwat water bills +39.0%, OBR lagged average earnings +14.4%, OBR average earnings +11.5%. All three indices already had the yoy_growth twin the YAML needs, so this is a YAML-only change. rail_usage is deliberately NOT included. Its declared index, gov.dft.rail.ridership_index, is a CUMULATIVE index with 2020 = 1.0 — "Used for uprating the quantity component of rail subsidy spending" — not a year-on-year rate. Adding it to a yoy_growth block compounds the level and uprates the variable by about 684% over 2024-27. Uprating it properly needs a year-on-year series, which is more than a YAML entry. The durable fix is the code-health test suggested in review of #1860: policyengine_uk/tests/code_health/test_uprating_declarations.py asserts that every variable declaring `uprating` appears in the YAML under the matching index. Verified it fails when an entry is removed, rather than only passing today. Three exceptions are listed with reasons rather than silently tolerated: rail_usage and employee_pension_contributions_reported as not uprated, and savings_interest_income as uprated by a different index from the one it declares — the mismatch noted in #1862, left for someone with the context to say which is intended. A third test keeps those lists honest by asserting each entry still exists and still declares uprating. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lose the gaps it now finds - ruff format test_uprating_declarations.py (the Lint failure). - Rename the fragment to uprate-four-remaining-columns.fixed.md so towncrier renders it under Fixed rather than dropping it. - After rebasing onto main, the guard flags ten variables declared since: seven are genuine gaps and now uprate by the index they declare (cash_isa, stocks_and_shares_isa, mortgage_debt, consumer_debt, rent_paid_as_boarder, rent_paid_as_lodger by per-capita GDP; self_employment_gross_receipts by per-capita mixed income); the three energy columns are listed as deliberately unuprated, per #1868. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The exception check only confirmed each entry still existed and declared `uprating`. It now also fails when a KNOWN_UNUPRATED variable has been added to uprating_indices.yaml, or when a KNOWN_INDEX_OVERRIDES variable is now uprated by its declared index (or has left the YAML), naming the list to edit. Unit tests feed the guard stale entries to prove each case is caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Suggested The check isn't required yet (decision d1368); it fails fix PRs whose description names no reproducer. 🤖 Generated with Claude Code |
155f50b to
d150116
Compare
|
Rebased onto main (d150116). The one conflict was in |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed d150116d0d08c9b9db2234f348dd38c84ea816e1. The rebase retains both main’s uc_reported_capital entry and this PR’s uprating entries. The stale-exception guard now fails as intended; all eight focused current-head guard tests pass. I found no new YAML membership defect.
I checked the official OBR March 2026 economy workbook: its earnings, nominal-GDP and mixed-income figures support the PR’s 11.5% and 14.4% earnings examples and the numerator series. The remaining source gap is the dated ONS population projection/vintage used to turn GDP and mixed income into per-capita factors. The YAML points only to the ONS homepage, and its 2025 population growth differs from the rate implicit in the OBR workbook.
For water bills, Ofwat’s published 2025–26 26% figure was a forecast; its later release reports 27% for that year and 5.4% for 2026–27, versus the model’s 26% and 5.5%. The 2027–29 factors are custom projections, and the local build expects an untracked ofwat_increases.csv, so I could not reproduce their company-weighted derivation. Please document the inputs, vintage and projection calculation, or bound the effect of using these existing series for the newly uprated columns. I can revisit approval with that provenance and completed current-head CI.
…series' provenance Water bill growth for 2025-26 becomes 27%, the figure in Ofwat's 29 January 2026 statement, replacing its 30 January 2025 forecast of 26%. 2026-27 becomes 5.4%, Water UK's 28 January 2026 national average, replacing a 5.5% projection. The 2027-2029 projections are kept and their method is recorded, including that their inputs were never committed. ons.population is documented as the OBR long-term economic determinants for the March 2025 EFO (19 June 2025), with the ONS 2022-based migration category variant from 2030-31, and the per-capita series note that they use it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks. Both points are addressed in 7ab0afa, with details under "Provenance" in the description.
Following Max's comment, the description now has the @juaristi22 could you re-review? |
Resolve uprating_indices.yaml: keep main's trading_loss and this branch's self_employment_gross_receipts under obr.per_capita.mixed_income. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
List main's four owned_company inputs under the indices they declare, which the guard caught after the merge. Narrow main's fallback assertion in test_projection_horizon.py to the columns that are still unlisted, and correct the guard docstring now that the uprating attribute drives the post-2030 fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged main into this branch (6c3f5d6). Beyond the conflict, which kept both
|
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed 6c3f5d60e4947512e1bc9a77b2ff40a252dbcd40 after the provenance updates and main merge. The code checks are clear: all 24 changed guard, water-growth and projection tests pass, including the Hypothesis checks. The new company columns use their declared indices, and the post-2030 fallback does not double-uprate them. Captured current-head CI passes.
The dated OBR release attribution is now documented, and I verified Water UK’s 2026–27 increase of 5.4% (£33 to £639). Two numerical source checks remain incomplete in this run:
- The official OBR workbook downloads returned “No Access” HTML or403, so I could not independently check the population row and the newly supplied per-capita vintage bounds.
- Both permitted routes to the cited Ofwat statement were blocked, so I could not newly corroborate the changed 2025–26 value of27%. I have not established that the value is wrong.
The explicitly disclosed 2027–2029 forecast/weights limitation remains tracked by #2384. I am leaving this review PARTIAL with no confirmed code defect. Accessible copies of the exact official workbook/table and Ofwat statement would let me close the remaining source checks without repeating the model tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… earnings Main added this input (e28e3b6) declaring obr.average_earnings without a YAML entry, so the guard fails once main is merged. It is the pre-9 July 2015 share of pension_contributions_via_salary_sacrifice, which the YAML already uprates by the same index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@juaristi22 thanks for the 15 Oct re-review. Two updates.
🤖 Generated with Claude Code |
Keep this PR's new columns under obr.per_capita savings growth and main's comment for uc_reported_capital. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 134 (thresholds in Notes
Moves flagged for review
94 more flags are in the report artifact. Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
Household income totals
Poverty rates
People in poverty
Inequality
Mean household net income in each side's own deciles
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileMean change in household net income per household, and as a share of the decile's base income; households stay in the base run's deciles.
No change in any year: components the model counts twice; population. Checks failing on both base and head (1)
Counted-once checkEach of 84 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Components the model counts twice: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
|
@juaristi22 here are readable copies of the two sources you could not download. The live sites block scripted requests, so these are Wayback Machine captures. GitHub does not let me attach files from the command line, so the archive links and quoted values below stand in for attachments. Ofwat, 27% for 2025-26
Ofwat, "Ofwat responds to water sector average bill figures", 29 January 2026:
The sentence before it gives the 2026-27 figure the PR uses: "an increase of 5.4% or £33". Both match The earlier statement does not contain 27%. It has the 26% forecast that the PR replaces ("the average increase forecasted for 2025/26 will be 26% or £123", Ofwat, 30 January 2025):
OBR long-term economic determinants, March 2025 EFO (19 June 2025)
The archived workbook is dated 19 June 2025 in cell B2 (SHA-256 The PR stores each fiscal year at 1 January of its first year (2025-26 at 2025-01-01), as a fraction rounded to four decimals. All 52 years from 2022-23 to 2073-74 match. There are no mismatches. The 2021 value (0.004) is not in the workbook, as the YAML comment already says.
Remaining years, 2031-32 to 2073-74 (all match)
🤖 Generated with Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed 270be00ac8da8235a9ab806345f12ae6251fb238.
The two source gaps are closed. I independently downloaded the archived official OBR workbook, verified the stated SHA-256, and checked the native headers, units and footnote: 52/52 population-growth values for 2022–2073 match the stored fiscal-year mapping after conversion and rounding. The workbook establishes the dated ONS-variant provenance.
The archived Ofwat article is readable original article content and corroborates 27% for 2025–26 and 5.4%/£33 for 2026–27. The independently downloaded March 2026 economy workbook also supports the documented earnings/vintage sensitivity checks.
25 focused code/diagnostic checks pass, covering the guard, mappings, projection and final AHC cost effect. Current-head hosted CI passes. The inherited custom 2027–29 water forecast/weight limitation and population-vintage refresh remain explicitly disclosed follow-ups (#2384/#2383); they are not presented as newly verified forecasts or private population validation.
I am approving this commit. Review status: COMPLETE; no open critical findings.
…benefits No conflicts: none of the ten files main changed since 76cfc9e is changed by this PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arge adjusted_total_income: main's #2488 relief limit (relevant earnings, raised towards the basic amount only by relief-at-source contributions) with this branch's Step 1 from total_income_charged_to_tax and its net pay add-back. Documentation keeps both. The net pay property test's reference takes #2488's limit for invariant 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ofwat's water bills series is the average household bill in England and Wales. Scottish Water sets Scotland's household charges within limits from the Water Industry Commission for Scotland, and they followed a different path: 8.8% in 2024-25, 9.9% in 2025-26 against Ofwat's 26%, and 8.67% in 2026-27. Northern Ireland has no domestic water charges. Add gov.economic_assumptions.yoy_growth.scottish_water.water_bills, with WICS's draft CPI + 2% cap for 2027-28 to 2029-30 until the Final Determination (29 October 2026), and uprate water_and_sewerage_charges by country in apply_single_year_uprating, as council tax is: England, Wales and unknown regions by Ofwat's series, Scotland by Scottish Water's, Northern Ireland not at all. The column was previously held flat to 2030 and then grown by the England and Wales index everywhere. It is kept out of the post-2030 fallback, reset with the other growth-factor inputs, and covered by the projection fingerprint. Fixes #2501. Part of #2523. Overlaps #1863, which lists the column under the Ofwat index: whichever lands second drops that line, and test_water_charges_are_uprated_once fails until it does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1863 lists water_and_sewerage_charges under the Ofwat index in uprating_indices.yaml. uprate_water_and_sewerage_charges now grows it by country, so the list entry would grow it a second time: drop it, and record the column among the code-health test's deliberate exceptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-up to #1860, closing most of #1862. Thanks @juaristi22 for finding these and for suggesting the guard.
The three fixes
Each declared an uprating index but was absent from
uprating_indices.yaml, so was never uprated (on main since #2376's projection fallback, it stays flat through 2030 and grows only after). Onenhanced_frs_2024_25, 2024 → 2027:water_and_sewerage_chargeshousing_service_chargespension_contributions_via_salary_sacrificeThe water row uses the published 2025-26 and 2026-27 bill increases (see Provenance below); it was 522.12 (+39.0%) with the old 26% and 5.5%. Each matches its own index exactly, which is the check that they are now uprated by the right thing rather than merely uprated.
All three indices already had the
yoy_growthtwin the YAML needs — your caution about missing parameters turned out not to apply to these — so this is a YAML-only change.rail_usageis deliberately excludedIts declared index is a cumulative level, not a year-on-year rate:
Values run 0.95 (2024) → 1.005 (2027). Putting it in a
yoy_growthblock compounds the level and uprates the variable by ~684% over 2024–27. I had it in the YAML at first and caught it on the before/after check. Uprating it properly needs a year-on-year series, which is more than a YAML entry.The guard
policyengine_uk/tests/code_health/test_uprating_declarations.pyasserts every variable declaringupratingappears in the YAML under the matching index — the durable fix you suggested, which would have caught #1859 at the point it was introduced.I verified it fails when an entry is removed, rather than only passing today. (That check matters: a guard I wrote on the dashboard side turned out to pass regardless of the thing it claimed to prevent, so I no longer trust one I have not seen fail.)
Three exceptions are listed with reasons rather than silently tolerated:
rail_usage— cumulative index, as aboveemployee_pension_contributions_reported— reported counterpart feeding imputationsavings_interest_income— the index mismatch you flagged; declaresons.household_interest_incomewhile the YAML appliesper_capita.gdp. Left as-is for someone with the context to say which is intended — I did not want to change an uprating basis on a guess.A third test keeps those lists honest: each entry must still exist and still declare
uprating, so a stale exception fails rather than lingering.Since review (rebased onto main, 7 Oct)
ruff formaton the guard test; the changelog fragment is typed.fixed.md, so it renders under "Fixed".cash_isa,stocks_and_shares_isa,mortgage_debt,consumer_debt,rent_paid_as_boarder,rent_paid_as_lodger. Per-capita mixed income:self_employment_gross_receipts.electricity_consumption,gas_consumptionanddomestic_energy_consumptionare listed inKNOWN_UNUPRATED, because the data build prices energy spend (Stop CPI-uprating energy, which the data build already prices #1868).8300b0a96: guard tests 3 passed (and fail withcash_isaremoved);tests/code_health1,953 passed; the YAML suite 2,605 passed;ruff format --check .andruff check .clean.Verification
265 passed, 45 skipped (262 before, plus the three new tests). Changelog fragment included.
Checks at
7ab0afa33: guard tests 8 passed;tests/code_health, the water test and the top-level uprating test files 2,314 passed; the YAML suite 3,058 passed;ruff format --check .andruff check .clean;.github/check_fix_pr.pyPASS against this description. The new water test fails with the old 0.26 and 0.055.#1862 stays open for
rail_usageand thesavings_interest_incomebasis question.Merge with main (14 Oct)
Merged
origin/main(232 commits behind). Three things changed beyond the conflict:uprating_indices.yamlconflicted onobr.per_capita.mixed_income: main addedtrading_loss, this PRself_employment_gross_receipts. Both kept.owned_company_capital,owned_company_holding_value,owned_company_trade_assetsunder per-capita GDP,owned_company_income_shareunder per-capita mixed income). Listed under the index each declares.upratingattribute to grow unlisted columns after 2030 (_unmapped_uprating_indices). The guard docstring no longer calls that attribute dead metadata, andtest_unmapped_inputs_preserve_legacy_years_and_grow_after_2030now asserts only the columns still unlisted (rail_usage,employee_pension_contributions_reported) are fallbacks, since the columns this PR lists grow every year instead.Merge with main (18 Oct)
Merged
origin/main(295 commits behind, no conflicts). The guard then failed on one input main added for the annual allowance taper (e28e3b6):pension_contributions_via_salary_sacrifice_pre_2015declaresobr.average_earningsbut was not in the YAML. It is the pre-9 July 2015 share ofpension_contributions_via_salary_sacrifice, so it is now listed beside it under average earnings. It defaults to zero in survey data, so no current output moves.Provenance of the series these columns now use (since review, 12 Oct)
Water bills (
ofwat.water_bills). Values are charging years from April, stored at 1 January (2025 holds 2025-26).utils/water/forecast_water_bills.py(Improve water forecasts #1239). The script takes the customer-weighted mean of the CCW per-company table and adds a hardcoded CPIH path. The CCW page says that table shows Ofwat's PR24 draft determinations. The weights were inofwat_increases.csv, which is gitignored and was never committed, so these values cannot be reproduced. Filed as Water bill projections for 2027-29 cannot be reproduced from the repo #2384.water_and_sewerage_charges, which this PR starts uprating.create_ahc_deflator.pyreads it offline and was not rerun. Effect: the 2026 and later water factors rise by 0.70% (1.27 × 1.054 against 1.26 × 1.055), about £3.50 a household a year on the mean above.Population (
ons.population), the denominator forper_capita.gdpandper_capita.mixed_income.household_weightis uprated by the same population series, so in the uprating step aggregates still grow with nominal GDP or mixed income. The vintage mainly shifts the split between amounts per household and household counts.ons.populationdriveshousehold_weightand everyper_capita.*series, so a refresh has to re-derive them together. Filed as Refresh ons.population and re-derive the per-capita series from one vintage #2383. The YAML now cites the dated release, the mapping and the vintage instead of the ONS homepage.Reproducer:
policyengine_uk/tests/code_health/test_uprating_declarations.py::test_declared_uprating_variables_are_in_the_yaml,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_yaml_uprates_by_the_declared_index,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_every_known_exception_still_holds,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_guard_passes_exceptions_that_still_hold,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_guard_flags_an_unuprated_exception_added_to_the_yaml,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_guard_flags_an_override_that_now_matches,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_guard_flags_an_override_dropped_from_the_yaml,policyengine_uk/tests/code_health/test_uprating_declarations.py::test_the_guard_flags_missing_or_undeclared_exceptions,policyengine_uk/tests/test_water_bills_growth.py::test_published_water_bill_growth,policyengine_uk/tests/test_water_bills_growth.py::test_water_index_compounds_the_published_yearsaxiom: n/a: uprating of survey input columns and economic-assumption provenance; no tax or benefit rule changes