Repository navigation
Count Form 4835 farm rent in net investment income, and limit its losses under section 461(l) - #10076
Conversation
Form 8960 line 4a takes Schedule 1 line 5, which includes Schedule E line 40 (Form 4835 net farm rental income or loss). Form 4835 is only for a rental activity under the passive activity rules, so it is rent under 26 USC 1411(c)(1)(A)(i). Add farm_rent_income to the NII sources, with worked Form 8960 tests and the dependent invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its federal target was the policyengine-us 2.37.2 evidence value. The -2,446.59 Form 4835 loss now reduces net investment income as it already reduced AGI, so NIIT falls by 3.8% x 2,446.59. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Section 1411(c)(1)(B) allows only deductions allowed by chapter 1, and section 461(l) disallows business losses beyond business income plus the threshold. NII added Schedule E rental, Form 4835, estate and passive pass-through losses in full while AGI capped them. Add excess_business_loss (Form 461 line 16) and net_investment_income_excess_business_loss, the part of it that falls on losses NII counts (all of it when they are the only business losses, a proportional share otherwise), and add it back in NII. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NII-follows-estate-income property now subtracts the estate losses that section 461(l) disallows, computed independently from the leaf inputs; the two edge rows with a 400,000 estate loss exercise it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Schedule K-1 (Form 1041) box 14 code H adjustment is positive only for section 1291 fund, CFC and QEF distributions that are NII but not taxable income (2025 Form 1041 instructions, Beneficiary reporting), so it is separate line 7 income and never takes a Part III loss out of NII. Count the whole Part III loss as an NII loss, add the review's counterexample (NIIT 29,906), and label the mixed-loss split provisional. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The authoring session hands this PR to the US + core hub, which adopted it and sent a cloud session (brief 888) to merge
Resolving the conflicts#9908 added
Keeping this PR's Single, 2025,
This session ran no model tests on the resolution, to leave memory free for the Microcosm release booked on this host ( Owed from review round 4None of these blocks landing, and all three fit in the merge:
Other checks on current
|
|
Picking this up for the US hub. I'll post a hand-off comment here when it's ready. Generated by Claude Code |
Main gained excess_business_loss and net_business_loss (#9908) and deducts each Schedule C category's loss on its own in limited_business_loss (#9995). Take main's versions of both files and keep main's four excess_business_loss cases beside this branch's five. The NII add-back drops this branch's section 461(l) helper, which netted the two Schedule C categories, and takes the gross loss as allowed plus disallowed. A new case with an SSTB loss and a non-SSTB profit shows the difference: 131,166.67, not 143,090.91. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add an edge row pairing a binding section 461(l) cap with a positive code H adjustment, so netting code H against the estate loss fails the property test (it passed before). Name the only positive code H case in the adjustment's documentation, and call the NII share provisionally proportional in the reference helper's docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Picking this up for the US hub, continuing an earlier session's work on this brief (that session pushed nothing). I'll post a hand-off comment here when it's ready. Generated by Claude Code |
|
Ready for hub review Head: What was asked and how it was resolved
Readings (judgment calls; I couldn't fetch the sources)The network policy refuses irs.gov, web.archive.org, law.cornell.edu, govinfo, uscode.house.gov and ecfr, so I couldn't read IRC 1411(c) or the Form 8960 line 4a and 4b instructions myself. The PR rests on these readings from the authoring session's quotes (issue #10075):
The hub should check them against the sources before landing. The add-back follows the statute (1411(c)(1)(B)) over Form 461's routing to Schedule 1 line 8p, which line 4a doesn't take. That is a statute-versus-form choice that Tests run (this head, one file per process)
Hand computationsNIIT = 3.8% × min(NII, MAGI − threshold).
All match the YAML. The 313,000 threshold is the repo's 2025 Mutants (each in its own process, restored after)
Invariants and property testsThe body states six invariants. The existing seeded property tests cover invariants 1–5, and the YAML covers invariant 6 at the boundary. I added no Hypothesis file, because each example would build a model. The seeded vectorized population already samples signed farm rent for every member, and its edge rows make section 461(l) bind. CIPull request run https://github.com/PolicyEngine/policyengine-us/actions/runs/41969669487 at Other judgment calls
Proposed follow-upRecord the section 461(l) NII add-back as a statute-over-form choice. Form 461 routes the excess to Schedule 1 line 8p, which Form 8960 line 4a doesn't take, and the model adds it back under 1411(c)(1)(B). Read both texts, add the register entry, and mark Generated by Claude Code |
…icts Point the add-back entry at the statute reading this PR implements, with the Form 461 and Form 8960 lines it departs from. Add the open question of how to split the excess, and the 2025 line 4a tip that the statute overrides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hub review in progress Review head: So far:
Mutants and CI still running. Generated by Claude Code |
|
Ready for hub review Independent review, round 5, at the head above. The head hasn't moved since Summary. Every value I computed by hand matches the model at this head: the body's table, the 131,166.67 case, PolicyBench 120's −92.97, and nine more situations of my own (dependents, a joint return, a capital loss, Minnesota, a passive loss inside a netted partnership amount, code H). I couldn't reach any primary source. This VM's network policy refuses irs.gov, law.cornell.edu, govinfo.gov, uscode.house.gov, ecfr.gov and revisor.mn.gov, through both the proxy (HTTP 403 on CONNECT) and the fetch tool (DNS refused). So every legal reading below is unverified, and I quote no law. FindingsP1. The body's
|
| Row | Body says main |
main actually gives |
Head gives |
|---|---|---|---|
| Single, wages 300,000, interest 500,000, Form 4835 −600,000 | 0 | 10,906 (NII 500,000; 3.8% × min(500,000, 287,000)) | 7,106 |
| Wages 600,000, interest 200,000, Schedule C −300,000, Form 4835 −300,000 | 0 | 7,600 (NII 200,000; 3.8% × 200,000) | 1,653 |
Command: the same 2025 single-filer situation, run through Simulation(situation=...) at origin/main (a second worktree, with PYTHONPATH pointing at it) and at the head:
main B 4835 -600k: NII 500000.0 NIIT 10906.0
head B 4835 -600k: NII 187000.0 NIIT 7106.0
main D SchC/4835: NII 200000.0 NIIT 7600.0
head D SchC/4835: NII 43500.0 NIIT 1653.0
This matters beyond the table. For a Form 4835 loss, this PR lowers NIIT against main, because main ignored the loss, and it raises NIIT only for Schedule E, estate and passive losses. A reader of the table would expect the opposite sign for farm landlords with losses, and that changes what the impact comparison should look for.
Replacement for those two cells: 10,906 and 7,600. Add under the table: "On main, NII leaves out Form 4835 amounts entirely, so a Form 4835 loss never reduces NII there. For Form 4835 losses this PR lowers NIIT against main; for Schedule E rental, estate and passive losses beyond the section 461(l) limit it raises it."
P2. The min_ on the passive loss is untested; deleting it passes every test
net_investment_income_excess_business_loss.py:44-47. Mutant (b) replaces the min_(...) with max_(0, -person("passive_partnership_s_corp_income", period)). It passes all 7 add-back cases, all 32 NIIT cases, MN 3, excess_business_loss.yaml 9, PolicyBench 8, dependent invariants 14 and estate invariants 22.
The property draws can't reach it. test_dependent_net_investment_income_invariants.py:118-121 draws passive income as partnership × {0, 0.5, 1}, so it never exceeds the netted partnership loss and never has the opposite sign.
Failure scenario: the head has passive −1,000,000 inside partnership_s_corp_income −100,000 (nonpassive +900,000), plus a rental loss of −600,000. Gross losses are 700,000 and the excess is 387,000. Without the min_, the numerator becomes 1,600,000 and the share 2.29, so the add-back is 884,571.43, more than the excess (invariant 5 broken). At the head the model gives:
G passive -1M, total -100k, rental -600k: lim 313000.0 exc 387000.0 add 387000.0 NII -313000.0
Add this case to net_investment_income_excess_business_loss.yaml:
- name: A passive loss counts only as far as the netted partnership loss
# Passive -1,000,000 within partnership/S corporation income of -100,000
# (nonpassive +900,000), and a rental loss of 600,000. Section 461(l) sees
# losses of 100,000 + 600,000 = 700,000 and an excess of 700,000 - 313,000
# = 387,000. The NII losses are 600,000 + min(1,000,000, 100,000) = 700,000,
# so the whole excess is the add-back. Without the cap the share would be
# 1,600,000 / 700,000 and the add-back 884,571.43.
period: 2025
input:
people:
person1:
age: 50
is_tax_unit_head: true
employment_income: 300_000
taxable_interest_income: 900_000
partnership_s_corp_income: -100_000
passive_partnership_s_corp_income: -1_000_000
rental_income: -600_000
tax_units:
tax_unit:
members: [person1]
households:
household:
members: [person1]
state_code: TX
output:
excess_business_loss: 387_000
net_investment_income_excess_business_loss: 387_000
net_investment_income: -313_000At this head the case passes, and under mutant (b) it fails: net_investment_income_excess_business_loss@2025: [884571.44] differs from 387000.0. Optionally, also draw passive income independently of the partnership amount in _draw_amounts, so the property test reaches the cap.
P2. Minnesota's add-back path is untested
Mutant (c) changes mn_niit.py:27 to net_investment_income - net_investment_income_excess_business_loss (Minnesota ignores the add-back), and it passes every file listed above. mn_niit.yaml's only new case has a positive Form 4835 amount, so section 461(l) never binds. Add:
- name: Minnesota net investment income takes the section 461(l) add-back
# Interest 2,000,000 and a Form 4835 loss of 600,000. Section 461(l) allows
# 313,000 of the loss: NII 2,000,000 - 600,000 + 287,000 = 1,687,000;
# 1% x (1,687,000 - 1,000,000) = 6,870. Without the add-back: 4,000.
period: 2025
absolute_error_margin: 0.01
input:
people:
person1:
age: 60
is_tax_unit_head: true
taxable_interest_income: 2_000_000
farm_rent_income: -600_000
tax_units:
tax_unit:
members: [person1]
households:
household:
members: [person1]
state_code: MN
output:
net_investment_income: 1_687_000
mn_niit: 6_870The case passes at the head, and under mutant (c) it fails: mn_niit@2025: [4000.] differs from 6870.0. On main, mn_niit is 10,000, because NII is 2,000,000 there.
Whether Minnesota conforms to section 461(l) for its NIIT base is a reading I couldn't check (revisor.mn.gov is unreachable). The hub should confirm it before the case asserts it.
P3. A dependent's loss in the add-back is caught only by the property tests
Mutant (a) drops not_dependent * at net_investment_income_excess_business_loss.py:49. It passes all YAML and fails 6 property tests in test_dependent_net_investment_income_invariants.py (test_dependents_income_stays_off_filer_form_8960, test_net_investment_income_matches_reference and test_section_461l_addback_bounds, in 2024 and 2025). That is enough to kill it. A YAML case would still document the rule next to the variable. The model gives, at the head, a filer with wages 300,000, interest 500,000 and rental −600,000, plus a dependent with Form 4835 −1,000,000: excess 287,000, add-back 287,000, NII 187,000, NIIT 7,106 (F dependent 4835 -1M in my probe).
P3. Invariants 5 and 6 hold only up to float32 rounding
The body says "These hold for every input". limited_business_loss + excess_business_loss is summed in a different order from the numerator, so in float32 the share can land above 1. Witness: a single filer in 2025, self_employment_income 12,345.67, interest 1,000,000,000, swept over rental_income from −1,000,000 to −900,000,000 in 5,000 steps:
se=12345.67: rows add>exc: 371 of 5000; max add-exc 16.0; worst row rental=-134618128.00 excess=134292768.00 addback=134292784.00; max |limited+excess-gross| 16.0
se=98765.43: rows add>exc: 374 of 5000; max add-exc 16.0; ... max |limited+excess-gross| 32.0
That is one float32 step at that size, and the property tests' rtol=1e-6 absorbs it. Either clip the share in code:
investment_share = min_(
1,
np.divide(
investment_loss,
business_loss,
out=np.zeros_like(investment_loss),
where=business_loss > 0,
),
)or change the body's line to "These hold for every input up to float32 rounding (a few dollars on nine-figure losses)."
P3. Other stale or overstated body statements
- "The existing property tests … check invariants 1 to 5." Their draws set no
self_employment_incomeorsstb_self_employment_income, and the passive draw never binds the cap (above). Replace with: "… check invariants 1 to 5 for rental, Form 4835, estate and partnership losses. The Schedule C category split is checked only by the YAML case, and the passive-loss cap by [the new YAML case]." - "The property tests keep their population size …" Not quite: the dependent file gains 3 edge rows and the estate file 1. Replace with "… gain four edge rows, one input column, two output variables and one test function."
- "The YAML cases check invariant 6 at the boundary." The five new
excess_business_loss.yamlcases sit on both sides of the threshold, not at it. Replace with "on both sides of the threshold". - The 9th table row's MAGI of 1,087,000 isn't explained in the body. It comes from main's
niit_magiadding a positive code H (the YAML comment says so). Add "(MAGI adds the positive code H)". - "Mutants" list: add the three in this review and what kills them (table below).
P3. The provisional split uses netted partnership amounts (methodology note, no change requested)
Numerator and denominator agree, because both see each person's partnership_s_corp_income netted as main's limited_business_loss sees it. But when a passive loss sits beside nonpassive income in one person's amount, the split doesn't follow the gross losses. Witness, 2025 single: wages 500,000, interest 900,000, partnership_s_corp_income −100,000 with passive −1,000,000, Schedule C −900,000. The model gives excess 687,000, add-back 68,700, NII −31,300 and NIIT 0. A proportional split over gross losses (1,000,000 of 1,900,000) would give 361,578.95, NII 261,578.95 and NIIT 9,940. The excess itself is the same either way, since it depends only on the net. This belongs with the open question us:niit-excess-business-loss-split, not as a blocker.
Mutants (each in its own process on a scratch worktree; all three worktrees restored, git status clean)
Files run per mutant: net_investment_income_excess_business_loss.yaml, net_investment_income_tax.yaml, mn_niit.yaml, excess_business_loss.yaml, policybench_ira.yaml, test_dependent_net_investment_income_invariants.py, test_estate_income_invariants.py.
| Mutant | Result | Killed by |
|---|---|---|
| Hand-off 1: estate loss netted with code H | killed | add-back YAML 1/7 (code H case), NIIT YAML 1/32, test_net_investment_income_adds_estate_income_and_code_h [2024, 2026] |
Hand-off 2: denominator net_business_loss |
killed | add-back YAML 1/7 (Schedule C category case) only |
Hand-off 3: drop farm_rent_income from sources |
killed | NIIT YAML 5/32, mn_niit.yaml 1/3, PolicyBench 1/8, dependent invariants 4/14 |
(a) investment_loss without not_dependent |
killed | dependent invariants 6/14 only; no YAML |
(b) passive_loss without the min_ |
survives | nothing |
(c) Minnesota: mn_niit subtracts the add-back |
survives | nothing |
(d) denominator limited_business_loss alone |
killed | add-back YAML 6/7, NIIT YAML 4/32, dependent invariants 6/14, estate invariants 4/22 |
The hand-off's counts for its three mutants are right.
Checked and sound
-
The merge.
limited_business_loss.py,excess_business_loss.pyandnet_business_loss.pyare identical toorigin/main(git diff --quiet origin/main pr-10076 -- <file>). Comparing the PR's net change atd1dec4b4cf(against065d0e9560) with the one at4e5da2d96b(against0315452583):- dropped from the diff:
limited_business_loss.py,excess_business_loss.py; - added:
estate_income_net_investment_income_adjustment.py(documentation); - patch changed:
net_investment_income_excess_business_loss.py, its YAML,excess_business_loss.yaml,test_dependent_net_investment_income_invariants.pyandtest_estate_income_invariants.py; 5ea9e6f718changes onlysource_conflicts/candidates.yaml.
- dropped from the diff:
-
The add-back, derived from main's variables (I couldn't read Form 461 itself). Let L be the head and spouse's gross business losses, I their gross business income (each Schedule C category on its own) and T the threshold. Then
limited = min(L, I + T).excess = max(0, net_loss − T), wherenet_loss = L − Ibecausetotal_self_employment_incomeadds the two categories. When L > I + T,limited + excess = (I + T) + (L − I − T) = L; otherwise excess is 0 and limited is L. So the denominator is L exactly. Every numerator term (rental, Form 4835, estate, passive capped at the netted partnership loss) is a per-person, non-dependent piece of L, so 0 ≤ share ≤ 1. Then add-back = excess × share ≤ excess, and since excess ≤ L, add-back ≤ the numerator. Category by category:- an SSTB or Schedule C loss is in L and not in the numerator;
- a capital loss is in neither (
I capital lossprobe: add-back 287,000, NII 184,000, NIIT 6,992, matching my hand value); - a dependent is in neither (probe F);
- a passive loss offset by nonpassive income in the same person is in neither (probe H: no excess, NII −100,000).
-
Years.
gov.irs.ald.loss.maxis infinite through 2020 (CARES Act), so the add-back is 0 before 2021 and the register entry'sfrom: 2021fits. -
Hand computations, all matching the model at this head (probe output above and in the interim comment):
- Body rows: 760; 1,140; 950; 190; MN 1,000; 7,106; 7,106; 1,653; 29,906.
- The Schedule C category case: 787,000 × 200,000 / 1,200,000 = 131,166.67. The old netting gives 787,000 × 200,000 / 1,100,000 = 143,090.91.
- PolicyBench 120: 40,423.17578125 − 40,330.20703125 = 92.97, matching 3.8% × 2,446.59 = 92.970.
- The defect reproduces at main: wages 190,000 and Form 4835 30,000 give NII 0 and NIIT 0 on main, against 30,000 and 760 at the head.
-
Conflict entries.
us:niit-excess-business-loss-addback: the example (NII 187,000, NIIT 7,106; in full, NII −100,000 and no NIIT) matches probe C.us:niit-excess-business-loss-split: 143,500 proportional, 287,000 NII-first, 0 other-first; NII 43,500 / 187,000 / −100,000, all correct by hand.us:niit-form-8960-line-4a-tip: 760 against 0 matches probe A.- No
# Source conflictmarkers were added, which is right for a ruling PR under CONTRIBUTING step 1. test_source_conflicts.py: 103 passed.
-
Readers of
net_investment_income:net_investment_income_tax,mn_niit, the Biden budget 2025 reform'snet_investment_income_tax, ands770_nii_base. The only reader ofgov.irs.investment.income.sourcesisnet_investment_income.- The first three should take both farm rent and the add-back: all read NII as the section 1411 base, Minnesota subject to the conformity question above.
s770_nii_basetakes both. Its active residual (s770_active_business_income) reads only partnership amounts, so nothing counts twice.- The new path
net_investment_income → net_investment_income_excess_business_loss → {excess_business_loss → net_business_loss, limited_business_loss}reads only income inputs andother_net_gain, and nothing on it reads NII. Every probe, including Minnesota and S.770 with the reform on, computed without a cycle error.
-
Property references. Both are independent numpy code from leaf inputs and import nothing from the formula. They mirror its structure, including the passive cap and the proportional rule, so they check the implementation against the stated rule, not the rule against the law. The draws reach a dependent's loss (random rows plus the HoH edge row) and a binding limit with both kinds of loss (the joint edge row with a nonpassive partnership loss). They don't reach a Schedule C category split or a binding passive cap.
-
Tests at this head, one file per process:
- YAML:
excess_business_loss.yaml9,net_investment_income_excess_business_loss.yaml7,net_investment_income_tax.yaml32,mn_niit.yaml3,policybench_ira.yaml8; - Python:
test_dependent_net_investment_income_invariants.py14,test_estate_income_invariants.py22,test_source_conflicts.py103. - All passed.
- YAML:
-
Scope. Nothing under
.github/ortests/policy/baseline/partners/. No open PR touches the same files exceptsource_conflicts/candidates.yaml, which many open PRs append to (Follow state statutes where forms differ: NM medical exemption, VT CDCC, NY/NYC household credit, PR EITC #10300, Follow the statute for the NYC school tax rate reduction, IN supplemental homestead, VA 529 and WA senior/disabled property tax exemption #10330, Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction #10093 and others), so expect a routine text conflict there. Fill IRS SOI uprating series gaps with Publication 1304 actuals #10128 and Extend IRS SOI uprating series to tax year 2023 #10132 touchfarm_rent_incomecalibration and tests only, with no overlap. -
Body lines.
impact:exists and is pending, as the brief expects.axiom:exists and I left it alone. The changelog fragmentchangelog.d/niit-farm-rent.fixed.mdis top-level and matches the change. -
Baseline movement. Yes, the diff moves baseline outputs:
- NII, NIIT and
mn_niitfor every unit with nonzero head or spousefarm_rent_income; - the same three for every unit whose
excess_business_lossis positive and which has rental, Form 4835, estate or passive losses.
The comparison should report the count and NIIT change for each group separately, with sign, because the two move in opposite directions for Form 4835 losses (P1).
- NII, NIIT and
-
CI. Pull request run https://github.com/PolicyEngine/policyengine-us/actions/runs/41978104913 at this head: success, 36 of 36 jobs passed (completed 07:07Z on 11/05).
Follow-ups (identical on main or outside this PR)
- S.770's active base takes no share of the section 461(l) excess. With the active-business option on (2026, single): wages 300,000, interest 900,000, nonpassive partnership −600,000 and rental −600,000. The model gives excess 944,000, add-back 472,000, NII 772,000,
s770_active_business_income−600,000 in full, and a combined NII base of 172,000. Main gives 300,000, −600,000 and 0. By the PR's own proportional rule, the active loss would be cut to −128,000 and the combined base would be 644,000. Default-off reform; the PR moves it toward the rule, not away. - No later-year carryover. The model has no federal NOL variable, so section 461(l)'s disallowed excess never returns in a later year in AGI or NII. I couldn't read Treas. Reg. 1.1411-4(h) or section 461(l)(2) to say whether that carryover reaches the section 1411 NOL. The body should say the treatment is single-year: "The model carries no disallowed excess into later years, in AGI or NII."
- Netted partnership amounts in main's
limited_business_loss(P3 above): a passive loss and nonpassive income of one person cancel before section 461(l) sees them.
What I couldn't check
- Every primary source. I couldn't confirm or refute any of these: 26 U.S.C. 1411(c)(1)(A)(i), (c)(1)(B), (c)(2), (c)(6) and 461(l); the 2025 Form 4835 title and purpose; the 2025 Form 8960 instructions (page 1, the line 4a tip, line 4b's list, line 7's list); 2025 Form 461 line 16; Treas. Reg. 1.1411-4(f)(2)(ii), (f)(2)(v) Example 1 and (h); Minn. Stat. 290.033. I also couldn't search eCFR for a regulation naming section 461(l).
- The line 4a tip. I can't confirm the hub's quote. If it reads as quoted ("not subject to self-employment tax under section 1401(b)", entered as a negative on line 4b), then taken literally it would also remove ordinary Schedule E rent, which self-employment tax doesn't reach either. That would undo line 4a's rental inclusion. So, on the hub's quotes alone, I agree the narrower reading tied to 1411(c)(6) is the coherent one. That is an inference from the quoted words, not a reading of the text.
- Population impact, which needs the hub's data.
Generated by Claude Code
…-back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR Review: Count Form 4835 farm rent in net investment income, and limit its losses under section 469SummaryThe PR makes two changes to
I read the law and the forms first. The code follows them. Farm rent is NII.
Section 469 and Form 4835.
Section 461(l) in NII.
Minnesota. Minn. Stat. 290.033(a) uses "the meaning given in section 1411(c)", with two exceptions: class 2a gains and opportunity zone gains. Neither touches rent or section 461(l), so Line 4a tip. The 2025 tip under line 4a says income "that is not subject to self-employment tax under section 1401(b) is also entered as a negative on line 4b". Taken literally, that would also remove ordinary Schedule E rent, and it contradicts page 1 and 1411(c)(6). It is new in 2025; the 2024 instructions have no such sentence. The PR follows the statute and records the conflict as I hand-checked 18 expectations, and all match. Every new term is pinned by a test. CI is green at the head (36 of 36), and the branch is level with main and merges cleanly. I have no critical or should-address findings. The suggestions cover documenting the post-section 469 input convention, the statutory wording, a companion case for the rulespec-us issue, and two follow-ups. CriticalNone. Should addressNone. Suggestions
Verified correct
Axiom line assessment
Its review finding states the general section 1411(c)(1)(B) rule, so it covers this head's add-back. Suggestion 3 asks for a non-farm companion case and a note on the open split. CI statusAll 36 checks pass at Branch status
Verdict: APPROVE |
DTrim99
left a comment
There was a problem hiding this comment.
Approving: 0 critical, 0 should. Form 4835 rent is passive rent under § 1411(c)(1)(A)(i)/(c)(2)(A), farm_rent_income is already the post-§ 469 amount, and the § 461(l) add-back is a recorded statute-first choice. Every new term is pinned; CI 36/36. Full report in the comment above.
…ment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
US + core hub merge audit, us#10076 at
🤖 Generated with Claude Code |
Fixes #10075.
What changes
net_investment_income, the base of the net investment income tax (NIIT) and Minnesota's NIIT, now countsfarm_rent_income, the Form 4835 net farm rental income or loss that filers carry to Schedule E line 40. AGI and NIIT MAGI already counted it, so on main a farm landlord's rent raised MAGI but never NII, and a Form 4835 loss never reduced NII.net_investment_income_excess_business_loss, computes the part of the section 461(l) excess business loss that falls on those losses, and NII adds it back.Why (sources)
rental_income. 2025 Instructions for Form 8960: https://www.irs.gov/pub/irs-prior/i8960--2025.pdf#page=8source_conflicts/candidates.yamlrecords this asus:niit-form-8960-line-4a-tip.limited_business_loss. On main, NII added the same losses in full: a single filer with $300,000 of wages, $500,000 of interest and a $600,000 rental loss had NII of −$100,000 and no NIIT. The limit gives NII of $187,000 and NIIT of $7,106. Form 461 line 16 puts the add-back on Schedule 1 line 8p, which Form 8960 line 4a doesn't take. Line 7 names line 8z but not line 8p, and it also takes modifications the form doesn't specify. The model follows the statute and adds the amount back, andsource_conflicts/candidates.yamlrecords this asus:niit-excess-business-loss-addback.The change
gov.irs.investment.income.sourcesaddsfarm_rent_incomeafterrental_income, andnet_investment_income_excess_business_loss. NII still sums sources over the head and spouse only (tax_unit_non_dep_add), so a dependent's farm rent stays on the dependent's own Form 8960.net_investment_income_excess_business_losstakesexcess_business_loss(Form 461 line 16, on main since Add the federal excess business loss back to Massachusetts income from 2022 #9908) times the share of gross business losses that NII counts. NII counts Schedule E rental, Form 4835 and Schedule E Part III estate and trust losses, and the passive part of partnership and S corporation losses. The variable addslimited_business_lossandexcess_business_lossto reach gross business losses, which counts each Schedule C category's loss on its own, as main has since Offset SSTB and non-SSTB Schedule C results in federal AGI #9995.farm_rent_incomegains documentation and a Form 4835 reference.estate_income_net_investment_income_adjustmentdocumentation now names the only positive code H case, a section 1291 fund, CFC or QEF distribution that is NII but not taxable income, and cites the 2025 Form 1041 instructions (https://www.irs.gov/pub/irs-prior/i1041--2025.pdf#page=38).This PR leaves
limited_business_lossandexcess_business_lossas main has them.Decisions
rental_income. The model has no Form 8960 line 4b input for nonpassive rentals, so it counts all Schedule E Part I rents and all Form 4835 amounts. A line 4b rental input would serve both, and the EITC passive basket needs the same split (EITC investment-income test after #9572: dependents' amounts, passive income that is also earned income, and farm rental #9635), so a later PR can add it.loss_ald, and NII now uses the same amount.source_conflicts/candidates.yamllists this as the open questionus:niit-excess-business-loss-split. Taking NII losses first or non-NII losses first would each change one line ininvestment_share.partnership_s_corp_incomenet, aslimited_business_lossdoes, so a passive loss and nonpassive income of one person offset each other before the split.Worked examples (hand-computed from the Form 8960 lines)
NIIT = 3.8% × min(max(0, NII), max(0, MAGI − threshold)); thresholds $200,000 single and $250,000 joint.
mainmainleaves Form 4835 amounts out of NII. This PR therefore raises NIIT for Form 4835 income and lowers it for a Form 4835 loss. It also raises NIIT for Schedule E rental, estate and passive losses beyond the section 461(l) limit.The add-back variable's own cases include one with a non-SSTB Schedule C profit of 100,000, an SSTB loss of 1,000,000 and a rental loss of 200,000. Gross losses of 1,200,000 exceed the 413,000 limit by 787,000, so the add-back comes to 787,000 × 200,000 / 1,200,000 = 131,166.67. Netting the two Schedule C categories first would give 143,090.91.
Snapshot update: PolicyBench scenario 120
policybench_ira.yamlscenario 120 (single, TY2026, Connecticut) hasfarm_rent_income−2,446.59. Its federal target comes from the policyengine-us 2.37.2 evidence value (#10032), not an outside oracle. NII falls from 4,300 to 1,853.41, and NIIT from 163.40 to 70.43 (3.8% × 2,446.59 = 92.97). AGI already included the loss, so the MAGI limb doesn't bind. The Connecticut target stays the same. A comment in the YAML gives the old value and the reason.Invariants
These hold for every input up to float32 rounding (a few dollars on nine-figure losses):
farm_rent_income, of either sign. When the limit binds, NII takes only the allowed part of the loss, through the add-back.net_investment_income_excess_business_loss≤ min(excess_business_loss, NII losses), with equality to the excess when NII losses are the only business losses.limited_business_loss+excess_business_lossequals gross business losses.The existing property tests in
test_dependent_net_investment_income_invariants.pyandtest_estate_income_invariants.pycheck invariants 1 to 5 for rental, Form 4835, estate and partnership losses. They compare a seeded random population plus edge rows, one vectorized simulation per scenario, with an independent numpy reference from leaf inputs. They draw farm rent signed for every member, dependents included. Edge rows make section 461(l) bind for a single Form 4835 loss, a joint return with a nonpassive partnership loss, a dependent with a large loss, and an estate loss with a positive code H adjustment. YAML cases check the Schedule C category split, the cap on the passive loss, and invariant 6 on both sides of the threshold.Tests
net_investment_income_tax.yamlandmn_niit.yamlhold the worked examples, andmn_niit.yamladds a case with the section 461(l) add-back;excess_business_loss.yamlholds main's 4 cases and this PR's 5;net_investment_income_excess_business_loss.yamlhas 9 cases, including the Schedule C category, passive-cap and dependent cases.test_dependent_net_investment_income_invariants.pyaddsfarm_rent_incometo its signed inputs and reference sum.test_estate_income_invariants.pyexpects the add-back on its cap-binding rows and gains the code H edge row.net_business_lossin place of gross losses fails the Schedule C category case;farm_rent_incomefrom the sources fails 5 of 32 NIIT cases;not_dependentfrom the NII losses fails 6 dependent property tests;mn_niitignore the add-back fails the Minnesota add-back case.0cc5041957(https://github.com/PolicyEngine/policyengine-us/actions/runs/42071455256), concluded success: all 36 jobs passed.CI cost: the property tests keep their number of simulations and gain four edge rows, one input column, two output variables and one test function. The PR also adds 24 YAML cases, each with its own simulation. We ran no before-and-after timing.
Impact
We estimate that the net investment income tax rises $145.1 million in 2026 on the default dataset, comparing main at the merge base
0315452583with this branch at6a95037abe, before its merge of main ate53b426082. Each row groups tax units by the sign of their Form 4835 amount.Household net income falls by the same $145.1 million. AGI,
mn_niitand benefits don't move, and state income tax rises $0.04 on one record.impact: we estimate that the net investment income tax rises $145.1 million in 2026, up $153.7 million on positive Form 4835 amounts and down $8.6 million on Form 4835 losses. Household net income falls by the same amount, and AGI,
mn_niitand benefits don't move.axiom: TheAxiomFoundation/rulespec-us#1643 queued
🤖 Generated with Claude Code