Skip to content

Send no dataset to policyengine-api and read the run's dataset from its bundle - #112

Draft
MaxGhenis wants to merge 5 commits into
uk-data-issue-routingfrom
us-api-dataset-param
Draft

MaxGhenis wants to merge 5 commits into
uk-data-issue-routingfrom
us-api-dataset-param

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Builds on #110 (base uk-data-issue-routing). Retarget to main once #110 merges.

policyengine-api's economy routes reject any query parameter they don't declare: StrictQueryParameters sets extra="forbid" in policyengine_api/query_parameters.py. microsim-runner's US call sent dataset=enhanced_cps, as did its budget-window helper and model-corroborator's mirror call, so each got HTTP 400 "dataset: Extra inputs are not permitted". The routes build every request with dataset="default" and run it on the one dataset /{country}/metadata lists under economy_options.datasets. On 2026-11-08 the metadata listed Microcosm's populace_us_2024 at populace-us-2024-spm-20260915 for the US and microcosm_uk_2024_25 for the UK.

Plan (WIP)

Changes

  • microsim-runner
    • Sends no dataset on the annual or budget-window route, and lists the parameters each route takes.
    • run_dataset replaces the advertised-dataset table and the "populace" in data_version check. It names the dataset from the hf:// URI in the result's policyengine_bundle the way policyengine.py's dataset_logical_name does. It fills a missing data version from the URI's @ revision, and anything else the bundle leaves out from the metadata default.
    • Takes the local path for a named dataset whatever mode says, and passes the name to pe.us.ensure_datasets.
    • On the budget-window route, the runner reads the result policyengine-api relays from the gateway's BudgetWindowResult: annualImpacts rows and a totals row, in dollars, with no bundle.
    • run_horizon takes a region instead of reusing the country, raises on an error status instead of returning None, and sets a timeout on each request. It gives up once a batch keeps computing past 45 minutes, and the runner then falls back to single-year calls. On 2026-11-08 a live two-year US window was still scoring 2026 two hours after the first request, while the same reform's single-year run for 2026 took about three minutes.
    • In step 3 the runner scores multi-year runs on the budget-window endpoint. The response example drops reform_net_income, which the live result lacks.
  • model-corroborator: the mirror call sends no dataset.
  • reform-comparator: records dataset, data_version and model_version from the runner's output, and notes a run whose dataset_matches_default comes back false.
  • policyengine-api and policyengine-app skills: list the economy query parameters, and describe the budget-window route and its status envelope.
  • test_dataset_naming.py: replaces the advertised-name test with property, differential and doc-scan tests. With PE_SKILLS_LIVE_API=1, it also checks the metadata snapshots and the 400 against the live API.
  • test_budget_window_polling.py (new): runs run_horizon against scripted answers and a fake clock.
  • test_no_stale_references.py: drops the runner's frozen "populace" in data_version span, bans that sample on the runner, and scans the dataset test, which names datasets by their frozen ids.

Invariants

Both test files exec the functions straight from microsim-runner.md, so the agent's code and its tests can't drift apart.

run_dataset, for any bundle whose dataset holds an hf:// URI, a bare name, default or nothing:

  • it returns a dataset name: never default, a path, an @revision or an .h5 suffix;
  • an empty bundle, or one that reads default with no data version, gives the metadata default, and dataset_matches_default holds;
  • the bundle's own dataset and data version win over the metadata;
  • a missing data version comes from the URI's @revision before the metadata;
  • dataset_matches_default holds if and only if both values equal the default entry's.

For any non-empty dataset string other than default, run_dataset returns the name policyengine.py's dataset_logical_name returns. A differential test calls the installed package's function, or a copy of its 6.16.0 body when policyengine isn't installed.

run_horizon, for any run of computing answers, any ending and any deadline:

  • it returns the result of the first ok answer, never None;
  • any other status raises RuntimeError with the API's reason;
  • a batch that keeps computing raises TimeoutError at the first poll at or after the deadline, never earlier, and a terminal answer on that poll still wins;
  • it sends no request after a terminal answer;
  • every request carries the region, start year and window it got, plus a timeout, and no dataset.

test_no_agent_or_skill_sends_dataset_to_the_api reads every Markdown file under skills/, targets/ and docs/. It flags dataset in a query string, or as a key or keyword inside a code block that calls /economy/, and two more tests pin what it must catch and allow.

Verification

  • uv run pytest --ignore=tests/test_skill_examples.py: 297 passed, 6 skipped (the opt-in tests), 1 xfailed.
  • Each of 14 mutants of run_horizon and run_dataset fails a test, including ones that drop the deadline, check it before the status, send dataset, ignore the URI's revision or leave it on the name.
  • With PE_SKILLS_LIVE_API=1, both metadata snapshots match api.policyengine.org, and the US annual, US budget-window and UK annual routes each answer dataset with HTTP 400 "dataset: Extra inputs are not permitted".
  • With policyengine 6.16.0 installed, run_dataset and the pinned formula agree with dataset_logical_name on generated strings.
  • A live US annual run (policy 97853 over 2, region us, 2026) returned a bundle naming hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915, which the check maps to populace_us_2024 and the metadata default.
  • scripts/build_claude_wrapper.py builds, and the built runner carries the check and the deadline.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis force-pushed the us-api-dataset-param branch from d97d175 to 76b0c69 Compare November 8, 2026 20:25
@MaxGhenis
MaxGhenis force-pushed the uk-data-issue-routing branch from 597ab30 to b4f7854 Compare November 8, 2026 20:30
MaxGhenis and others added 5 commits November 8, 2026 15:32
…ts bundle

policyengine-api's economy routes reject query parameters they don't
declare, so microsim-runner's `dataset=enhanced_cps`, its budget-window
helper and model-corroborator's mirror call each got HTTP 400 "dataset:
Extra inputs are not permitted". The routes run every economy request on
the one dataset /{country}/metadata lists: Microcosm's `populace_us_2024`
for the US on 2026-11-08.

- microsim-runner: drops `dataset` from the annual and budget-window calls
  and lists the parameters each route takes. `run_dataset` replaces the
  advertised-dataset table and the "populace" substring check: it names the
  dataset and data version from the result's `policyengine_bundle` and
  falls back to the metadata default. The runner takes the local path for a
  named dataset and passes it to `pe.us.ensure_datasets`. The budget-window
  section describes the live result (`annualImpacts` and `totals`, no
  bundle), and `run_horizon` takes a region and raises on an error status.
- model-corroborator: drops `dataset` from the mirror call.
- reform-comparator: records the dataset, data version and model version
  from the runner's output and flags a run off the default.
- policyengine-api and policyengine-app skills: the economy query
  parameters, the budget-window route and the status envelope.
- test_dataset_naming: example, property and differential tests of
  `run_dataset`, which the test runs straight from microsim-runner.md, and a
  scan that fails when any doc sends `dataset` to an economy route.
  `PE_SKILLS_LIVE_API=1` checks the metadata snapshots and the 400 against
  the live API.
- test_no_stale_references: drops the runner's frozen "populace" span and
  the dataset test's exclusion.

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

On 2026-11-08 a live two-year US budget window for policy 97853 still read
"Scoring 2026, 2027 (0 of 2 complete)..." after more than half an hour, and
run_horizon would have polled it forever. It now raises TimeoutError at the
first poll at or after max_wait_minutes (45 by default) and sets a timeout on
each request, and the runner falls back to single-year calls in that case too.

- test_budget_window_polling: runs run_horizon from microsim-runner.md against
  scripted answers and a fake clock, with example and property tests of the
  polling contract.
- test_dataset_naming: pins the URI-revision fallback for a missing data
  version and a revision with dots in it; no earlier test failed when either
  broke.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Scoring 2026, 2027 (0 of 2 complete)" first came back at 19:27Z; the
19:26Z answer still read "Queued 2026". The batch later moved on, so the
comment drops "never moved".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#110's 597ab30 made this paragraph take both values from the metadata
default when a result has no policyengine_bundle. The rebase kept this
branch's rewrite of the paragraph, which now names that case and the
budget-window results it covers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One full-suite run at 905b4dd failed one test with the host at load 81,
and the rerun passed; the run's log kept only the counts. These nine
property tests check run_dataset's and run_horizon's logic, not their
speed, so a busy host shouldn't fail them on Hypothesis's 200 ms default
deadline. test_build_manifest.py already sets deadline=None the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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