Repository navigation
Serve UK constituency and local-authority results from the routed local-area dataset - #881
Draft
juaristi22 wants to merge 19 commits into
Draft
juaristi22 wants to merge 19 commits into
juaristi22 wants to merge 19 commits into
Conversation
When the policyengine.py bundle routes UK constituency and local-authority
regions to a local-area dataset and the national default carries no area
codes (policyengine.py 6.10.0), a national or nation-level run builds its
two area breakdowns from a second baseline and reform run: the same
policies and nation filter, on the routed dataset. That dataset must carry
constituency_code_oa and la_code_oa, and its authority codes must match one
packaged boundary version. The national figures still come from the run's
own pair, and the builder computes the second pair when the first
breakdown needs it.
Once routed, constituency and local-authority regions resolve by name or by
GSS code in any case to the row-filter regions in policyengine.py's
registry, so policyengine-api's constituency/{name} and
local_authority/{name} requests keep working, and no weight-matrix region is
built for them. The 17 districts replaced in April 2023 are unsupported.
Nothing changes on policyengine.py 6.7.5, which routes no area regions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The executor and Stage 12 image smokes read the installed UK default's local-authority codes. When the default carries none and the bundle routes area regions to another dataset, they now download that dataset next to the default, check it against the bundle digest, and read its codes instead. Both smoke functions mount the Hugging Face credential for that download, and the Stage 12 smoke asks for 2 GiB instead of 512 MiB to read the routed file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Stage 12 UK national or nation-level report whose bundle routes area regions to another dataset plans no constituency_code_oa or la_code_oa columns, since its national dataset may carry none, and its aggregate leaves the constituency and local-authority breakdowns out. Stage 12 has no second run on the routed dataset yet, so its shadow comparison reports the two breakdowns as missing for such reports. The planning tests that describe unrouted reports now pin routing off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main up to 1f59a56 (#885 and the rules, Pyright and locked-install work before it). Git merged every file without conflicts, including changelog_entry.yaml, which keeps both sides' entries. Checked after the merge: the contract (274 passed), gateway (261 passed) and entry (662 passed) suites and the quality guards pass. Two known failures remain, both fixed by the next commit: - The executor suite has 2 failures out of 1789, the two routed-national tests in test_simulation_output_builder.py. main's run now passes data_folder= to _load_dataset (for the baked state folders), and the tests' fake does not take that keyword: TypeError: ... got an unexpected keyword argument 'data_folder'. - The executor's Pyright check reports five reportPossiblyUnbound errors in _uk_area_simulations, which opens its spans with runtime.span(...) instead of scopes.span(runtime, ...). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the merge of main, two routed-national tests and the executor's Pyright check failed: - main's run passes data_folder= to _load_dataset, and the routed tests' fake did not take it (TypeError: unexpected keyword argument 'data_folder'). The fake is now a named function that takes data_folder=None. - _uk_area_simulations opened its three spans with runtime.span(...), which Pyright reads as possibly suppressing exceptions, so it reported selection and dataset as possibly unbound. The spans now open with scopes.span(runtime, ...), as docs/engineering/skills/type-checking.md requires; the span names and attributes are unchanged. The executor suite passes (1789 passed), and Pyright is back to the 77 errors its baseline lists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… local authorities The executor built UK constituency and local-authority results with policyengine's deprecated compute_uk_constituency_impacts and compute_longwise_uk_geography_impacts. They apply no minimum sample, so a small area with negative-income households shows an exploding relative change (Cardiff West: -£192 average, +1372% relative), and their output is not disclosure-controlled for licensed survey microdata. Both levels now go through compute_uk_geography_disclosure in one release, nested in regions and the nation, with the constituency/local-authority overlap acknowledged. Blocking findings are resolved by withholding the suggested published cells; region and national reference cells are never withheld, and a release that cannot be resolved publishes no area. The share families, which the output does not publish, are withheld up front. Contract: existing fields keep their types. A withheld area, or one whose relative change is undefined, is left out of the list. Published records gain optional disclosure_tier and 90% interval fields. An empty list is now valid. Stage 12 keeps region_code_oa and source_household_id. The local-authority breakdown no longer strips weights, so the last recorded weight-stripping exception is removed and its baseline is 0. Adapted for #881: the builder keeps #881's uk_area_simulations and omit_uk_area_breakdowns. _uk_geography_disclosure_output() computes the release over the pair _uk_area_breakdown_inputs() returns (the routed local-area pair when given, otherwise the run's own pair), the local-authority impact takes its boundary metadata from _uk_area_breakdown_inputs()[2], and both builders return None before computing anything when the breakdowns are omitted. #881's builder tests now patch compute_uk_geography_disclosure_output(country, baseline, reform, *, year), test_omitted_uk_area_breakdowns_compute_nothing fails if it is called, and #881's planning test expects the four household columns. The changelog keeps both sides' entries. #860 also removes six executor Pyright errors in simulation_output_common.py and simulation_output_geographic.py, so the executor baseline shrinks from 77 to 71 entries, and docs/generated/rules.md is rendered again. Fixes #859 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 3c99e59)
A containment finding without a suggested cell names only the outer area. When that area is withheld and the public areas of the other level inside it add up to it (Orkney and Shetland is Orkney plus Shetland), the loop withheld the outer cell again, made no progress, and published no area at all. It now withholds the inner areas that lie wholly inside the outer one, read from the household area pairs as the release reads them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 8fb0b78)
The previous commit's _uk_areas_inside unpacks each key of a two-column
groupby count as a pair of area codes. pandas types those keys as
Hashable, so the executor's Pyright check reported a new error
("Hashable" is not iterable). The key is now checked to be a tuple
before it is unpacked; a two-column groupby always gives one, so results
do not change.
Pyright is back to the 71 errors its baseline lists, and the executor
suite passes (1804 passed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2e98847)
…lating #860's disclosure-controlled area outputs read region_code_oa and source_household_id as well as the two area codes. The check a routed local-area dataset gets as it loads (detect_uk_area_dataset_metadata) required only the area codes, so a dataset without the other two would simulate both pairs and fail only when the outputs were built. UK_AREA_DATASET_COLUMNS in uk_local_authority_metadata.py now lists the four household columns those outputs read. The load check requires each of them and names the dataset and the missing column, and Stage 12's UK_GEOGRAPHIC_DATASET_VARIABLES copies the same tuple, so the two cannot drift apart. Tests, written first: - test_routed_area_dataset_needs_every_column_area_outputs_read, over the four columns (replacing the two-column test). Before the change, the region_code_oa and source_household_id cases failed with "DID NOT RAISE <class 'ValueError'>". - test_uk_geographic_plan_copies_the_routed_dataset_columns. Before the change it failed with AttributeError: module ... has no attribute 'UK_AREA_DATASET_COLUMNS'. The routed-national runtime test's local-area fixture gains the two columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A UK report's constituency and local-authority results will come from a separate run, requested on demand, instead of from every national run. This adds the contract for that run: - SimulationRequest gains breakdown: Literal["local_areas"] | None. It is accepted only for a UK request whose region is absent, uk or country/<nation>, and never with include_cliffs=true, segmented=true or a region_group. include_cliffs=false stays accepted, because the Public API always sends it. The rules live in local_area_breakdown_error() in the new local_area_breakdown module, so the worker can apply the same ones. BudgetWindowBatchRequest does not take the field. - LocalAreaBreakdownOutput (extra="forbid") is the result of such a run: kind "local_areas", model_version, policyengine_version, dataset, data_version, and the constituency_impact and local_authority_impact lists, which may be empty. - JobStatusResponse.result is SingleYearMacroOutput | LocalAreaBreakdownOutput | None. Both models forbid extra fields, so a payload validates as only one of them. - local_area_breakdown also defines the routing manifest capability (key local_area_breakdown, version 1) and supported_local_area_breakdown_capability(), copied from job_progress. The gateway OpenAPI golden is regenerated; its diff adds the field, the result model and the result union. Tests, written first, and how they failed before the change: - contract: the request tests failed with extra_forbidden where they expect value_error or literal_error, or with AttributeError: no attribute 'breakdown'; test_local_area_breakdown.py failed to import (No module named 'policyengine_simulation_contract.local_area_breakdown'). - gateway: polling a complete breakdown result returned 500 (the result failed SingleYearMacroOutput validation); it now returns 200. - entry: forwarding a breakdown got 422; the constituency-region refusal was 422 extra_forbidden instead of value_error; the OpenAPI test failed with KeyError: 'breakdown'. - executor: the published result schema had no LocalAreaBreakdownOutput. The entry tests that national submissions carry no breakdown and that the Stage 12 adapter skips a breakdown (unsupported_options) but ignores an absent one passed before the change: the adapter already skips any field it does not know. They pin that behaviour. The budget-window rejection test passes before and after for the same reason. Suites: contract 312, gateway 262, entry 668 and executor 1808 passed; make check (ruff, Pyright in every package, after regenerating the client) passes, and the generated-client tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A request with breakdown=local_areas now takes its own path through the executor (_run_local_area_breakdown_core), which _run_simulation_impl_core branches to before loading anything: - It checks the request again with the contract's local_area_breakdown_error() (UK only; region absent, uk or country/<nation>; no include_cliffs=true, segmented=true or region_group), refuses any other breakdown value, and refuses a bundle that does not route UK area regions to a local-area dataset. Each of these raises ValueError before the country module or any dataset loads. - It reuses _uk_area_simulations to load only the routed local-area dataset (microcosm_uk_2024_25_dense on .py 6.10.0) and build one baseline and reform with the request's policies and nation filter, and _report_calculation_start for the baseline and reform stages. The national file is never loaded, and the dataset_load span carries dataset_role=uk_area. - The builder's new serialize_local_area_breakdown() computes the baseline, then the reform, enters the outputs stage, and builds the two existing area breakdowns from one disclosure release under simulation_output_build. It returns a LocalAreaBreakdownOutput with the model and policyengine.py versions, the dataset name and its revision. No national impact analysis runs, and no new stage name is needed. - _selected_dataset_revision is split so _manifest_dataset_revision(country, name) reads the local-area dataset's revision from the release manifest. - UKAreaSimulations also carries the loaded dataset and its name, and the start-of-run logging moved into _log_simulation_start to keep _run_simulation_impl_core within ruff's statement limit. Tests, written first (the routed-run helper is generalised into _run_routed_uk(monkeypatch, *, params, local_area_dataset)), and how they failed before the change: - the breakdown run test: AttributeError, no _manifest_dataset_revision; the UK-wide run loaded the national dataset and then the dense one; - the invalid-request cases: DID NOT RAISE ValueError (the US case raised SPMInputError from the national path instead); - the unrouted bundle: the national path's missing la_code_oa error, not the breakdown refusal; - the three builder tests: TypeError, unexpected keyword 'resolved_dataset_name'. The test that a breakdown needs all four routed-dataset columns, and the pin in test_baseline_artifacts.py that a non-default UK dataset gets no deterministic baseline id, passed before the change: the load check and the id rule already hold. They pin both for the breakdown path. Suites: executor 1826, contract 312, gateway 262 and entry 668 passed; Pyright stays at the executor baseline's 71 errors; ruff and the quality guards pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Until now a UK-wide or nation-level run on a bundle that routes area regions to the local-area dataset loaded that dataset as well and built a second baseline and reform there, only for the constituency and local-authority results: about 70-90 s and 12 GiB more per request, in every budget-window year and in the UK release sentinel. Those results now come from the separate breakdown=local_areas run, so: - uk_area_breakdowns_omitted(country, region) in uk_local_authority_metadata.py is True for a UK-wide or nation run on a routing bundle, and False for area regions, unrouted bundles and other countries. The runtime passes it to the builder as omit_uk_area_breakdowns, and Stage 12's aggregation uses the same helper, so the runtime and the Stage 12 shadow results agree again. - The runtime no longer calls _uk_area_simulations for national runs, and the builder loses uk_area_simulations and _uk_area_breakdown_inputs: its disclosure release reads its own baseline and reform. Such runs return null for constituency_impact and local_authority_impact. - UKAreaSimulations, now used only by the breakdown run, moves to simulation_runtime.py. Tests, written first, and how they failed before the change: - test_area_breakdowns_are_omitted_only_from_routed_uk_wide_and_nation_runs and the Stage 12 omission test's new check: AttributeError, no uk_area_breakdowns_omitted. - test_a_routed_uk_wide_or_nation_run_builds_no_area_pair (uk and country/wales): the run loaded the national and the local-area datasets instead of one. #881's test that national runs build the area pair is deleted, with its companion that checked the local-area dataset's columns during a national run (the breakdown run's tests cover that check), and so is the builder test of the routed pair. The expected builder arguments now carry omit_uk_area_breakdowns, and the test of LAD22 detection on a national file pins routing off. Suites: executor 1832 passed; Pyright stays at 71; ruff and the quality guards pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build_bundle_manifest_metadata now adds local_area_breakdown: 1 to the routing bundle manifest of an app whose installed policyengine.py bundle routes UK area regions to a local-area dataset (uk_area_regions_routed()), and leaves the key out otherwise. The routing publisher builds the manifest only for the app it deploys, so frozen older apps, such as py6-7-5, keep manifests without the key. The next commit makes the gateway refuse breakdown requests for those apps. The value comes from the bundle manifest packaged inside the installed policyengine package; the change reads no new environment variable. Test, written first: test_bundle_manifest_declares_local_area_breakdowns_only_for_routed_area_regions. Before the change its routed case failed with KeyError: 'local_area_breakdown'; the unrouted case passed, as the key was never written. Suites: executor 1834 passed; Pyright stays at 71; ruff passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frozen worker apps, such as py6-7-5, accept and ignore unknown payload fields, so a breakdown=local_areas request routed to one would silently come back as a national result. submit_simulation now calls _require_route_serves_breakdown(request, route) inside its existing route-resolution try block: a breakdown request whose route's bundle manifest does not declare local_area_breakdown version 1 gets a 400 before anything is spawned. The message names the policyengine.py version and app the request resolved to. Routes from the legacy version dicts have no manifest and are refused the same way. Requests without breakdown are unchanged, and the submission's observability context now records breakdown. Tests, written first (new tests/test_local_area_breakdown.py): a worker without the capability, an older pinned worker (3.9.0) and legacy-only routes all returned 200 and spawned the job before the change; they now get 400 with no spawn. The tests that a declaring worker receives the breakdown and that a national request needs no capability passed before the change, since the previous commits already forward the field. Suites: gateway 267 and entry 668 passed; gateway Pyright has no errors; ruff and the quality guards pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In projects/policyengine-apis-integ/tests/simulation/test_calculate.py: - test_calculate_uk_local_area_breakdown (beta_only) submits a UK-wide breakdown=local_areas request pinned to the deployed UK model and checks the result: kind local_areas, a dataset whose name ends in _dense, at least one constituency and one local authority, and a disclosure_tier of reported or approximate on every published area. This also checks that disclosure control does not withhold every area of a level on the real local-area dataset. - test_calculate_uk_model now also checks that the national result has no constituency_impact or local_authority_impact. - assert_uk_local_area_breakdown and assert_no_uk_area_results accept the generated client's objects as well as plain mappings. Tests, written first: the local tests of the two helpers (a generated LocalAreaBreakdownOutput parsed from a job status, four incomplete breakdowns, and national results with and without area data) failed with NameError before the helpers existed, and pass now. The two beta tests need a deployment and run in the beta deploy's integration suite. The local apis-integ tests pass, except test_ping, which needs the docker-compose services and could not connect here; Pyright stays at the apis-integ baseline's 4 errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The gateway README gains a "UK local-area breakdowns" section: the breakdown field and its rules, the LocalAreaBreakdownOutput fields, the local_area_breakdown manifest capability and the gateway's 400, the null area fields of UK-wide and nation runs, the reported stages, and the Stage 12 skip. - The Stage 12 migration doc lists the four household columns the area outputs read, and says the production national run now leaves the area breakdowns out too, by the same uk_area_breakdowns_omitted rule, with breakdown requests skipped by Stage 12. - docs/engineering/skills/microdata-weights.md says which pair, and so which dataset's weights, the UK area breakdowns aggregate. - changelog_entry.yaml replaces #881's entry about the second baseline and reform run with one about national runs no longer building it, and adds the field and result, the manifest key and gateway 400, the earlier column check, and the Stage 12 skip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a bundle that did not route UK area regions, _resolve_region built a weight-matrix region for a constituency or local authority (_build_uk_weight_replacement_region), and the runtime and the Stage 12 worker then checked the matrix against the certified release (_require_certified_uk_weight_matrix). From this branch onward, every executor app is built with policyengine.py 6.10.0 or later, which routes those regions to the local-area dataset, and apps already deployed for older releases are never rebuilt (only the gateway and entry are redeployed from main), so nothing uses the fallback any more. The two functions, the runtime's and Stage 12's calls, tests/test_uk_weight_matrix_guard.py and the _forbid_weight_matrix_regions test helper are deleted. Test, written first: test_resolve_region_rejects_uk_area_regions_on_an_unrouted_bundle replaces the test that kept weight-matrix regions until routing. Before the change both of its cases failed with DID NOT RAISE <class 'ValueError'>: the fallback built a WeightReplacementStrategy region. They now get "Unsupported UK region". Three executor Pyright errors were in the deleted code, so the baseline shrinks from 71 to 68 entries and registry.toml's count follows (a change under rules/, which needs code-owner approval); docs/generated rules are rendered again. The changelog records the removal. Suites: executor 1826 passed; Pyright matches the 68-entry baseline; ruff and the quality guards pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #880
Part of #728. This makes the executor serve policyengine.py 6.10.0 (PolicyEngine/policyengine.py#685), which makes the Microcosm national dataset the UK default and routes constituency and local-authority regions to the Microcosm local-area dataset. UK-wide and nation-level reports run on the national file alone. Their constituency and local-authority results come from a separate run, requested on demand with
breakdown: "local_areas", on the local-area file alone.Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Summary
After this PR, on a release built with .py 6.10.0:
regionabsent,ukorcountry/<nation>) load onlymicrocosm_uk_2024_25and build one baseline and reform. They compute no constituency or local-authority results: both fields are null, and the job status body omits them. This removes the second baseline and reform run on the local-area file from every such request (about 70-90 s and 12 GiB each), from every UK budget-window year and from the UK release sentinel.breakdown: "local_areas"loads onlymicrocosm_uk_2024_25_denseand builds one baseline and reform there with the region's filter. Both area levels then come from one disclosure-controlled release (Use disclosure-controlled UK geography outputs for constituency and local authority impacts #860), and the job returns aLocalAreaBreakdownOutput.Contract
SimulationRequest.breakdown: Literal["local_areas"] | None = None. Accepted only for UK requests whose region is absent,ukorcountry/<nation>. Refused with 422 for other countries, constituency, local-authority or bare-name regions,include_cliffs: true,segmented: true, aregion_groupor any other value.include_cliffs: falsestays accepted, because the Public API always sends it.BudgetWindowBatchRequestdoes not take the field.LocalAreaBreakdownOutput(extra="forbid"):kind: "local_areas",model_version,policyengine_version,dataset,data_version,constituency_impactandlocal_authority_impact. Both lists areGeographicImpactOutputand may be empty.JobStatusResponse.resultisSingleYearMacroOutput | LocalAreaBreakdownOutput | None. Both models forbid extra fields, so a payload validates as only one of them.local_area_breakdown, version 1, written by the routing publisher only for apps whose bundle routes UK area regions. The gateway refusesbreakdownwith 400, before spawning, for any route whose manifest lacks it, including routes from the legacy version dicts. The error names the .py version.unsupported_options).The gateway OpenAPI golden and the generated client are regenerated.
Commits
María's commits (unchanged):
5ad72b0fBuild routed UK area results from the local-area dataset6aad6520Check the routed area dataset in the image smokesc2728bceCopy no area codes into Stage 12 for a routed UK national report24690a27Record the routed UK area changes in the changelogAdded on top:
a96c72c27dad9206acc1d2bf3c99e59d(Pavel), adapted for this PR897e4b568fb0b788(Pavel)0e13242373dfd8952e98847d(github-actions)0ec2f4d43f031fd2f14cc7ba841fc732362a904cef6b7ecaad9c35da3cdc8149895d20dcThe cherry-picks use
git cherry-pick -x, so each keeps its author and names its source commit.acc1d2bf's message has an "Adapted for #881" paragraph. Its disclosure release reads the pair the area breakdowns use, #881's builder tests patchcompute_uk_geography_disclosure_output, and the planning test expects the four household columns. #860 and #883 are still open; this PR now contains their commits.Red-green
Each commit marked red-green was written tests first. The new tests were run and failed for the expected reason before the code was written, and the tests and the code were committed together. Each commit message records how its tests failed. For example:
extra_forbiddenwhere they expectvalue_errororliteral_error;DID NOT RAISE);local_area_breakdown(KeyError);A few new tests pinned behaviour that already held, and passed before the change; their commit messages say which. Examples are the Stage 12 adapter skipping an unknown field, and the non-default-dataset rule for deterministic baseline ids.
A merge and cherry-picks cannot be written test-first. They were checked by running the full suites, as below.
Testing
On the final tree, with Python 3.13 and the pinned ruff 0.15.22:
Unit tests, each in its own locked environment (
uv run --frozen pytest tests/):make checkpasses. It regenerates the client, runs ruff format and check, and runs Pyright in every package. The executor's Pyright errors went from 77 to 68: Use disclosure-controlled UK geography outputs for constituency and local authority impacts #860 removed 6 and the weight-matrix removal 3, and no new error is recorded.Other checks:
python scripts/run_quality_guards.pypasses.python -m scripts.rules renderleaves no diff.uv lock --checkpasses for the executor.Generated client:
./scripts/generate-clients.sh, then thepolicyengine-apis-integtests that need no services (24 passed).test_pingneeds the docker-compose services and was not run.On the real 6.10.0 bundle, without loading data, the breakdown path resolves
uk,country/walesandcountry/northern_irelandtomicrocosm_uk_2024_25_densewith its revisionmicrocosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe. It refusesconstituency/Aldershot, andcountry/niis an unsupported region.Not run locally:
test_calculate_uk_local_area_breakdownis new, andtest_calculate_uk_modelgains an assertion;CI and the beta deploy run them.
Environment variables
None of the changed code reads a new environment variable.
update_version_registry.py, changed in362a904c). The new key comes fromuk_area_regions_routed(). That function reads the bundle manifest packaged inside the installed policyengine package (policyengine/data/bundle/manifest.json), with no network access and no environment variable.MODAL_TOKEN_IDandMODAL_TOKEN_SECRETare GitHub Actions repository secrets; both names are listed in the repository's secrets.simulation-deploy.ymlpasses them tosimulation-deploy.reusable.ymlwithsecrets: inherit, and the plan, record and publish steps set them inenv:.MODAL_ENVIRONMENT,APP_NAME,POLICYENGINE_VERSION,POLICYENGINE_US_VERSION,POLICYENGINE_UK_VERSION,FORCE_LATESTandRELEASE_ENVIRONMENTcome from workflow inputs and job outputs.GITHUB_WORKFLOW_REF,GITHUB_ACTIONS,GITHUB_REPOSITORY,GITHUB_RUN_IDandGITHUB_RUN_ATTEMPTare set by GitHub.run_simulationModal function. That function already attaches thepe-uk-private-hf-read-tokenModal secret, with required keyHUGGING_FACE_TOKEN, andgcp-credentials. Constituency and local-authority runs on 6.10.0 already use the same download. I confirmed the attachment in code only; I did not check that the Modal secret exists in Modal.Code-owner review
These commits change files under
rules/, which need code-owner approval (sim.process.rule-machinery-code-owned):rules/baselines/pyright-executor.txtand itscountinrules/registry.toml: 77 to 71 inacc1d2bf, then 71 to 68 in895d20dc. Both only remove entries.rules/baselines/sim-microdata-weight-stripping.txtand its count: 1 to 0, from Use disclosure-controlled UK geography outputs for constituency and local authority impacts #860's commit.Rollout
policyengine-simulation-py6-10-0with thelocal_area_breakdowncapability, and moveslatestto 6.10.0. The Public API still pins 6.7.5, so its users see no change. Unpinned clients get national UK results without area data.breakdownand moves to 6.10.0. It needs this PR deployed first.Risks and follow-ups
breakdown.country/ni, the Public API's spelling, is not a .py region; .py names itcountry/northern_ireland. The Public API change maps it.data_versionon single-area UK runs still names the national release (_selected_dataset_revisionis US-only).compat_models.SimulationOptions, used by docker-compose) does not takebreakdown.🤖 Generated with Claude Code