Repository navigation
Measure benefit-taxation revenue as chapter 1 liability - #10566
Merged
Merged
Conversation
MaxGhenis
marked this pull request as ready for review
November 1, 2026 02:46
tob_revenue_oasdi taxed the pre-1993 section 86 amount in its old-law branch whatever a reform did to current inclusion. A reform that cut inclusion below that amount credited OASDI with inclusion the unit never reached: under repeal the total was $0, OASDI positive and HI the same amount negative. The old-law branch now taxes min(pre-1993 amount, actual inclusion), read from the outer simulation before any branch exists. At current law the cap changes nothing, because both laws share the first tier. HI stays total minus OASDI, with signed shares and no per-unit floor. Reform scoring cases in tob_old_law_bound.yaml cover repeal, raised thresholds and higher inclusion. The generated property test now requires pre-1993 inclusion never to exceed actual inclusion at current law, with no tolerance. Ruling: us:tob-oasdi-old-law-bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The witness gives no state, so the header no longer says Texas. Say "counterfactual" where "state" meant the inclusion scenario. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
to PolicyEngine/crfb-tob-impacts
that referenced
this pull request
Nov 1, 2026
PolicyEngine/policyengine-us#10566 makes `income_tax_chapter_1` the input that freezes the tax states the TOB variables compare, so a stored value gives zero TOB and raises nothing. The derived-variable guard now names it, and a test covers every name on that list, which nothing did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #10563, whose OASDI accounting text this branch's chapter 1 measure makes stale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three reform cases in tob_old_law_bound.yaml made Core's YAML runner build and keep a cloned policy system for each parameter override set. In CI that file alone peaked at 8,244 MB, above the batch runner's 8,000 MB budget, and took its batch process to 13,540 MB. One Python test now runs the same three cases in turn on a single clone of the shared system and the same retiree situation. Before each case it restores all eight reformed leaves, applies the case's parameter inputs with the modifier the YAML runner uses, clears the parameter caches and builds a new simulation, then asserts the same six outputs to a cent. The shared system's leaves are checked unchanged in a finally block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#10563 described tob_revenue_oasdi as differencing income_tax, so the benefit-taxation term carried benefit-triggered NIIT and overlapped the S.770 NIIT transfer. The TOB outputs now difference income_tax_chapter_1, which leaves NIIT out, so that overlap no longer exists. State the actual-inclusion cap wherever the text defines the pre-1993 share, and list the chapter 1 tax among the columns the preflight rejects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The TOB outputs difference income_tax_chapter_1. A stored value is an input that every counterfactual branch keeps, so it freezes each measured tax state and zeroes TOB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A full clone rebuilds every dated parameter entry. Together with the TOB branches' own clones, that took the test's process past a 4 GB cap locally. Copy the nodes and each leaf's list with copy_parameter_nodes, borrowing the entries, which Parameter.update replaces rather than edits; the process then peaked at 3.53 GiB. The shared system's tree is still never written, and the test checks that each copied leaf holds its own list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
US + core hub merge audit, us#10566 at
|
This was referenced Nov 1, 2026
MaxGhenis
pushed a commit
that referenced
this pull request
Nov 2, 2026
#10566 changed main's TOB formulas to difference income_tax_chapter_1 and to cap old-law inclusion at actual inclusion. This branch had moved those formulas onto cached counterfactual helpers, so taking either side of the conflict would have dropped one change. The shared branch helper now measures income_tax_chapter_1, the total measures current-state chapter 1 liability, and the old-law helper forms min(pre-1993 inclusion, actual inclusion) in the outer simulation before any branch exists. The mixin prepares old-law before no-benefits so the bound is read before either counterfactual. Main's TOB documentation and references are kept, and the sequencing and lifecycle oracles follow the new measure and order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Nov 3, 2026
Add policyengine_us.tools.calculate_chunked. It splits a population into household chunks, runs a factory-built simulation on each chunk in turn, and returns source-order entity results and weighted totals, so full-population taxation-of-benefits runs fit in memory. It reads US entity-table datasets and variable-centric .h5 files. It keeps each tax unit, SPM unit, family and marital unit inside one household's chunk, and rejects the four formulas that reduce across the whole simulation. Carry #10566: TOB measures chapter 1 liability (income_tax_chapter_1), caps old-law inclusion at actual inclusion, and computes the two counterfactual states once, in the outer simulation, before any branch exists. The signed allocation matches main's. The tests compare chunked and unchunked results on a 32-household fixture and on the same households as a variable-centric file. They check the guard on direct, dependency, branch, baseline and cross-period routes, and compare TOB sequencing with main's reference algorithm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Social Security benefit-taxation transfers currently difference
income_tax, so they include benefit-triggered NIIT and changes in refundable-credit payments. This changes all three TOB outputs to difference a newincome_tax_chapter_1measure: chapter 1 tax after nonrefundable credits, before refundable credits.The 42 U.S.C. 401 note, P.L. 98-21 §121(e)(1), as amended appropriates the “aggregate increase in tax liabilities under chapter 1.” Section 1411 imposes NIIT in chapter 2A. Unreported payroll tax is chapter 21 FICA. Both are excluded. Investment-credit recapture (§50(a)) and the retirement penalty (§72(t)) remain chapter 1 components. The new variable honors the federal-income-tax abolition switch. Existing
income_taxandincome_tax_before_refundable_creditsare unchanged.Refundable credits are measured as payments against liability, not reductions of that liability: 26 U.S.C. 6401(b) applies subpart C credits against tax after nonrefundable credits and treats excess as an overpayment; subpart C also contains withholding. IRM 21.6.3.2(2) agrees with that payment treatment. Treasury’s published 1998 and 2024 reports describe the with/without comparison but do not define tax liability or its refundable-credit treatment. This implements the chapter 1 and refundable-credit rulings; it is not a claim about an unpublished Treasury calculation.
Only the measured variable changes in TOB formulas. Signed unit allocations and branch cleanup are preserved; HI remains total minus OASDI. The premium tax credit remains outside the measure, as it already is outside
income_tax.Hand-calculated witnesses
2026 single filer, age 70, Texas, $40,000 benefits and $190,000 taxable interest:
The deduction is $16,100 + $2,050 = $18,150; the separate senior deduction is fully phased out. Tax through $105,700 is $12,400×10% + $38,000×12% + $55,300×22% = $17,966. None/pre tax adds 24% of income above $105,700. Current tax is $17,966 + $96,075×24% + $4,075×32% = $42,328. NIIT is 3.8% of MAGI above $200,000 here. Refundable credits are zero.
On the main base used here (2.88.9), the after values agree with the independent 2.61.0 recomputation. Existing $769/$252/$1,021 (2026) and −$17.50/−$12.25/−$29.75 (2024 itemizer) witness inputs and expectations are unchanged. CI run 41316271650 ran those YAML witnesses and passed them at this head. The generated test also exercises the exact negative-itemizer inputs and independent identities, without hardcoding its expected dollar amounts.
Two invented 2024 disabled-worker families pin the refundable-credit ruling:
income_taxdifferential is $1,593 + $2,213.23 = $3,806.23.income_taxdifferential is $640 ($360 OASDI/$280 HI).Old-law inclusion capped at actual inclusion (reform scoring)
tob_revenue_oasditaxed the full pre-1993 amount in its old-law branch, whatever a reform did to current inclusion. When a reform cut a unit's inclusion below that amount, OASDI got credit for inclusion the unit never reached. Under a repeal the total came to $0, OASDI positive and HI the same amount negative. Rulingus:tob-oasdi-old-law-boundcaps it: the old-law branch now taxes min(pre-1993 amount, actual inclusion). The formula reads actual inclusion from the outer simulation before it creates any branch.tax_unit_taxable_social_security_pre_1993stays the plain historical counterfactual.tob_revenue_totaland HI's total-minus-OASDI identity don't change, and both shares stay signed, with no per-unit floor (us:tob-oasdi-hi-signed-allocation).At current law the cap changes nothing. Both laws use the same base amounts and the same first tier, and current law only adds inclusion above that tier, so the pre-1993 amount never exceeds actual inclusion. The generated property test now asserts this with no tolerance, and its independent pre-1993 differentials still match OASDI. No existing witness input or expected value changes, and CI passes all of them at this head, including $769/$252/$1,021, −$17.50/−$12.25/−$29.75 and the chapter 1 witnesses.
test_reforms_cap_old_law_inclusion_at_actual_inclusion, intest_tob_revenue.py, scores three reforms for the 2026 single retiree behind the $769/$252/$1,021 witness ($20,000 benefits, $30,000 interest, a $7,500 pre-1993 amount):Under the raised thresholds, $40,000 of provisional income stays in the first tier: min($10,000, 50% × $10,000) = $5,000. Taxable income is $35,000 − $24,150 = $10,850, so tax is $1,085 against $585 with no inclusion. OASDI and the total are both $500, and HI is $0.
The three cases ran as a YAML file,
tob_old_law_bound.yaml, until this round. CI run 41277496089 atf21fd89passed every job with them. Core's YAML runner builds a cloned tax-benefit system for each distinct set of parameter inputs and keeps it until the process ends, so that file held three reform systems. On run 41270134195 it peaked at 8,244 MB alone, above the batch runner's 8,000 MB budget per process. On run 41277496089 its batch process peaked at 13,540 MB, against 7,954 MB for the other three batch 1 files on the main-base run 41228874440. The runner stopped batch 2 for memory and reran it alone, and the ssa-usda job took 32.2 minutes against 19.0.The Python test runs the same three cases in turn on the same retiree situation and one private copy of the shared system's policy, which owns its parameter nodes and lists and borrows their dated entries. Before each case it restores all eight parameter leaves that any case reforms, applies the case's inputs with the
set_parametermodifier the YAML runner uses (overyear:2000:40), clears the parameter caches and builds a new simulation. It asserts the same six outputs at the same values to the same cent, and checks that the simulation reads each reformed value and current law for the other leaves. It never writes to the shared default system, and afinallyblock asserts that system's eight leaves unchanged, after a failure too. It doesn't supply taxable benefits as inputs, so each case still runs its literal reform through the section 86 formula.Cost: run alone in one process under the required 4 GB and 1,800-second caps, at host load 55 to 70, the test passed. Its call took 1,334.6 seconds of wall time, the whole process used 171 seconds of user CPU, and the process peaked at 3.53 GiB (3,793,649,664 bytes of maximum resident set). That local process imported the country with automatic garbage collection paused and then froze the imported objects; the test itself doesn't. A first version cloned the whole system, and with the TOB branches' own clones the 4 GB cap killed that run, so the test now copies the nodes instead. On CI run 41316271650 at
1cf200d, which passed, the test took 165.2 seconds in Rest Python shard 6's policy group. That group's process ran 26 files, peaking at 11,486,580 kB over 19:21. On round 1's run 41277496089, the policy group that ran this file (shard 7, 25 files) peaked at 11,873,100 kB over 13:54. The added time is this test and #10563'stest_oasdi_accounting.py(157.6 seconds), which main added to the group.The ssa-usda job took 34.3 minutes, against 32.2 in round 1. Its revenue step took 15.7 minutes, against 18.1 in round 1 and 7.4 at the main base. The runner still stopped revenue batch 1 (the two employer files, the pre-1993 file and the HI file) for memory and reran it alone, where it peaked at 11,373 MB. At the main base those four files ran in two processes, at 7,954 and 5,364 MB. Its other SSA step grew from 2.7 to 6.9 minutes, because main added #10563's
ssa/oasdiYAML, a 307-second batch that peaked at 8,994 MB.Mutant: without the cap, the repeal case gives OASDI $769 and HI −$769, and the threshold-rise case $769 and −$269, against expected $0/$0 and $500/$0. The build's own mutant run never reached an assertion. The round 1 review killed the mutant on isolated Core with the exact formula bodies, so the claim rests on that run and the hand values above.
Invariants and CI cost
The existing Hypothesis file checks:
income_taxdifferential minus NIIT plus the refundable-credit differential.The reform test checks two more by example:
Generated batches retain the existing signed/cache witnesses and add elderly single/joint NIIT threshold crossings, below-threshold controls and beneficiaries with children and earnings in EITC/ACTC ranges. Fresh simulations share unchanged policy while keeping independent populations, holders and caches. Secondary algebraic differential checks use zero-payroll partners to avoid float32 cancellation; the independent chapter 1 oracle, component identity and payroll invariance cover every unit. Python coverage is needed for independent simulation and cache/input invariants; policy examples use YAML, except the three reform cases, which run in Python on one private policy system rather than three cached ones. The file remains in the existing Rest Python policy CI group; no runners or concurrency are added.
At the exact main base
10c7f5a93d472ed05247e4cfae729133112f6b16, the successful CI timing artifact reports 75.508 seconds for the generated test and 73.603 seconds for cache/order isolation: 149.111 seconds total. These are JUnit test times within a shared 25-file process, excluding shared startup/collection/shutdown. At this head, run 41316271650 reports 81.660 and 85.417 seconds for the same two tests (167.077 seconds total) and 165.172 seconds for the moved cap test, in Rest Python shard 6. None of these CI figures compares directly with isolated local process wall times.CI runs every file. Pull request run 41316271650 passed at this head, every YAML case and Python test included. Local runs under a 4 GB and 1,800-second cap covered only part of the set, so CI is the complete check.
Readers and overlap
The reader audit found no current downstream formula requiring an update beyond HI’s total-minus-OASDI identity. Existing revenue YAML, property tests, IRA branch-isolation tests and stored-column preflight readers do not rely on the old measure. Every case in #10563's OASDI accounting YAML that reads a TOB output keeps its value under chapter 1: the units with benefits owe no NIIT and get no refundable credit, and the rest have no benefits. The preflight now also rejects a stored
income_tax_chapter_1, which every TOB branch would keep as an input, freezing each measured tax state and zeroing TOB. None of the five stored-input inventories pinned in Microcosm's fixture (377, 385, 380, 397 and 418 columns) holds it. S.770 documentation combines its separate sources with TOB without defining TOB as including NIIT.The two
employer_*_tax_income_tax_revenuevariables are unchanged. They differenceincome_tax(including NIIT and refundable-credit effects), with a zero floor when the reform is active. Their documentation calls this marginal income-tax revenue from including employer payroll contributions, credited to OASDI/HI; neither has a legal reference or chapter-specific definition. Follow-up scope is left to the hub.Overlap: open #10561 (
chunked-microsimulation-calculate), inspected at5da5df7893f9e3c69f5a71ee2d5c41a72da5d349, restructures the same total/OASDI formula lines. This PR is based only on main and copies none of that change. The later merge must carry the chapter 1 measure into its counterfactual helpers and current-law read. #10563 merged first, so this PR merges main and corrects #10563's text for the chapter 1 measure: the usage guide's share definition and NIIT section, and theoasdi_incomeandoasdi_benefit_taxation_revenuedocumentation, which now also state the actual-inclusion cap.Hub microsimulation impact
The certified default dataset ran through
calculate_chunkedin 8 chunks, with these formulas against main at10c7f5a93d. #10486 and #10563 landed after that base and change none of these outputs at current law.tob_revenue_oasditob_revenue_medicare_hitob_revenue_totaloasdi_incomeandoasdi_modeled_balancereadtob_revenue_oasdi, so they fall by its change. The two-year pair doesn't split the fall between NIIT and refundable credits.axiom: queued rulespec-us#415 (blocked: corpus re-pin, axiom-rules-engine#58, encoder dispatch). That issue’s encoding model already defines the three liabilities as chapter 1 liabilities; this change moves PE-US toward it.
impact: TOB outputs fall 2.4% (2026: OASDI -$1.70B, HI -$1.00B, total -$2.71B; 2035: -$3.00B, -$1.80B, -$4.80B); OASDI accounting outputs follow; nothing else moves
🤖 Generated with Claude Code