Repository navigation
Accept UK area records with or without x and y - #985
Merged
Merged
Conversation
Make x and y optional on ConstituencyImpactRecord and LocalAuthorityImpactRecord, the first of three deploys that remove them. 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. The executor still sends both. A push to main deploys the executor before the gateway, and the gateway validates every result it returns against the contract. This deploy lets the gateway accept records without x and y before #918's executor change stops sending them; #923 then removes the fields. The gateway's /jobs/{job_id} route already drops null fields (response_model_exclude_none), and the new tests check the JSON it returns for a breakdown 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; only that specifier line changes in each of the five locks that record the contract's metadata. Regenerate the gateway's golden OpenAPI, which no longer lists x and y as required. Written red-green: the new 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. Made from the contract commit of #919 (cherry picked from commit 5b5fbde), with its gateway tests moved to test_simulation_job_status.py after main split test_endpoints.py, its changelog entry turned into changelog.d/984.changed.md, and wording that kept x and y for older apps replaced. Fixes #984 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
marked this pull request as ready for review
November 2, 2026 14:47
anth-volk
added a commit
that referenced
this pull request
Nov 2, 2026
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>
anth-volk
added a commit
that referenced
this pull request
Nov 2, 2026
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>
2 tasks done
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>
2 tasks done
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 #984
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Changelog
changelog.d/<name>.<type>.md, whose type is breaking, added, changed, fixed or removed:changelog.d/984.changed.md.Summary
The contract and the gateway accept UK constituency and local-authority records with or without
xandy, each area's integer map position. The executor doesn't change and still sends both.Why this is its own deploy
main,xandyareint | Nonewith no default, so every record must carry both keys, andMacroOutputModelforbids unknown keys.JobStatusResponseinjob_status_routes.py). The entrypoint relays the gateway's response without validating it.maindeploys the executor before the gateway (deploy_gatewayneedsdeploy_executorinsimulation-deploy.reusable.yml). In the prod deploy of 30 Oct, the executor was live at 18:08:58 UTC and the gateway at 18:11:26 UTC.If one deploy both made the fields optional and stopped the executor sending them, the old gateway would report every UK local-area breakdown polled in that gap as failed. After this PR is deployed, the gateway accepts records either way, so #919's deploy has no such gap.
What changes
xandyonConstituencyImpactRecordandLocalAuthorityImpactRecorddefault toNoneand use pydantic's field-levelexclude_if. A record without them validates, and every dump leaves the keys out instead of writingnull. Records with integerxandystill validate and keep them.null, matters for step 3: once Remove x and y from the contract's UK area records #924 removes the fields, the gateway refuses"x": nullas an unknown key, so results Stop sending x and y on UK area results #919's executor returns must not carry it.exclude_if. Only that specifier changes in the five locks that record the contract's metadata.xandyas required, and the constituency description is updated.This is #919's first commit (
5b5fbde0), rebuilt onmain:test_simulation_job_status.py, sincemainsplittest_endpoints.py;mainreplacedchangelog_entry.yaml;xandyfor older apps now describe the removal instead (test_records_from_older_executors_keep_their_map_positionis nowtest_records_with_a_map_position_keep_it).Overlap with #976
#976 adds an identical
_is_nonehelper elsewhere inmacro_output.py, and the samepydantic>=2.12lock changes. Trial merges show that merging this PR, #919 and #924 before #976 leaves one helper and the right pydantic floor, with no conflicts in the contract. Merging #976 first would leave two_is_nonedefinitions until #924 removes this one.Deployment code and environment variables
No workflow, Dockerfile, Modal app definition or Terraform changes. The Modal images change only through the contract code and the one-line lock changes. No environment variable is added, removed or read differently.
Testing
Written red-green: the tests were written first and seen failing against
main's code, then the code was written, and both are committed together.Field requiredforxandy, or because anullxorywas written to the dump. The other 2 check that records withxandykeep them, which already held./jobs/{job_id}returned 500 for a breakdown and a national result whose records lackxandy, with "28 validation errors for JobStatusResponse". The case that sends them asnullalready passed, because the route drops null fields../scripts/generate-clients.shonmain's code, the client didn't read a breakdown withoutxandyas aLocalAreaBreakdownOutput.Commands run locally, on the new tests only:
uv run --frozen --all-extras pyteston the new tests intest_macro_output_uk_geography.pyandtest_local_area_breakdown.py(contract): 10 passed.uv run --frozen --extra test pyteston the two new tests intest_simulation_job_status.py, andtest_openapi_golden.py(gateway): 4 passed../scripts/generate-clients.sh, thenuv run --frozen pytestontest_generated_client_reads_area_records_without_x_and_y(apis-integ): passed.python -m policyengine_simulation_gateway.generate_openapi.uv lock --checkin the six affected projects, ruff 0.15.22checkandformat --checkon the changed Python files, and.github/scripts/check-changelog-fragment.sh.🤖 Generated with Claude Code