Repository navigation
Conversation
anth-volk
added this pull request to stack #3971
October 20, 2026 01:26
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## apiv2/1e-user-policy-repoint #3969 +/- ##
=================================================================
+ Coverage 46.19% 84.13% +37.93%
=================================================================
Files 239 257 +18
Lines 13997 15553 +1556
Branches 2275 2587 +312
=================================================================
+ Hits 6466 13085 +6619
+ Misses 6960 1699 -5261
- Partials 571 769 +198 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
GET /v2/model-catalog returns every parameter node, every parameter with its current-law values (keyed by start date) and every variable of the selected catalog version. The app's v3 workspace loads the whole model at start-up; through the paged routes the US catalog (100,276 gov parameters and 1,174,586 values) would take about 2,550 requests of 500 rows. Each instance builds the serialized document once per country and catalog version, streaming the values from the database, and keeps the four most recently used. Concurrent requests for a document being built wait for that build. The response carries an ETag (country, version, format) and a Cache-Control lifetime of a day when the request names its version, five minutes otherwise. The release workflow's deployed checks request the UK document and revalidate it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /v2/model-catalog declared country_id and policyengine_version as plain function parameters. As a new route it must use the shared typed query models (api.routes.shared-query-parameters): it ignored misspelled parameters (so a mistyped policyengine_version silently served the running version with the five-minute cache policy), took the last value of a repeated parameter, passed country_id through without normalizing it, and its OpenAPI operation omitted the country enum and the version bounds. The route now takes CatalogQuery through query_dependency, as the policy routes do. Unknown or repeated parameters, an unsupported country and an invalid version return 422 before any database read; "US" reads the "us" catalog. The Stage 9 runbook notes the difference from the paged metadata routes, which return 400 for the last two. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
apiv2/1e-metadata-catalog-route
branch
from
October 20, 2026 13:25
929bcf1 to
adc603d
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 #3966
Summary
Adds
GET /v2/model-catalog?country_id=…[&policyengine_version=…], which returns a whole catalog version in one response:name,label,description);YYYY-MM-DD) to value (a value applies until the day before the next start date; the last has no end);/v2/variables).Why: the app's v3 workspace loads the whole model at start-up, as it does today from
GET /{country}/metadata. The paged v2 routes allow 500 rows per request, and the US catalog has 100,276 government parameters and 1,174,586 values, so loading it through/v2/parametersand/v2/parameter-valueswould take about 2,550 requests. This is the first of the two PRs that fill the plan's item 1E-1 (v2 metadata gaps for v3); the full gap list is in #3970.How it works:
MetadataCatalogCache, created once per process infastapi_routes/dependencies.py). Concurrent requests for a document that is being built wait for that build; a failed build keeps nothing.ETag: "<country>:<version>:<format>"andCache-Control: public, max-age=86400when the request namespolicyengine_version,max-age=300otherwise (the running version changes on deploy). A matchingIf-None-Matchreturns 304. The existing gzip middleware compresses the body.CatalogQuerymodel (query_dependency), as the repository's route rules require for new routes: unknown or repeated parameters, an unsupported country and an invalid version return 422 before any database read, andcountry_idis normalized to lower case. The paged metadata routes, which predate that rule, return 400 for an unsupported country or an invalid version.metadataroute group and themetadata_resources_v2_previewcontract.Measured locally against the real 6.7.5 catalog published to a disposable Postgres: the US document builds in 5.8 s and is 60.7 MB (12.1 MB gzipped); the UK document builds in 1.0 s and is 1.3 MB. With #3970's fields the US document is 70.1 MB (12.5 MB gzipped, 6.8 s). For comparison, v1's
GET /us/metadatais 74.7 MB. The building process peaked at 544 MB resident.Note on naming:
/v2/metadata-catalogwould collide with the migration-contract guard, which treats every path starting with/v2/meas user-owned (USER_OWNED_PATH_PREFIXESinscripts/guards/migration_contracts.py). This PR uses/v2/model-cataloginstead and leaves the guard unchanged; the prefix match may deserve a separate fix.API contract change
Additive: one new GET route, with
MetadataCatalogResponse,MetadataCatalogResult,MetadataCatalogParameterandMetadataParameterNodeschemas in/v2/openapi.json. The Stage 9 runbook's statement that the routes "never assemble the complete catalog into one response" is updated to describe this exception.What stays on the v1 routes
Everything.
GET /{country}/metadataand its callers are unchanged; nothing calls the new route until the app's switch (plan item 4A-1). PolicyEngine/policyengine-app-v2#1347 adds the unused caller.Testing
tests/unit/v2/test_metadata_catalog.py(SQLite): the document holds every node, parameter, value and variable; its body validates against the published response model; only current-law values are included, in start-date order, with empty maps for parameters without values; an explicit version reads that version; a second request is served from the cache without re-reading values or variables; the cache evicts the least recently used document, builds once for concurrent requests and keeps no failed build.tests/unit/v2/test_metadata_routes.py: headers and body, 304 on a matching ETag, documented error statuses with no internal detail, OpenAPI schema references, and the default reader receiving the shared cache; the query contract (normalized country, 422 for missing, unknown or repeated parameters, an unsupported country or an invalid version, without calling the reader) and its OpenAPI parameters.tests/integration/test_v2_metadata_routes.py(disposable Postgres): the published catalog's document matches the paged routes and revalidates with 304.tests/integration/test_live_v2_metadata.py: the release workflow's deployed checks now request the UK document (pinned to the installed version) and revalidate it.uv run pytest tests/unit/v2 tests/contract tests/unit/test_migration_contract_artifacts.py -qpassed; Postgres integration suites passed;mypy,ruff format --check,ruff check,run_quality_guards.pypassed;export_migration_contracts.pyregenerateddocs/engineering/migration-contracts.mdanddocs/generated/migration_contracts.jsonwith the new request.Merge order and deployment
push.ymlalready receivesAPI_BASE_URL.GET /us/metadatacosts today.Possible conflicts
The open ruff refactor PRs (#3920, #3924, #3932–#3934) may touch the metadata service and route modules.
🤖 Generated with Claude Code