Repository navigation
Conversation
The Public API listed its countries in several places: the v2 routes' `CountryId` type (`Literal["us", "uk"]`), an unused `SUPPORTED_COUNTRY_IDS` set in the query models, catalog extraction's `SUPPORTED_COUNTRIES` and its required packages and installed models, the set of countries the v1 routes copy into v2, and the household copy command's options. `policyengine_api/supported_countries.py` now holds the one list, `SupportedCountryId`, and `SUPPORTED_COUNTRY_IDS` derived from it; each of those places derives from it. The list lives in its own module, not in the query models, because the query models import the catalog package, which must import the list too. Tests: in a fresh interpreter, adding "zz" to the one list adds it to every derived list (this failed before the change: `CountryId` rejected "zz"); the catalog's per-country settings must cover exactly the listed countries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reference
Adds the v2 `countries` table: one row per country the Public API serves,
with `household_enabled` and `society_wide_enabled` saying which
calculations it supports (a check requires society-wide countries to be
household capable). `users.primary_country` and the `country_id` of
households, policies and the four user association tables reference it,
with `ON DELETE RESTRICT`, instead of each checking `IN ('us', 'uk')`. The
tables that pair v1 records with their v2 copies keep their checks.
Revision `fb1a3e40d3d6` was autogenerated. Its one hand-written operation
inserts the US and UK rows, both capable of both, between the table and the
foreign keys, which otherwise fail on every database whose rows already
name either country. This insert is an exception to the rule that revisions
contain only generated operations, approved for this revision alone; the
revision's comment and the Alembic skill say so. The generated downgrade
restores the checks and drops the table with its rows.
The privilege manifest gives the countries table no role access yet.
Tests (failed first because the `countries` table and `Country` model did
not exist): model structure and foreign keys; SQLite persistence (an
unlisted country is refused, a listed country in use cannot be deleted, a
household-only row lets records name that country); the revision's
operations; and the Postgres lifecycle (upgrade keeps existing US and UK
rows, unlisted countries are refused, downgrade and re-upgrade, a downgrade
is refused while records name another country, and the previous release's
table usage still works after the upgrade).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…service The new script requests /health from the Simulation Entrypoint URL that SIM_ENTRYPOINT selects, reads the Cloud Run revision from the x-policyengine-simulation-revision header, and removes the revision's number and suffix to get the service name. Staging must be served by policyengine-simulation-entry-staging and production by policyengine-simulation-entry; a missing header, a name that is not a Cloud Run revision, a failed request or an unknown environment fails. The shared old gateway reports no environment and is not checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The staging and production model-version alignment jobs now set DEPLOYMENT_ENVIRONMENT and run check_simulation_entrypoint_environment.sh right after checkout, before the bundle compatibility check. Every staging and production deploy job needs these jobs, so a release whose staging SIMULATION_ENTRYPOINT_URL is served by the production Simulation Entrypoint, or the reverse, stops before it deploys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`GET /v2/countries` lists every row of the countries table, ordered by ID: each country's `id`, `household_enabled` and `society_wide_enabled`. It takes no query parameters (any is refused with a 422), needs no user credential, and answers 503 when the v2 database is not configured or cannot be reached, and a secret-safe 500 for anything else, through the shared v2 error mapping. Requests are logged under the `metadata` route group. The route follows the v2 layout: a `countries` route package, and a service package with a read connector, a session container, a transformation and a read type. The Public API's runtime role gets SELECT on `countries` in the privilege manifest, which the deploy grants after the schema upgrade. The migration contract registry gains the `country_selection_v2` workflow, and the cross-database integration workflow runs the new integration test. Tests (failed first: the route and service tests could not import `policyengine_api.services.v2.countries`, the OpenAPI test did not find `/v2/countries`, the generated contract files were stale, and the integration workflow did not name the new test): route responses, the query refusal, 405 for other methods, 503 and 500 mapping and the OpenAPI document; the service against SQLite; and, against migrated disposable Postgres as the runtime role, the route returning the two rows the revision inserted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`policyengine_api.asgi` now checks, while the application loads, that the v2 `countries` table holds one row for each country in `policyengine_api/supported_countries.py` and no other. A worker that finds a difference raises `CountryListMismatchError`, naming the listed countries without a row and the rows not listed, so it never serves a request; on Cloud Run the revision then fails its health checks. The check runs on Cloud Run (`K_SERVICE` is set) and wherever a v2 runtime database URL or its secret resource is configured. A local or test startup without v2 configuration opens no connection. A missing table, a missing privilege and unusable v2 configuration also stop startup. A database that cannot be reached is logged, by error type only, and startup continues: the v1 routes do not read the v2 database, and their copies into it already continue without it. The countries service gains `verify_countries_match_code_list`, which reads the row IDs through a new read connector and compares them in a validator. Tests (failed first: `policyengine_api.services.v2.countries.validators` did not exist): the comparison in every direction; the service against SQLite; the startup function's selection, each failure that stops startup, and the logged skip without the error's detail; importing the ASGI module with an unusable v2 runtime URL failing without printing the password; and, against migrated disposable Postgres as the runtime role, the check passing on the revision's rows and failing once an extra row exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review found that the startup check sorted its failures by exception type in a way that stopped startup for a brief Secret Manager outage (reported as unusable v2 configuration) while only logging rejected credentials (a connection error). The check now stops startup only when the database answers and the answer disagrees with the country list: different countries, a missing table, or no privilege to read it. When it cannot get an answer, because the v2 configuration cannot be used, the secret cannot be resolved, or the database cannot be reached or queried, it logs why and startup continues; the readiness check already reports unusable v2 configuration as not ready and recovers once the secret resolves. The rule `api.startup.no-import-side-effects` now says what deployed startup does: it opens no network connections unless the process runs on Cloud Run or has a v2 runtime database configured. The country list and the startup check join the mypy file list and rule `api.quality.mypy-v2`. The country list's docstring and the Alembic skill now say that a deploy runs a country-adding revision before the new release starts, so the previous release refuses to start afterwards, and list the US-and-UK checks a new country must also widen. Tests (failed first: configuration errors and `InterfaceError` were raised, and importing the ASGI module with an unusable v2 URL exited with an error): configuration and secret failures are logged with their message and startup continues, connection and interface errors are logged by type only, and importing the ASGI module with an unusable v2 URL logs the skipped check without the password. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a full test run, the tests that run Alembic commands load Alembic's logging configuration, which disables every logger that already exists, including the startup check's. Its four log tests then captured nothing and failed, though they passed on their own. An autouse fixture now re-enables the check's logger for each of its tests. Tests (failed first: running the Alembic unit tests before the startup check tests failed all four log tests); with the fixture, both files pass together (46 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto v2plan/2b-204-household-calculations # Conflicts: # docs/engineering/migration-contracts.md # docs/engineering/skills/alembic-migrations.md # docs/generated/migration_contracts.json # policyengine_api/data/v2/catalog/extraction.py # policyengine_api/data/v2/migration_target.py # policyengine_api/data/v2/models/associations.py # policyengine_api/fastapi_routes/dependencies.py # policyengine_api/query_parameters.py # tests/integration/test_alembic_v2_lifecycle.py # tests/unit/test_migration_contract_artifacts.py # tests/unit/v2/test_alembic_v2.py # tests/unit/v2/test_models.py
…eck' into v2plan/2b-204-household-calculations
…rvice
Catalog versions gain spm_availability: whether the version offers SPM
settings, with their JSON schema and the certified defaults, or
{"available": false}. After publishing, catalog initialization reads the
simulation service's public versions answer and records it on each
published version that has none yet; a US version offers SPM when the
service lists a canonical capability with pinned defaults for its
PolicyEngine.py version, and other countries never do. A bundle pins its
SPM release, so a recorded value never changes, and versions published
earlier get it on the next run. The publication step now receives
SIMULATION_ENTRYPOINT_URL. The three database steps of initialization are
grouped as PublicationSteps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The model catalog result gains spm: available, and when it is, the JSON schema of a household's SPM selection and the certified defaults. A version whose availability is not recorded yet, or that does not offer SPM, is not available. The route contract and the stage 9 guide name the field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… takes A stored v2 household becomes the situation shape the simulation service's household functions take: people and each group collection by its plural, each group listing the people whose memberships name it, every record named by its ID so a calculated value maps back to its record. The saved SPM choice is read separately. A v2 policy's values become parameter values keyed by YYYY-MM-DD.YYYY-MM-DD period ranges, with an open end as 2100-12-31. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /v2/household-calculations checks the country's countries row allows
household calculations (422 naming the country otherwise), reads the stored
household and optional policy through the households and policies services,
selects the country's running catalog version and sends the situation, saved
SPM choice, policy parameters and options (axes, add_missing_variables) to the
Simulation Entrypoint. GET /v2/household-calculations/{calculation_id} reads
the calculation back with its household, warnings, spm_config,
spm_provenance and error. Neither route needs an account.
The simulation service is called through the generated simulation client,
one per process, with the gateway credentials and correlation headers; a
missing or partial configuration, an unreachable service, an error answer or
a refusal is a 503.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parallel live staging job now runs a submit, poll and result cycle for a new US household; six calculations submitted at once against submit and finish time limits; a household across 401 axis points, which must finish; and a parity set of two US and two UK households (one of each with axes) compared value by value with API v1's /calculate on the same candidate. The job's time limit rises from 20 to 30 minutes for the Modal calculations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 422 from the simulation service (a household or options it finds invalid, such as a group no person belongs to) now answers 422 with each invalid field and why, instead of 503 without a reason. A body that is not JSON or not of the documented shape, and a gateway token that cannot be fetched, now answer 503 instead of 500. Each answer is decoded once, by the generated client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The axes of one group must share one point count, and the groups' point counts may multiply to at most 10,000, as the simulation service requires. Such requests now get 422 from the Public API before anything is read or sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The request no longer takes `year`: the calculation's year is the household's default year, the year its values name, so a calculation is never labelled with a year it did not calculate. It no longer takes `force` either, so callers without an account cannot skip the 28-day reuse of a succeeded calculation and start a new Modal call for each request. The stage 11 guide notes the year the household function uses when it adds missing variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ctions A household calculation's catalog reader, with its database session, is now opened only once a submission needs the catalog version, and closed after it; polls and refused submissions open none. The situation's group collections come from the households service's own lists, and the live checks share one helper for their run-specific amounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Catalog publication recorded {"available": false} for a US version the
simulation service's /versions did not serve yet, and a recorded value is
never replaced, so the version could never offer SPM settings. Such a
version now stays unrecorded and a later publication run records it. The
stage 9 runbook lists SIMULATION_ENTRYPOINT_URL for the publication command,
and the catalog cache note says how long an earlier answer may be reused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… set The parity set gains a US household under a stored reform that also adds missing variables (compared with /calculate-full) and a UK household under a stored reform, so the policy conversion and the added variables are checked against API v1 too. The memory check's description now says what it asks for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generated client's models wrote the household with its groups before its people and each group's members after its values, in the request and in the answer. The household function lists warnings and calculated values in the household's key order, as API v1 does, so the order could differ from API v1's. The submission and the answers are now checked against the generated models but sent and returned as built and decoded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default service factory built the simulation client before any check, so with the simulation service unconfigured a country without household calculations, or a household that does not exist, got 503 instead of 422 or 404. The client is now built only when a submission or read is sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An axis without a period varied the country model's default period, the current year, rather than the year the household's values name. Such an axis is now sent with the household's default year as its period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/v2/household-calculations requests were logged with the route group "unknown"; they now belong to the household group. The contract registry records both routes as a typed v2 workflow, and the generated migration contract documents include them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Catalog publication now stops before extraction when SIMULATION_ENTRYPOINT_URL is unset, instead of after the catalog is written. When the simulation service's /versions answer cannot be read, publication logs a warning and leaves SPM availability unrecorded for the next run, so a slow or unreachable service no longer stops a release. The cache note and the stage 9 guide say that an instance keeps an earlier answer until it restarts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The simulation service answers 503 with Retry-After while its database or version manifest cannot be read. The live staging checks retried such answers when polling but not when submitting, so one short outage failed the job that staging promotion waits on. Submissions are now sent again, after the Retry-After wait, until the calculation's deadline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Requests to /v2/household-calculations were logged with the household route group's v1 database flags and its configured compute backend. They now log a Supabase read for a submission, no database read for a poll, no write, and the Cloud Run Simulation Entrypoint's v2 workers as the compute, whatever those flags select. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The household client held its HTTP client inside the generated AuthenticatedClient with an empty token but never called a generated route; it now keeps the HTTP client itself. Its description says the household's key order is the stored household's as the v2 database returns it. The SPM availability description and changelog entry say a version gets its record on the next publication run of its own PolicyEngine.py version, and that only an unreadable versions answer never fails publication. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…any order The live household checks sent a request again after any 5xx answer until the calculation's 600-second deadline, so a lasting failure kept each check waiting until the job's time limit and lost the failing answer. They now retry 502, 503 and 504 for at most 90 seconds in a row and fail at once, with the answer's body, on anything else. The parity set compares warnings regardless of order, since the v2 database orders stored keys itself, and one US household now has a deprecated input so that both calculations warn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An invalid SIM_ENTRYPOINT or SIM_COMPUTE_HOUSEHOLD, which household calculations never read, reduced their migration log record to a flag error. The record is now built from what the route uses, checking only the route implementation. The guide and the live check helper say which requests are sent again after a 502, 503 or 504, and that failures the Public API answers with 503 take about 90 seconds to fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The helper that reads a simulation service answer took a generated model class as Any, with an ANN401 suppression; it now takes the model's from_dict reader, typed by the model it returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The simulation service now reads the stored household, the policy's parameter values and the catalog version from the Public API's tables itself. A household calculation's submission therefore carries only the country, the household's and policy's IDs, the options and the running catalog version's PolicyEngine.py version. The Public API still checks the country allows household calculations and that the household and policy exist in it, and fills an axis's missing period with the household's default year. The conversion of stored households and policies, which moved into the simulation service, is removed here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eriod The household function now takes the run's year, the household's default year, from the simulation service and uses it for an axis without a period, so the Public API no longer fills that period itself. The stage 11 guide says the household's default year is also the year of the variables add_missing_variables adds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The simulation service now refuses the caller's data with 422 and its reason as text: a policy changing a parameter the catalog version lacks, or a stored policy value or household the household functions cannot take, such as a group no person belongs to. The Public API read only a list of refused fields, so such a refusal became 503. It now reads the service's refusal model and answers 422 with the reason, or with each refused field. The service's other refusals (400), for references the Public API has already checked, stay 503. The route's declared 422 answer, the adapter's description and the stage 11 guide say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s variables The staging parity household that adds missing variables named its person `adult`, so API v1's /calculate-full added them for the current calendar year while the v2 path adds them for the household's default year, and the two would differ from 2027. It now names its person `you`, whose age gives /calculate-full the household's year, and a test with the date fixed in 2027 checks both sides use the same year. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A test now sends an axis with an explicit period and one with a null period and checks both reach the simulation service unchanged, as they must now that the household function fills a missing period itself. The check that a household has a default year returns nothing, so it is named require_default_year. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generated refusal model keeps a 422's detail as it came when it is neither text nor a list of field errors, such as null, an object, a number, a list of strings or an entry without a type, and building the reason from it raised, so the route answered 500. Such a refusal now means the simulation service's answer could not be read, and the route answers 503 as documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ault year The declared 503 answer now lists what is left for it (an unavailable dependency, an unreadable answer, a refused reference the Public API has already checked, or no v2 worker), and the stage 11 guide says a refused request field gets 422 with each field. The parity year test checks the default year the live check stores the household with, not a constant, and the axis-period tests are one parametrized test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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 #4087
Summary
The Public API now sends stored households to the Simulation Entrypoint's v2 household functions, records which catalog versions offer SPM settings, and checks both against its own
/calculatein staging. Nothing calls the new routes yet: no frontend traffic reaches them.POST /v2/household-calculationstakescountry_id,household_id, an optionalpolicy_id, optionalaxesandadd_missing_variables. It needs no account.countriesrow allows household calculations (422 naming the country otherwise; a country with household calculations only is accepted), that the household and policy exist in that country, through the same services as/v2/householdsand/v2/policies(404 otherwise), and that the household has a default year (422 otherwise).SELECTgrant, converts the household, and calculates the household'sdefault_year, which it sends the household function as the request's requiredyear: the year of added variables, and of values and axes without a period.GET /v2/household-calculations/{calculation_id}returns the calculation as it stands: once it succeeded, the calculated household with its warnings, the PolicyEngine.py version and, for US households,spm_configandspm_provenance; once it failed, its error and the household function's error detail./v2/householdsaccepts). A request field it refuses gets 422 with each field.GET /versionslists a certified SPM capability for its PolicyEngine.py version, with the settings schema and certified defaults;/v2/model-catalogserves it asspm. A version the service does not serve yet, or an unreadable answer, leaves it unrecorded for the next publication run./versionsitself is unchanged./v2/household-calculationsbelongs to thehouseholdroute group. Its migration request log records the Supabase read and the v2 workers on the Cloud Run Simulation Entrypoint, not the household group's v1 database and compute flags. The contract registry and the generated migration contract documents include both routes./v2/policies, added variables, and a deprecated input that produces a warning), compared value by value with the Public API's/calculateand/calculate-full. The checks cannot see how many Simulation Entrypoint instances served them or how much memory the household functions used. The job's time limit rises from 20 to 30 minutes.Route groups covered:
household(new routes only). Everything else stays on its current path:/calculate,/calculate-fulland the stored-household calculation routes still calculate in process. User-visible API changes: the two new routes, andspmin/v2/model-catalog.The parity household that adds missing variables names its person
you, because/calculate-fulladds them for the first age period of a person namedyou(or else the current year) while the v2 path adds them for the household's default year; a test with the date fixed in 2027 checks both use the same year.Testing
Run locally on the branch head (with the simulation client generated from PolicyEngine/policyengine-sim-api#957 at 3a2bbe02 installed over the locked release):
uv run pytest tests/unit tests/contract: 3717 passed;tests/integration/test_alembic_v2_lifecycle.pyandtests/integration/test_v2_catalog_spm_availability.py, 14 passed;format --checkandcheck), mypy, andscripts/run_quality_guards.pypass;scripts/export_migration_contracts.pyregenerated the contract documents;/calculateand/calculate-full, a 503 on each first submission): 9 passed; a lasting 500 fails at once and a lasting 503 fails after the outage limit. They have not run against staging: they run only in the staging deployment job.Environment variables and operator steps
Operator step: a
SELECTgrant. The Simulation Entrypoint's runtime rolesimulation_runtimeneedsSELECTontax_benefit_models,tax_benefit_model_versions,regions,datasets,parameters,parameter_values,policiesandhouseholds, granted by the owner of these tables. Until then, calculations answer 503. It stays an operator step for now: once that role exists, a later change declares the grant in the v2 privileges manifest (policyengine_api/data/v2/privileges.py), so each deploy grants and checks it. This PR does not add it there, because the manifest's deploy-time reconciliation would fail in an environment where the role does not exist yet.SIMULATION_ENTRYPOINT_URLstagingandproduction.env:of both Cloud Run deploy jobs, and written to the revision bydeploy_cloud_run_candidate.shwhen it is set. The new routes always call this URL, whateverSIM_ENTRYPOINTselects (both environments selectcloud_run_simulation_entrypoint). Without it, both routes answer 503.env:inseed-v2-database.yml; that job declaresenvironment: ${{ inputs.deployment_environment }}and is called withsecrets: inherit. Publication stops before extraction when it is unset.GATEWAY_AUTH_ISSUER,GATEWAY_AUTH_AUDIENCE,GATEWAY_AUTH_CLIENT_ID,GATEWAY_AUTH_CLIENT_SECRET_RESOURCE: organization secrets, unchanged. The deploy step passes them to the deploy script, which writes them withGATEWAY_AUTH_REQUIRED=1; the client secret is read from Secret Manager at runtime, as for every existing simulation call.API_BASE_URLandSTAGING_API_TEST_PROBE_IDcome from the deploy job's outputs in the live job's stepenv:, as before.STAGING_HOUSEHOLD_PARALLEL_COUNT,STAGING_HOUSEHOLD_SUBMIT_LIMIT_SECONDS,STAGING_HOUSEHOLD_FINISH_LIMIT_SECONDS,STAGING_HOUSEHOLD_CALCULATION_TIMEOUT_SECONDSandSTAGING_HOUSEHOLD_TRANSIENT_LIMIT_SECONDSare optional limits with defaults; none is set.Not verified: that the
SELECTgrant exists in staging or production; that each environment'sSIMULATION_ENTRYPOINT_URLnames that environment's Simulation Entrypoint (the existing release check covers this); and that the gateway's Auth0 client holds the permissions the Simulation Entrypoint will require for its household routes once it enforces them.Waits for
countriestable) and Check each release calls its own environment's Simulation Entrypoint #4003 (the release check), merged into this branch with plain merge commits. Thecountriesrevision is re-parented onto the catalog revisions in this branch only.SELECTgrant above. The new staging checks call those routes, staging promotion waits on them, and production waits on staging promotion, so merging this earlier stops every release.policyengine-api-simulation-clientthat contains those routes (an operator step), then raising this PR's dependency (>=0.20261027.288, the latest published release, which lacks them) and the lock to it. Until then, the unit tests that import the client adapter fail in CI, and the routes would answer 500.aeb5f3e8f662(SPM availability) followsfb1a3e40d3d6(countries). Migration pull requests in the v2 chain merge one at a time; another open revision on the same parent is moved onto the new head by whichever merges second.Suppressions added
# noqa: PLC0415twice infastapi_routes/dependencies.py: the household calculation service and its simulation client are imported inside the default factory, so the generated client is loaded only on the first calculation request, as the other default factories do.# type: ignore[import-untyped]forpolicyengine_observabilityin the simulation client module: the package has no type information, and the module is in mypy's file list.CI: the Pull request workflow on 4c515e2 failed three jobs, as on every run since the branch was opened.
policyengine-api-simulation-client0.20261027.288 lacks the household calculation models, so mypy cannot find them andtests/unit/v2/test_household_calculation_simulation_client.pyfails at import. This is the client release this PR waits for.test_v2_catalog_installed.py::test_installed_policyengine_catalog_is_complete_and_boundedand the eighttest_v2_catalog_metadata_parity.pytests fail with "the installed PolicyEngine.py does not publish catalog format 2's fields". Those tests come from Store the metadata v3 reads in the v2 catalog from PolicyEngine.py's public records (catalog format 2) #4023 in the base; the base PRs' runs skipped this job, and this PR changes neither the catalog extraction nor the installed PolicyEngine.py.Every other job passed, including lint, the quality guards, both Alembic qualifications and the container builds.
🤖 Generated with Claude Code