Skip to content

Remove the fallbacks in the household year list - #4166

Draft
anth-volk wants to merge 1 commit into
masterfrom
remove-household-year-fallbacks
Draft

anth-volk wants to merge 1 commit into
masterfrom
remove-household-year-fallbacks

Conversation

@anth-volk

Copy link
Copy Markdown
Collaborator

Tracking issue: none yet.

Summary

Removes the two fallbacks in the household year list (household_options.time_period) that #4159 added to /{country}/metadata.

  1. US years without a certified SPM forecast. When the bundle certified no SPM forecast, or the forecast failed to load, the US household years were replaced by the US society-wide years (economy_options.time_period) with a logged warning. certified_spm_forecast_years() now raises SPMValidationError with code SPM_CONFIGURATION_UNAVAILABLE instead of returning None, and build_household_options() no longer catches anything. Building the US metadata therefore fails with that error, and no other years are offered.
  2. Countries other than the US and the UK. build_household_options() copied such a country's society-wide years. That code is gone: the method builds years for us and uk only and raises ValueError for any other country. It no longer takes economy_options, which only the fallbacks used.

The US society-wide years are about to be narrowed to start at 2024. With fallback 1 in place, a missing forecast would then have silently removed US household 2022 and 2023.

The served US and UK years, the response shape and economy_options are unchanged. With the locked packages the US household years are still 2035 to 2022, newest first, and the UK years still follow uk_economy_years.

Behaviour when the certified forecast is missing

The SPM code already treats a missing certified forecast as follows:

  • spm_metadata("us") reports {"available": false}.
  • When the installed US model can run canonical SPM, normalize_spm_selection raises SPM_CONFIGURATION_UNAVAILABLE. The readiness check (/health) then answers 503, which stops the staging deploy's health check, and every US request fails with that code.
  • When the installed US model cannot run canonical SPM (an older bundle without measurements.spm), normalize_spm_selection returns None and SPM runs as it did before canonical SPM.

certified_spm_forecast_years() now raises SPM_CONFIGURATION_UNAVAILABLE in every case in which spm_metadata reports SPM as unavailable, the older-bundle case included. The message names the cause, for example The certified US SPM forecast is unavailable: The installed bundle has no certified SPM measurement configuration.

The metadata is built when policyengine_api.country is imported, so this error stops the Public API from starting. Before choosing this, I checked that no configuration that starts the Public API today lacks a certified forecast that loads:

  • The forecast is part of the installed packages. The bundle manifest in policyengine==6.12.0 pins measurements.spm (forecast hash 3d86d5c4…, scenario ce_trend). spm_calculator.rolling_forecast.load_forecast reads that forecast from the spm-calculator==1.0.0 wheel and never downloads it. Both policyengine-us==2.2.1 and policyengine[models] depend on spm-calculator, so every install from uv.lock has it.
  • Tests and local development install from uv.lock. In that environment certified_spm_forecast_years() returns 2022 to 2035. Tests that patch the bundle to an uncertified one do so after COUNTRIES is built and never rebuild the US metadata. The Public API cannot start without the bundle at all: policyengine_api.constants raises RuntimeError when the manifest is missing.
  • Staging and production both run the image built from gcp/cloud_run/Dockerfile with uv sync --frozen --no-dev. Production /us/metadata reports spm.available: true with the same forecast hash; it does not serve household_options yet. Staging was not queried directly. It runs the same image, and its deploy already requires /health, which already fails when the certified SPM configuration cannot be resolved.

A future bundle without a usable certified forecast now stops the Public API from starting instead of serving a substitute list. The pull request that installs such a bundle would also fail CI, because the tests import policyengine_api.country.

Other changes

  • The supported_countries docstring lists a household year rule among the things a new country needs.
  • Changelog fragment: changelog.d/household-year-fallbacks.changed.md.
  • No inline ruff suppressions are added.

Tests

The tests that covered the removed fallbacks are replaced:

  • tests/unit/test_household_options.py::test_us_household_years_fail_without_a_certified_forecast: no certified forecast with an older US model, no certified forecast with a canonical-SPM US model, and a certified forecast that fails to load. Each raises SPM_CONFIGURATION_UNAVAILABLE.
  • tests/unit/test_household_options.py::test_a_country_the_public_api_does_not_serve_has_no_household_years
  • tests/unit/test_spm_settings.py::test_certified_forecast_years_raise_without_a_certified_forecast
  • tests/unit/test_spm_settings.py::test_certified_forecast_years_raise_when_the_forecast_cannot_load

These seven cases were run by node ID and pass. ruff format and ruff check pass on the changed files, scripts/run_quality_guards.py passes, and .github/scripts/check_changelog_fragment.sh finds the fragment. The full test suite was not run locally; CI runs it.

🤖 Generated with Claude Code

The US household years no longer fall back to the US society-wide years.
certified_spm_forecast_years() now raises SPMValidationError
(SPM_CONFIGURATION_UNAVAILABLE) when the bundle certifies no SPM forecast
or the certified one fails to load, so building the US metadata fails with
that error instead of offering other years.

build_household_options() no longer copies the society-wide years for a
country other than the US and the UK; it raises ValueError for any country
the Public API does not serve, and no longer takes economy_options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.22%. Comparing base (8c3fb84) to head (91aef70).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4166       +/-   ##
===========================================
+ Coverage   53.29%   88.22%   +34.93%     
===========================================
  Files         276      288       +12     
  Lines       15936    17130     +1194     
  Branches     2579     2812      +233     
===========================================
+ Hits         8493    15113     +6620     
+ Misses       6730     1315     -5415     
+ Partials      713      702       -11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch was previously deployed

1 inactive deployment
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