Repository navigation
Count Montana's pension exemption in the state AGI invariant - #10498
Merged
Merged
Conversation
#10392 subtracts Montana's 2021-2023 partial pension, annuity, and IRA income exemption outside mt_subtractions: mt_agi_joint takes mt_pension_exemption_joint once, and mt_agi_indiv takes each spouse's mt_pension_exemption_indiv. Property 3 treated a pension-only filer as unadjusted and expected federal AGI, so a 2021 single filer aged 25 with a 12,000 pension failed with 7,600 against 12,000. - Property 3 now expects max(federal AGI - mt_pension_exemption_joint, 0) on the pooled path through 2023, and the docstring states it. - The pooling guard reuses property 3's mask and requires no pension exemption on either path, so its gap is the pooling alone. - A new guard requires, through 2023, a pooled unit with a nonzero pension exemption; the seeded grid gains a pension-only single filer for it. - The shrunk failing draw is an explicit @example on test_drawn_batches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now carries #10496 (11a9dee), which excluded units with a Montana pension exemption from property 3 and the seeded grid's joint_path guard. This branch keeps those units in property 3 and checks the exemption's effect instead, so the merge resolves the test file as follows: - Property 3's docstring keeps this branch's statement, max(federal AGI - mt_pension_exemption_joint, 0), and keeps #10496's explanation that the exemption is outside mt_subtractions, credited. - Property 3's mask drops #10496's two zero-exemption conditions; the expected value nets mt_pension_exemption_joint instead. - The joint_path guard keeps one copy of the no-exemption condition on both paths, on top of property 3's mask. - TAX_UNIT_OUTPUTS and PERSON_OUTPUTS keep main's single entries for the two exemption variables; this branch's duplicates are dropped. - This branch's changelog fragment now describes the change relative to main. #10496's fragment was already released by main's 2.72.2 bump. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main up to fe9ec12 (S.770 section 8 options, #10402, and the 2.75.0 bump). Neither commit touches the Montana AGI invariant test or this branch's changelog fragment, so the merge applied cleanly with no resolved hunks; the test file's resolution is the one in the previous merge, 56ec6ef. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merged docstring kept #10496's "so it is checked separately", which described excluding those units. Say instead that the no-subtractions condition does not exclude them, and that their expected value nets the exemption. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Merge audit (US + core hub), #10498 at
|
DTrim99
added a commit
that referenced
this pull request
Oct 29, 2026
Take main's Montana AGI invariant test (#10496, #10498) and drop the duplicate Montana changelog fragment, which main already released. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqCGPgxDukji8Bhs57c5rH
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.
The failure
policyengine_us/tests/core/test_mt_state_agi_invariants.py::test_drawn_batchesfailed in Rest Python shard 3 of run 40505543884. That run tested #10402 merged onto maincbd6fb4a9e(12:39Z on 10/27), just after #10392 merged at 12:34Z. Property 3 ("Pooling") expected state AGI 12,000 and got 7,600. The shrunk draw is ten 2021 Montana single filers aged 25, nine with no income and one whose only income is a 12,000 pension (@reproduce_failure('6.168.3', b'AXicY3RkQIWMg04AygIiAAsPFJ0=')).While this PR was in review, #10496 (
11a9dee7da) fixed main by excluding those units: property 3's mask, and the seeded grid'sjoint_pathguard, now also requiremt_pension_exemption_indivandmt_pension_exemption_jointto be zero. So on main, a unit that claims the exemption is no longer checked by property 3. This PR replaces that exclusion with a check of the exemption's effect: those units stay in property 3, and their Montana AGI must equalmax(federal AGI − mt_pension_exemption_joint, 0). It also adds the shrunk draw as an@example, a guard that the new term is never vacuous, and a pension-only filer in the seeded grid. The branch is merged with main (merge commits56ec6ef3e0and7a91e5efd0), so the diff against main is this change alone.The model is right; the test was wrong
#10392 subtracts Montana's partial pension, annuity, and IRA income exemption outside
mt_subtractions.mt_agi_jointaddsmt_pension_exemption_joint(the spouses' combined amount, reduced once by the joint federal AGI) to the pooled subtractions.mt_agi_indivadds each spouse'smt_pension_exemption_indiv(reduced by that spouse's own federal AGI). Somt_subtractionsstays 0 for a pension-only filer, and property 3'sunadjustedfilter as it stood before #10496 (mt_additions == 0 & mt_subtractions == 0 & social_security == 0) treated that filer as unadjusted and expected federal AGI.I checked the model against #10392's cited sources:
cap.yamlhas 4,400,phase_out/threshold.yaml36,700 andphase_out/rate.yaml2.sources.yamlalready notes it is not separately modeled.For the drawn filer that gives min(4,400, 12,000) = 4,400, no reduction (federal AGI 12,000 is below 36,700), and Montana AGI 12,000 − 4,400 = 7,600. That is what PE computes.
The change
Only
policyengine_us/tests/core/test_mt_state_agi_invariants.pychanges, plus this PR's changelog fragment. Relative to main:max(federal AGI − mt_pension_exemption_joint, 0); for a unit with no exemption that is federal AGI, as before. The docstring states this, describes the exemption and its source, and keeps Check Montana's pension exemption in the Montana AGI pooling test #10496's explanation, credited, that the exemption is outsidemt_subtractions.mt_pension_exemption_joint(tax unit) andmt_pension_exemption_indiv(person, summed to the tax unit).joint_pathwitness; one copy of that condition is kept, on top of property 3's mask (run["pooled"]), which selects the same units as main's spelled-out mask. The exemption is reduced by each spouse's own AGI on the separate path and by the joint AGI on the joint path, so the witness gap must come from pooling alone. The existing witnesses (age-40 couples with a self-employment loss) have no pension, so the guard still holds.@exampleontest_drawn_batches(year 2021). Aderandomize=Truetest is seeded fromfunction_digest(test), which hashes the test's source with decorators and comments removed (hypothesis/internal/reflection.py,_clean_source), so the@exampleadds a case without changing the random draws. The superset check below confirms it: this PR's five random draws equal main's.max_examplesand the other settings are unchanged.Why #10392's own CI passed
This section separates what was shown from what is inferred.
Shown from the two CI runs (#10392's, and #10402's run 40505543884). Both used CPython 3.14.8, Hypothesis 6.168.3 and the
ciprofile (database=None, derandomize=True). The test file was identical, and so was the core-1 process's list of 116 test files. The MT variables and parameters were also identical between the two merge trees (git merge-treeofc81ca258onto0e7ced1747, and of0e66a950ontocbd6fb4a9e). Withderandomize=True, Hypothesis 6.168 seeds the test'sRandomfromfunction_digest(test)(get_random_for_wrapped_testinhypothesis/core.py), so both runs started from the same seed.Shown from Hypothesis's code (6.168.5, read for this PR). Each integer draw first has a 5% chance (
_maybe_draw_constant,p=0.05) of returning a value from a pool of constants instead. Part of that pool comes from local modules insys.modules:_get_local_constantsadds the constants of every newly loaded module whose file is local (is_local_module_fileskips the standard library,site-packagesand test files) wheneversys.modulesgrows, and never drops any. So a test's pool also includes modules that earlier tests in the same process loaded.Shown by runtime probes (local CPython 3.13.9 and Hypothesis 6.168.5, not CI's environment), at this head
fc8bdad577unless marked:[0, 150000](the wage range). The test file does not count as local. At the previous merge head56ec6ef3e0(the same test code; its package lacks only main's two newest commits), the pool also held 67 integers, the same set as a recomputation from the loaded modules with Hypothesis's own functions. PE variable modules were insys.modulesunder hashed names (for example…_mt_pension_exemption_joint) and counted as local.test_drawn_batchesdrew different batches: the five random draws were (year, units) (2021, 10), (2021, 21), (2023, 14), (2022, 19), (2027, 30), against (2021, 10), (2021, 21), (2024, 30), (2025, 30), (2021, 13) with the live pool. Rerunning with the live pool reproduced the live draws. So the derandomized draws depend on that pool, not just on the seed.policyengine_us.tools.social_securitymodules, and they count as local. At56ec6ef3e0, insys.modulesorder,policyengine_us.toolsmodules added 21 of the 67 integers.Withdrawn. An earlier version of this section counted 623 and 639 such constants in the two merge trees. Those counts came from a static scan of every
.pyfile in the package plus the 116 test files. Hypothesis reads only loaded modules and skips test files, so they were not the pools the CI processes built, and I've dropped them.Inferred, not shown. The #10402 merge tree contained #10401's
tools/social_security/*, which the #10392 tree did not, andvariables/gov/ssa/social_security/calculator/results.pyimports them. If those modules were loaded beforetest_drawn_batchesran, the #10402 process's pool differed from #10392's, and with it the draw. That would explain why #10392's draw contained no pooled pension-only unit and #10402's did. The probes don't reproduce either CI process's pool, which also depends on the modules its earlier tests loaded, and nothing here shows #10392's actual draw.Tests
All local runs below are at this head (
fc8bdad577), one process each, with thread caps of 4 andOMP_WAIT_POLICY=PASSIVE, on CPython 3.13.9 and Hypothesis 6.168.5. The host's filesystem was heavily contended during them: importing the package took 12 to 17 minutes, against under a minute earlier the same day.test_drawn_batches) in 752.68 s of pytest time, 1,517 s of wall time including startup and shutdown, with a peak RSS of 3.2 GB.test_drawn_batchesdraws (the@exampleand the five derandomized draws, which are the same under main's file and this PR's) and for the seeded grid for 2021–2023, I ran one simulation, then main's_checkand this PR's_checkon that same run. Both pass every case. Main's property 3 asserts 49 units and this PR's asserts 54. Main's units are a subset of this PR's, with the same expected values (they have no exemption). The five extra units are pension filers: the@example's (federal AGI 12,000, exemption 4,400, Montana AGI 7,600), a drawn 2021 unit (12,291, 4,400, 7,891), and the grid's pension-only filer for 2021, 2022 and 2023 (12,000 less 4,400, 4,640 and 5,060).@examplealone (test_drawn_batcheswith Hypothesis's explicit phase only): 1 passed.- mt_pension_exemption_joint), under a 2 GB / 1,800 s cap: 2 failed, both at property 3'sassert_allclose(line 231).test_drawn_batchesfailed on the explicit example: its pension filer reports 7,600 against the mutant's expected 12,000.test_seeded_grid[2021]failed on the grid's pension-only filer: 7,600 against 12,000.core-2), not shard 3. That job passed all 8 of the file's tests in 21.6 s of call time, under CPython 3.14.9, Hypothesis 6.168.3 and theciprofile.impact: none (one test file changes; no variable, parameter or reform file changes)
axiom: n/a (test only)
🤖 Generated with Claude Code