Repository navigation
Conversation
The v2 household functions take one household in the Public API's situation shape, with optional axes, and one to ten policies, so a baseline and its reform are calculated in one call. The request names the PolicyEngine.py bundle it expects, whether to add missing variables (as the Public API's /calculate-full route does), and an optional SPM selection whose omission is kept distinct from an empty choice. A household keeps its caller's key order through validation and transport, at the top level and in each group, because the Public API walks a household in that order and the order of its input errors, warnings and values follows it. The result carries each policy's calculated household, warnings and SPM receipt, the applied SPM selection and the bundle provenance; a failure carries a typed error whose messages and input errors match the Public API's error responses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Schema version 2 of the v2 version manifest records, for each country a worker application serves, whether it is economy-capable (it names a single-simulation worker) and which function calculates its households, and the validation receipt names an invocation for each of those functions. The Simulation Entrypoint's comparison adapter now accepts only a country the manifest marks as economy-capable, so the report coordinator never receives a report for a country that only calculates households. Deployed and rollback revisions read schema version 1 and reject unknown fields, so the publisher writes schema version 2 under a new key and keeps writing the schema version 1 forms (with region datasets, without them, and without cache releases) under the existing keys whenever schema version 1 can describe the manifest. Current readers use the schema version 2 document while the older keys hold its schema version 1 forms, or, for a manifest schema version 1 cannot describe, unless the older keys hold a later validation; otherwise they read the older keys as before, as economy-capable countries without household functions. Code released before schema version 2 that publishes after a rollback therefore leaves no stale document behind, and the next publish starts from what it wrote. The publisher records every bundle country as economy-capable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The v2 household functions prepare a household exactly as the Public API's household calculation does before calculating it: /calculate-full adds every missing yearly, monthly and eternal variable for the year of the first age entered for "you"; deprecated inputs are dropped, including axes that vary them, with the Public API's warning text; unknown entity groups, variables, variables on the wrong entity, axis variables and policy parameters are reported in the order the household was sent, with the Public API's error types and messages; and the requested values are the null values, or with axes every value of a known variable, in document order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A US household's SPM selection is merged over the installed bundle's certified defaults with the Public API's rules and error codes: settings for any other country, or for a US model bundle without canonical SPM, are unsupported; a model that can run canonical SPM without a certified configuration fails closed; a forecast hash the bundle lacks is an invalid setting from a request but an unavailable configuration from a saved household; and an unknown scenario, county vintage or area is reported with the Public API's code and message. After each calculation, the country model's SPM receipt is reduced to the compact public receipt and checked against the resolved selection. SPM errors that end a calculation are recognized through wrapped causes, as the Public API recognizes them. The executor's SPM module now exposes the certified forecast it already loads once per process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A v2 household function call is measured as one operation that covers the whole call, with nested spans for loading the country model, preparing the household, and calculating it under each policy. The stage plan lists those names so the household functions open spans only from registered stages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
calculate_household runs one household request: it checks the request names the installed bundle, resolves the SPM selection, prepares the household once, then builds a simulation and calculates the requested values for each policy in order. It returns each policy's calculated household, warnings and SPM receipt with the applied SPM selection and bundle provenance, or a typed failure. Each step follows the Public API's household calculation: reform values are converted to each parameter's value type, values are written as its country module writes them, a value that cannot be calculated is null (one null per point, with a warning, when the household has axes), and an SPM error ends the calculation only for a chosen measurement or a configuration the build cannot certify. Failures carry the Public API's messages: an invalid household payload, unrecognized inputs, an SPM error, or an error calculating the household under a policy. Country models are loaded once per process. The tests calculate real US and UK households, including US households with axes and with every missing variable added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… app Each function takes a household request, calculates it with the executor's household calculation and returns the result or typed failure as JSON. It continues the caller's trace through the keyword-only observability context and measures the call as one v2 household operation. The functions run in their own image: the CA store, the locked packages and the source, without the bundle's datasets or the Stage 12 cache, so neither the image build nor the functions use a secret. Each reserves 2 CPUs and 4 GiB, may use up to 16 GiB for large households with axes, times out after 900 seconds, is not retried, and runs at most 20 containers. The other v2 images keep their layers and order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nifest After the country validation functions, release validation calls each deployed household function with a one-adult, current-law household and requires a successful calculation with the deployed bundle, reported by that function, with a number for the household's net income. The release record keeps each household invocation ID, and the manifest publisher records a country's household_callable only when the record shows its function was validated. A household function that fails validation stops the release, as a failed country validation already does. The validation household and its check live in the executor package, so the pre-merge image check can calculate the same household inside the household image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image check now also runs a v2 image smoke. It builds the report coordinator, UK worker and household images the v2 application deploys, and the US worker image without its last layer, the Stage 12 cache, which only a deployment can fetch: the deployment's precompute writes the cache, and the image build reads it with the deployment's Cloud Storage credential. That layer adds data files, not Python packages. In each report image the smoke imports the v2 application module, as Modal does before it runs a function, and the modules the coordinator, workers and validation functions import while handling a request. In the household image it imports the household entry points and calculates the release validation household for each country, which shows the image calculates without datasets. The smoke functions use no secret. Modal builds the smoke's images together. The job's timeout rises from 45 to 90 minutes, because after a relock both the executor smoke and the v2 smoke download certified datasets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A household's SPM receipt that the installed country model reports in a form that cannot be certified now fails with the Public API's reason, checked in its order: the forecast hash, scenario and geography kind are all required before they are compared with the selection, then the years mapping, the runtime versions and the forecast ID. The contract's receipt model words its year and metro-area errors as the Public API's model does, and a receipt that is not an object, or whose years are not a mapping, is reported the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s codes The request's spm field now carries the caller's SPM settings as any JSON value, and the household function checks them as the Public API's routes do: settings for a country other than the US are unsupported, an explicit null for the US is invalid, and malformed settings are invalid with the Public API's message, before the bundle's certification is read. A metro selection needs an area and any other kind accepts none, judged on the settings as sent, as the Public API's model judges them. Before, a malformed selection or an explicit null failed the request's validation instead of returning a typed SPM failure. The request's documentation now says which parts of a saved household's calculation match the Public API's stored-household route (the calculated household, warnings, SPM receipt and SPM codes) and which do not (two error messages, which that route words with the household and policy IDs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The household SPM code now loads the certified forecast with its own per-process loader instead of a wrapper added to the main path's SPM module, which returns to its state on main. The household path and the main path share no SPM code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The household calculation module's documentation now lists each known difference from the Public API's household calculation, which were described in several places or not at all: how undefined numbers and dates are written, the UTC default year, an age given as one value under calculate-full, an explicit null axes, checking every policy's parameters first, the request routes' error wording, and requests the contract rejects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A review asked whether a publish after code released before schema version 2 (as a deployed revert would run) could start from a stale schema version 2 document and drop what that code wrote. The schema version 2 reader already uses the document only while it is current; these tests pin that down for a publish: it keeps an application the older code redeployed without household functions, it takes the default from what the older code wrote, and older keys that a household-only schema version 2 publish left alone change nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A v2 worker version now returns the simulation callable of an economy-capable country, and raises LookupError for any other country. The Simulation Entrypoint's comparison backend stores that callable on the prepared report and sends it to the coordinator, instead of looking up the country's worker by matching and sending a value that schema version 2 allows to be null. The adapter already skips a country that is not economy-capable, so a prepared report always has one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… models Two real-model tests join the PR checks: a US household without a county leaves the SPM threshold uncalculated under the inherited county selection, fails with SPM_GEOGRAPHY_REQUIRED when the selection is chosen, and is calculated with a national receipt when the national kind is chosen; and the installed US and UK models calculate the household that release validation and the image check send. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A US household calculation resolved its SPM selection before the model-load span opened, and that resolution imports the US model, so a cold container's model load was measured in none of the household stages. The country model now loads first, in its own span, and the SPM selection is resolved while the household is prepared, as the Public API resolves it while preparing a calculation. A test checks both, and that a saved household's settings are resolved as saved. The module's list of differences from the Public API now says that a failure under any one policy returns one failure for the whole request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
v2plan/1d-111-household-functions
branch
from
October 22, 2026 17:41
bcedd0d to
7455b24
Compare
…eholds The contract's SPM receipt model is shared with the main path, so it returns to its wording on main. The household SPM code now translates that model's year and metro-area errors into the Public API's wording when it reports a receipt it cannot certify, so household messages still match the Public API and nothing the main path runs changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… limits The tests' older-publisher documents now carry only the fields those publishers wrote: no region datasets under the keys that predate them. Two tests join them: the fixtures hold only released fields, and a publish refuses an unreadable schema version 2 document. The migration document now says what a rollback to code released before schema version 2 costs once current code publishes again: the manifest names no household functions for versions published before the rollback until each is published again, and it loses what only the schema version 2 document held. It also says that current readers do not read a publish by code released before cache releases, so a rollback that far needs the newer keys deleted first; the region dataset readers on main already behave this way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been 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 #902
Summary
Adds Modal functions that calculate one household under one to ten policies in a single call, for the US and the UK, in the versioned v2 Modal application. They reproduce the Public API's household calculation and run in their own image with no datasets and no secrets. Nothing calls them yet: they are deployed but unused until the Simulation Entrypoint gains household routes.
Each commit is one piece:
policyengine_simulation_contract.household_execution): a household in the Public API's situation shape with optional axes, kept in the caller's key order (the Public API's order of errors, warnings and values depends on it); one to ten policies; a schema version; the expected PolicyEngine.py bundle; the caller's SPM settings, where leaving them out differs from sending any value (even an empty object);add_missing_variablesfor/calculate-full. Results carry each policy's household, warnings and SPM receipt, the applied SPM selection and the bundle provenance, or a typed failure.active-with-region-datasets,active-with-cache,active), which the publisher still writes in schema version 1 form. Current readers use the newactive-schema-2document only while the older keys hold its schema version 1 forms, so after a rollback to code released before schema version 2, what that code publishes is read, and the next publish starts from it.household_inputs): missing variables for/calculate-full, deprecated inputs with the Public API's warnings, unrecognized names with its error types, messages and order, and the requested values.household_spm): the selection merged over the bundle's certified defaults with the Public API's codes and messages, and the compact receipt checked against the selection.household_calculation): one simulation per policy, the Public API's reform value conversion, value formatting, null values for values that cannot be calculated (one per axis point, with a warning), and its failure messages. Country models load once per container.calculate_household_usandcalculate_household_ukin a household image: the CA store, the locked packages and the source, without the bundle's datasets or the Stage 12 cache. No secrets. 2 CPUs, 4 GiB reserved and 16 GiB maximum memory, 900-second timeout, no retries, at most 20 containers.household_callable.Changes made after three structured review rounds (the second found no bugs; the third, after rebasing onto the region dataset manifest keys, found one minor issue that
mainalready has, now documented):spmcarries the caller's settings exactly as sent, and the function returns the Public API's SPM codes for settings sent for another country, an explicit null, or malformed settings.main, the schema version 2 reader took the same approach as those keys: a newer document is read only while the older keys match it.)Every known difference from the Public API is listed in the
household_calculationmodule documentation: undefined numbers are the string"NaN"and dates are HTTP date text; the default household year is the UTC year; an age given as one value under calculate-full; an explicit nullaxes; a failure under any one policy fails the whole request; the request routes' error wording; and requests the contract rejects.Tests
main): the executor (1847 passed), entrypoint (665), contract (331), gateway, observability and persistence suites; Ruff format and lint, and Pyright for each changed package, on every commit; the quality guards.bcedd0d4, including thePR image smokejob, which built every v2 image and calculated the validation household in the household image for the US and the UK. After the rebase, thePR image smokejob passed again on7455b240; the other runs on the rebased commits (7455b240,08f13016) were cancelled at the user's request, so the local runs above are the evidence for them, and CI needs to run again before merge.Environment variables and Modal secrets
secrets=[]; the image downloads nothing.MODAL_ENVIRONMENT,MODAL_TASK_ID,MODAL_REGIONMODAL_IDENTITY_TOKENOBSERVABILITY_SERVICE_NAMESPACE,OBSERVABILITY_TRACE_PROJECT_ID,OBSERVABILITY_LOGGING_PROJECT_ID,OBSERVABILITY_LOG_NAME,OBSERVABILITY_GOOGLE_WORKLOAD_IDENTITY_PROVIDER,OBSERVABILITY_GOOGLE_SERVICE_ACCOUNT_EMAIL,OTEL_EXPORTER_OTLP_ENDPOINT(fromOBSERVABILITY_OTLP_ENDPOINT),POLICYENGINE_OTEL_GOOGLE_AUDIENCE(fromOBSERVABILITY_OTLP_GOOGLE_AUDIENCE)env:of the "Deploy the additional versioned v2 Modal application" step insimulation-deploy.reusable.yml, baked into the image bymodal_image_environment()atmodal deploy, as for the other v2 images.MODAL_TOKEN_ID,MODAL_TOKEN_SECRETenv:of the v2 validation step and of the image smoke step.PE_UK_PRIVATE_HF_READ_TOKENenv:;modal-image-smoke.shstores it in the staging Modal secretpe-uk-private-hf-read-tokenbefore the smokes run. The household image does not use it.No new variable or secret is introduced.
Before merging
PR image smokejob (runs on the rebased commits were cancelled at the user's request).🤖 Generated with Claude Code