Skip to content

Require spm-calculator 1.1 for the 2022–2036 SPM forecast - #10620

Merged
DTrim99 merged 1 commit into
mainfrom
spm-calculator-1.1
Nov 3, 2026
Merged

DTrim99 merged 1 commit into
mainfrom
spm-calculator-1.1

Conversation

@DTrim99

@DTrim99 DTrim99 commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Part of the US 2036 extension (PolicyEngine/policyengine-api#3926).

What and why

spm-calculator 1.1.0 is on PyPI (PolicyEngine/spm-calculator#51). It extends the canonical SPM forecast to 2022–2036 without changing any 2022–2035 value. Its content digest is 335e6755dc8e0bd883e09b76c2bd53a5315683773e44d50085c7d6b8832b6291; 1.0.x was 3d86d5c4…. This PR moves policyengine-us onto it, so SPM thresholds and resources calculate for 2036.

  • pyproject.toml: spm-calculator>=1.1.0,<1.2, up from >=1.0.0,<=1.0.0.post1.
  • uv.lock: spm-calculator 1.0.0 → 1.1.0, relocked with the repo's uv 0.13.4. Only the spm-calculator entries change. The wheel and sdist hashes (c528ec0f…, 1be418c6…) match PyPI.
  • test_spm_unit_spm_threshold.py: the every-year test expects 2022–2036, so it checks the 2036 canonical amounts for all three tenures. The unknown-year test also checks 2037, the first year after the horizon.
  • Docs: README.md, docs/spm.md and .github/release-lock.md give the new pin and coverage.

Downstream: callers that pass forecast_content_sha256 need to move to 335e6755… when their bundle adopts this release. That means the policyengine.py bundle (PolicyEngine/policyengine.py#618), the policyengine-api SPM defaults and policyengine-sim-api's spm.py. Production is unaffected until then, because the API runs a certified bundle that pins policyengine-us 2.2.1 and spm-calculator 1.0.0.

Test plan

  • For policy changes: not applicable. This is a dependency pin. The expected values come from the installed spm-calculator forecast, and no formula changes.
  • Source conflicts: not applicable.
  • python .github/release_lock.py --committed passes with uv 0.13.4.
  • test_spm_unit_spm_threshold.py with spm-calculator 1.1.0 from PyPI: 26 passed.
  • Changelog fragment changelog.d/spm-calculator-1.1.changed.md.

axiom: n/a: dependency pin to the 2036 SPM forecast, no change in law

🤖 Generated with Claude Code

spm-calculator 1.1.0 extends the canonical forecast to 2036 without
changing any 2022-2035 value (content 335e6755...). The pin moves to
>=1.1.0,<1.2 and the lock to 1.1.0, whose wheel and sdist hashes match
PyPI. The threshold test expects 2022-2036 and checks that 2037, the
first year after the horizon, has no entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

@PavelMakarchuk ready for your review. CI is green on all 35 jobs at 5dfcd13. This moves the spm-calculator pin to >=1.1.0,<1.2 and the lock to 1.1.0, now on PyPI after PolicyEngine/spm-calculator#51 (your review). Only the spm-calculator lock entries change, relocked with the repo's uv 0.13.4, and their hashes match PyPI. release_lock.py --committed passes, and the threshold tests now cover 2022–2036, with 2037 as the first unavailable year. It merges cleanly with Max's #10589 (core 3.34.2), in either order.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program Review

PR #10620, "Require spm-calculator 1.1 for the 2022–2036 SPM forecast" (DTrim99). Round 1.

IDs: C = critical, SA = should address, G = suggestion.

Source Documents

  • Reviewed head SHA: 5dfcd13
  • Mode: full (round 1)
  • PyPI JSON for spm-calculator 1.0.0, 1.0.1 and 1.1.0. The 1.1.0 wheel (c528ec0f…, 9,881,340 bytes) and sdist (1be418c6…, 9,625,137 bytes) were uploaded on 2026-11-02 at 1:48pm EST. Both hashes, sizes and URLs equal the uv.lock entry. Neither file is yanked.
  • PolicyEngine/spm-calculator#51, merged 2026-11-02 at 1:41pm EST. It extends the forecast horizon to 2036, keeps every 2022–2035 value, and gives the new content digest 335e6755…. It reports the 2036 national renter threshold (ce_trend) as $60,388.65, up 3.19% from $58,520.42 in 2035.
  • requires_dist and requires_python are the same in 1.0.0 and 1.1.0 (pandas, numpy, requests, census, us, openpyxl; Python >=3.9). The lock's dependency list for spm-calculator does not change, and that agrees.
  • This is a dependency pin. No statute or regulation value changes, so I checked no legal reference.

Branch Status

  • 1 commit ahead of main, 74 behind. git merge-tree with current main is clean.
  • CI on the head: 35 pass, 1 skipped.

Findings

Critical

None.

Should address

None.

Suggestions

  • G1. One paragraph in docs/spm.md still gives the old pin.
    • Where: docs/spm.md, the paragraph that starts "The published country wheel requires" (line 144).
    • Evidence: The PR changes line 6 of the same file to "1.1.0 artifact covers 2022 through 2036". Line 144 still says the wheel requires spm-calculator>=1.0.0,<=1.0.0.post1, that the range admits the post-release, and that uv.lock pins 1.0.0, published on 2026-09-11. All three statements are now false. No test reads this paragraph.
    • Fix: Change it to >=1.1.0,<1.2, drop the post-release sentence, and say that uv.lock pins 1.1.0, published to PyPI on 2026-11-02.
  • G2. The 2036 test is self-referential.
    • Where: test_every_amount_uses_canonical_final_value_for_every_supported_year in test_spm_unit_spm_threshold.py.
    • Evidence: The test compares the model's output with forecast.calculate_unit() from the installed package. It confirms that the model passes 2036 through, but it does not fix any 2036 amount. A later 1.1.x release that changes 2036 values would also pass it. The forecast.years assertion does catch a horizon change.
    • Fix: Optional. Add one pinned 2036 value, for example the national renter 2-adult, 2-child reference threshold of 60,388.653854 (the value that spm-calculator#51 publishes), or assert the content digest 335e6755…. This is a design choice of the existing suite, so it does not block.
  • G3. Two YAML header comments name the 1.0.0 forecast.
    • Where: spm_unit_spm_threshold_housing_portion.yaml and spm_unit_capped_housing_subsidy.yaml, line 1.
    • Evidence: Both say "Canonical spm-calculator 1.0.0 forecast spm-rolling-2026-09-09". The 2024 values do not change in 1.1.0, so the tests still pass and the comments are still true for that year.
    • Fix: Optional. Change them to "1.0.0 and 1.1.0" or "1.x" so that they stay current.

Downstream effects

  • I compared the 1.0.0 and 1.1.0 forecasts directly. I took 1,176 unit calculations for 2022–2035: 6 counties (06037, 36061, 48201, 01001, 56021, 17031) plus national, 3 tenures and 4 unit compositions, all 5 amounts. All 1,176 are equal in the two versions. The only new output is 2036.
  • 2036 values from 1.1.0 (national, 2 adults, 2 children, ce_trend): renter 60,388.65; owner with mortgage 58,602.76; owner without mortgage 47,860.75. The renter value equals the value in spm-calculator#51. The 2035-to-2036 renter growth is 3.192%. The housing share (0.4412) and the county geographic factors (06037: 1.22085; 36061: 1.11665; 56021: 0.89576) are the same as in 2035, which agrees with the rule in spm-calculator#51.
  • Variables that change: spm_unit_reference_spm_threshold, spm_unit_unadjusted_spm_threshold, spm_unit_geographic_adjustment, spm_unit_spm_threshold and spm_unit_spm_threshold_housing_portion now calculate for 2036 and no longer raise. SPM poverty outputs that read the threshold (spm_unit_is_in_spm_poverty, in_poverty, person_in_poverty and related) also become available for 2036. Before this PR they failed for 2036, so no existing value changes. 2037 and later still fail with "no entry", as the new parametrized test checks.
  • Callers that pass forecast_content_sha256 with the 1.0.x digest (3d86d5c4…) fail against 1.1.0 by design. The PR body names the policyengine.py bundle, the policyengine-api SPM defaults and policyengine-sim-api. The production API uses a certified bundle with policyengine-us 2.2.1 and spm-calculator 1.0.0, so it does not change until that bundle moves.
  • Partner contract tests: the PR does not edit any file under policyengine_us/tests/policy/baseline/partners/. No partner test uses 2036 or later, and no 2022–2035 SPM value changes, so no partner output changes.
  • Overlap: three open PRs change pyproject.toml and uv.lock (#9462, #10000, #10131). #10000 and #10131 do not touch the spm-calculator lines, so a rebase relock is enough. #9462 (draft) adds test_spm_production_parity.py with "spm-calculator": "1.0.0". Whichever of #9462 and this PR merges second must update that pin, or the parity test will fail.

Tests run

  • I built a private environment from the head's uv.lock (spm-calculator 1.1.0, policyengine-core 3.33.2). With spm-calculator 1.0.0, the changed file gives 3 failed and 23 passed: the 3 tenure cases of the every-year test fail, because forecast.years ends at 2035. The tests need the new version.
  • test_spm_unit_spm_threshold.py: 26 passed.
  • test_spm_integration_contract.py, test_spm_housing_allocation.py and core/test_spm_system.py: 132 passed.
  • YAML tests in policyengine_us/tests/policy/baseline/household/income/spm_unit/: 69 passed.
  • The 1.0.0 against 1.1.0 forecast comparison in "Downstream effects": 1,176 of 1,176 values equal for 2022–2035.

Verdict

APPROVE: The pin, the lock and the PyPI artifacts agree, 2022–2035 outputs do not change, and the tests pass with 1.1.0. Only suggestions G1–G3 remain. No C or SA items are open.

@DTrim99
DTrim99 merged commit 6246317 into main Nov 3, 2026
36 checks passed
@DTrim99
DTrim99 deleted the spm-calculator-1.1 branch November 3, 2026 16:48
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.

2 participants