Skip to content

/reports takes data_source: "local_area_dataset" for UK-wide and nation reports, and no request takes dataset_name - #1070

Draft
anth-volk wants to merge 22 commits into
v2plan/c1-23-local-area-report-profilefrom
v2plan/c1-29-data-source
Draft

anth-volk wants to merge 22 commits into
v2plan/c1-23-local-area-report-profilefrom
v2plan/c1-29-data-source

Conversation

@anth-volk

Copy link
Copy Markdown
Contributor

Fixes #1069

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.

Changelog

  • This PR adds a changelog fragment, changelog.d/<name>.<type>.md, whose type is breaking, added, changed, fixed or removed: c1-29a-data-source.added.md and c1-29a-no-dataset-name.removed.md.

Stacking, and what must merge and deploy first

Summary

  • data_source: "local_area_dataset" on EconomyReportRequest (economy reports only), left out unless given and never stored as an option: the dataset it chooses is stored with the report's simulations. Null or another value, another country, or a region other than uk and the four country/* nations: 422 at data_source, with no code.
  • Where the dataset comes from: the default v2 worker's UK manifest entry (local_area_reports), checked before any database read. No declaration, or another input version: 400 local_areas_unsupported. A declaration with no dataset, meaning the release routes the areas to no dataset of its own: 422 no_local_area_dataset at ["body", "data_source"], since that release's national reports calculate both area levels. Otherwise the store reads the catalog version's local-area dataset (the one default dataset every constituency and local-authority region uses) in place of the region's default, and answers 400 local_area_dataset_mismatch unless it is the declared one. Both 400s are the service's state, which the Public API answers with 503.
  • The report is an ordinary report on that dataset: its simulations, population and the coordinator's input name it, with the report's own options (cliffs included when asked). A re-run reads the stored dataset by name, so it runs on it again; the re-run route takes no data_source.
  • No dataset_name on EconomyReportRequest, EconomySimulationRequest or BudgetWindowReportRequest: given or null, it is refused with 422 extra_forbidden. The persistence library's EconomyRequest.dataset_name stays for re-runs, and the catalog read refuses a request naming both a dataset and the local-area dataset. A bundle lacking the catalog dataset is always dataset_not_in_bundle.
  • Staging economy check: .github/scripts/simulation-staging-economy-check.sh sends data_source: "local_area_dataset" on its current-law Wales 2026 report. Calculate UK-wide and nation reports on the local-area dataset, with their areas and withheld codes #1068's coordinator fails such a run unless each area level has a published area or a withheld code, and writes those rows in the transaction that marks the run succeeded, so the check's requirement that the run succeeds covers them. DEPLOYMENT.md and the staging_economy_check job's comment say so.
  • economy_run_store passed 800 lines once merged with Calculate UK-wide and nation reports on the local-area dataset, with their areas and withheld codes #1068's merge of main, so its catalog-row reads moved unchanged to economy_catalog_rows (read_catalog_rows), with the moved names re-exported.
  • Docs: the entry README ("Local areas", the request and refusal paragraphs), the /reports 400 and 422 descriptions, the persistence library's README.

Deployment code and environment variables

The script, a workflow comment and DEPLOYMENT.md change; no variable or secret is added. The script reads SIMULATION_TEST_AUTH_ISSUER, SIMULATION_TEST_AUTH_AUDIENCE, SIMULATION_TEST_AUTH_CLIENT_ID and SIMULATION_TEST_AUTH_CLIENT_SECRET, mapped in the job step's env: from the GitHub Actions environment secrets SIMULATION_ENTRYPOINT_AUTH_ISSUER, SIMULATION_ENTRYPOINT_AUTH_AUDIENCE, SIMULATION_ENTRYPOINT_TEST_AUTH_CLIENT_ID and SIMULATION_ENTRYPOINT_TEST_AUTH_CLIENT_SECRET. All four exist in beta; simulation-deploy.yml and stage12-deploy.yml call the reusable workflow with secrets: inherit and release_environment: beta, and the job runs in environment: ${{ inputs.release_environment }}. ECONOMY_CHECK_DEADLINE_SECONDS and ECONOMY_CHECK_POLL_SECONDS stay unset (1800 and 15).

Not verified (deployed state only): that staging's default v2 worker declares the local-area dataset when the check runs, which is true once #1068's release has published the manifest; that staging's catalog gives both UK area types that dataset; and that a Wales run on the dense dataset (each new UK worker downloads it, about 90 s) finishes within the check's 30 minutes.

Testing

Test-first throughout: each failing test was committed first ("(failing)" in the subject), seen failing for the expected reason, then passing. Only the tests added or modified were run, each by node ID. The PostgreSQL tests ran against a disposable PostgreSQL 17 database with the Public API's v2 chain at the pinned public-api-commit and its grants applied. The apis-integ client tests ran on a client regenerated from this branch's OpenAPI document. Sim's checks (Ruff, rules rendering, quality guards including file length, lock checks, Pyright for the entry and the persistence library) pass on each commit.

  • Entry, test_local_area_submissions.py: test_the_uk_worker_fixture_calculates_on_its_default_dataset, test_the_local_area_dataset_the_worker_cannot_run_is_refused_before_any_read (2 cases), test_a_report_without_a_data_source_needs_no_declaration, test_a_release_that_routes_no_areas_has_no_local_area_dataset, test_the_report_is_calculated_on_the_local_area_dataset_the_worker_routes_to, test_a_catalog_whose_area_regions_use_another_dataset_is_refused, test_a_report_on_the_local_area_dataset_is_rerun_on_it.
  • Entry, test_economy_routes.py: test_a_data_source_a_report_cannot_have_is_refused (5), test_a_uk_wide_or_nation_report_may_ask_for_the_local_area_dataset (5), test_a_request_naming_a_dataset_is_refused (6), test_a_local_area_refusal_answers_with_its_code (3), test_only_an_economy_report_takes_a_data_source (2); test_economy_rerun_routes.py::test_a_report_rerun_takes_no_data_source.
  • Entry, test_openapi.py: test_economy_requests_name_no_version_and_no_record_id, test_a_report_request_is_an_economy_report_a_budget_window_or_a_household, test_the_local_area_refusals_are_in_the_error_codes.
  • Entry, test_economy_submissions.py: test_a_region_dataset_the_bundle_lacks_is_the_services_state, test_the_catalog_dataset_in_the_bundle_is_the_one_calculated, test_a_stored_dataset_the_catalog_lacks_is_named_in_the_refusal, test_a_refusals_detail_is_built_from_the_request_not_the_error; test_staging_economy_check.py::test_the_check_creates_a_small_report_and_waits_for_its_run_to_succeed.
  • Persistence, PostgreSQL: test_economy_run_store.py::test_a_report_on_the_local_area_dataset_is_read_on_it, ::test_a_declared_dataset_the_catalog_does_not_give_both_area_types_is_refused (3), ::test_every_side_refusal_is_raised_with_its_side, ::test_a_request_naming_a_dataset_and_the_local_area_dataset_is_refused; test_economy_run_records.py::test_the_local_area_dataset_is_the_one_both_area_types_use, ::test_a_catalog_version_without_one_dataset_for_both_area_types_has_none (3), ::test_the_local_area_dataset_may_be_the_national_default.
  • apis-integ: test_economy_generated_client.py::test_a_report_request_keeps_omitted_spm_settings_omitted, ::test_a_created_report_is_read_with_its_runs, ::test_a_created_simulation_is_read_with_its_run; test_budget_window_generated_client.py::test_a_budget_window_request_is_sent_as_given, ::test_a_created_budget_window_is_read_with_its_years.

Review rounds

Three read-only adversarial review rounds, each over the change against its base at the time; none found a bug.

  • Round 1 (the whole change): quality findings fixed: documentation drift, test gaps (the routes' answers for the three local-area codes, the OpenAPI enums, data_source refused on budget windows, household reports and re-runs, a re-run of a report on the local-area dataset), and the catalog read now refuses a request naming two datasets (its test failed first).
  • Round 2 (round 1's fixes): documentation and two test setups fixed.
  • Round 3 (round 2's fixes): one description's wording fixed.

No inline suppression is added.

🤖 Generated with Claude Code

anth-volk and others added 22 commits November 13, 2026 18:53
…ing)

POST /reports accepts breakdown "local_areas" for a UK-wide or nation
report, with or without include_cliffs, and records it in the report's
options. A null, another value, another country, or a constituency or
local-authority region is refused with 422 at the breakdown field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EconomyReportRequest gains an optional breakdown, left out unless given.
Null and any other value are refused with one reason, and the contract's
local_area_breakdown_error refuses another country or a region other than
uk and the four nations, all with 422 at the breakdown field. Neither
include_cliffs nor segmented is passed to it: the national run calculates
the cliffs, so the pair is accepted. The report's stored options record
the breakdown only when it was asked for.

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

A catalog version's local-area dataset is the one default dataset its
constituency and local-authority regions all use, even the national
file. A version with no area regions, only one area type, or a dataset
for each type has none, and another version's regions are never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
read_local_area_dataset returns the one default dataset every
constituency and local-authority region of the version uses, or None
when the version has no such regions, only one type, or a dataset for
each. It is never chosen from the request's dataset_name. It may be the
national file: Stage 12 leaves the areas out of a UK-wide or nation run
whenever the release routes both types to one dataset, so the areas are
calculated apart even then.

The read-only regions model gains region_type, modelled as the Public
API's v2_region_type enum, which the pinned schema comparison checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stacks C1 · 24 on C1 · 23's branch at c924097, for the manifest's UK
local_area_reports declaration that the service reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (failing)

A report asking for local areas is refused with 400
local_areas_unsupported, before any read, when the default worker's UK
entry declares no local-area report runs or another input version. When
the declaration names no dataset, the release routes the areas to none
of their own and no area part is made. When it names one, the store reads
that catalog dataset with the report's inputs, never the request's
dataset_name, and refuses it (400 local_area_dataset_mismatch) unless the
catalog gives both area types that dataset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The service reads the default worker's UK local_area_reports declaration
from the v2 manifest (EconomyWorker.local_area_reports). A report asking
for local areas is refused with 400 local_areas_unsupported, before any
read, when the worker declares none or another input version. A
declaration naming no dataset means the release gives the areas none of
their own, so no area part is made. One naming a dataset makes the store
read that catalog dataset with the report's inputs, never the request's
dataset_name, and refuse it with 400 local_area_dataset_mismatch unless
the catalog version gives both area types that dataset.

The rule comes from one source, the publisher's own reading of the
release (uk_area_regions_routed), so the service, Stage 12's national
aggregation and the UK worker decide alike on every release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user's 2026-11-13 design makes breakdown a request option: it finds
or creates the report on the local-area dataset beside the national one,
and is never stored in the report's options, so it never changes which
report a request names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
breakdown is a request option: EconomyReportRequest.options() no longer
records it, so a report's stored options, and whatever names the report
by them, are the same with or without it. Its validation and 422 refusals
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ith data_source (failing)

data_source: "local_area_dataset" replaces breakdown: the report itself
runs on the dataset the default worker's UK entry declares for
local-area report runs, read from the catalog in place of the region's
default, with its own options. Another value, country or region is
refused with 422 at data_source, and so is a release that routes the
areas to no dataset of its own (no_local_area_dataset); a worker that
declares no local-area runs, and a catalog whose area regions use
another dataset, are still refused with 400.

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

data_source: "local_area_dataset" replaces breakdown on economy reports.
The report runs on the dataset the default worker's UK entry declares for
local-area report runs: the store reads the catalog version's local-area
dataset in place of the region's default, so the report's simulations,
population and calculation are on it, and its options are its own,
cliffs included. Null, another value, another country and a region other
than uk and the four nations are refused with 422 at data_source. A
release that routes the areas to no dataset of their own has no
local-area dataset, and is refused with 422 no_local_area_dataset (its
national reports calculate both area levels). A worker declaring no
local-area runs (400 local_areas_unsupported) and a catalog whose area
regions use another dataset (400 local_area_dataset_mismatch) stay the
service's state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…set (failing)

No request chooses a dataset: a report or simulation runs on its
region's dataset, or a UK-wide or nation report on the local-area
dataset with data_source. POST /reports (economy reports and budget
windows) and POST /simulations refuse dataset_name, given or null, with
422, and the OpenAPI document lists it in none of the three requests.
The Public API stops sending it first (PolicyEngine/policyengine-api#4307).
A re-run still reads its stored report's dataset by name, so a missing
stored dataset keeps its unknown_dataset refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EconomyReportRequest, EconomySimulationRequest and
BudgetWindowReportRequest lose dataset_name, so their strict models refuse
it, given or null, with 422: a report or simulation runs on the dataset
the catalog gives its region, or a UK-wide or nation report on the
local-area dataset with data_source. A bundle lacking the catalog dataset
is always the service's state (dataset_not_in_bundle). The persistence
library's EconomyRequest keeps dataset_name, which re-runs set from the
stored report. The Public API stops sending the field first
(PolicyEngine/policyengine-api#4307), and this deploys only after that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…area dataset (failing)

The check's POST /reports sends data_source: "local_area_dataset", so its
current-law Wales 2026 run also calculates Wales's constituencies and
local authorities. The coordinator fails a run of such a report unless
both area levels have rows or withheld codes, so the check's requirement
that the run succeeds covers them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its POST /reports sends data_source: "local_area_dataset", so the run
also calculates Wales's constituencies and local authorities, and the
coordinator's condition that both area levels have rows or withheld codes
makes the run's success cover them (decision 9). DEPLOYMENT.md and the
deploy workflow's job comment say so. Changelog fragments for data_source
and for removing dataset_name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C1 · 23 merged main (#1059's re-runs) and pinned the Public API commit
that adds withheld_area_codes. Conflicts: the store's report read keeps
main's reuse search and reads the local-area dataset; the refusal-code
comments name both a re-run's stored rows and a create's region dataset;
the package exports both sets of names. The merged store was over the
800-line limit, so the catalog-row reads move unchanged to
economy_catalog_rows (read_catalog_rows), re-exported from
economy_run_store, and the side-refusal test patches the name where the
store now looks it up, taking keyword arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1 found no bugs; these cover its test gaps. Passing as
guards: the routes answer 400 local_areas_unsupported and
local_area_dataset_mismatch, and 422 no_local_area_dataset at
data_source, with their codes; the OpenAPI document lists the three
codes; budget windows, household reports and report re-runs refuse
data_source as an unknown field; and a report stored on the local-area
dataset is re-run on it. Failing: the catalog read refuses a request
that names a dataset and the local-area dataset at once, which today
reads the local-area one silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog read refuses, before any read, a request naming a dataset
together with the local-area dataset (a re-run names its stored dataset,
a create the local-area one), so neither silently wins. The /reports 400
and 422 descriptions, the entry README and the persistence library's
README now say a create names no dataset, list the local-area codes, and
describe read_local_area_dataset, economy_catalog_rows and
LocalAreaDatasetMismatchError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 found no bugs. The /reports 400 description and the entry
README now tell the two local-area codes apart from missing rows;
read_report_inputs and the persistence README say a request naming two
datasets raises ValueError; the re-run test uses a worker declaring no
local-area runs, since a re-run never reads the declaration; and the
catalog read test builds its request with replace and a UK dataset name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 3 found no bugs; its one finding: the /reports 400
description now says local_areas_unsupported covers a declaration of
another input version too, and local_area_dataset_mismatch a catalog
version whose area regions don't share the declared dataset (or lack a
type).

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.

1 participant