PolicyEngine / PolicyEngine/microcosm

Populate passive_partnership_s_corp_income (NIIT passive pass-through leg, pe-us#9306): SCF non-active-business shape, SOI/8960 level

Open
#722 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
0
Forks
4
Avg merge
1d 3h
Merged PRs (30d)
94

Description

What

PolicyEngine/policyengine-us#9306 adds passive_partnership_s_corp_income — the §469 passive subset of partnership_s_corp_income — and puts it in the NIIT base (§1411(c)(1)(A)(ii)). It defaults to zero, so until Microcosm populates it the engine under-taxes passive pass-through income under NIIT exactly as before. Microcosm needs a stage that assigns each record's passive share.

Design (follows the #530 doctrine: measured / derived-from-law / anchored prior)

  • Not derivable from law alone: passivity is a facts-and-circumstances material-participation test (§469(h), the 500-hour and related tests); the PUF and CPS observe neither hours nor participation. So this is an anchored-prior stage, not a derivation.
  • Survey shape: the SCF distinguishes businesses the household actively manages (X3103/X3104, the basis of qbi_employer_structure_v1) from holdings with no active management role (X3401-X3452) — the cleanest public survey signal for "owner is passive." Extend the v3 SCF extract to that section: P(passive holding | income band, legal form) and the passive share of pass-through income by band.
  • Administrative level: SOI partnership passive/nonpassive tables and Form 8960 line-4 aggregates (chronicle ingestion companion issue) pin the aggregate passive share the assignment must hit — same SCF-shape / SOI-level rule v3 used for employer presence.
  • Placement: a per-record seeded draw of passive share (own rng family, so v1–v3 streams stay byte-identical), conditioned on income band and latent entity form (the v3 partnership/S-corp split already exists), emitting passive_partnership_s_corp_income = share × partnership_s_corp_income where positive. Gate behind the version flag like every other QBI family.
  • Validation: NIIT aggregate vs the JCT/SOI NIIT totals becomes a new row on the #535 standing surface; the passive-income aggregate vs Form 8960 line 4 is the direct anchor check.

Dependencies

  • Upstream: chronicle#179 (the anchors). Engine side: pe-us#9306 (open, CI green).
  • Builds on the #530 stack (qbi-v3-wiring); v4 of the QBI family or a sibling stage — decide at implementation.

Consistency note

Today the engine's NIIT base includes rental_income in full while Schedule E rental is often passive-by-default; the new stage should not create an asymmetry where pass-through passivity is modeled but rental passivity is asserted. Document the treatment either way.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the #530 qbi-v3-wiring stack and the v3 SCF extract, then review chronicle#179, pe-us#9306, and the #535 standing validation surface. Define the passive-share assignment using SCF income bands, legal form, and SOI/Form 8960 anchors while preserving earlier RNG streams. Done means the version-gated stage populates passive_partnership_s_corp_income, matches the aggregate anchors, and documents rental-income treatment.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data-engineering
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.