Skip to content

Build congressional districts for US state runs only - #955

Draft
anth-volk wants to merge 4 commits into
mainfrom
claude/state-only-districts-149119
Draft

anth-volk wants to merge 4 commits into
mainfrom
claude/state-only-districts-149119

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #954

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Do not merge yet

Merging to main deploys staging and then production. Until PolicyEngine/policyengine-app-v2#1439 is released, the app reads congressional districts from nationwide results, so this may merge only after that release has been in production for at least a day.

That app release in turn waits for the Public API's PolicyEngine/policyengine-api#4064 and this repository's #940 to deploy.

Summary

Why:

  • A US national run computes congressional district impacts from the national dataset. A state run computes them from its own state dataset. So the same district shows different figures in a nationwide report and in its state's report.
  • With Nationwide congressional districts from state runs, in v3 and v2 policyengine-app-v2#1439, nationwide reports take each district from a separate run of its state (and DC) on that state's dataset instead. National runs no longer need to compute districts.
  • A search of PolicyEngine's repositories found no other reader of nationwide districts. The Public API passes the field through unchanged.

What changes:

  • _should_build_us_congressional_district_impact (simulation_output_geographic.py) now returns true only for a state/* region, in any case.
  • Returns congressional_district_impact: null:
    • national runs, with no region or us;
    • region-group runs, which include the children of a segmented national run;
    • the segmented national reduce and Stage 12 aggregation of a national report, which build their output with the national region;
    • district and place runs, as before.
  • Still computes districts: a state run, and Stage 12 aggregation of a state report, which passes on that state's region.
  • The contract field is already nullable, so the schema and the generated clients are unchanged.
  • Docstrings updated in simulation_output_geographic.py and segmented_national_reduce.py, and a changelog entry.
  • Integration checks:
    • a new helper, assert_no_congressional_district_results, checks that the field is present and null;
    • test_calculate_default_model, a US national run, uses it in place of the at-large district check;
    • the two Utah state checks still expect UT- districts.
  • _AT_LARGE_DISTRICT_IDS and assert_congressional_district_results' expected_district_ids option served only the national check, so they are removed.

Older model versions: a deployment rebuilds only the Modal app for the current policyengine.py version. Apps already deployed for older versions are not rebuilt and keep their code, so a request pinned to an older model version can still return nationwide districts.

Testing

Each commit is red-green: the tests were written first and run before the code change.

  1. Build congressional districts for state runs only
    • Seen failing first:
      • TestCongressionalDistrictRegions::test__rejects_national_and_group (None, us, region_group/…);
      • test_builder_skips_districts_for_national_and_region_group_runs for None, us and a region group, because the output builder still ran the district calculation.
    • Passed before and after:
      • test__allows_states (state/ca, STATE/CA);
      • test__rejects_district_and_place;
      • test_builder_builds_districts_for_state_runs (state/ut).
  2. Expect no congressional districts on a US national integration result
    • Seen failing first, because the helper did not exist: its local tests, which accept a null field in a dict or a generated client object, and refuse an empty district list, a listed district, a generated client object with districts, and a result without the field.
  3. Say which Stage 12 reports still compute congressional districts
    • Documentation only: the check's docstring and the changelog item now say "Stage 12 aggregation of a national report", so there is no failing-test step.

Run locally:

  • uv run --frozen pytest tests/ in projects/policyengine-simulation-executor: 1881 passed, 22 skipped.
  • uv run --frozen pytest tests/simulation/test_calculate.py -m "not beta_only" in projects/policyengine-apis-integ: 23 passed.
  • make check: Ruff format and lint, and Pyright for every package, passed.
  • GITHUB_BASE_REF=main python scripts/run_quality_guards.py: every guard passed.

Not run locally: the beta_only integration tests, which run against the beta deployment. After this deploys to beta, a US national result from the current version's app should have congressional_district_impact: null, and a state/ut result should still list UT- districts. The null check applies only to the current version's app, for the reason under "Older model versions".

🤖 Generated with Claude Code

anth-volk and others added 4 commits October 27, 2026 21:19
Nationwide reports in the app now take congressional districts from a
separate run of each state and DC on that state's dataset, so a district
shows the same figures in a nationwide report as in its state's report.
National runs no longer need to compute them.

_should_build_us_congressional_district_impact now allows only state/*
regions, in any case. National runs (no region, or us) and region-group
runs return congressional_district_impact null. Segmented national
children run as region groups, and the segmented national reduce and
Stage 12 aggregation build their output with the national region, so all
of them follow the same check. District and place runs still compute
none. The contract field is already nullable.

Tests, seen failing before the change:
- test_region_group_resolution.py: state/ca and STATE/CA are allowed;
  None, us and region_group/... are refused
- test_simulation_output_builder.py: the output builder skips the
  district calculation for None, us and a region group, and still runs it
  for state/ut

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
National runs now return congressional_district_impact null, so the beta
check in test_calculate_default_model would fail against them. A new
helper, assert_no_congressional_district_results, checks that the field
is present and null, and test_calculate_default_model uses it in place of
the at-large district check. The state checks still expect UT- districts.

_AT_LARGE_DISTRICT_IDS and assert_congressional_district_results'
expected_district_ids option served only the national check, so they go.

Tests, seen failing before the helper existed: local tests that the
helper accepts a null field, in a dict or a generated client object, and
refuses an empty district list, a listed district, a generated client
object with districts, and a result without the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docstring of _should_build_us_congressional_district_impact and the
changelog item said Stage 12 aggregation computes no districts. Stage 12
aggregation passes on the report's own region, so it computes none for a
national report, but a report for one state still computes that state's
districts. Both now say so. Documentation only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main added two changelog items at the top of the changed section, where
this branch also added one, so the two sides conflicted. Both are kept,
with this branch's item first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build congressional districts for US state runs only

1 participant