Repository navigation
Count only filers' tips and overtime toward the deductions, and require only the recipient's SSN - #10396
Merged
Conversation
…d SSN 26 U.S.C. 224(e)(1) and 225(d)(1) require "such individual's social security number", meaning the recipient's. The 2025 Schedule 1-A cautions and Form 1040 instructions say the spouse who received the tips or overtime must have a valid SSN, and proposed Treas. Reg. 1.224-1(e)(2) needs both SSNs only when both spouses claim tips. The model instead denied both deductions to any joint return with one spouse lacking a valid SSN. The deductions also summed every tax-unit member's tips and overtime, so a dependent's amounts raised the filer's deduction although the dependent's wages are outside the filer's gross income (irs_gross_income; 26 U.S.C. 73(a) for a child's services). New person-level tip_income_deduction_eligible_person and overtime_income_deduction_eligible_person mark a head or spouse with a valid SSN; only their amounts count. The tax-unit SSN variables now hold when any head or spouse has a valid SSN, and stay the deductions' gates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th-spouses rule Round-1 review: is_tax_unit_head_or_spouse can differ from ~is_tax_unit_dependent when dependents are input. When no adult filer is left, the role inference keeps input dependents as head candidates, so an input dependent counted, and a filer under 18 with an adult dependent did not. The eligibility variables now use ~is_tax_unit_dependent, the mask irs_gross_income uses. Four tip_and_overtime_income_deduction.yaml cases still asserted that a joint return with one spouse lacking a valid SSN gets nothing; they now expect the eligible spouse's amounts. New YAML cases cover the two input-dependent households, and the property draws include 17-year-old filers with adult dependents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-2 review: the paired-return test passed with the role-based mask restored, because its guard counted returns where the mask makes no difference (joint returns, or deductions already phased out). The test now adds returns below both thresholds and caps, plus two minor-filer households whose only adult is an input dependent. Each guard counts only returns where breaking its property would change the deduction. The eligibility documentation no longer equates a filer with the head or spouse, which a filer under 18 may not be. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
fix-tips-overtime-dependents-ssn
branch
from
October 23, 2026 03:12
51d7f66 to
dcf7e5b
Compare
Round-3 review nit: the dependent and minor-filer guards counted amounts of dependents without a valid SSN, which a mask that ignores dependent status would still exclude, so those returns could not show the regression. The guards now count only valid-SSN dependents' amounts, and the minor-filer guard checks the role-based mask's counterfactual directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 23, 2026
MaxGhenis
added a commit
that referenced
this pull request
Oct 23, 2026
- The two in_effect parameters cite the enrolled bill once each, with both pages in the title, as CLAUDE.md's one-entry-per-source rule asks. - The year cases move into the variable-named files (tip_income_deduction.yaml, overtime_income_deduction.yaml, at the top, clear of #10396's appended cases), the list-level cases into the existing tip_and_overtime_income_deduction.yaml, and the Arizona cases into az_subtractions.yaml beside its variable. - The property test draws once for its year check, so the switched-on reform builds one model: 41.8 s and 3.13 GB peak footprint locally, down from 64.5 s and 3.18 GB. - The changelog says the lists now name both deductions in every year. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 23, 2026 11:21
Contributor
Author
|
Hand-off to the US + core hub. Head Review. Independent review by GPT-6.1 Sol, four rounds (reports in
Passed:
Pending:
Depends on: nothing; #10393 is merged. Interacts with:
Decision: none. This is a bug fix that moves aggregates, which needs no approval under the merge rules. Follow-ups:
|
Contributor
Author
|
US + core hub merge audit, us#10396 at
|
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.
Follows #10393 (merged as
647fec60f9), which fixed separate filers and the phase-out steps in the same two deductions. Both defects here were left out of #10393 to keep it to the findings an outside reviewer reported. #10393's law review flagged the dependents issue as its finding 9, and the SSN issue came from comparing the code with the 2025 Form 1040 instructions. This branch was first stacked on #10393 and is now rebased ontomain; its own diff is unchanged.axiom: us:statutes/26/224 TheAxiomFoundation/rulespec-us#1882 queued; us:statutes/26/225 TheAxiomFoundation/rulespec-us#1882 queued
What this fixes
Two defects in the federal qualified tips and overtime deductions (26 U.S.C. 224 and 225, TY2025-2028):
tip_income_deductionandovertime_income_deductionsummedtip_incomeandfsla_overtime_premiumover every tax-unit member.irs_gross_incomeleaves dependents' income out.tip_income_deduction_ssn_requirement_metandovertime_income_deduction_ssn_requirement_metreturned false for a joint return unless both spouses held a valid SSN, so the couple got neither deduction. The law asks only for the SSN of whoever received the tips or overtime:Statute, form, instructions and proposed regulation agree, so there is no source conflict to register.
The change
tip_income_deduction_eligible_personandovertime_income_deduction_eligible_person(Person). True for a filer who has a valid SSN. A filer is a tax-unit member who is not a dependent; that is the~is_tax_unit_dependenttestirs_gross_incomeuses. A valid SSN isssn_card_typein the existingeligible_ssn_card_typeparameter: CITIZEN or NON_CITIZEN_VALID_EAD.tip_income_deductionandovertime_income_deductionsum only those people's qualifying amounts. The caps, the whole-$1,000 phase-out steps and the separate-filer rule from Deny tips and overtime deductions to separate filers and phase them out in whole $1,000 steps #10393 apply as before, to that sum.*_ssn_requirement_metvariables keep their names and entity and stay the deductions'defined_forgates. They now hold when any filer has a valid SSN; before, a joint return needed both. Setting one tofalseas an input still zeroes the deduction. Setting it totrueno longer counts a person without a valid SSN, because the person-level check applies anyway.eligible_ssn_card_typedescriptions now say the rule is per recipient. Their references add 224(e) / 225(d), 24(h)(7) and the instructions pages.The mask is
~is_tax_unit_dependentrather thanis_tax_unit_head_or_spouse, as the round-1 review found. When no adult filer is left, the head-or-spouse inference can pick an input dependent as head and pass over a filer under 18. The YAML and property tests now cover both of those households.Invariants (all hold for every input; property tests in
policyengine_us/tests/core/test_tip_and_overtime_deduction_properties.py)I1-I4 compare the model with itself on paired returns, so they don't depend on the restatement in I5. The paired returns mix the seeded draws, returns below both thresholds and caps, and two households where a filer under 18 lives with an adult input dependent. Each property asserts that the draw contains returns where breaking it would change the deduction.
Tests
YAML, in the variable-named files:
tip_income_deduction_eligible_person.yaml,overtime_income_deduction_eligible_person.yaml(new).tip_income_deduction_ssn_requirement_met.yaml: renamed fromtip_income_deduction_ineligible_cardholder_present.yaml, whose first case pinned the both-spouses rule.overtime_income_deduction_ssn_requirement_met.yaml.tip_income_deduction.yamlandovertime_income_deduction.yaml: the dependent-teen household above; joint returns with an ITIN spouse who has no amounts, has all the amounts, or has some; and the two input-dependent households.tip_and_overtime_income_deduction.yamlasserted the old rule and now expect the eligible spouse's amounts.Targeted run at
dcf7e5b906: the 34 test files (27 YAML, 7 Python) thatrg -l 'tip_income|fsla_overtime_premium|overtime_income_deduction|tip_income_deduction' policyengine_us/testslists, one process per file under the shared heavy-job lock, 4 workers or fewer.test_run_selective_tests.pyfails 2 tests locally only because the local virtualenv has nocoveragemodule ("No module named 'coverage'"). Deny tips and overtime deductions to separate filers and phase them out in whole $1,000 steps #10393's own targeted runs, at its commits660be20f18andf098447ef8with the same virtualenv, failed the same 2 tests with the same error. CI installscoverage.bbf9b859e1changes only the property test. That test passes there (see thenonemutant run below).Mutants, each applied to a copy of the tree at
bbf9b859e1. Each run covers the six YAML files for the changed variables plustip_and_overtime_income_deduction.yaml(101 cases), and the property test file (5 tests). All ten are killed, and thenonecontrol passes:tips-sum-all/overtime-sum-all(every member counts again)tips-dependents-count/overtime-dependents-count(SSN check only)tips-ssn-ignored/overtime-ssn-ignored(filer check only)tips-both-spouses/overtime-both-spouses(old joint SSN rule)tips-head-or-spouse/overtime-head-or-spouse(round-1 mask)nonePython test cost (CLAUDE.md "Test design and CI cost"):
Scope. The change adds one test to
policyengine_us/tests/core/test_tip_and_overtime_deduction_properties.pyand widens the draws of the existing ones. That file runs in thecoregroup of Rest Python shard 2 (REST_PYTHON_SHARD_2_GROUPSin the Makefile). It adds no job, group or worker.Why Python. YAML can't express the new test: it compares the model with itself on 162 paired returns in six variants. It builds one batched simulation on the shared read-only
system, like the existing tests.Local measurements (macOS; pytest's own time, which leaves out the import; wrapper wall times include heavy-lock queue waits, so they are not reported):
5b3a4cf8d7(see note)dcf7e5b906Note: the baseline ran in Deny tips and overtime deductions to separate filers and phase them out in whole $1,000 steps #10393's worktree. That worktree was clean at
5b3a4cf8d7when this work began, and its 4-test count matches that commit's file; this PR's heads have 5 tests. The runner's summary header recorded this PR's worktree head instead (a runner bug, since fixed). Deny tips and overtime deductions to separate filers and phase them out in whole $1,000 steps #10393's worktree no longer exists, and a host reservation blocked a rerun on main, whose copy of the file is identical.The final head
bbf9b859e1only changes the guards' arithmetic over those 162 returns. Its run under thenonemutant took 5.10 s with no memory measurement. Another timed run was blocked by a host reservation, so no peak RSS was taken at that head.Linux CI before. Deny tips and overtime deductions to separate filers and phase them out in whole $1,000 steps #10393's run 39876196022: the
coregroup ran 1,784 tests in 52:49 wall time with an 11.54 GB peak (11,268,120 KiB,rest-python-core-resources.txt). The four property tests took 0.28 + 0.26 + 1.82 + 0.86 s (rest-python-core.xml).Linux CI after. This PR's own run will report the same group in its
rest-python-test-timings-shard-2artifact (rest-python-core-resources.txt,rest-python-core.xml) when it completes. The first ready run, 39945560511, tests a merge with main atae34b9d4a5, three commits behindc5837fb00b; none of them touches tips, overtime, SSNs or tax-unit roles.Impact
These are real microsimulation runs on the default dataset for 2025 and 2026. They compare main at the merge base
647fec60f9(which includes #10393) with this PR's headdcf7e5b906. The runs went one at a time under the shared heavy-job lock (hubimpact_queue.sh). Sums are weighted; records are tax units for the deductions and income tax, and households for net income. The later headbbf9b859e1changes only the property test.tip_income_deductionovertime_income_deductionstate_income_taxincome_taxhousehold_net_incomehousehold_benefitsThe two fixes move the deductions in opposite directions, and the code fixes the direction of each. Where the old SSN rule held, the new rule can only remove dependents' amounts. Where it failed, the old deduction was zero. Both deductions are non-decreasing in the amount counted.
Federal income tax falls by $80.8M (2025) and $84.0M (2026), net. State income tax moves through conformity:
An earlier run compared #10393's head
5b3a4cf8d7with this PR's first head40c97f8733, before the change from the head-or-spouse mask to the dependent mask. It matched this one to the dollar on every output. The default dataset supplies tax-unit roles but no explicit dependent flags, so on it the two masks agree.Comparison files:
fixes/10396-impact/(compare.<year>.json,by_state.<year>.json,heads.txt) on the hub host.Downstream
ga_cash_tip_exclusionandga_qualified_overtime_exclusioncompute their own amounts. They still sum dependents' tips and overtime, a sibling issue outside this PR (addressed separately in Leave dependents' tips and overtime out of Georgia's HB463 exclusions #10405).*_ssn_requirement_metnames among its inspected constant-membership formulas (test_support/microcosm_build/us_batched_target_materialization.py). These formulas now aggregate person-level variables instead of comparing card types directly, so its inspection may need the new*_eligible_personnames when it next bumps policyengine-us.🤖 Generated with Claude Code