Repository navigation
Conversation
anth-volk
added this pull request to stack #850
October 19, 2026 22:12
This was referenced Oct 19, 2026
Closed
anth-volk
force-pushed
the
apiv2/1a-report-tables
branch
from
October 20, 2026 01:13
d516011 to
56755b1
Compare
Add simulation.reports and simulation.report_results (revision 0003). A report row records an economy or household report. A row migrated from the Public API keeps the v1 report ID in a unique, nullable legacy_report_id and starts not_started with no job; once a job is attached the report is submitted and its progress is that job's status. A check constraint ties the status to the presence of the job. report_results holds one aggregate per finished report job, so reports with identical inputs share one stored result. JobStore.complete_report validates the aggregate with the Pydantic model registered for its report kind and schema version (economy version 1: SingleYearMacroOutput) before writing, then stores the JSONB aggregate, its schema version and SHA-256, budget summary columns, the model and data versions and validated artifact references, finishing the job in the same transaction and only while it is running. A job kind with report_kind set now stores its result there, and a status read returns that aggregate. No report job kind is registered and nothing creates report rows yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Documentation-only naming change in the persistence README, the reports module docstring and the docstring of revision 0003: reports migrated from the Public API keep its v1 report ID. No behaviour changes, so no test can fail first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The persistence README now requires every module that defines tables to be imported by migrations/env.py. The report tables already reached the schema comparison through the job store's own import of the reports module, so no test can fail first; the standalone "alembic check" test covers the report tables and passes before and after this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReportArtifact is the first Pydantic model in policyengine-simulation-persistence, but rule sim.quality.runtime-evaluated-models-resolve checked only the executor's packages and policyengine_simulation_entry, so nothing verified that ruff treats the library's models as runtime-evaluated or that they resolve their annotations. Only the entrypoint installs the library, so its copy of the guard now imports and checks it too. A new test asserts that the guard checks at least one model from the library; it failed before the package was added to the checked list. It is registered under the rule, and the typing-only imports skill document names the package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
apiv2/1a-report-tables
branch
from
October 20, 2026 13:18
56755b1 to
0391726
Compare
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 #846
What this changes
Step 1 of the API v2 migration, item 1A-5. Third of three stacked PRs: #847, then #848, then #849 (this one). It needs both.
Tables (revision 0003).
simulation.reports:kind(economyorhousehold),country(two-letter code), a nullable, uniquelegacy_report_idfor reports migrated from the Public API, an optionaljob_id, and timestamps. A migrated report startsnot_startedwith no job. Once a job is attached the report issubmitted, and its progress is that job's status, so the report and its job cannot disagree. A check constraint ties the status to the presence ofjob_id.simulation.report_results: one aggregate per finished report job, keyed by the job, so reports with identical inputs (and therefore one shared job) share one stored result. It holds the aggregate as JSONB,aggregate_schema_version,aggregate_sha256, summary columns (the budget impacts, households,baseline_net_income,model_version,data_version) andartifacts, a JSON array of validated artifact references (role,gs://,hf://orhttps://URI, SHA-256, media type).Validation.
REPORT_AGGREGATE_MODELSmaps a report kind and schema version to its Pydantic model. Economy version 1 is the contract'sSingleYearMacroOutput.JobStore.complete_reportvalidates the aggregate before writing anything, then stores the result in the same transaction as the conditionalrunning->succeededupdate, like every job write in #848.Collector. A job kind with
report_kindset stores its result inreport_resultsinstead ofjob_results, and a status read returns that aggregate. No report job kind is registered and nothing creates report rows, so nothing uses these tables yet.ReportStoreadds report creation, an idempotentcreate_migrated_reportkeyed bylegacy_report_id(for the one-time migration), and reads.Before merging
No new operator steps. The operator steps of #847 (database roles and secrets) and #848 (Cloud Tasks queue, invoker service account, Cloud Scheduler job and GitHub variables) must be done in both environments first. The runtime role receives its privileges on the new tables from the default privileges set by revision 0001; this was confirmed locally with
\dpafter migrating to0003.No environment variables or deployment files change in this PR.
Checks run
libs/policyengine-simulation-persistence: against a local PostgreSQL 17 withSIMULATION_REQUIRE_DATABASE_TESTS=1, 61 passed, including the upgrade,alembic check, downgrade and upgrade cycle through0003and a race of eight concurrent report collectors.projects/policyengine-simulation-entry:uv run pytest tests/, 657 passed.ruff check .,ruff format --check, the rules guard and the quality guards.0003.Decisions to confirm in review
not_startedorsubmitted, and a submitted report's progress is read from its job, rather than a second copy of the job status on the report.report_resultsis keyed by job, so identical reports share one result.policyengine-simulation-contractfor the aggregate model.🤖 Generated with Claude Code