Add SSTB self-employment income variable and split QBID by category#7944
Add SSTB self-employment income variable and split QBID by category#7944PavelMakarchuk wants to merge 5 commits intomainfrom
Conversation
…ation Per IRC §199A(d), a specified service trade or business (SSTB) is treated differently from non-SSTB qualified businesses for the §199A deduction: above the §199A(e)(2) threshold, the SSTB component phases to zero while the non-SSTB component still receives the W-2/UBIA capped deduction. The existing PolicyEngine model used a single per-person `business_is_sstb` flag, which forced an all-or-nothing treatment and could not represent mixed filers (e.g., a doctor with rental property). This change introduces a new `sstb_self_employment_income` input variable, splits qualified business income into non-SSTB (`qualified_business_income`) and SSTB (`sstb_qualified_business_income`) components with QBI deductions pro-rated by gross-income share, and refactors `qbid_amount` to compute the §199A(b)(2) per-business limit separately for each category before summing. The legacy `business_is_sstb` flag is preserved for backward compatibility by routing the legacy QBI through the SSTB component when set. `sstb_self_employment_income` is also added to SECA gross income, IRS gross income, market income, and earned income so it flows through the rest of the tax model parallel to `self_employment_income`. Closes #7939 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7944 +/- ##
===========================================
- Coverage 100.00% 99.09% -0.91%
===========================================
Files 2 14 +12
Lines 42 222 +180
Branches 2 3 +1
===========================================
+ Hits 42 220 +178
- Misses 0 2 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
MaxGhenis
left a comment
There was a problem hiding this comment.
Requesting changes for one blocking regression.
sstb_self_employment_incomeis added to person-levelmarket_income, federal gross income, earned income, and SE tax, but not togov.household.market_income_sources, which driveshousehold_market_incomeand thenhousehold_net_income. Repro on this branch: a one-person situation withsstb_self_employment_income = 100000returnsmarket_income = 100000,household_market_income = 0, andhousehold_net_income = -23082.287109375because the income is omitted from household resources while tax liabilities are still included. Please add it to household market income sources, and audit the other parameterized income-source lists that currently countself_employment_incomeif SSTB income should remain equivalent outside the QBI calculation.
Verified locally: uv run python -m policyengine_core.scripts.policyengine_command test policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid policyengine_us/tests/policy/baseline/gov/irs/self_employment/taxable_self_employment_income.yaml policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_business_income_deduction.yaml policyengine_us/tests/policy/contrib/taxsim/outputs/taxsim_outputs.yaml -c policyengine_us passed: 71 tests.
MaxGhenis
left a comment
There was a problem hiding this comment.
Approved after pushing the household market-income fix. Local verification: the household market income fixture passes, and the prior reproduction now returns household_market_income = 100000.
Summary
Closes #7939.
Adds a new
sstb_self_employment_incomeinput variable (Person, USD) and refactors the qualified business income deduction so that the §199A(d)(3) phaseout above the §199A(e)(2) threshold reduces only the SSTB component, leaving the non-SSTB component subject to the W-2/UBIA cap as before. This fixes the mixed-filer case (e.g., a doctor who also owns a rental property) where the legacybusiness_is_sstbboolean forced an all-or-nothing treatment.Changes
New variables
sstb_self_employment_income(Person, USD) — SE income from a specified service trade or business under IRC §199A(d)(2). Subject to SECA, treated separately for QBID.sstb_self_employment_income_would_be_qualified(Person, bool, default True) — parallels the existing<source>_would_be_qualifiedflags.sstb_qualified_business_income(Person, USD) — SSTB QBI before the §199A(d)(3) phaseout, with QBI deductions pro-rated by gross-income share.Modified variables
qualified_business_income— now excludes SSTB SE income from gross QBI and pro-rates the existing QBI deductions by non-SSTB share.qbid_amount— computes the §199A(b)(2) per-business limit separately for the non-SSTB and SSTB categories before summing them with the REIT/PTP component. The legacybusiness_is_sstbflag is preserved for backward compatibility by routing the legacy QBI through the SSTB component when set.taxable_self_employment_income,earned_income,market_income, and the IRSgross_income/sourcesparameter — addsstb_self_employment_incomeso it flows through SECA, AGI, and downstream income concepts in parallel withself_employment_income.Notes on the issue's example tests
The integration tests in #7939 use round numbers (
$20,000and$10,000) that assume QBI is not reduced by the SE-tax / health-insurance / pension ALDs. PolicyEngine's existingqualified_business_incomeformula does subtract those ALDs (per IRS Publication 535 Worksheet 12-A), so the new tests in this PR follow the same approach: I include the issue's two scenarios verbatim but zero-out the QBI ALD inputs so the expected values isolate the SSTB-vs-non-SSTB routing rather than the deduction pro-rating.Test plan
policyengine_us/tests/policy/baseline/gov/irs— 725 tests passpolicyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid— 34 QBID tests pass (24 existing + 10 new)policyengine_us/tests/policy/baseline/gov/irs/self_employment— 11 SECA tests pass (10 existing + 1 new)policyengine_us/tests/policy/baseline/gov/states/ca— 520 California tests passpolicyengine_us/tests/policy/baseline/gov/usda— 384 USDA tests passpolicyengine_us/tests/policy/baseline/gov/hhs— 386 HHS tests passmake formatclean🤖 Generated with Claude Code