Skip to content

Add contributed owners draw SECA reform - #10483

Merged
MaxGhenis merged 17 commits into
mainfrom
reform-pass-through-owners-draw-seca
Oct 29, 2026
Merged

MaxGhenis merged 17 commits into
mainfrom
reform-pass-through-owners-draw-seca

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Add an opt-in contributed reform that taxes eligible pass-through owners' draws under SECA OASDI. It is disabled by default and supports configurable phase-in, net-earnings factors, cap treatment, HI, deduction and benefit credit.

Review-update head: b5fb5cabb2227fab8e00d40a9fc3bb964768f0cd. Main is merged through bf108e931a1caed757339fadf79fa9e4da3166f9; this includes the SECA-room correction in #10380, Social Security benefit-input compatibility in #10400 and the S.770 section 8 options in #10402.

Design and defects fixed

For each owner, net signed entity income before flooring: G = T - Q - K + overlap, where T = s_corp_income + partnership_income, Q is the combined passive subset and K is the already-SE partnership subset. If Q and K are both nonzero, require a validated disjointness assertion or a supplied signed intersection. Losses net within the supplied entity totals; a negative residual does not reduce wages or existing SE earnings. Nonpassive income is the material-participation proxy. Source income remains in gross income once.

Track the planned draw, actual gross draw D, adjusted earnings N, cap room and taxed base B separately. The default factor is q = 1 - employer_share × (SE_OASDI_rate + SE_HI_rate); a positive override replaces it. The planned adjusted draw must independently meet the $400 reporting floor. The default phase-in rises from 10% in 2027 to 100% in 2036, with configurable start, duration and annual shares. The default cap basis preserves the current-law cap before user reforms; the explicit alternative removes the draw ceiling. Wages have priority, then the existing adjusted/capped SE base, then B = min(N, max(preserved_cap - wages - existing_SE_base, 0)).

Added liability enters the existing SECA tax leaves and OASDI accounting once. HI defaults off; enabling it taxes all N without a ceiling. The added deduction uses the existing deductible-share parameter, with nondependent aggregation. Benefit credit adds adjusted annual earnings before the AIME helper's annual wage-base cap, preserves positive supplied AIME precedence and has no tax effect. It inherits main's age/lookback rules and separate benefit wage-base path; it does not change reported benefits or provide a complete annual-history ledger. Population scoring uses the tax side only. Reform documentation.

Generated coverage reproduced a default-45-year AIME crash: an installed draw reform read the reporting-floor parameter before its 2013 start, even in inactive earning years. Commit 4877a973f20f01788983e875811086be7b2d16ba returns zero from owners_draw_gross before that read when the reform is inactive. The hand-computed YAML regression keeps the default lookback; the public Simulation reproduction and final-head validation are distinguished below.

Main merge and #10402 bridge

  • 225c449fa88727ee8cce848e0f838b17e121bedf merges main 8ea43422e68fdb7dbae21ac2cc13e0081688ee4b. Its only resolved hunk is Makefile's test-yaml-structural-other: retain main's recipe, excluding SSA from the shared contrib per-subdir command and running SSA separately with POLICYENGINE_BATCH_WORKERS=1 .../ssa --mode per-file --workers 1.
  • 5d0d943611b141241dde4726e52100de10a0b911 merges main fce6c03f9dfec72a58b538d281cab11b2c6dc932 without conflicts.
  • d3532f1e79dc14c545a8d2478c01c718dc4b1e3a merges main fe9ec1270f and Add S.770 section 8 investment and active income tax options #10402. Resolve the structural-reform-list insertion in policyengine_us/reforms/reforms.py by retaining both owners' draw and S.770 section 8 registrations. Main's new S.770 implementation is retained.
  • 7f1f875d55e7b65a0fb45cbd3c280cfaefe0608c merges bf108e931a1caed757339fadf79fa9e4da3166f9; resolved hunks: none; automatic merge.

system.py merged without a textual conflict. The draw's cap snapshot occurs after loading, interpolation, uprating and backdating, before user/structural reforms. #10400's module-level compatibility installer wraps input/array methods; its constructor migration moves stored benefit-component arrays. Neither rebuilds parameters, and the draw does not read those benefit components. The recursive system clone copies the cap's parameter/value history and clears instant caches. The public wrapper clones a supplied system before applying a constructor reform. No order or shared-state interaction was found; the constructor and isolation tests exercise these paths.

#10402 supplied manual draw hooks, but they did not consume this reform's calculated exclusion. The only model file from #10402 changed here is s770_active_business_income.py, at that integration point. An installed, enabled draw at/after its start year now supplies owners_draw_niit_exclusion: B / q for OASDI-only treatment, or all actual D when uncapped HI applies. This respects the floor, cap room and an explicit q override. The signed active residual, existing-SE/FICA exclusions, dependent exclusion, tax-unit aggregation and S.770 rates/allocation remain main's. Disabled, pre-start and uninstalled draws retain main's manual hooks and rate-derived factor, including nonzero manual inputs.

The automatic joint bridge requires material_participation_only=True. Setting it false admits passive draw income, but the aggregate draw does not identify its active/NII allocation. An active installed bridge therefore raises ValueError until a validated allocation is available; silently subtracting the whole exclusion from active income would misallocate categories. Standalone passive-inclusive draw calculations remain supported, and inactive/pre-start/uninstalled S.770 manual hooks retain main's behavior. This unsupported joint sensitivity is explicit, rather than a claimed calibrated policy result.

The first CI run at 7f1f875d55 found two test defects: six reform combinations in the net-earnings YAML exceeded the existing five-combination budget, and the raised-cap class fixture supplied date strings where Core required Instant objects. Commit b0c7f37394 fixes that fixture and moves the default-q joint case into the existing credited-earnings file, adding the hand credit 400,000 × .9235 = 369,400 for both owners. All 28 named cases remain; each file has exactly five combinations.

At 7f1f875d55, the Python file had 28 passes and one class-fixture failure; original 13 phases totaled 23.651 s and added 16 totaled 141.234 s. At b0c7f37394, all 29 Python cases passed in CI, but added phases still took 143.155 s (old 13: 28.817 s; total: 171.972 s). These are JUnit phase sums, excluding import/collection, not an isolated before/after file-wall benchmark. Reducing public generated batches from five to three had not established the requested approximately two-minute added-cost target.

Commit 5d1d622855922167472a7b1beaba47186c918f0e increases only the default-q joint YAML's absolute margin from $.03 to $.05: one float32 rounding step near its active base is $.03125. Its hand expectations remain unchanged. This responds to the earlier Quick Feedback precision failure. That run also reported 81.81% changed-hook coverage; the new existing-case assertions exercise main's nonpositive-q rejection on disabled, pre-start and uninstalled fallback paths. Both corrections passed Quick Feedback at 4b0a0aedf303; its changed-hook report has 26 statements and six branches, 100% coverage, no missing or partial branches. Codecov patch and project checks passed.

Commit 4b0a0aedf303c93854d29188e67166fc4f4841a7 reduces public generated batches to two while retaining every explicit batch, both cap-constructor routes, shrinking and all 40-example fast properties. The benefit-credit test retains its full 45-year actual calculation and compares credit-off AIME with an independent hand baseline for age 62, zero earlier earnings and the scoring-year wage base, avoiding a redundant third simulation. It checks exact array equality after casting that hand oracle to the actual stored AIME dtype. The disabled differential still calculates every required output through independent real public baseline and reform simulations with default 45-year histories. It snapshots baseline results, releases derived parameter caches and the simulation before calculating the disabled system, and releases that system's caches after each example. It clears root and descendant instant caches, not policy values or inputs. This responds to an earlier local 4 GiB RSS termination while both histories were resident. At 4b0a0aedf303, all 29 cases passed ordinary Rest CI: original 13 phases 27.244 s, added 16 phases 189.133 s, total 216.377 s. The disabled differential alone took 114.523 s, so the approximate two-minute added-cost target was still missed. A local run timed out at 1,800.403 s capped wall with 2.602966 GiB peak RSS, before a complete file result.

Commit 4640c0c630d85a16223406f7d3e2efe8c76d9c17 responds to that measured cost by retaining one privately owned parameter tree and its warm 45-year views. Each generated example restores the entire saved baseline variable registry and data_modified flag, calculates and copies every baseline output through a fresh public Simulation, installs the production draw factory into that private registry, and compares every disabled output through a second fresh public Simulation. It releases the first holder graph before the second and restores the registry in finally. Parameter-root identity, inactive status and a streamed fingerprint of all scalar histories and scale/bracket structure protect policy equality. This differential now installs via the factory apply method; the original constructor activation/isolation tests and both generated raised-cap routes still exercise the public reform= constructor argument. No model code, output assertion, explicit example or lookback changes. The fingerprint correction and latest-head validation are recorded below.

At 4640c0c630d8, Quick Feedback passed all 28 draw YAML cases twice and 28 Python cases, but the new fingerprint helper failed before its differential comparison. A regular Core parameter node can expose a child named brackets; generic attribute inspection mistook that child scale for a bracket list and raised KeyError: 8. Commit b5fb5cabb2227fab8e00d40a9fc3bb964768f0cd restricts child keys, bracket lists and scalar histories to their actual Core parameter types. It retains complete descendant traversal, scale structure and policy-history checks. This is a test-helper correction; model arithmetic and all differential assertions are unchanged. Final-head ordinary Rest and Quick Feedback now pass; the measured added-test cost still exceeds the approximately two-minute target, as recorded below.

Joint hand calculations

All joint YAML owners have no preexisting SE earnings, phase-in share 1, cap $200,000, MAGI $1,000,000, no NII and single filing status. Thus the MAGI excess does not constrain these active bases. Set R = max(200,000 - W, 0), N = D × q, B = min(N, R), E = B/q without HI or E = D with HI, and A = D - E. S.770's active tax is 0.162 × A. All listed draws exceed the floor.

The paired rows show every joint fixture with and without the explicit q=.8 override. The changed-rate fixture has OASDI=.20 and HI=.04, so its rate-derived q=.88; all other fixtures have OASDI=.124 and HI=.029, giving q=.9235. These arithmetic comparisons include sensitivities beyond the exact YAML rows and are not additional passing-test claims.

Joint fixture: wages / gross draw q Adjusted N OASDI B Gross exclusion E Active A Active tax
Default room: 100,000 / 400,000 .9235 369,400 100,000 108,283.7033 291,716.2967 47,258.0401
Same, factor override .8 320,000 100,000 125,000 275,000 44,550
Default no room: 250,000 / 400,000 .9235 369,400 0 0 400,000 64,800
Same, factor override .8 320,000 0 0 400,000 64,800
Changed rates, no wages: 0 / 100,000 .88 88,000 88,000 100,000 0 0
Same, factor override .8 80,000 80,000 100,000 0 0
Changed rates, room: 100,000 / 400,000 .88 352,000 100,000 113,636.3636 286,363.6364 46,390.9091
Same, factor override .8 320,000 100,000 125,000 275,000 44,550
HI, room: 100,000 / 200,000 .9235 184,700 100,000 200,000 0 0
Same, factor override .8 160,000 100,000 200,000 0 0
HI, no room: 250,000 / 150,000 .9235 138,525 0 150,000 0 0
Same, factor override .8 120,000 0 150,000 0 0
HI, larger draw: 100,000 / 400,000 .9235 369,400 100,000 400,000 0 0
Same, factor override .8 320,000 100,000 400,000 0 0

OASDI liability is B times the applicable rate: $17,600/$16,000 for the changed-rate no-wage pair, $20,000 for its room-limited pair and $12,400 for other room-limited rows. For the three HI fixtures, uncapped HI is respectively $5,356.30, $4,017.225 and $10,712.60; with q=.8 it is $4,640, $3,480 and $9,280. Applying the default half deduction yields $8,878.15/$2,008.6125/$11,556.30 without the override, or $8,520/$1,740/$10,840 with it. No-room OASDI is zero in both HI modes.

Invariants

Tests below are in policyengine_us/tests/reforms/test_owners_draw.py. Formula properties generate batches; public Simulation properties use one private system per test and household batches. Hypothesis uses the repository's module-level SETTINGS convention, deterministic generation, explicit examples and shrinking; no registered profile exists. Private policies are released at test end. Fast formula properties retain 40 generated examples; public-simulation properties use two generated batches plus every explicit example. The disabled differential uses fresh baseline and disabled holders on one private parameter tree, with policy-history fingerprints and the full default lookback. Explicit boundaries include adjusted $399.99/$400/$400.01, exact/zero cap room, above-cap wages, losses netting to zero and a phase-in step.

# Property Generated test / regression
1 Gross/base nonnegative; N=D×q; B bounded by adjusted earnings and room after wages/existing adjusted SE. test_generated_draw_bounds_and_adjustment
2 Otherwise-capped wages + SE + draw stay within the applicable cap; both parametric and Reform-class raised-cap paths preserve the current-law draw ceiling. test_generated_raised_cap_paths_preserve_current_law_room
3 Inactive reform matches public baseline for SECA, income tax, AIME and NIIT. test_generated_disabled_reform_matches_public_baseline
4 Added OASDI tax is non-decreasing in phase-in share. test_generated_phase_in_tax_is_monotone
5 Signed subset/overlap netting avoids existing-SE double counting and leaves wages/existing SE unchanged. test_generated_signed_residual_never_double_counts_se
6 The adjusted-draw reporting floor is independent of existing SE earnings. test_generated_reporting_floor_is_separate_from_existing_se
7 HI defaults off; when enabled it taxes the full adjusted draw without a cap. test_generated_hi_defaults_off_and_is_uncapped_when_enabled
8 Added deduction follows the existing deductible share; disabling it preserves the old deduction. test_generated_deduction_tracks_existing_deductible_share
9 Benefit credit respects the annual wage base and supplied AIME precedence; credit-off AIME matches an independent hand baseline with the full lookback. test_generated_benefit_credit_cap_override_and_historical_lookback
10 Inactive lookback years before the floor parameter exists return zero without raising. Same generated history test; default-lookback hand-computed YAML regression.
11 Joint active income consumes actual floor/cap/factor-aware exclusion in both HI modes; unsupported passive-inclusive automatic classification is rejected. test_generated_s770_excludes_actual_owners_draw; test_s770_rejects_unclassified_passive_inclusive_draw
12 Draw off/pre-start/uninstalled preserves main's signed S.770 result for arbitrary manual hooks, overlaps, FICA, dependents and tax-unit membership; a draw-factor override cannot change the fallback. test_generated_disabled_owners_draw_preserves_main_s770, with an independent scalar main-formula oracle.

Scratch mutants change one source expression each. Their country-importing checks run sequentially against the same committed test file in one foreground interpreter under a shared total 2 GiB / 900 s cap, including import. Formula/factory aliases and model source are restored between variants. These source-injection checks share imported country state; they do not reconstruct a registry independently per variant. An interrupted variant is unrun, never a mutant rejection.

# Source mutant Recorded result and head
1 Remove OASDI room cap. Intended assertion rejected, 315.69 s; inherited target/cache manifest identifies 4877a973f2, but the raw runner omitted embedded HEAD.
2 Use raised payroll cap for draw room. Parametric-route assertion rejected at 7f1f875d55, 310.914 s. A later attempt at b0c7f37394 timed out before a completed result.
3 Add liability while disabled. Intended assertion rejected at 7f1f875d55, 12.713 s.
4 Reverse phase-in share. Intended assertion rejected at 7f1f875d55, 6.206 s.
5 Stop subtracting already-SE income. Intended assertion rejected at 7f1f875d55, 11.150 s.
6 Reject adjusted draw exactly at $400. Intended assertion rejected at 7f1f875d55, 14.115 s.
7 Cap optional HI at OASDI room. Intended assertion rejected at 7f1f875d55, 15.132 s.
8 Hardcode a half deduction. Intended assertion rejected at 7f1f875d55, 29.159 s.
9 Ignore benefit-credit-off. Intended assertion rejected at 7f1f875d55, 54.624 s.
10 Remove inactive-year floor guard. Expected missing-floor AttributeError in the deliberately incomplete historical-parameter harness at b0c7f37394, 53.220 s.
11 Ignore actual calculated draw exclusion. Intended call-phase AssertionError at b0c7f37394, 23.692 s.
12 Consume calculated exclusion while draw is disabled. Intended call-phase AssertionError at b0c7f37394, 257.696 s.
Optional 13 Remove passive-inclusive classification guard. Intended pytest.raises failure at b0c7f37394, 35.197 s; verified by a separate posthoc audit.

The priority attempt covering 12, 11, 10 and 13 completed within the unchanged common caps: 761.657 s capped wall, 0.867706 GiB peak RSS, no timeout/RSS kill/monitor error; wrapper wall 763.144 s, excluding 423.484 s waiting for load. The runner exited 2 because its classifier mislabeled property 13's builtins.Failed: DID NOT RAISE ValueError as unexpected. A 36/36-check source/log audit verified the exact intended failure and preserved the raw classifier status and logs. This is an audited rejection, not a successful aggregate runner exit.

Properties 2–9 completed in the earlier common-cap 7f1f875d55 invocation before it timed out during 10: 900.188 s, .846100 GiB. The separate b0c7f37394 attempt timed out after 900.235 s while property 2 was active, before a completed pytest result, at 0.825790 GiB; later variants were not reached. Neither timeout counts as a mutant rejection. The table distinguishes completed observations from that interruption. The owners-draw and S.770 model blobs remain unchanged by the later YAML/runtime-only commits. None of these observations is relabeled as execution at the latest head. The earlier 51/51-check source audit established unchanged production expressions and mutant-catching probes at 4b0a0aedf303; the refreshed audit also passes 51/51 checks at this test-only head.

Routing and validation

The batch runner recursively includes .yaml and .yml, including loose top-level SSA files. Per-file mode creates singleton batches before combination packing; it cannot split one file, and continuation checks run between files. With the explicit worker override, the six SSA files form six logical sequential file batches plus the supervisor; each attempt has one model subprocess, and retries/continuations can add attempts. Python remains in the existing Rest route, and .github/ is unchanged.

Static production-helper checks confirm five structural combinations, weight 5.0, in each draw file. All 28 named cases and hand expectations remain, including the added joint credit assertion; the later float32 correction changes only its absolute margin. Static budget checks are not runtime pass counts.

Draw YAML file Named cases CI route Final-head full-file Quick result Whole-file peak RSS
owners_draw.yaml 17 One logical singleton file batch; retries may add attempts 17 passed twice; pytest 113.47 s / 119.14 s Unavailable in collected logs
owners_draw_credited_earnings.yaml 6 One logical singleton file batch; retries may add attempts 6 passed twice; pytest 137.81 s / 140.59 s Unavailable in collected logs
owners_draw_net_earnings.yaml 5 One logical singleton file batch; retries may add attempts 5 passed twice; pytest 146.27 s / 141.42 s Unavailable in collected logs

These pytest elapsed times exclude earlier country import and are not whole process wall. Quick Feedback did not record file-process RSS. No final-head local whole-YAML runs were attempted; historical scratch processes cannot establish their whole-file peaks.

Local ordinary-file checks use fresh foreground interpreters, one file at a time, thread limits of four, load1≤100 at launch, and unchanged 4 GiB / 1,800 s limits. Mutants retain 2 GiB / 900 s. Python invokes pytest; YAML invokes the direct Core CLI with -c policyengine_us. An exact-HEAD, Git-blob-verified byte/discovery cache reduces filesystem latency while tests remain uncached. No preloaded country or initialized-system fallback replaces these file runs. A planned/load-gated job is not an execution result, and incomplete scratch splits do not establish a whole-file pass.

Check at b5fb5cabb2227fab8e00d40a9fc3bb964768f0cd Result / count Timing / memory scope
tests/reforms/test_owners_draw.py Rest 29 passed; Quick 29 passed. Local attempt timed out, rc137, without a final pytest count. Local cap wall 1,800.296 s; wrapper 1,802.954 s; peak 2.796494 GiB. Quick pytest 204.06 s under coverage.
Three draw YAML files Quick 17 / 6 / 5 passed, each file twice; no new-head local YAML execution. Pytest times above; full-file RSS not inferred from scratch splits.
Default-lookback AIME regression Included in the credited-earnings YAML's six passing CI cases. No latest-head local run. Historical 4640 split printed 1 passed in 365.48 s, then timed out during shutdown (rc137): cap 1,800.307 s, wrapper 1,801.683 s, peak 3.956268 GiB. This is not a successful whole-file process.
Added generated-test cost Target unmet: added 16 JUnit phases 174.248 s, versus approximately 120 s; original 13 phases 29.049 s; total 203.297 s. These exclude import/collection. No isolated before/after wall result, because the latest local file timed out.
Lock / format / coverage Lock and format/lint passed. Selected changed-hook coverage 100%: 26 statements, 6 branches, no missing statements or partial branches; Codecov patch/project pass. Lock resolved 150 packages in 68 ms; 7,893 files unchanged; Ruff passed. Coverage scope is one selected changed source file, not repository-wide.

The final local Python process launched with load1 66.268 after 32.843 s waiting (300 s maximum); load1 ended at 95.606. It used four threads and the unchanged 4 GiB/1,800 s cap. The original 13-test snapshot passed at b0c7f37394 in 407.613 s wrapper wall, peak 2.881088 GiB; production model bytes match final HEAD. A timeout is not a completed timing pair. Shared-host load, filesystem QoS and cache warmth also limit observational comparisons. No unverified cleanup/GC optimization was applied.

Earlier local evidence at b0c7f37394: the original 13-case baseline passed in 407.613 s wall, with 2.881088 GiB peak RSS. The then-current 29-case file was terminated at the unchanged 4 GiB cap after 893.499 s, peak 4.003418 GiB, after 21 completion markers. This is incomplete and does not establish a final pass/fail count or a whole-file result. It motivated the new staged-cache cleanup. At 7f1f875d55, an earlier full-file local attempt timed out at 1,800 s after 12 completed cases, peak 2.41983 GiB.

At b0c7f37394, CI passed all 29 owners-draw Python cases, with 171.972 s total phases: 28.817 s original and 143.155 s added. These sums exclude import/collection and differ from isolated file wall time; the latest measured added cost is 174.248 s, still above the target. Earlier Quick Feedback's float32 assertion and 81.81% changed-hook coverage are historical failures addressed by the new tests, not new-head passing evidence.

The earlier lock check resolved 150 packages in 3 ms resolver time without lock changes; that log did not record process wall. Earlier make format and Ruff checks passed with 7,893 files unchanged. The historical original 13-test run (108.3 s process wall / 39.3 s pytest, 2.95 GiB) is separate from the later matched baseline above. The public historical AIME crash reproduction completed its assertions before its time -l wrapper failed on denied kern.clockrate access; it establishes the reproduced defect, not a fixed-head passing regression.

Under the continuation's runtime scope, long YAML folders, test_tob_revenue.py and outside-change files are delegated to CI. This includes test_ss_calculator_differential.py, the requested code-health files and #10400's real-core/lever-property files unless a completed local result is recorded. The calculator reference removes gov.contrib.ssa.pia and gov.contrib.congress.s770, retaining the neutral owners-draw subtree. Individual requested file results will be reported from completed logs, not inferred from an in-progress containing job.

At historical 4b0a0aedf303, Quick Feedback and all five Rest shards passed. At final b5fb5cabb2227fab8e00d40a9fc3bb964768f0cd, run 40760043706 is exact-head verified. The saved 2026-10-28 21:12:46 UTC snapshot has 33 passing checks, 2 pending and 1 skipped, with no failures. All five Rest shards, Quick Feedback and both Codecov checks pass. Quick Feedback's 116 selected test executions all passed, including the three draw YAML files twice and all 29 Python cases. These results resolve the historical float32/coverage and fingerprint failures; earlier CI and mutation observations retain their actual heads.

Final-head Rest JUnit artifacts report:

Requested Python file Passed Test-phase seconds
tests/reforms/test_owners_draw.py 29 203.297
tests/unit/test_ss_calculator_differential.py 8 116.591
tests/code_health/test_make_test_matches_ci.py 5 .141
tests/code_health/test_rest_python_groups.py 61 33.865
tests/code_health/test_source_conflicts.py 103 18.399
tests/core/test_social_security_real_core.py 120 .283
tests/core/test_social_security_lever_properties.py 24 .850
tests/policy/baseline/gov/ssa/revenue/test_tob_revenue.py 2 173.438
tests/reforms/test_s770_section_8.py 19 39.328

The containing remaining-Python process ran 3,119 cases in 1,644.46 s wall, peak 7.9022 GiB. Those resources belong to the entire process, not the owners-draw file. JUnit sums setup/call/teardown and exclude import/collection; they do not establish isolated file wall/RSS. The added 174.248 s exceeds the approximate two-minute target by 54.248 s, so runtime acceptance remains unmet despite passing assertions.

Final structural-other job 123086651928 (Contrib other-shard-1) succeeded in 54:40, from 20:38:00Z to 21:32:40Z; the same shard took 45:15 at main c55b806541. Its serial SSA command took 967.297 s, against 426.408 s at main. The three draw YAML files account for about 538 s of that increase; the original SSA files kept nearly the same times. These figures come from the completed job logs.

Data and sources

Current production exports force S-corporation income to zero, combine nonpassive donor income in partnership income and lack the passive column; calibration can overstate the active residual. Current-data scoring remains provisional. Nonprovisional results require a pinned artifact containing Microcosm #1251 and #1261, plus a column-contract check. The intended contract separates entity totals, subtracts the combined passive subset once and removes already-SE income without double counting.

  • OACT coverage F12: “make all distributions to all pass-through business owners up to the earnings cap subject to the SECA tax”. This reform's custom phase-in and OASDI-only default are distinct choices.
  • OACT legacy E5: “Raise the taxable maximum amount (the contribution and benefit base) to include 90 percent of total OASDI covered earnings.” This legacy table uses 2008 Trustees assumptions. The current payroll table uses different provision categories.
  • 26 U.S.C. 1402(a) supplies the comparable income-minus-deductions approach; this contributed expansion is not enacted law.

impact: none at baseline. Hub full-file pair, main bf108e9 against head b5fb5ca, 2026: zero change in self-employment tax, taxable SE income, NIIT, AGI, Social Security, taxable Social Security, income tax, state income tax, household net income and benefits. The reform is off by default.

axiom: n/a: contributed reform, not enacted law

CI at the final head: run 40760043706 completed with success. 35 checks passed and 1 skipped, including Contrib other-shard-1 and states-shard-1.

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base bf108e931a against this PR's head b5fb5cabb2, for 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.

Output 2026 change 2026 records changed (weighted)
self_employment_tax +0 0 (0)
taxable_self_employment_income +0 0 (0)
net_investment_income_tax +0 0 (0)
adjusted_gross_income +0 0 (0)
social_security +0 0 (0)
taxable_social_security +0 0 (0)
state_income_tax +0 0 (0)
income_tax +0 0 (0)
household_net_income +0 0 (0)
household_benefits +0 0 (0)

The draw reform is off by default, so this pair checks that the disabled path leaves the baseline unchanged.

Comparison files are in fixes/10483-impact/ on the hub host.

@MaxGhenis
MaxGhenis marked this pull request as ready for review October 27, 2026 08:44
MaxGhenis and others added 7 commits October 28, 2026 00:11
Merges main 8ea4342 (which carries #10400) into 1aeb9e4.

Resolved hunks:
- Makefile, test-yaml-structural-other: the one conflict. Took main's
  recipe unchanged. SSA stays excluded from the per-subdir run and has
  its own per-file run with POLICYENGINE_BATCH_WORKERS=1, so this PR's
  three SSA YAML files run one file per process (review P2 #2).
- policyengine_us/system.py: merged without a conflict. #10400 adds an
  import, a module-level install of its input hooks, a class attribute
  and a migrate call in each simulation constructor. This PR adds the
  current-law cap snapshot inside CountryTaxBenefitSystem.__init__.
  The hunks do not overlap.

make format changed nothing after the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No conflicts. Since 8ea4342, main has changed none of Makefile,
policyengine_us/system.py, the SSA variables, reforms or contrib tests,
the self-employment tax variables, reforms.py or the code-health tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover signed subsets, phase-in, cap preservation, disabled equality, reporting floor, HI, deduction, and benefit credit with generated batches and explicit edge examples. Keep private simulation policies isolated and supply explicit earlier earnings history for the default AIME lookback.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Avoid reading a post-2012 SE parameter while computing default-lookback AIME before the reform starts.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep both structural reform registrations after the new main-side S.770 section 8 addition.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Use the calculated cap-, floor- and factor-aware exclusion while owners' draw is active, and preserve the existing manual hooks otherwise. Add joint examples and generated inactive-path equivalence; reject passive-inclusive joint draws until their active/NII allocation is supplied.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Retain the reviewed S.770 bridge and both reform registrations. Main updates the version and state AGI invariant generation; no merge hunk needed manual resolution.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@codecov

codecov Bot commented Oct 28, 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 (b5fb5ca).
⚠️ Report is 732 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10483   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         1    -3     
  Lines           76        26   -50     
  Branches         2         3    +1     
=========================================
- 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.

MaxGhenis and others added 5 commits October 28, 2026 12:55
Use Core instants in the raised-cap class fixture and retain every YAML case while grouping each existing file within five reform combinations. Keep three generated public-simulation batches per route, all explicit edges, and forty fast formula examples to reduce measured test cost.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Stage the independent baseline and disabled calculations so their full historical parameter caches do not coexist. Retain the 45-year checks, use an exact hand baseline for credit-off AIME, and cover the inactive NIIT factor validation path.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Compare fresh public baseline and disabled simulations with exact outputs while sharing one privately owned, fingerprinted parameter history. Restore the complete baseline variable registry between examples to avoid repeated cold 45-year histories.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Use Core parameter types when reading child keys, scale brackets and scalar histories. A regular parameter node can expose a child named brackets; treating that child as a list raised KeyError in the disabled-reform differential.

Co-authored-by: OpenAI Codex <noreply@openai.com>
@MaxGhenis
MaxGhenis merged commit 315a9a3 into main Oct 29, 2026
36 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10483 at b5fb5cabb2227fab8e00d40a9fc3bb964768f0cd (squash): owners' draw as SECA earnings, a contributed reform that is off by default.

  • Reviews: GPT-6.1 Sol, two rounds.
    • Round 1 at 1aeb9e497f requested changes: commit Hypothesis properties for the stated invariants, and keep Add composable Social Security benefit levers #10400's per-file SSA YAML route through the main merge.
    • Round 2 APPROVE at b5fb5cabb2 with no P1 or P2 (Subfleet job 20261028-175102-hub-us10483-r2-g; ~/reviews/us-hub/reviews-gen/us-10483/review-r2.md). It found both round 1 items fixed and checked every merge's two parents, the S.770 §8 bridge and the current-main merge-tree. Its one P3: the 16 added Python tests take 174 s of JUnit time against a target of about 120 s. A test-only follow-up is parked for after this merge.
  • Mutants: the hub ran all 13 of the build's mutants at default QoS on this head, one process each under 2 GB and 900 s. All 13 were killed, and the unmutated file passes 29 of 29 (~/reviews/us-hub/reviews-gen/hub-mutants-1029/RESULT.md). Mutant 10 removes the AIME guard; its kill shows the read of the $400 reporting floor, a parameter whose values start in 2013, in inactive earlier lookback years. Its end-to-end YAML run hit the 2 GB cap during import and is unrun.
  • Gates checked live at merge:
    • gh pr checks exits 0 (35 pass, 1 skipped);
    • the latest Pull request run at the head (40760043706) concluded success;
    • MERGEABLE;
    • not a draft;
    • no reviewer's latest review requests changes;
    • head pinned.
  • Overlap with main: main moved from the CI base bf108e931a to 8b17892d2c: Add North Dakota LIHEAP regular heating assistance #10342 (North Dakota LIHEAP), Limit the self-employed health insurance deduction to net profit less half of SE tax and plan deductions #10109 (the self-employed health insurance limit), Extend Maine's childless EITC expansion to 2021 students aged 19 to 23 #10526 (Maine childless EITC) and version bumps. None touches an area this PR touches, so the overlap guard passes, and the merge-tree is clean. Semantically, Limit the self-employed health insurance deduction to net profit less half of SE tax and plan deductions #10109's limit reads self_employment_tax_ald_person, which this reform overrides. With the reform disabled, both are unchanged, as the disabled-reform property shows. With it enabled, the limit's line 6 share (Schedule C over Schedule C + farm + partnership profits) leaves the draw's net earnings out of the denominator, while line 7's SE tax now includes the draw's SECA. A filer with both a Schedule C plan business and an owners' draw gets too much SE tax charged to the plan business, which understates the deduction. That affects only the contributed reform, for people with both, and is queued as a follow-up (the reform adds its draw earnings to line 5). The hub verified the combined tree at main 8b17892d2c: the three draw YAML files (17, 6 and 5 cases), test_owners_draw.py (29), the person-adds guard (10) and the source-conflict register test (103) all pass.
  • Impact: none at baseline. The hub's full-file pair (main bf108e931a against b5fb5cabb2, 2026) shows zero change in all ten outputs: self-employment tax, taxable SE income, NIIT, AGI, Social Security, taxable Social Security, income tax, state income tax, household net income and benefits. The reform is off by default. The PR body's impact section and line are updated.
  • Axiom: n/a. A contributed reform, not enacted law.
  • CI cost: Contrib other-shard-1 took 54:40 against 45:15 at main; the three draw YAML files add about 538 s to its serial SSA step. The parked follow-up targets this too.

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.

1 participant