Repository navigation
Calculate UK-wide and nation reports on the local-area dataset, with their areas and withheld codes - #1068
Draft
anth-volk wants to merge 45 commits into
Draft
Calculate UK-wide and nation reports on the local-area dataset, with their areas and withheld codes#1068anth-volk wants to merge 45 commits into
anth-volk wants to merge 45 commits into
Conversation
…et (failing) The coordinator must plan a UK local-area report run's simulations with only the geographic aggregate and no cliff impacts, and the UK worker must recognise the dataset a bundle gives its constituency and local-authority regions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ea dataset ReportOutputRequirements accepts LOCAL_AREA_AGGREGATES, the geographic aggregate alone with no cliff impacts, beside the complete profile. Stage12CountryBundle.local_area_dataset names the one dataset both UK area region types load when it differs from the national one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ling) The plan names only the household columns disclosure control reads, the same for every policy pair, so a local-area baseline run is reusable by any report with that baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
resolve_local_area_output_plan gives both simulations one household plan: the identifier, net income and weights, which the UK model writes by default, and the area columns copied from the local-area dataset whatever the region. It adds no cliff, labour-supply or budget variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UK worker refuses a UK-wide or nation simulation whose provenance names the dataset the bundle gives constituency and local-authority regions, which a local-area report run's simulations calculate on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… names The UK worker accepts a UK or nation simulation whose provenance names the dataset the bundle gives constituency and local-authority regions, selects that dataset in place of the region's own, keeps the region's scoping, and checks every column the area outputs read as the dataset loads. It reuses only the runtime helpers Stage 12 already calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (failing) The simulation service refuses a local-area breakdown before writing anything unless the default v2 worker declares that its coordinator runs local-area report runs. Readers released before the declaration forbid unknown fields and can read the manifest before the entry is promoted, so it goes under a new key that readers with it read first, while active-schema-2 keeps the manifest without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t runs V2WorkerVersion gains local_area_reports, the LOCAL_AREA_REPORTS_VERSION of the UK local-area report runs a worker's coordinator runs, left out of the JSON when None so other workers' documents and digests stay the same. The publisher sets it. It is written only under the new active-schema-2-with-local-area-reports key, which readers with it read first; active-schema-2 keeps the manifest without it, and a copy left behind by an older publisher after a rollback is no longer read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd worker (failing) The simulation service can't tell from the catalog whether a release's UK national runs leave the area results out, so the UK country entry of the v2 manifest declares the local-area dataset by the rule Stage 12's national aggregation uses, and the UK worker accepts that dataset by the same rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…outing rule The UK country entry's LocalAreaReports (input_version, dataset) replaces the worker-wide field. The publisher sets dataset by uk_area_regions_routed and routed_uk_area_dataset_name, the rule Stage 12's national aggregation uses, and the UK worker accepts a UK-wide or nation simulation on the local-area dataset by the same rule, so the simulation service creates an area part exactly when the national run leaves the areas out. The bundle helper local_area_dataset, which compared region datasets instead, goes. The manifest's models move, unchanged apart from the new one, to stage12_manifest_models, which stage12_manifest re-exports, keeping it under its line limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dataset A local-area report is now an ordinary report on the local-area dataset, linked to its national report by key, with no marker in its input, so the manifest declaration and the Stage 12 doc describe the runs the coordinator accepts that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a dataset (failing) The local-area report is an ordinary report on the local-area dataset, so the coordinator recognises its runs by their simulations' dataset and region, not by a marker in the input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
is_local_area_report decides by the report's region and its simulations' dataset, by the routing rule the UK worker and the national aggregation use, so the coordinator can calculate the area results alone for any such report, however the simulation service found or created it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C1 · 21's decision 7 puts the codes disclosure control withholds in a new withheld_area_codes table, keyed like the other comparison tables. A local-area run stores its constituency and local-authority rows, one withheld-code row per withheld area, and its data version, and nothing else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
local_area_result validates a LocalAreaBreakdownOutput, refusing one that doesn't list each level's withheld codes, and returns its constituency and local-authority rows (through the national result's area rows, now area_record_rows), one withheld_area_codes row per withheld area, and the run's data version. ReportRunRecorder.complete_local_areas writes them in the completion's transaction under the call's claim. The table model follows C1 · 21's decision 7; its column types are checked against C1 · 22's revision once it exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing) C1 · 26's stored output plans copy the area columns for a UK-wide or nation simulation on the local-area dataset, by the same rule the worker and the coordinator use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
calculates_on_local_area_dataset is the rule is_local_area_report and the worker's dataset check share; C1 · 26's stored output plans use it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rea dataset (failing) A simulation run the simulation service starts on its own may later be named by a local-area report, which reads the area columns. Today the plan copies them only for constituency and local-authority regions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-area dataset The output plan copies UK_AREA_DATASET_COLUMNS for such a simulation too, by C1 · 23's rule (calculates_on_local_area_dataset), whether it is planned for a report or alone, so its run's file holds what a local-area report reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under the data_source design a report on the local-area dataset returns its full results, cliffs included when asked, and its areas, so the area-only output plan (resolve_local_area_output_plan) and the contract's local-area aggregate profile go. The output plan's area columns come from C1 · 26's change, taken here; a test checks such a report plans every aggregate, its cliffs and the area columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A UK-wide report on the local-area dataset returns a national result, so its withheld area codes travel in that result and become withheld_area_codes rows when it is stored. The area-only result's tests go with the area-only output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SingleYearMacroOutput gains constituency_withheld_codes and local_authority_withheld_codes, left out of the JSON when null so every other result is written and compared as before, and report_run_result writes one withheld_area_codes row per code. A UK-wide report on the local-area dataset returns a national result, so the area-only result (local_area_result, complete_local_areas) goes. The gateway's golden OpenAPI spec gains the two fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under the data_source design a report on the local-area dataset returns its full results as well as its area results, so the manifest declaration, the recognition rule's docstring and the Stage 12 doc say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…failing) C1 · 21's decision 14: Stage 12's national aggregation stops leaving the areas out of a report on the local-area dataset and reads the withheld codes from the same disclosure pass. A UK-wide report on the national dataset still leaves them out, and an area report lists no withheld codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ggregation C1 · 21's decision 14: build_aggregate_report stops leaving the constituency and local-authority results out of a report on the local-area dataset (is_local_area_report), and adds each level's withheld codes from the same disclosure pass, through the new SimulationOutputBuilder.uk_withheld_area_codes. Other reports, and their comparison with production, are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The report output planning section says how a UK-wide or nation report on the local-area dataset loads it, plans its area columns, and keeps its areas and withheld codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1: deleting active-schema-2 and active-with-region-datasets before deploying code released before region datasets must stop readers released with the local-area key from reading it, and the publisher must require active-schema-2 again, since those readers use the new key only while it holds its copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1: the documented step before rolling back to code released before region datasets deletes active-schema-2 and active-with-region-datasets, but readers released with the local-area key counted a missing active-schema-2 as matching and kept reading the new key, with its region datasets. The new key's document is now current only while active-schema-2 holds its copy, and the publisher requires both keys. The Stage 12 doc says so, and that a rollback drops local_area_reports from versions until each is published again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SingleYearMacroOutput's description, which the gateway's OpenAPI spec publishes, no longer names Stage 12 and covers nation reports; the uk_area_breakdowns_omitted and SimulationOutputBuilder docstrings say that Stage 12 keeps the areas for a report on the local-area dataset; and the aggregation imports is_local_area_report at module level, since the UK metadata module is already loaded with the package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The aggregation tests cover a nation report on the local-area dataset, and a new test stores a complete local-area aggregate through AggregateReportArtifactPayload and report_run_result, with its withheld rows. Test names now say what they check, and the worker tests' bundle has no nation region dataset, as .py 6.17.1's has none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2: no test depended on the publisher requiring active-schema-2. With it deleted, current readers stop using the local-area report key and read an older copy, so --verify-plan must refuse the store; without the requirement it accepted it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2: every worker test replaced the bundle, the routing and the dataset selection. This one uses the installed .py bundle and routing, so a release that renames or moves the local-area dataset, or gives it another URI in dataset selection, fails here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Stage 12 doc's older manifest paragraphs say which key current readers use, now that the local-area report key comes first, and neither the doc nor is_local_area_report's docstring says a request can already ask for such a report: the simulation service names the dataset, and no field marks the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Stage 12 doc says a manifest with a country that isn't economy-capable goes under both schema version 2 keys, and no longer says the simulation service already uses the local-area declaration. The installed-release worker test calls the worker's own provenance check on the selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Against a database built from C1 · 22's revision of the Public API (withheld_area_codes, 1e1e3f079f2c): completing a run whose result lists withheld codes, as the Modal worker role, writes one row per code, naming the run's two simulation runs, beside its area rows, and a repeated completion writes no second set. Seen failing with the rows left out. The table is reflected in these tests alone, so the others still run on a database built before it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new test makes the withheld insert itself fail (a code listed twice) and checks the whole completion rolls back: the run stays running, with no area or withheld rows. The local-area tests also check the local-authority rows, share one fixture (uk_output_with_withheld_codes), and the package README says which Public API revision they need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iling) A run of a report on the local-area dataset succeeds today even when a level has neither published nor withheld areas. Failing it instead means a succeeded run proves each level's rows exist, which the staging check needs, since it reads only the run's status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…evel build_aggregate_report refuses, for a report on the local-area dataset, a level with neither published nor withheld areas, which a UK-wide or nation report always has. A succeeded run then proves each level's area or withheld-code rows exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A test covers a level whose areas are all published, which a check ignoring the published areas would fail; the stand-in builder takes its published and withheld areas through monkeypatch; and the aggregation's docstring, the Stage 12 doc and the changelog fragment say a local-area run fails when a level has no estimated area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI: a manifest schema version 1 can't describe is now written under active-schema-2-with-local-area-reports as well as active-schema-2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ca4d396e merges PolicyEngine/policyengine-api#4305, whose revision 1e1e3f079f2c adds the withheld_area_codes table that Stage 12 writes a local-area report run's withheld codes to. Since the previous pin it adds no other migration and changes no table sim's persistence models describe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SingleYearMacroOutput's fields and its published OpenAPI schema now hold constituency_withheld_codes and local_authority_withheld_codes, which only a UK-wide or nation report on the local-area dataset lists. The schema tests expect the current result keys plus those two (LOCAL_AREA_REPORT_KEYS); the tests of dumped and built results still expect the current keys alone, since every other result leaves the two out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The persistence README says the pin may move to a later merged commit that alters no modelled table, and that the job then still checks only this package's tables. The fixture's set of the two withheld-code fields is LOCAL_AREA_WITHHELD_CODE_KEYS, since it holds only those two keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 13, 2026
Draft
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 #1067
A run of a UK-wide or nation report on the release's local-area dataset (the dataset UK constituency and local-authority regions use,
microcosm_uk_2024_25_denseon .py 6.17.1) now calculates its full results, cliffs included when asked, together with its constituency and local-authority results and the codes of the areas disclosure control withholds, and storesconstituency_impacts,local_authority_impactsandwithheld_area_codesrows. It is an ordinary report run, started by run ID; no field marks it, its simulations' dataset does.Deploy order: the Public API's
withheld_area_codestable and its grants (PolicyEngine/policyengine-api#4305, revision1e1e3f079f2c) must be live in production before sim's release. They are: #4305 merged on 2026-11-13 asca4d396e, and the Public API's release run 43519970359 applied the revision and its grants, and verified them, in staging and then in production (2026-11-13 23:20 UTC). Without them, a run of such a report would fail at its completion's insert; no other report writes to that table. Nothing creates such reports yet: the simulation service'sdata_sourceoption comes in a later PR, which reads this PR's manifest declaration.What changes
stage12_runtime/simulation.py, newstage12_runtime/local_area_datasets.py): it accepts aukorcountry/*simulation whose provenance names the local-area dataset, which it refused before ("simulation provenance differs from the installed bundle"). It selects that dataset in place of the region's own, keeps the region's scoping (a nation's filter), and checks every area column the area results read as the dataset loads, before simulating. The rule is .py's routing rule (uk_area_regions_routed,routed_uk_area_dataset_name), the one the national aggregation'suk_area_breakdowns_omittedalready uses. It calls none of the v1 executor's local-area code.output_planning.py): a UK-wide or nation simulation on that dataset also copiesUK_AREA_DATASET_COLUMNS, whether planned for a report or alone.aggregation.py,SimulationOutputBuilder.uk_withheld_area_codes): for a report on that dataset, the existing national aggregation stops leaving the constituency and local-authority results out, and adds each level's withheld codes from the same disclosure pass. Such a run fails if a level has neither published nor withheld areas, so a succeeded run always has rows for each level. Every other report's output, and its comparison with production, is unchanged.SingleYearMacroOutput): optionalconstituency_withheld_codesandlocal_authority_withheld_codes, left out of the JSON when null, so every other result is written exactly as before. The gateway's golden OpenAPI spec gains the two fields.report_run_results.py): thewithheld_area_codestable model, in the Public API's shape, and one row per withheld code, keyed by the run and its two simulation runs, written in the completion's transaction. A result that lists none writes none, so national runs never touch the table.stage12_manifest.py, newstage12_manifest_models.py, the publisher): the UK entry declareslocal_area_reports(input_version,dataset), the dataset by the same routing rule, so the simulation service, which can't read the release kind from the catalog, can tell whether such a report is possible. Readers released before the field forbid unknown fields, andpublish_stage12_v2_manifestcan run before Cloud Run traffic moves, so the field is written only under a new key,active-schema-2-with-local-area-reports, which current readers read first whileactive-schema-2holds its copy;active-schema-2and the schema version 1 copies leave it out. The publisher requires both schema version 2 keys. The manifest's models moved, unchanged apart from the new one, tostage12_manifest_models.py(re-exported), keeping both files under the length limit. The Stage 12 doc's manifest section describes the key, the rollback handling, and that deletingactive-schema-2before a rollback also stops readers using the new key.The design decisions it follows
From the local-area design's decisions (recorded 2026-11-13 and 2026-11-14):
ukand the fourcountry/*nations, with the executor's routing rule deciding which releases have a local-area dataset (decision 3, reworded; item 12 lets nations take it);data_source, it is an ordinary report on that dataset returning its full results (thedata_sourcedesign, replacing decisions 6, 8, 9 and 12);withheld_area_codestable (decision 7);data_sourcechange; the area check above lets it read only the run's status.Environment variables and secrets
None added. What these runs read, all already attached to
coordinate_reportandrun_single_simulation_ukinv2_app.py:HF_MODAL_SECRET_NAME(required_keysdeclares it), inworker_secrets: the worker downloads the private local-area dataset with it;GOOGLE_APPLICATION_CREDENTIALS_JSON, from the Modal secretstage12-evaluation-gcp-credentials, inworker_secrets: output files and the constituency lookup file. That secret declares norequired_keys, so the key it holds is unverified;MODAL_WORKER_DATABASE_URL, frompolicyengine-modal-worker-database(required_keys), inreport_run_writer_secrets.The manifest publisher reads no new variable; it reads only the packaged .py manifest, and its job still needs only
MODAL_TOKEN_IDandMODAL_TOKEN_SECRET. No workflow changed.Tests
Each new test was seen failing for the expected reason first; only new and modified tests were run, by node ID.
Contract: the manifest declaration under its own key, never seen by older readers, ignored after a rollback, only on an economy-capable UK entry, naming a bundle dataset; the region-dataset rollback step sending every reader to
active-with-cache.Executor: the worker accepting, selecting and checking the local-area dataset for
ukand nations, refusing it on a release that doesn't route and at another revision, and over the installed .py bundle unpatched; the recognition rule; the plan's area columns and cliffs; the aggregation's areas, withheld codes and area check, for UK-wide, nation, constituency, local-authority and national-dataset reports; the publisher's declaration and its two required keys (including--verify-planwithactive-schema-2deleted).Stage 12 persistence: withheld rows from a result, and, on PostgreSQL 17 built from the Public API at #4305's revision with its privileges applied, writing them as the Modal worker role beside the area rows, no second set on a repeated completion, and a failed withheld insert rolling the whole completion back.
The gateway's golden OpenAPI spec, regenerated for the two fields.
The economy output's schema tests:
SingleYearMacroOutput's fields and its published OpenAPI schema are the current result keys plus the two withheld-code fields (LOCAL_AREA_WITHHELD_CODE_KEYS); dumped and built results still have the current keys alone.libs/policyengine-simulation-persistence/public-api-commitnow points at #4305's merge commit,ca4d396e, for thePublic API schemajob, which compares onlypolicyengine-simulation-persistence's tables, so it doesn't check Stage 12'swithheld_area_codesmodel. Since the previous pin the Public API added no other migration.Not covered by CI: no CI job sets the
REPORT_RUN_TEST_*URLs, so Stage 12's PostgreSQL tests skip there (as before this PR); the three new ones were run by hand.Unverified
policyengine bundle installinstalls one per country), so each new worker container downloads the local-area dataset first, about 90 s in production.Reviews
Six read-only review rounds. Round 1 found one bug, fixed test-first: the documented rollback step (deleting
active-schema-2andactive-with-region-datasets) didn't stop readers released with the new key from reading it. Rounds 2 to 6 found none; their code-quality findings are fixed. Round 6 covered the two CI test-expectation fixes and the pin.🤖 Generated with Claude Code