Repository navigation
Require spm-calculator 1.1 for the 2022–2036 SPM forecast - #10620
Conversation
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>
|
@PavelMakarchuk ready for your review. CI is green on all 35 jobs at 5dfcd13. This moves the spm-calculator pin to |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
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 theuv.lockentry. 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_distandrequires_pythonare 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-treewith currentmainis clean. - CI on the head: 35 pass, 1 skipped.
Findings
Critical
None.
Should address
None.
Suggestions
- G1. One paragraph in
docs/spm.mdstill 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 thatuv.lockpins 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 thatuv.lockpins 1.1.0, published to PyPI on 2026-11-02.
- Where:
- G2. The 2036 test is self-referential.
- Where:
test_every_amount_uses_canonical_final_value_for_every_supported_yearintest_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. Theforecast.yearsassertion 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.
- Where:
- G3. Two YAML header comments name the 1.0.0 forecast.
- Where:
spm_unit_spm_threshold_housing_portion.yamlandspm_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.
- Where:
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_thresholdandspm_unit_spm_threshold_housing_portionnow calculate for 2036 and no longer raise. SPM poverty outputs that read the threshold (spm_unit_is_in_spm_poverty,in_poverty,person_in_povertyand 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_sha256with 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.tomlanduv.lock(#9462, #10000, #10131). #10000 and #10131 do not touch the spm-calculator lines, so a rebase relock is enough. #9462 (draft) addstest_spm_production_parity.pywith"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, becauseforecast.yearsends 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.pyandcore/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.
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 was3d86d5c4…. 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.README.md,docs/spm.mdand.github/release-lock.mdgive the new pin and coverage.Downstream: callers that pass
forecast_content_sha256need to move to335e6755…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'sspm.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
python .github/release_lock.py --committedpasses with uv 0.13.4.test_spm_unit_spm_threshold.pywith spm-calculator 1.1.0 from PyPI: 26 passed.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