Skip to content

Leave dependents' losses out of the D.C. self-employment loss addition split - #9857

Merged
MaxGhenis merged 4 commits into
mainfrom
fix-dc-se-loss-addition-filers
Oct 9, 2026
Merged

MaxGhenis merged 4 commits into
mainfrom
fix-dc-se-loss-addition-filers

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What this fixes

dc_self_employment_loss_addition (Schedule I, Calculation A, line 6) adds back filers' self-employment losses over $12,000, and on a joint return splits the addition between the spouses in proportion to their own losses. Its loss total summed every tax-unit member, dependents included. Since #9647, loss_ald leaves out a dependent's losses, but this formula still counted them, which caused two errors:

  1. The addition itself. The addition is max(0, min(loss total, business losses in loss_ald) − 12,000). When filers had other business losses (rental, farm, partnership, estate), a dependent's self-employment loss raised the addition, for any filing status.
    Example: a head of household with a $5,000 self-employment loss, a $30,000 rental loss, and a dependent with a $20,000 self-employment loss. Main gives $13,000; the head's own $5,000 is under the threshold, so the addition is 0.
  2. The split between spouses. Example: a head with a $20,000 loss, a spouse with none, and a dependent with a $20,000 loss. The addition is $8,000, but main gave the head $4,000 (20,000 / 40,000 of it).

The loss total and the split now count the head and spouse only. I also corrected the comment on the loss_ald cap: what remains after the capital-loss parts is all business losses, not self-employment losses only.

Tests

  • dc_self_employment_loss_addition.yaml, three new cases (inserted after the first case). Main gives a different result in each; all pass here:

    case main this branch
    joint, dependent's loss [4,000, 0, 0] [8,000, 0, 0]
    head of household with a rental loss [13,000, 0] [0, 0]
    both spouses with losses, plus a dependent [8,000, 4,000, 0] [12,000, 6,000, 0]
  • tests/core/test_dc_self_employment_loss_addition_invariants.py (new property test, below).

Invariants

The test draws a seeded random population of 200 D.C. tax units (single, head of household, joint, joint with dependents):

  • filers have wages, and self-employment and rental income of either sign;
  • dependents have self-employment income of either sign;
  • losses stay below the section 461(l) limit.

It runs the population as drawn and again with dependents' self-employment income set to zero. The invariants:

  1. Dependents' self-employment income never changes anyone's addition.
  2. Dependents get no addition; no addition is negative.
  3. The tax unit's addition equals the head's and spouse's self-employment losses less $12,000, floored at zero. This is computed independently of the model; a filer's rental loss or a dependent's loss leaves it unchanged.
  4. A joint return splits that amount in proportion to each spouse's own loss.

On main, invariants 1, 3 and 4 fail (23 of 528 people, 19 of 200 tax units, 19 of 260 joint filers). Local cost (macOS, on a loaded host, not Linux CI): 25 s wall time including two model builds, peak 1.0 GB. It runs in the core group of make test-other-python-rest.

Test runs: the YAML file and the property test (both directly). The states/dc/tax folder and contrib/dc_single_joint_threshold_ratio.yaml ran one process at a time under the shared heavy lock: still running; results will be added here.

Impact

A real microsimulation on the default dataset, main at the merge base 7b956eb401 against this head 4a616e36cb, for 2025 and 2026. The runs went one at a time under the shared heavy-job lock.

No change in any compared output.

  • dc_agi is $55.62bn (2025) and $58.49bn (2026) on both sides, and dc_income_tax is $3.55bn and $3.68bn.
  • state_income_tax, income_tax, household_net_income and household_benefits are identical in every record.

The change only affects D.C. units where a dependent reports a self-employment loss, and the default dataset doesn't move any of them. For units it does reach, the addition can only fall: the new loss total is never larger than the old one.

Other checks requested for this file

  • Net capital gain distributions inside the capital loss limit in AGI #9788 (merged) also edited this file; it changed only the capital-loss parts subtracted from loss_ald. This branch is on top of it.

  • limited_business_loss: the per-spouse AGI work (branch fix-agi-person-ald-split, not yet a PR) splits loss_ald into limited_business_loss plus the capital-loss deduction. On main, loss_ald − capital_losses_allowed_against_gains − limited_capital_loss, which this formula computes, equals that variable, so it can swap it in when it lands. That branch edits the last case of the test file; the new cases here sit earlier, and a three-way merge of the two versions has no conflict.

  • Threshold (not changed here; D.C. Schedule I line 6: the $12,000 threshold is a business gross-income test, not a loss threshold #9867). The law ties the $12,000 to the business, not the loss.

    • § 47-1805.02(6) requires a D-30 from an unincorporated business "having a gross income of more than $12,000".
    • § 47-1803.02(a)(2)(D) excludes that business's share from D.C. gross income.
    • § 47-1808.01 excludes some businesses from "unincorporated business", including ones with more than 80% personal-service income where capital is not a material income-producing factor.

    So a covered business over $12,000 has its whole loss added back, and any other business has none added back. The model subtracts $12,000 from the loss because it has no gross-receipts input or business classification. Fixing that needs a methodology choice; D.C. Schedule I line 6: the $12,000 threshold is a business gross-income test, not a loss threshold #9867 has the details.

axiom: TheAxiomFoundation/rulespec-us#1504 queued

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 6, 2026 07:11
…n split

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…against filers' own losses

Review r1: a dependent's self-employment loss also raised the addition itself when filers had other business losses. Add a head-of-household case, make the property test independent of the model, correct the loss_ald comment, and update the changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (909176a) to head (4a616e3).
⚠️ Report is 315 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9857   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         1    -3     
  Lines           76        26   -50     
  Branches         2         0    -2     
=========================================
- Hits            76        26   -50     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@DTrim99

DTrim99 commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Leave dependents' losses out of the D.C. self-employment loss addition split

PR #9857 (author: MaxGhenis, head 08ba81c7f6). 4 files, +247/-8.

Summary

dc_self_employment_loss_addition (D-40 Schedule I, Calculation A, line 6) summed the self-employment losses of every tax-unit member, dependents included. Since #9647, loss_ald already leaves dependents' losses out, so main had two errors:

  • The amount. For any filing status, a dependent's loss raised the addition when the filers had other business losses (rental, farm, partnership, estate) that lifted the loss_ald cap.
  • The split. On a joint return, a dependent's loss diluted the head's and spouse's shares.

The PR masks the loss to the head and spouse (is_tax_unit_head_or_spouse), renames se_loss_in_ald to business_loss_in_ald with an accurate comment, adds 3 YAML cases and a seeded property test, and adds a changelog fragment.

The fix is correct under D.C. law:

  • Line 6 adds back losses "reported as a loss on federal 1040 or 1040-SR return".
  • D-40 line 4 is the filer's own federal AGI.
  • A dependent files their own D-40 under the "Dependent claimed by someone else" status, and a parent can bring only a child's interest and dividends onto the parent's federal return (Form 8814).

So a dependent's Schedule C loss never reaches the parents' line 6. The per-spouse split by each spouse's own loss also matches the Calculation J instruction to figure "each person's additions".

I found no critical or should-address issues. The suggestions are a title and PR-body cleanup, optional extra boundary cases, and reference polish. The PR's known limitation, that the $12,000 is really a business gross-income test, is pre-existing, documented, and tracked in #9867.

Source documents

  • 2024 D-40 booklet (the year of the new YAML cases): https://otr.cfo.dc.gov/sites/default/files/dc/sites/otr/publication/attachments/2024_D40_Booklet_011525.pdf
    • Schedule I, Calculation A, line 6 (#page=55): "Pass-through losses from DC unincorporated businesses that exceed the $12,000 threshold (reported as a loss on federal 1040 or 1040-SR return)." The instructions repeat it on #page=56.
    • Line 4 (#page=16): "Federal adjusted gross income. Enter the amount from 1040, 1040-SR, 1040-NR, or 1040-NR-EZ."
    • Filing status (#page=9): "Dependent claimed by someone else. If you are claimed as a dependent on someone else's return, fill in the 'dependent claimed by someone else' oval."
    • Married filing separately on the same return (#page=9): "Before completing Calculation J, and the Form D-40, you will need to figure the following for you and your spouse/registered domestic partner: Each person's federal adjusted gross income; Each person's additions to federal income; Each person's subtractions from federal income; and Each person's deductions."
  • Same line 6 text in the other years:
  • Statute:
  • Federal: 2024 Form 8814, "Parents' Election To Report Child's Interest and Dividends" (https://www.irs.gov/pub/irs-prior/f8814--2024.pdf#page=1). The election covers only interest and dividends, so a dependent's Schedule C stays on the dependent's own 1040.

Critical (must fix)

None.

Should address

None.

Suggestions

  1. Retitle the PR. The fix changes the amount, not only the split.

    • The squash-merge title "Leave dependents' losses out of the D.C. self-employment loss addition split" describes only the joint-return dilution.
    • The new head-of-household case (dc_self_employment_loss_addition.yaml:42-67) has no split. There the fix changes the addition itself, from $13,000 on main to $0.
    • The changelog fragment already says both ("either in the amount added back or in its split between spouses").
    • Fix: retitle to, for example, "Leave dependents' losses out of the D.C. self-employment loss addition".
  2. Fill in or remove the PR-body placeholders.

    • The Tests section says the states/dc/tax folder run is "still running; results will be added here".
    • The Impact section says microsimulation runs are "queued ... results will be added here".
    • CI now covers the first one: 37/37 checks pass, including all state shards. Either paste the 2024/2026 microsimulation deltas or drop the Impact paragraph, so the merged description doesn't promise results it never shows.
  3. Optional extra YAML cases. These are hand-computed for 2024 and reproduced on the PR head and on the merge-base. The last two fail on main; the first anchors the threshold beside them.

    Case Inputs PR Main
    Threshold boundary with a dependent joint; head wages 60,000, SE -12,000; spouse wages 40,000; dependent SE -10,000 [0, 0, 0] [0, 0, 0] (anchors the unchanged threshold)
    One dollar over same, head SE -12,001 [1, 0, 0] [0.55, 0, 0] (12,001 / 22,001 of $1)
    Only the dependent has an SE loss head of household; head wages 60,000, rental -30,000; dependent SE -20,000 [0, 0] [8,000, 0] (min(20,000, 30,000) - 12,000)

    The property test already covers these patterns statistically. A YAML case would pin them where a reader can see them.

  4. Reference polish on the variable (pre-existing lines next to the change).

    • dc_self_employment_loss_addition.py:10-13 cites only the 2021 (#page=63) and 2022 (#page=55) Schedule I pages. Both anchors are correct and show line 6.
    • Consider adding:
      • the 2024 booklet #page=55
      • the 2025 booklet #page=64
      • the 2024 booklet #page=9 (the Calculation J "each person's additions" instruction, which supports the per-spouse split)
      • D.C. Code § 47-1803.02, which the sibling additions/sources.yaml already cites
    • Use one reference per page.
  5. Update rulespec-us#1504's "Encoding debt" paragraph. It describes only the dilution effect ("the head and spouse received only part of the addition"). Commit 08ba81c7f6 also fixes the amount for non-joint filers. A one-sentence addition keeps the parity issue in step with the PR. This does not affect whether the axiom line is valid.

Verified correct

  • The formula change (dc_self_employment_loss_addition.py:17-24, 45):
    • loss_person is now is_head_or_spouse * max_(0, -total_self_employment_income). Both the tax-unit total and the joint split use it.
    • The return mask reuses the same variable, so no dependent can receive an amount.
    • is_tax_unit_dependent defaults to ~head & ~spouse, so this mask and loss_ald's not_dependent mask select the same people.
    • The code is vectorized, hard-codes no values, and reads the $12,000 from gov.states.dc.tax.income.additions.self_employment_loss.threshold.
  • The cap comment and the rename (:25-35):
    • loss_ald is limited_business_loss + capital_losses_allowed_against_gains + limited_capital_loss, so max_(0, loss_ald - capital parts) equals the §461(l)-limited total of all business losses: self-employment, farm, rental, farm rent, estate, partnership/S-corp and other.
    • "business_loss_in_ald" is the accurate name. The PR body's claim that this equals a future limited_business_loss variable holds.
  • The PR fixes the law.
    • Line 6 reaches only losses on the filers' own federal return (2024 booklet #page=55, #page=16).
    • A dependent files their own D-40 (#page=9).
    • Federal law lets a parent bring only a child's interest and dividends onto the parent's return (Form 8814).
    • On a joint return, the share proportional to each spouse's own loss implements "each person's additions" for Calculation J (#page=9).
  • New YAML expectations, computed by hand (2024, joint §461(l) limit $610,000, HoH $305,000):
    • :13-40: loss_taxunit = 20,000 (the dependent is masked); loss_ald = 20,000; addition = 20,000 - 12,000 = 8,000. The head's share is 20/20, so [8,000, 0, 0]. Main gives 20/40 × 8,000 = 4,000.
    • :42-67 (HoH): own loss 5,000; cap = 5,000 + 30,000 = 35,000; min(5,000, 35,000) - 12,000 < 0, so [0, 0]. Main: min(25,000, 35,000) - 12,000 = 13,000.
    • :69-96: losses 20,000 + 10,000 = 30,000; addition 18,000, split 20:10, so [12,000, 6,000, 0]. Main: 18,000 × 20/45 = 8,000 and × 10/45 = 4,000.
  • Existing expectations, still correct:
    • :98-108: min(25M, 305,000) - 12,000 = 293,000
    • :148-157: 12,000 - 12,000, so 0 at the exact threshold
    • :170-200 (2021): [12,000, 6,000, 0], DC AGI [57,000, 41,000, 0]
  • Test runs:
    • On the extracted PR head, the YAML file passed 11/11 and the property test passed 5/5.
    • Against the merge-base model, the 3 new YAML cases fail. The property test fails invariants 1, 3 and 4 with exactly the PR body's counts (23/528 people, 19/200 tax units, 19/260 joint filers).
  • The property test (tests/core/test_dc_self_employment_loss_addition_invariants.py) is sound:
    • Both populations draw the dependent amount before zeroing it, so they differ only in dependents' SE income.
    • Its expected values are computed outside the model.
    • Losses stay under §461(l) (at most 200,000 joint versus 610,000), so its no-cap assumption holds.
    • test_population_has_dependent_losses_and_additions guards against a vacuous pass.
    • It sits beside the sibling invariant files and runs in the core Rest Python group (Makefile:274-275).
  • Household reproductions on the PR head (DC income tax before credits, main → PR):
    • Headline HoH case: $766 → $124. The $13,000 addition goes away.
    • Joint case with a dependent's loss: $2,888 → $3,128.
    • Total DC AGI now equals federal AGI plus the addition: 80,000 + 8,000 = 88,000. Main gave 84,000.
    • The 2021 and 2026 runs give the same pattern. The rule is not year-specific; the sources list starts in 2021, and the threshold is constant.
    • SSTB losses still count (single, $5,000 SE + $20,000 SSTB → $13,000).
  • Callers. dc_additions (via additions/sources.yaml) is the only formula reader, followed by dc_agi, dc_taxable_income_indiv, dc_deduction_indiv and dc_files_separately.
    • Dependents got $0 before and after the change.
    • The other test that reads the variable (schedule_d_capital_gain_distributions.yaml:316-335) has no dependent and is unaffected.
    • No partner test is touched, and "Household API Partners" passes.
  • Changelog. changelog.d/fix-dc-se-loss-addition-filers.fixed.md is a top-level file of the right type (fixed), and its text is accurate.
  • PR-body claims checked:
Pre-existing issues (not blocking, outside this PR's change)
  1. The $12,000 is a business gross-income test, not a loss threshold.
  2. Only Schedule C losses are added back.
  3. The §461(l) cap is not pro-rated.
    • When §461(l) binds and the filer has both SE and other business losses, min_(loss_taxunit, business_loss_in_ald) treats the whole allowed business loss as self-employment loss. The excess business loss disallowance applies across all business losses.
    • Example: single, SE -400,000 and rental -400,000. The addition is 293,000, against about 140,500 if the cap were pro-rated.
  4. Per-spouse federal AGI splits loss_ald 50/50, while this addition follows each spouse's own loss.
    • adjusted_gross_income_person shares loss_ald evenly on a joint return.
    • Joint example (head SE -20,000, spouse wages only): DC AGI is [58,000, 30,000]. Calculation J's "each person's federal adjusted gross income" gives [48,000, 40,000].
    • The total is right, but the combined-separate allocation is off.
    • The PR body says the author's fix-agi-person-ald-split branch addresses this.

Axiom line assessment

Valid. The line axiom: TheAxiomFoundation/rulespec-us#1504 queued points to an open issue (created 2026-10-06, updated 2026-10-06T12:21 just after the head commit) with the pe-parity label. The issue is dispatch-ready on the rulespec-us#1467 model:

  • Module path: us-dc/statutes/47/47-1803.02.yaml.
  • Corpus citations: us-dc/statute/47/47-1803.02 and 47-1805.02 in the 2026-07-16 title-47 snapshot.
  • Verbatim law: § 47-1803.02(a)(2)(D) and § 47-1805.02(6) match code.dccouncil.gov. It also quotes the OTR Schedule I line 6 and § 47-1808.01 text.
  • Required outputs: the per-business D-30 test, and the line 6 amount with each spouse's share.
  • Companion tests: five TY2025 cases taken from the statute and Schedule I, not from PolicyEngine output.

The stated blocker (confirm the corpus rows, then wait for the next encoder pin) is the normal reason for "queued". The issue matches the PR on dependents: "A dependent's business is on the dependent's own return and never enters the parents' addition". It deliberately encodes the gross-income test as written rather than PolicyEngine's loss-minus-$12,000 approximation, and documents that gap with #9867. The only nit is Suggestion 5.

CI status

All green: 37/37 checks pass (gh pr checks 9857). That includes Lint, the changelog check, every Full Suite baseline and contrib shard, Microsimulation, Rest (Python + variables), Household API Partners, and codecov patch and project.

Branch status

The branch is 37 commits behind main and 3 ahead (merge-base bab1a47c60). git merge-tree --write-tree PolicyEngine/main 08ba81c7f6 merges cleanly with no conflicts. No commit on main since the merge-base touches this variable, loss_ald, or the DC additions. A rebase is optional.

Verdict: APPROVE

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving: Schedule I Calc A adds back only losses on the filers' own federal return, and a dependent's losses belong on the dependent's own D-40, so leaving them out (and splitting per spouse as Calc J does) is correct. Formula and tests hand-checked and reproduced on the head; rulespec-us#1504 is dispatch-ready. Five optional suggestions in the comment above.

@MaxGhenis
MaxGhenis merged commit 14b636a into main Oct 9, 2026
28 checks passed
@MaxGhenis
MaxGhenis deleted the fix-dc-se-loss-addition-filers branch October 9, 2026 00:15
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#9857 at 4a616e36cb17a5fe4c9df7b194e44e783196be9d (squash)

  • Change: dependents' self-employment losses stay out of the D.C. loss addition for filers.
  • Review: delta review r1 APPROVE at this head (Subfleet). Its P2-1, the placeholder Impact section and stale status, is fixed in the body.
  • Gates checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head concluded success;
    • MERGEABLE;
    • not a draft;
    • no CHANGES_REQUESTED review;
    • the semantic-overlap guard;
    • head pinned.
  • Impact: a real microsim, merge base 7b956eb401 against this head, for 2025 and 2026. Zero change in dc_agi ($55.62bn and $58.49bn), dc_income_tax, state_income_tax, income_tax, household net income and benefits. The fix changes results only for D.C. units where a dependent reports a self-employment loss.
  • --admin: used only because a required approving review is the sole block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants