Skip to content

UK: supply the engine's local_authority input from the ladder's local_authority_code, plus council tax follow-ups from policyengine-uk#1855/#1864 and uk-data#482/#484 #953

Open

Description

@juaristi22

Summary

Every household row in the UK build carries an ONS local authority code (local_authority_code, written by the geography ladder in uk_runtime/geography_ladder.py, April 2023 LAD vintage, 361 areas), but nothing maps it into the engine's local_authority enum, and the policyengine-uk adapter only supplies engine inputs that exist as frame columns. So the engine sees the enum default (MAIDSTONE) for every household in every Microcosm build, exactly as it does on the uk-data datasets.

This is the one council-tax gap Microcosm can close and uk-data structurally cannot: uk-data reweights one national sample per authority, so a household-level enum can never vary there; Microcosm assigns an authority per row. It matters now because:

  • Compute gross council tax from LA Band D amounts and statutory band ratios policyengine-uk#1855 adds council_tax_band_d_amount (an LA-keyed parameter read through household("local_authority")) and simulated_council_tax; on any dataset without the input they collapse to Maidstone's Band D times the national band mix (the £61.21bn England artefact on that PR).
  • The bespoke Council Tax Reduction schemes in variables/gov/local_authorities/council_tax_reduction/config.py test local_authority == LocalAuthority.MERTON (and Kingston upon Thames, Newham, Oxford, Westminster). They never fire on any dataset today.
  • The CT-C reconciliation on WS-C deferred adjudications and the reconciliation layer #736 (rules-based liability from band, the authority's Band D level and the statutory ratios, with CTR engine-side) is unmeasurable until the engine knows each row's authority.

Review context: PolicyEngine/policyengine-uk#1855 (changes requested 2026-09-18), PolicyEngine/policyengine-uk#1864, PolicyEngine/policyengine-uk-data#482, PolicyEngine/policyengine-uk-data#484, TheAxiomFoundation/rulespec-uk#346, tracker PolicyEngine/policyengine-uk#1854.

Deliverable A: supply local_authority as an engine input

Design, following the region precedent (FRS gvtregno mapped to enum key strings such as SOUTH_EAST on the household table; council_tax_band is supplied the same way):

  1. Names source. The ladder sources carry codes only (no LAD23NM-style column). Vendor a hash-pinned ONS names-and-codes lookup for the three roster vintages the crosswalk declares (ew:2023_april_lad, scotland:2019_council_area, ni:2014_lgd), the way geography_sources.py pins its other lookups.
  2. Key derivation. The engine enum key is a mechanical function of the ONS display name for 378 of its 379 members (uppercase, non-alphanumerics to single underscores, e.g. Bristol, City of to BRISTOL_CITY_OF, Na h-Eileanan Siar to NA_H_EILEANAN_SIAR). The single exception is King's Lynn and West Norfolk (KINGS_LYNN_AND_WEST_NORFOLK, apostrophe dropped). So: mechanical rule plus a one-entry alias table, not a hand-written 361-row map.
  3. Fail closed. A build-time test resolves every local_area_crosswalk.json area id to a LocalAuthority member at the pinned engine version and refuses on any miss. No silent fall-through to the enum default.
  4. Write point. Immediately after the ladder assignment (the column is per row, so clones already differ), consistent with region_code by construction since authorities nest in regions. Add local_authority to uk/release_input_coverage_manifest.json as required.

Dependency. The engine enum is the 2021 LAD list and lacks the six April 2023 unitaries (North Northamptonshire, West Northamptonshire, Cumberland, Westmorland and Furness, North Yorkshire, Somerset), which are exactly the codes on our roster with no member. Adding them is requested on PolicyEngine/policyengine-uk#1855; if it lands elsewhere, link it here. Until the pinned engine carries them the fail-closed test blocks the stage, which is the intended behaviour (a partial map would misprice about 1.4 million people silently). Northern Ireland's 11 districts resolve to the enum's members and correctly get zero council tax.

Acceptance.

  • Every household row resolves; the test above is green at the pinned engine.
  • On a build with the engine pin at or past Compute gross council tax from LA Band D amounts and statutory band ratios policyengine-uk#1855: distinct council_tax_band_d_amount values per nation equal the authority count (296, 22, 32); the five bespoke CTR schemes show non-zero caseloads; the England simulated_council_tax aggregate is reported next to the PR's single-authority figure as a receipt, not bound.

Other follow-ups these PRs expose

Already tracked elsewhere

🤖 Generated with Claude Code

Activity

  1. added 2 commits that reference this issue on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions