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
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_incomewhere 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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