Repository navigation
Stop sending x and y on UK area results - #919
Merged
Merged
Conversation
Make x and y optional on ConstituencyImpactRecord and
LocalAuthorityImpactRecord. Both default to None and are left out of
every dump while None (pydantic's field-level exclude_if), so a record
without them validates and its JSON has no x or y keys rather than
"x": null. Apps deployed for older policyengine.py versions are never
rebuilt and keep sending both; their records still validate and keep
the values.
The gateway's /jobs/{job_id} route already drops null fields
(response_model_exclude_none), and the new endpoint tests check the
JSON it returns for a breakdown result and a national result whose
records carry no x or y, and for records that send them as null.
Raise the contract's pydantic floor to 2.12, the first release with
exclude_if, and relock the contract and the four packages whose locks
record its metadata; only that specifier line changes in each lock.
Regenerate the gateway's golden OpenAPI, which no longer lists x and y
as required.
Written red-green: the contract, gateway and generated-client tests
failed first because x and y were required ("Field required", and a
500 from the gateway), and because a null x or y was written to the
dump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The executor no longer puts x or y on constituency or local-authority
records. It reads only each constituency's code and name from
policyengine.py's lookup, whose x and y columns it now ignores, and no
longer attaches the packaged local-authority positions. The check that
refused a published area without a map position ("missing lookup
coordinates") is removed; the code and lookup-name checks stay. With the
contract leaving absent x and y out of every dump, the result an app
deployed from now on returns has neither key.
Apps deployed for older policyengine.py versions are not rebuilt and
keep sending both, which the contract still accepts.
The beta integration check of a local-area breakdown now requires its
records to carry no x or y, since it runs against the app the same
deployment builds.
Written red-green: the executor tests failed first on the "missing
lookup coordinates" error and on x in the constituency and
local-authority records, and the breakdown check's new cases failed
with "DID NOT RAISE".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The executor no longer ships coordinates_lad22.csv or coordinates_lad23.csv. Nothing reads their x and y any more, but they were also the only list of each boundary version's authority codes, so codes_lad22.csv and codes_lad23.csv replace them with a single code column holding exactly the same codes in the same order. UKLocalAuthorityResources now carries those codes per boundary version (codes) and returns one version's names with names_for, which replaces metadata_for. The load-time checks are unchanged apart from the dropped coordinate comparison: names must exactly cover both lists, and each version's distinguishing codes must match its list. LAD22 and LAD23 detection reads the names and the distinguishing code sets, so it is unchanged. The Stage 12 static runtime file smoke reports lad22_codes and lad23_codes in place of lad22_coordinates and lad23_coordinates, with the same expected counts (374 and 361). A test pins each version's codes to a SHA-256 of the codes the coordinate files listed, and new tests check the loader refuses a repeated code, a list with other distinguishing codes, and a list with x and y columns. Written red-green: the tests failed first because the resources had no codes or names_for, the package still held the coordinate files and no code lists, and the smoke reported the coordinate keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It said map positions are "now" drawn by the front end, which depends on an app change that has not shipped. Say that results carry no map positions and that the app draws them from its own layout files. Docs only; no behaviour changes, so there is no test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
This was referenced Oct 28, 2026
Resolves three conflicts with changes that landed on main: - changelog_entry.yaml: main replaced it with changelog fragments. This branch's three entries become changelog.d/918.changed.md. - The executor's test_simulation_output_builder.py: main split it, and the two UK record helpers this branch changed moved to test_simulation_output_fakes.py, so they lose x and y there. - The gateway's test_endpoints.py: main split it. This branch's two tests of records without x and y, and their helper, move to test_simulation_job_status.py, after the breakdown polling test they followed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
added a commit
that referenced
this pull request
Nov 2, 2026
…ields Brings in #919's merge of main and resolves two conflicts with it: - changelog_entry.yaml: main replaced it with changelog fragments. This branch's entry becomes changelog.d/923.changed.md. - The gateway's test_endpoints.py: main split it. This branch drops x and y from the shared breakdown result, now in fixtures/gateway_responses.py, and its changes to the x-and-y tests, with its new test of an older app's records, go to test_simulation_job_status.py, where #919's tests now are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 tasks done
Brings in #985, which made x and y optional in the contract as this branch's first commit (5b5fbde) did, with reworded comments, docstrings and test names. The five files where the two versions differ (macro_output.py, its two contract test modules, the gateway's golden OpenAPI and test_simulation_job_status.py) take main's version, so this branch's diff against main is now only the executor change: it stops sending x and y and ships local-authority code lists instead of the coordinate files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Requests never reach apps built for older policyengine.py versions, and #985 (fixes #984) already made x and y optional in the contract, so: - The changelog fragment keeps only this branch's executor change and code lists; the contract paragraph is #984's fragment. - The breakdown check's docstring says the executor no longer sends x or y. - The generated-client test that read records with and without x and y now covers only records with them, from breakdowns that started before this deploy; the case without them is #985's test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
marked this pull request as ready for review
November 2, 2026 17:50
anth-volk
added a commit
that referenced
this pull request
Nov 2, 2026
Brings in #985 (x and y optional in the contract, with reworded comments and test names) and #919 (the executor stops sending them). In each of the six conflicts this branch's side wins, because this branch removes the text main reworded: - macro_output.py: the record docstrings lose the x and y paragraph; the fields, _is_none and the pydantic 2.12 floor stay removed. - The contract tests read records without a map position and refuse records that carry x or y, even as null, instead of keeping them. - apis-integ's generated-client test checks the client's record models have no x or y, replacing the tests that read records with and without them. - The gateway's job-status tests use the shared breakdown fixture, which has no x or y, and the golden OpenAPI equals the document the merged code generates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 #918
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Summary
New results stop carrying
xandy(each area's integer map position) on UK constituency and local-authority records, and the executor stops shippingcoordinates_lad22.csvandcoordinates_lad23.csv. Requests never reach apps built for older .py versions, so nothing is kept for them. This PR first also madexandyoptional in the contract; that change is now #985, onmain, and withmainmerged in (1ecb51d9) this PR's diff is only the executor change.Three commits, each with one behavioural change, a fourth that rewords one sentence of the migration doc, and, after the merges of
main,b54712ce, which drops the wording that keptxandyfor older apps:mainas Accept UK area records with or without x and y #985, which carries the same code with reworded comments and test names.xandyonConstituencyImpactRecordandLocalAuthorityImpactRecorddefault toNoneand use pydantic's field-levelexclude_if, so a record without them validates and every dump leaves the keys out instead of writingnull. Records with integerxandystill validate and keep them. The contract's pydantic floor rises to 2.12, the first release withexclude_if; the contract and the four packages whose locks record its metadata are relocked, and only that specifier line changes in each lock. The gateway's golden OpenAPI no longer listsxandyas required.xory.codes_lad22.csvandcodes_lad23.csv(onecodecolumn) replace the coordinate files, with exactly the same codes in the same order.UKLocalAuthorityResourcescarriescodesper boundary version andnames_for(...)replacesmetadata_for(...). The Stage 12 static runtime file smoke reportslad22_codesandlad23_codes.How the JSON loses the keys
output.model_dump(mode="json")fromSimulationOutputBuilder.serializeandLocalAreaBreakdownBuilder.serialize; Stage 12 stores the same dump as its aggregate.JobStatusResponseand/jobs/{job_id}serializes it withresponse_model_exclude_none=True, which already drops null fields.exclude_if(from Accept UK area records with or without x and y #985), the dump itself has noxory, so neither the Modal result, the Stage 12 aggregate nor the gateway JSON carries the keys. The gateway tests check the response JSON for a breakdown and a national result whose records omitxandy, and for records that send them asnull.Boundary-version detection
detect_uk_local_authority_boundary_versionreads the names file and the two hard-coded distinguishing code sets, which this PR doesn't change. Each version's full code list, whichnames_forand the "not part of the detected boundary version" check use, now comes from the code files. The load-time checks stay: the names must exactly cover both lists, and each version's distinguishing codes must match its list. A test pins each list to a SHA-256 of the codes the coordinate files listed (374 for LAD22, 361 for LAD23), and new tests check the loader refuses a repeated code, a list with another version's distinguishing code, and a list withxandycolumns.Deployment code and environment variables
The Modal images change content only:
add_static_runtime_filescopies the wholestatic_runtime_filesdirectory, which now holdsnames.csvand the two code lists.src/modal/v2_static_runtime_files_smoke.pychanges only the keys it reports. No environment variable is added, removed or read differently. For reference, the smoke's wiring is unchanged:MODAL_TOKEN_IDandMODAL_TOKEN_SECRET: GitHub Actions secrets, set in theRun image smokesstep'senv:inpr-image-smoke.yml.PE_UK_PRIVATE_HF_READ_TOKEN: GitHub Actions secret, set in the same step;.github/scripts/modal-image-smoke.shwrites it to the Modal secretpe-uk-private-hf-read-tokenasHUGGING_FACE_TOKEN, which the smoke function attaches throughhf_secret(required_keys=["HUGGING_FACE_TOKEN"]).Compatibility
Results the executor returns after this deploy carry neither key, which #985's contract accepts. Breakdowns that started before this deploy may still return records with
xandy; the contract keeps accepting them until #924 removes the fields, which is why #924 waits until no such breakdown is still running.Testing
Written red-green: for each commit the tests were written first and seen failing for the expected reason, then the code was written and both were committed together.
Field requiredforxandyand on anullxandyin the dump; the gateway returned 500 for records withoutxandy; the generated client did not read such a breakdown as aLocalAreaBreakdownOutput.xin the constituency and local-authority records; the breakdown check's new cases failed withDID NOT RAISE.codesornames_for, the package still held the coordinate files, and the smoke reported the coordinate keys.Commands run (focused, locally):
uv run --frozen pytestontest_macro_output_uk_geography.py,test_local_area_breakdown.pyandtest_gateway_models.py(contract);test_endpoints.pyandtest_openapi_golden.py(gateway);test_openapi.py(entry);test_simulation_output_geographic.py,test_simulation_output_builder.py,test_uk_local_authority_metadata.py,test_stage12_modal_app.py,test_smoke_app.py,test_stage12_runtime.py,test_stage12_output_planning.py,test_stage12_worker_validation.pyandtest_modal_bundle_image.py(executor).python -m policyengine_simulation_gateway.generate_openapi; the diff removesxandyfrom both records'requiredlists and updates the constituency description../scripts/generate-clients.sh, thenuv run --frozen pytest tests/ -m "not requires_gcp and not beta_only"inpolicyengine-apis-integ: everything passed excepttest_ping, which needs a gateway running on localhost.make check(ruff and Pyright in every package),python -m scripts.rules pyright --sync projects/policyengine-simulation-executor,python scripts/run_quality_guards.pywithGITHUB_BASE_REF=main,python -m scripts.rules render(no changes) and.github/scripts/check-uv-locks.sh.mainwith Accept UK area records with or without x and y #985:test_generated_client_reads_area_records_with_x_and_y(apis-integ) passes against the regenerated client, ruff passes on the changed test file, and.github/scripts/check-changelog-fragment.shpasses.🤖 Generated with Claude Code