Repository navigation
Apply each growth index's first listed year - #2693
vahid-ahmadi wants to merge 1 commit into
Conversation
Set index_base_year to the year before the first listed growth on the ten series a direct input dated before that year could hit: per-person GDP and mixed income, house prices, mortgage interest, petrol and diesel spending, rail journeys per person, population, and the generated private pension and lagged earnings indices. No growth value changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22
left a comment
There was a problem hiding this comment.
Program review
Base repository: PolicyEngine/policyengine-uk
PR number: 2693
Reviewed head SHA: b213aaf
Merge base SHA: b1ee29e
Mode: full
Scope: Ten explicit first-growth index anchors, generated metadata forwarding and affected direct-input/projection mechanics against the declared stacked PR2638 base; no full historical statistical-source audit
Source manifest: /private/tmp/policyengine-command-runs/881387a1b745/pr2693-direct-review-20261027-review-sources.json
Review status: PARTIAL
Source Documents
No source documents registered; see scope and validation.
Critical
None.
Should Address
None.
Suggestions
S1 — code-s1 — Qualify the dataset-projection statement (OPEN)
Location: policyengine_uk/parameters/gov/economic_assumptions/README.md:17–18, also changelog.d/2692-index-first-growth-year.fixed.md:1 and the new test module's lines 6–8. These passages describe projection as reading growth rates directly and being unchanged. That accurately describes the ordinary mapped growth loop (data/economic_assumptions.py:356–376), but residential-property projection explicitly reads the rebased, five-decimal rounded cumulative house-price index (:311–313, :533–546). Its ratios can therefore change slightly even for an observation after 2021, as the PR body already discloses. For example, the unchanged literal 2021/2022 house-price growth rates produce old levels 1 / 1.09260 and new levels 1.08200 / 1.18219, so the 2021→2022 ratio changes from 1.0926 to approximately 1.09259704. Qualify the prose to preserve the body's rounding caveat. This is an optional accuracy correction, not evidence that the tiny numerical drift is an incorrect policy output. Independent scalar arithmetic is saved in pr2693-direct-review-20261027-code-rounding-arithmetic.json; no model or dataset was run.
Coordinator assessment: POLICY independently corroborates this same documentation item; count it once. No material incorrect household tax or benefit result was established from the disclosed rounding drift.
Evidence Gaps
- Exact-head execution remains unverified: the 32 new assertions and directly affected lagged-series, CGT and residential-property/HVCTS regressions were NOT RUN in this review; hosted CI was absent at capture. Static inspection does not prove test collection, supported-Core integration or passing results. A matching-head run of those tests or equivalent associated CI would close this gap; private population data is not required.
- Role checks incomplete: pr2693-direct-review-20261027-review-code.md; see recovered report.
Notes
- No functional defect or critical finding was established. The ten opt-ins expose the already-listed first growth interval; no growth observation, lag, statutory value or source vintage changes. Both generated series are created before index generation; default CGT indices still copy per-person GDP, and lagged CPI retains its prior metadata behavior.
- The existing five-decimal cumulative rounding can slightly change later ratios; the body already discloses that effect. The optional prose correction does not demand a different index algorithm or establish a material policy error.
- Primary-source revalidation is not needed for this normalization-metadata delta; the empty manifest means no new primary documents verified. Parent PR2638’s OBR1.9/1.11 corroboration gap remains unresolved and this review does not approve the entire stack against main. No blocked source route was retried.
Validation Summary
Reviewed exact b213aaf against its declared stacked base b1ee29e. Local model tests, collection, Core/country imports and environment setup NOT RUN; hosted CI absent at capture. Author-reported passes are not reviewer execution. Changed Python files parse under isolated AST inspection; scalar rounding arithmetic is independent engineering evidence, not a model run. Source fetches: 0. Both role reports and source-manifest integrity are complete; exact snapshot was removed normally after both DONE notifications. Cleanup: /private/tmp/policyengine-command-runs/881387a1b745/pr2693-direct-review-20261027-Root-normal-cleanup.json.
Timing
setup seconds: 9.49s; parallel review seconds: 347.95s; policy role seconds: 295.06s; code role seconds: 317.98s; adjudication seconds: 0.00s; consolidation cleanup seconds: 72.44s; elapsed seconds: 612.51s
Review Severity
COMMENT. Open findings: 0 critical, 0 should address, 1 suggestions.
Fixes #2692.
Stacked on #2638 (
savings-interest-per-capita-uprating), which adds the opt-inindex_base_yearsetting. This merges after #2638, and I will retarget it tomainthen.What changes
The index generator puts an index of 1 in each series' first listed year, so that year's growth is never in the index. A direct input dated before that year (Core's class-level
uprating, or the legacy-gross savings fallback) is then carried forward by a factor of 1 for that step. This setsindex_base_yearto the year before the first listed growth on the ten series #2692 found can be hit:index_base_yearobr.per_capita.gdpobr.per_capita.mixed_incomeobr.house_pricesobr.mortgage_interestobr.petrol_spending_litre_proxyobr.diesel_spending_litre_proxyorr.per_capita.rail_passenger_journeysons.populationobr.private_pension_index(generated)obr.lagged_average_earnings(generated)No growth value is added or changed: only the year the index is 1 in. Every first-year growth used is already listed. The two generated series get the setting through their generators (
create_private_pension_uprating.py, and a new optionalmetadataargument onadd_lagged_parameter).capital_gainscopies the per-person GDP index exactly (data/capital_gains_projection.py), so it moves with it.Why explicit, not a default for every series
I considered anchoring every series a year earlier by default and did not, for three reasons:
earnings_upratingandseptember_cpi_upratingstart with aNoneplaceholder, so a default needs a carve-out.create_economic_assumption_indices.py:63). Re-anchoring moves the rounding points, so later-year ratios shift by up to about 1e-5 relative. A default would do that to all ~40 other series, including CPI, RPI and earnings, which uprate rounded benefit rates.Effect on later years
I dumped every index at b1ee29e and on this branch (
CountryTaxBenefitSystem, 2008 to 2039). The other 41 series are identical. For the 11 changed series, year-on-year ratios after the first growth year move by at most 9.1e-6 relative (ons.population, 2024), the same rounding effect #2638's anchor has on its own series. The largest reader of this is the house price index: the dataset projection ofmain_residence_valueand the HVCTS bands both read it, so they stay on one basis, but projected values move by up to about 6.5e-6 relative (about £13 on £2m). @juaristi22, if you would rather the rounding kept later ratios exact (for example by rounding relative to the first growth year), that is a change to #2638's generator, so I have left it out of here.Tests
New
policyengine_uk/tests/test_growth_index_first_year.py:capital_gainsstill equals the per-person GDP index;rail_usageinput dated 2020 and queried for 2021 gives 254.33, not 100.Against b1ee29e the first-year and rail tests fail (11 failed, 21 passed); on this branch all 32 pass.
Existing tests run locally, one file at a time:
code_health/test_uprating_declarations.py(8),test_rail_usage_uprating.py(65),test_savings_interest_uprating.py(24),test_lagged_series.py(4),test_high_value_council_tax_surcharge_liability.py(5),test_residential_property_uprating.py(26),test_wealth_components.py(3),test_isa_accounting.py(12),test_isa_income_contract.py(53),test_capital_gains_projection.py(115),test_dated_reform_routes.py(114),test_population_vintage.py(86) andtest_pension_credit_uprating.py(34), all passed.Not run: the full suite and anything needing the enhanced FRS (microsimulation tests are left to CI).
🤖 Generated with Claude Code