Skip to content

Carry Schedule E line 32 and its passive subset on the US PUF donor - #1251

Merged
MaxGhenis merged 16 commits into
mainfrom
us-puf-line32-passive-1239
Oct 31, 2026
Merged

MaxGhenis merged 16 commits into
mainfrom
us-puf-line32-passive-1239

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Resolves #1239. The US PUF donor carried only nonpassive partnership and S-corporation income. This PR has it carry Schedule E line 32 as partnership_income and the section 469 passive subset as a new leaf, passive_partnership_s_corp_income, which feeds the NIIT base in policyengine-us 2.2.1.

Max ruled yes on d1472, d1489 and d2183 (see "Decision"). This PR lands first, then #1259, then #1261.

What was wrong

PolicyEngine-US 2.2.1 counts partnership_income + s_corp_income (= partnership_s_corp_income) in gross income. It documents passive_partnership_s_corp_income as "the section 469 passive subset of partnership_s_corp_income" and adds it to net_investment_income. The calibration targets that bind this column are the line-32 totals: HT2 A26270 and the Table 1.4 partnership and S-corp legs.

The donor built the column from the nonpassive fields only:

  • puf_2024.h5 (sha256 7669f5b5…) stores partnership_s_corp_income equal to (E25980 − E25960) + (E26190 − E26180) on all 207,692 regular records (ratio 1.0000 on every nonzero record).
  • Microcosm copied that into partnership_income. Its raw-field fallback read the same fields.
  • passive_partnership_s_corp_income was never populated, so NIIT saw no pass-through income.

The PUF fields

These come from the IRS SOI General Description Booklet for the 2014 Public Use Tax File. Its annotated Schedule E Part II maps each line 28 column to a field, and the TY2015 file uses the same codes.

Line 28 column Partnership S corporation
(f) passive loss allowed (Form 8582) E25920 E26160
(g) passive income E25940 E26170
(h) nonpassive loss E25960 E26180
(i) section 179 expense deduction E26110 E26100
(j) nonpassive income E25980 E26190
  • Line 32 is E26270: (g) + (j) − (f) − (h) − (i), summed over both entity types.
  • The passive subset is (g) − (f). Column (f) is the loss allowed, so the section 469 limit is already applied.
  • Section 179 sits under the form's "Nonpassive Income and Loss" heading, so it belongs to the nonpassive remainder.

Checked on the pinned TY2015 file (puf_2015.csv, sha256 0a7fd643…):

  • Line 32 equals its columns within rounding. E26270 equals the ten column fields on all 207,692 regular records, to within the booklet's per-field rounding (disclosure step 9: four significant digits above $100K, the nearest $100 or $10 below that, and $2 for nonzero amounts under $5). The weighted absolute residual is $0.135B on a $621.9B total.
  • The passive legs are what filers report as investment income. On Form 8960 filers, the weighted regression of investment income on Schedule E legs (E85120 less interest, dividends and gains) gives 0.97 on passive and 0.03 on nonpassive. For filers with only passive pass-through income, the Form 8960 Schedule E amount (E85050, less rent, estate and farm rent) divided by passive has median 1.000 (89% within ±10%); for filers with only nonpassive income, the same ratio has median 0.000.

The change

  • New us_runtime/puf_source_schedule_e.py. It reads E26270 and the passive legs from the pinned TY2015 rows and aligns them to the processed tax units by RECID, the same seam puf_source_agi uses for E00100. It fails closed when:
    • a regular record's E26270 departs from its columns beyond the booklet rounding;
    • the processed partnership_s_corp_income is no longer exactly the archived nonpassive sum. That catches a future artifact that already carries line 32, or one that has been uprated.
    • a synthetic bucket does not carry its aggregate row's total.
  • The four disclosure aggregate rows. They were replaced upstream by 3,985 synthetic records whose amount columns were calibrated one at a time, so no per-record line 32 exists for them. Each row's line-32 and passive totals are allocated over its records in proportion to their processed nonpassive amounts. This keeps every bucket's totals and each record's sign. It covers about $12B of $677B.
  • puf_source_agi.py. The source-year layout is factored out so both seams share it. The E00100 vector stays bit-identical to its pinned hash on the real inputs.
  • puf_support.py:
    • The new leaf follows s_corp_income in the chained QRF, so its draw conditions on the drawn total.
    • It is placed on the people who carry the line-32 total, in proportion to the magnitude of each person's own line 32. A person whose line 32 is a loss takes a share; a person with no line 32 takes none.
    • It is zeroed where the unit drew no line-32 total. On the donor this pattern is 27 of 207,692 regular records: 3,120 weighted returns carrying −$14.0M of net passive amounts ($20.5M in absolute value; out/zero_total_passive.txt).
    • It is signed-mass calibrated like the other sparse two-signed leaves.
    • The processed nonpassive column no longer stands for partnership_income. A processed donor without the supplement now fails with "cannot derive".
    • The raw-field path derives each entity's share of line 32 and the passive subset.
  • The late ACS transfer now carries the leaf (it joins partnership_income in puf_tax_itemization__batch_3), so _apply_post_transfer_structure applies the same coherence to the cells it fills. The rule zeros a transferred passive amount when the person's own line 32 or the tax unit's line-32 total is zero, and leaves pre-existing cells untouched. The rule is bound into the transfer's execution-contract identity and the imputation schema.
  • Both donor loaders (load_puf_tax_unit_donor and the base tool's _puf_tax_unit_donor_from_h5) pass the supplement through and record its receipt under schedule_e_line_32.
  • A release reform probe, niit_passive_pass_through_exclusion, drops the leaf from the 2024 NIIT sources and requires a positive NIIT effect of at least $1M. A build that loses the leaf therefore fails as a structural zero.
  • tools/graph_us_post_transfer_fixture.py was still loading helpers from a test file that has since moved. It now loads them from test_support/, and the H3 fixture is regenerated: only the pool digest line changes, and the sources and expected outputs are byte-identical.

Dollar basis

Line 32 keeps the processed column's TY2015 dollars. #1248 (evidence PR #1223) owns aging the donor. It already lists partnership income among the columns left at TY2015. Its rebase can age both leaves from TY2015 with the partnership/S-corp series. The drift check above refuses to layer line 32 onto a column that has already been changed.

What this means for #722 (and PR #734)

The PUF observes passivity directly, with the section 469 limit applied, and filers' own Form 8960 agrees with it (above). #722's anchored-prior design (an SCF-shaped passive share, levelled to Form 8960) is unnecessary for the PUF channel. The ASEC channel carries no partnership income, so nothing is left for it to assign. Building #722's share on top of this PR would assign passivity twice.

#722's open note still applies: the engine counts rental_income in NII in full.

Invariants

Invariants 1 to 3 are Hypothesis property tests with 300 examples each. So are the frame coherence rule (200 examples, on both the primary and the transfer paths) and person placement (300 examples). Invariant 4 is tested on fixed fixtures and also has a pinned check on the real inputs.

  1. Line-32 identity. E26270 equals (g) + (j) − (f) − (h) − (i) within the booklet's per-field rounding. This is tested after independent disclosure rounding of every field, and a line 32 beyond the bound is refused. It also holds on every regular record of the real file (out/rounding_bound.txt).
  2. Conditional dominance. Where a return has no nonpassive loss and no section 179 deduction, passive ≤ line 32 + rounding. In the real file, 9,179 records have both amounts positive and neither deduction; none breaks the rule.
  3. Leg bounds. −(f) ≤ passive ≤ (g), and −(all loss columns) ≤ passive ≤ (all income columns).
  4. Donor alignment. Every regular processed tax unit carries its record's E26270 and passive amount exactly. Each aggregate bucket's weighted line-32 and passive totals equal its row's. The output is bit-deterministic. The real-input vectors are pinned by hash in a test that runs when the restricted files are present.
  5. Frame coherence. No passive amount where the unit drew no line-32 total, after the primary pass and after the late transfer. The transfer also clears a unit whose people's line-32 amounts cancel. Within a unit, the passive amount goes only to people with their own line 32, in proportion to its magnitude, and sums to the unit's draw. The public transfer is tested end to end, including its provenance counts.

"Passive ≤ total wherever both are positive" is not an invariant, by design. A $300 passive gain and a $100 nonpassive loss put $200 on line 32, while Form 8960 counts the $300. The donor carries that pair as it is (test_passive_can_exceed_a_positive_line_32). In the real file, 2,745 records (0.20M weighted returns, $2.6B) have passive above a positive line 32. Each of them either has a nonpassive loss or section 179 deduction, or exceeds line 32 by no more than the PUF's rounding (invariant 2).

Do the tests catch wrong formulas? I seeded 11 mutants into puf_source_schedule_e.py:

Each mutant fails between 1 and 13 tests, and the unmutated module passes (out/mutation-schedule-e.txt).

Three more mutants check the frame-side rules: placing passive by the clipped basis, clearing on the person's own line 32 only, and dropping the transfer hook. Each fails at least one test (out/mutation-r2.txt).

Donor totals (2024 donor weights, TY2015 dollars)

Before (nonpassive only) After (line 32)
Net $642.4B $676.6B
Positive leg — $815.0B
Negative leg — −$138.4B
Returns with nonzero amount 7.70M 9.46M
Passive subset, net (positive / negative) $0 $83.0B ($105.7B / −$22.7B)

HT2 N26270 (TY2022) counts 9.55M returns; the line-32 carrier count is now 1.0% below it (9.46M against 9.55M), against 19.4% below before.

Frame and engine results

Setup

  • Treatment. The Route A base stage rebuilt on Modal from Route A's own commit (4b57d15a2) plus this change only. The code is on the evidence branch us-puf-line32-passive-1239-route-a (d02fb2bca), which is not for merge.
    • It used Route A's plan inputs, run line32-1239-base-d02fb2bca843.
    • It completed in 6,446 s for about $5.86 at list price.
    • Base h5 sha256 e0722906…, matching the receipt.
  • Control. The certified Route A base (0fb05b67…).
  • Weights. Each base is weighted two ways: by its own initial weights, and by the certified Route A final calibrated weights. The release keeps every base household id, and under those weights the control reproduces the release's $1,020.5B exactly.
  • Engine. PolicyEngine-US 2.2.1 ran on each base on Modal, loading the dataset the way microcosm's reform validation does (USSingleYearDataset, county SPM).
  • These are fixed-weight comparisons. A re-calibrated release was not run: the release stage took 33 hours on the build Mac and is not registered for Modal.

Line 32 against SOI TY2022 (Route A final weights)

Control Treatment SOI
Line 32, net $1,020.5B $1,055.7B HT2 A26270 $1,033.3B
Positive leg $1,128.5B $1,212.1B Table 1.4 net-income legs $1,299.8B (by entity)
Negative leg −$108.0B −$156.4B Table 1.4 net-loss legs −$268.6B (by entity)
Returns with nonzero line 32 11.49M 12.76M HT2 N26270 9.55M
Passive subset, net (positive / negative) — $140.4B ($156.9B / −$16.5B), 5.10M carriers —

At initial weights the line-32 net goes from $306.9B to $455.3B, the carriers from 4.86M to 6.37M, and the passive subset is $46.2B.

One draw dominates that change. A single tax unit with weight 644 drew a $218.9M line-32 value, a Forbes-tail synthetic donor record, and contributes $141B on its own. The control's draw happened to miss that record. Leaving out the 10 largest contributors in each base, the net rises from $257.1B to $301.6B (+17%). Carrier medians fall, because passive income adds many small carriers.

Engine deltas (treatment − control, Route A final weights)

Delta Of which: 10 largest line-32 draws Rest of the frame
Net investment income +$140.4B +$25.8B +$114.6B
NIIT +$3.80B (on $60.47B) +$0.97B +$2.82B
QBI deduction −$0.80B (on $229.25B) +$7.63B −$8.43B
AGI +$76.7B +$83.3B −$6.6B
Federal income tax +$29.2B +$29.4B −$0.2B
  • The robust effect is the passive leg. NII and NIIT move broadly across the frame.
  • The AGI and income-tax deltas are the tail draws. Outside them, AGI and income tax barely move.
  • QBID falls outside the tail. That comes through the outputs drawn after partnership_income in the chain. At initial weights, UBIA moves −$113.0B, W-2 wages −$13.0B, SSTB self-employment income +$25.6B and SSTB W-2 wages +$24.7B.

Frame invariants and the tail gate

  • Frame invariants.
    • No passive amount sits in a unit with zero line 32.
    • No person carries passive income without their own line 32.
    • This base predates the round-2 placement fix, and the receipts stand for two reasons:
      • Every receipt here is a tax-unit or frame total, and the fix only moves amounts between people in a unit.
      • It moves nothing on this frame either. The ASEC half carries no line 32 (its channel's line-32 total is $0 in out/compare_bases.json). So on the PUF clones partnership_income has no person basis and lands on each unit's first person, which is where both the old and the new rule put the passive amount.
    • Passive exceeds a positive line 32 for 0.029M of 1.68M both-positive units (1.7%), against 10% on the donor.
  • Tail gate. The release's tail-concentration gate fails a column whose top 100 records hold more than 75% of weighted absolute mass.
    • partnership_income: 0.324 in control (Route A recorded 0.324) and 0.399 in treatment.
    • The new passive leaf: 0.660 at final weights. It passes, but with thin headroom on a sparse column (7,513 carriers).

Why calibration still has to lift the carriers

The certified Route A release's pre-calibration partnership net is $314.7B (out/release_route_a_partnership.json). Three things separate that from HT2's $1,033B:

Pins this moves

These are the values the repo's own checkers observed; nothing was transcribed by hand.

  • Late transfer: 19 groups / 71 targets (52 numeric). The new leaf joins partnership_income in puf_tax_itemization__batch_3.
  • Stacked surface: 135; post-PUF: 71; PUF-clone producers: 44; full transfer: 119.
  • Pool projection provisioning: 762 declared absent / 123 materialized, plus the remaining-stage manifest digest. tools/repin_us_pool_engine_contracts.py --check reports them current.
  • Primary QRF target-order digest, and the generator's stage, catalog and graph counts.
  • source_stages.json declares the leaf as a PUF stage output; the frozen generation-0 digest is re-pinned in the generator and its test.
  • Regenerated spec bundle (US spec_sha256 e38e234b…), engine ABI lock, imputation schema (the new post-transfer feature), field-usage claims, inventory digests and counts, and coverage evidence. tools/spec_engine_coverage.py --check passes 42,236/42,236 fields and 41/41 checks.
  • The seed-protocol digests, because the protocol attests puf_support.py's source bytes, and the calibration contract's legacy projection, because the leaf joins its tail-control field list.
  • The late-transfer completion receipt's counts are now derived from the canonical partition and surface rather than written as literals. Their sizes stay pinned once at import (19 groups, 71 targets). The docstrings and docs/us-multispine-operator-ordering.md quote the new counts.
  • The pool tool's reached runtime graph grows from 74 to 75 modules (puf_source_schedule_e.py). The post-export scorer issues 108 requests (42 smoke probes).
  • The reform-probe drift test (engine_scenario) declares the new probe's one removed item. It checks that the probe's NIIT source list differs from the installed engine's baseline by exactly the passive leaf.
  • Test pins that enumerate the surface: stacked spine, multispine pool, late DAG, spec bundle, lineage dashboard, release scorer, battery semantics and field usage. Each value is the one the test observed. Also the graph H3 fixture.
  • generate_us_bundle_from_constants.py --check is clean.
  • The multispine-pool tool spec sha and the minimal-loader golden.

Tests run locally

These ran on head 45a5b9891 with the US-engine venv and pytest -n 4 (out/wide-tests-4.log in the evidence folder):

  • The new and changed PUF tests: test_us_puf_schedule_e.py (new; 36 tests, including the restricted-input check through MICROCOSM_RESTRICTED_PUF_DIR), test_us_puf_support.py, test_us_puf_partnership_se_derivation.py, test_us_puf_donor_io.py, test_us_puf_support_base_builder.py, test_us_puf_e19200_decomposition.py and test_us_acs_transfer.py, including the new late-transfer reconciliation tests.
  • Every file that pins the surface: test_us_stacked_spine.py, test_us_multispine_pool*.py, test_us_late_producer_dag.py, test_us_spec_bundle.py, test_us_post_export_scoring.py, test_us_release_head_to_head_scorer.py, test_us_spine_blindness.py, the engine_free/shared/test_spec_engine_*.py files, engine_contract/us/, engine_workflow/us/test_us_multispine_pool_tool.py, the engine_scenario reform-probe drift test, and microcosm-graph and microcosm-data.

All pass except two microcosm-graph orrery tests. Those fail with ERR_MODULE_NOT_FOUND, because the @axiom-foundation/orrery npm package is not installed in this worktree; they touch nothing this PR changes. ruff check . is clean.

Review

Six rounds of independent review. Rounds 1, 2 and 4 ran on GPT-6.1 Sol; rounds 3 and 6 ran on Opus 5.5 lanes because the Codex lanes were full or held.

Decision

Max ruled yes on d1472 and d1489 (10/24): land #1251, then #1261, with a fresh review that clears the top-tail draws, then a certified US build. That condition needs #1259, and Max ruled yes on d2183 (10/31), so the order is #1251, #1259, #1261. The certified release is untouched.

Follow-ups

axiom: n/a: microdata donor construction; no policy rule changes.

🤖 Generated with Claude Code

MaxGhenis and others added 6 commits October 12, 2026 20:54
The processed PUF's partnership_s_corp_income is the archived nonpassive-only
sum (E25980 - E25960) + (E26190 - E26180). PolicyEngine-US reads
partnership_income + s_corp_income as Schedule E line 32 and
passive_partnership_s_corp_income as its section 469 subset.

- puf_source_schedule_e: read E26270 and the passive legs (E25940 - E25920)
  + (E26170 - E26160) from the pinned TY2015 rows, aligned by RECID; refuse a
  record whose line 32 departs from its line 28 columns beyond the PUF's
  documented per-field rounding, and refuse a processed column that no longer
  equals the archived nonpassive sum. Aggregate-row buckets get their row's
  line-32 and passive totals in proportion to processed nonpassive amounts.
- puf_source_agi: factor the source-year layout so both seams share it (the
  AGI vector is bit-identical on the pinned inputs).
- puf_support: new passive output after s_corp_income in the chain, placed on
  the line-32 carrier, zeroed where the unit drew no line-32 total, and
  signed-mass calibrated like the other sparse sign-mixed leaves; the
  processed nonpassive column no longer stands for partnership_income; raw
  Schedule E fields derive line 32 per entity and the passive subset.
- Both donor loaders thread the supplement and record its receipt.

WIP: registries, tests and the Modal rebuild follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ins (#1239)

The new PUF chain target moves every count and digest that enumerates the
primary QRF chain, the late transfer surface and the stacked authority. Each
value below is the one the repo's own checker observed:

- late transfer: 19 groups / 71 targets (52 numeric); the passive leaf joins
  partnership_income in puf_tax_itemization__batch_3; stacked surface 135 /
  post-PUF 71 / PUF-clone producers 44 / full transfer 119
- pool projection provisioning (762 declared absent, 123 materialized) and the
  remaining-stage manifest digest; tools/repin_us_pool_engine_contracts.py
  reports them current
- primary QRF target-order digest (puf_qrf_chain) and the generator's stage
  counts, catalog closure and graph counts
- source_stages.json declares the leaf as a PUF stage output (frozen
  generation-0 digest re-pinned in the generator and its test)
- regenerated spec bundle (US spec_sha256 e909a04e...), engine ABI lock,
  field-usage claims, inventory coverage digests and the coverage evidence;
  tools/spec_engine_coverage.py --check: 42229/42229 fields, 41/41 checks
- multispine pool tool spec_sha256 and the minimal-loader golden

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- test_us_puf_schedule_e: booklet field roles; the line-32 identity holds
  within the documented per-field rounding after independent disclosure
  rounding and refuses a line 32 beyond it (Hypothesis); passive never
  exceeds a positive line 32 when (h) and (i) are zero; the passive subset
  stays within its loss and income columns; the intended counterexample
  (passive $300, nonpassive loss $100, line 32 $200) is carried, not clipped;
  donor alignment carries literal E26270 on regular records and each
  aggregate row's weighted totals on its bucket; determinism; fail-closed
  refusals; and a pinned-vector check on the restricted inputs when present.
- test_us_puf_support: raw fields derive line 32 per entity and the passive
  subset; the processed nonpassive column no longer stands for line 32;
  supplement validation; passive zeroed wherever the unit drew no line 32
  (Hypothesis); passive placed on the line-32 carriers; passive is
  signed-mass calibrated.
- Loader tests thread the new seam and its receipt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps #1209's archived Schedule SE derivation for
partnership_self_employment_net_earnings beside this branch's line-32 raw
derivation, and re-pins the identities the combined puf_support bytes and
source manifest move: seed protocol/map, US spec_sha256 98ab131c..., the
minimal-loader golden, and the regenerated coverage evidence
(tools/spec_engine_coverage.py --check: 42229/42229, 41/41).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eleases (#1239)

Review round 1 (Sol): the new leaf is also a late ACS-transfer target (it
joins partnership_income in puf_tax_itemization__batch_3), but the
zero-total coherence rule ran only in primary PUF finalization, so a
person-grain transfer draw could place a passive amount where no line-32
total exists.

- acs_transfer: reconcile_acs_passive_partnership_s_corp zeros newly
  imputed passive cells whose person's own line 32 or tax unit's line-32
  total is zero, keeps pre-existing cells, and records its counts in the
  provenance. It is bound into the transfer execution-contract identity
  (post_transfer_structure.passive_partnership_s_corp) and declared in the
  generated imputation spec and its schema.
- Release reform probe niit_passive_pass_through_exclusion: dropping the leaf
  from the 2024 NIIT sources must move NIIT by at least $1M, so a build that
  loses it fails as a structural zero.
- stacked_spine: the late-transfer completion receipt's canonical target
  count is 71 (it was a runtime literal 70); docstrings follow.
- Tests: reconciliation examples, refusals, a Hypothesis property over random
  draws, and the contract binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ure tool (#1239)

Review round 1 (Sol): stale pins remained outside the spec-engine checker.

- Tests that enumerate the stacked, late-transfer and primary surfaces now
  carry the values the tests observe (71 late / 135 terminal / 119 transfer
  targets, 66 primary targets, 44 PUF-clone producers, 177 catalog columns,
  376 battery legs, field-usage mode counts and the rest), plus the golden
  digests that moved with them.
- Field-usage and inventory pins follow the post-transfer contract change;
  tools/spec_engine_coverage.py --check: 42236/42236 fields, 41/41 checks.
- tools/graph_us_post_transfer_fixture.py loaded helpers from a test file
  that has since moved; it now loads them from test_support. The regenerated
  H3 fixture changes only the pool digest line.
- docs/us-multispine-operator-ordering.md quotes the new counts and batches.
- The new module is classified in the spine-blindness registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 4 commits October 13, 2026 03:36
…1239)

Review round 2 (Sol): the passive leaf's fallback placement read its
line-32 basis through the shared allocator, which clips negative values to
zero. In a unit whose line 32 is a loss carried by its second person, the
passive amount fell to the first person, who has no pass-through activity.

- _write_person_tax_unit_totals takes fallback_basis_is_signed_superset. For
  outputs that are a subset of their basis's signed sum (only
  passive_partnership_s_corp_income), the weight is |own line 32|, and a unit
  whose people all have a zero line 32 gets nothing rather than a
  first-person park. It refuses when no basis column is on the table.
- _allocation_basis_values carries the shared validation unclipped;
  _nonnegative_allocation_basis_values clips it as before.
- Tests: the reproduced case and mixed signs, a refusal, a Hypothesis
  property (no passive without own line 32, shares share the total's sign,
  units sum to their draw or to zero), and the finalize path parametrized
  over a positive and a negative line-32 draw. A mutant that drops the call
  site's flag fails the negative case.
- The zero-total docstring now gives the donor receipt's sign: -$14.0M net.

On the Modal-built evidence frame no person carried passive without their
own line 32, so the tax-unit receipts in the PR are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oves (#1239)

Review round 2 (Sol):

- Transfer tests: an explicit unit whose people's line-32 amounts cancel
  (an own-amount-only rule would keep both subsets), property draws with
  equal and opposite amounts, and a public transfer_acs_inputs regression
  that checks final values, the kept measured cell and the provenance
  counts. Dropping the unit clause or the hook now fails a test.
- engine_scenario reform-probe drift test: the new
  niit_passive_pass_through_exclusion probe declares its one removed item;
  the installed engine's NIIT source list differs by exactly the leaf.
- stacked_spine: the late-transfer completion receipt derives its group and
  target counts from the canonical partition and surface; their sizes stay
  pinned once at import.
- Pins moved by the puf_support.py edit (the seed protocol attests its
  source bytes): seed protocol and seed map digests, coverage evidence, US
  spec sha (1516b2f6...), the minimal-loader golden.
- Pins the leaf moves that the engine-contract suite enumerates: inventory
  counts (71 late, 66 primary, 177 columns, 229 compiled outputs, 2,746
  producer inputs, 335 references, 935 tail-control fields), the typed
  closure and catalog counts, the calibration legacy projection (the leaf
  joins its tail-control field list), the pool tool's 75-module runtime
  graph and the scorer's 108 requests.

tools/spec_engine_coverage.py --check: 42236/42236 fields, 41/41 checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new monetary leaf is one more catalog column (177) and one more column
whose unit the engine metadata does not declare (90). Observed by
test_us_catalog_has_complete_explicit_contracts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nts (#1239)

Review round 3 (APPROVE WITH NITS):

- Finalization refuses a person-output order that places the passive leaf
  before partnership_income or s_corp_income, or without either in the same
  pass, so it can never be split by a stale line-32 column.
- Signed-subset placement ignores the values the passive column already
  holds and always follows the magnitudes of the people's own line 32.
- Tests: the refusal on a reordered chain through finalize and on the
  helper, and a stale mixed-sign passive column that no longer steers the
  split.
- Docstrings say 42 smoke probes; docs/spec-engine.md marks its 70/37
  counts as of its ruling (71/38 since #1239).
- The puf_support.py edit moves the seed-protocol digests again: seed
  protocol and map, coverage evidence, US spec sha (e38e234b...), the
  minimal-loader golden.

tools/spec_engine_coverage.py --check: 42236/42236 fields, 41/41 checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 13, 2026 08:36
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Status at 45a5b9891: four review rounds, the last two APPROVE WITH NITS (nits fixed) and APPROVE; the local suite passes apart from two orrery tests that need an npm package not installed here (out/wide-tests-4.log); CI was queued behind a runner backlog at this writing. The merge is queued for Max as d1472, together with closing PR #734 as superseded for the PUF channel. Merge only on green CI.

MaxGhenis added a commit that referenced this pull request Oct 13, 2026
- test_support/spec_engine_inventory_coverage.py: the engine-contract
  inventory counts follow the S-corp transfer target (late targets 72,
  itemization targets 39, producer inputs 2747, compiled outputs 230).
- Inherited from #1251, whose own CI is red on them at 45a5b98:
  - test_puf_qrf_chain.py locked the primary target order at 65; #1239's
    passive leaf made it 66 (the order digest was already current).
  - engine_workflow test_us_post_export_scoring.py pinned 18 smoke
    baseline keys; #1239's NIIT passive probe made it 19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 13, 2026
… unchanged

single_record_concentration gets Hypothesis properties for the share chain
(0 < top-1 <= top-k <= 1), the pigeonhole floors, scale-freedom in the
weights (exact for powers of two), sign and value-scale invariance,
totality on any float64 input (zero or non-finite mass is reported as
None, never divided by), order-freedom, monotonicity, and two
differentials: against tail_concentration_gate's share and counts, and
against exact rational arithmetic. Example tests pin the #1251 line-32
shape, ties, and two float cases: the gate's own top-k sum can pass the
total by one rounding step (the measurement caps at 1), and a column with
at most top_k carriers reports exactly 1. All ten mutants of the function
are killed.

The release tool once again resolves weights only where main did for
sparse outputs. A dense output whose entity's weights do not resolve is
listed under single_record_concentration.unmeasured with the reason, so
the reporting-only measurement cannot fail a run the gate would pass. A
property checks the gate's verdict, failures and details equal the shared
gate on the sparse columns alone for random geometries. The dry run's
verdicts, lines and details are unchanged by any values in the block, or
by its absence; the probe compares the block with a release that has it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 13, 2026
…its weights

README and a changelog entry describe surface.single_record_concentration:
reported for every numeric QRF output, never graded, no share for a column
without mass. The motivating figures are now paired with the weights they
were measured at: in the #1251 rebuild the weight-644.7 unit carried 21.7%
of line 32's weighted absolute mass at initial weights while the top 100
tax units carried 44.9%; at the certified Route A final weights the two
were 4.4% and 39.9%. The first commit's message paired 21.7% with 0.399.
The worked example is re-tuned to the initial-weight top-100 share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main moved 508 commits since 45a5b98. The four pin files conflicted:
- inventory_coverage.py, the F0 coverage evidence, the loader golden and the multispine spec digest take main's history.
- This branch's own puf_support counts and digests are re-applied: targets 71, inputs 2,746, compiled outputs 229, primary tuples 66, the 38-stage source manifest.
- The seed protocol and map digests, the US spec digest and the loader golden are recomputed on the merged tree.
- Coverage checks 41/41.

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

Copy link
Copy Markdown
Contributor Author

Synced with main (2026-10-21). Head f59263c2c merges origin/main (508 commits) into the r4-approved 45a5b9891.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 21, 2026 20:16
The locked-order test still said 65 targets. This PR adds
passive_partnership_s_corp_income after s_corp_income, and its digest constant
already matches the 66-target order; only the count was stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1239)

main moved the engine-dependent post-export scoring test into
engine_workflow/ after this branch updated its engine_free copy. The moved copy
still expected 18 reform-coverage-smoke baseline keys. The passive
partnership/S-corp probe adds the 19th, as the engine_free copy already says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's microcosm#1380 adds a self_employment_components post-transfer
contract beside this PR's passive_partnership_s_corp contract. Both are
kept in imputation.yaml, the imputation schema, acs_transfer.py and the
bundle generator. The post-transfer step runs the passive reconcile, then
the self-employment step. The two touch disjoint columns.

The repo's repin sequence regenerated the pins:
- the legacy repin pass;
- generate_us_bundle_from_constants;
- the compiler repin pass, run twice (the first pass counts coverage
  before its own digests land);
- spec_engine_coverage.

The source_stages.json digest moves in its four pin sites. Counts equal
ours + theirs - base: 42,287 configuration fields, 81 runtime modules and
938 tail fields. Coverage reports 41/41.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 29, 2026
The merge brings in us-puf-line32-passive-1239 at c50bda1, which
carries main at 846b48a. The conflicts are spec pins plus one test
comment, where #1251's note on the 66th primary target stays. Counts take
ours + theirs - base. The repo's repin sequence regenerates the digests:
the legacy pass, generate_us_bundle_from_constants, the compiler pass run
twice, and spec_engine_coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's microcosm#454 and this PR each add one reform coverage probe. Each
side moved the same pins from 41 to 42 probes and from 18 to 19 smoke
baseline keys, so the merge took identical text and lost one of each.
The merged release ships 43 probes, 20 smoke baseline keys and 109
recorded reform passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit f36a14a into main Oct 31, 2026
10 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merged at 0550bb838 on Max's rulings d1472, d1489 and d2183. Round 6 (Opus 5.5 lane) approved 49bfd37b1. The merge of main after it touched no draw-channel file, and same_edit.py and increment_check.py found no pin problem. All 10 checks passed at 0550bb838.

MaxGhenis added a commit that referenced this pull request Oct 31, 2026
#1251 landed first, as d2183 ordered. Resolved count pins as ours + theirs -
base and re-derived every digest with the repin sequence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 31, 2026
#1259 refuses a tail-bound finalize with no sink, and #1251's two passive
tests landed on main without one, so neither PR's own CI saw the clash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 3, 2026
CI's engine-free jobs failed test_a_rounding_step_never_lifts_the_top_k_share_past_1.
The test asserted that the gate's share passes 1 on an input where that
depends on the order np.partition leaves the tail in, which numpy leaves
undefined. On CI's x86-64 runners numpy 2.4 partitions float64 through
x86-simd-sort, which sorts a small array outright; the sorted tail sums to
the record-order total, a share of exactly 1.0. macOS arm64 uses
introselect, whose order sums 2 higher, a share of 1.0000000000000002. The
exact share rounds to 1, so the measurement's capped 1.0 was right on both
platforms and the test was wrong.

- Replace the example with [2**53 + 2, 3, 1, 0.5] at top_k = 3, the
  smallest gate-checked input that passes 1 under every legal tail order
  (1.0000000000000002 or 1.0000000000000004), and keep CI's input as a test
  that walks all 5,040 of its tail orders.
- Add a numpy stand-in that returns a chosen legal partition, and
  properties that draw the arrangement: no arrangement lifts top_k_share
  past 1, it equals min(the gate's share, 1), top_k = 1 gives the largest
  mass over the total, and a larger top_k lowers the share by at most
  rounding. The totality property now fails on any numpy warning. The
  module docstring states eleven invariants; all 14 mutants of the
  measurement die.
- Quote one measurement of the #1251 line-32 draw everywhere: #1259's
  seed-0 rerun at initial weights, 21.7% top-1 and 45.1% top-100 of
  16,001 carriers (docstring, README, changelog, probe fixture, example
  test).
- State that top_k_share is the gate's share capped at 1, why the gate's
  share can pass 1, and which bounds hold exactly and which to rounding.
- Docstrings and comments only elsewhere: the release tool's rule for
  dense outputs whose weights do not resolve, and active voice in the
  prose this PR adds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

US frame partnership/S-corp income is nonpassive-only: the donor builds it from E25980-E25960 + E26190-E26180, not line 32 (E26270)

1 participant