Skip to content

Split PUF E19200's non-mortgage residual into points, premiums and investment interest - #1137

Draft
MaxGhenis wants to merge 2 commits into
mainfrom
e19200-points-premiums-investment-leaves
Draft

MaxGhenis wants to merge 2 commits into
mainfrom
e19200-points-premiums-investment-leaves

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

PUF E19200 currently assigns deductible points and qualified mortgage-insurance premiums to investment_interest_expense. This makes the section 163(d) cap disallow those mortgage deductions for the wrong reason. Max's decision d1054 calls for separate leaf facts before revisiting PolicyEngine-US #9977.

This change preserves split_us_puf_e19200_by_agi_band(total, agi) and adds split_us_puf_e19200_residual_by_agi_band(residual, agi). Keeping the existing two-output API lets existing callers retain their mortgage calculation and makes its bit identity directly testable. The new API returns points, premiums and investment; each uses the same 22 TY2015 SOI AGI bands.

Published component Person input Residual allocation
CN deductible points home_mortgage_points CN / (CN + CP + CR)
CP qualified mortgage-insurance premiums, before AGI phase-out mortgage_insurance_premiums CP / (CN + CP + CR)
CR investment interest investment_interest_expense CR / (CN + CP + CR)

Conservation uses three nested binary splits: the existing total/mortgage split, residual/investment versus points-and-premiums, then points-and-premiums/points versus premiums. Each uses x = parent * share; y = parent - x; x = parent - y. At least one subtraction is exact by Sterbenz's lemma, as the existing proof establishes; the two children therefore sum to their parent in exact real arithmetic. Consequently the four leaves sum exactly to E19200, and math.fsum reconstructs every positive finite input bit-for-bit. Donor construction stores the mortgage splitter's array directly, avoiding an extra divide/multiply round trip. The loader fails closed on zero CN + CP, and the splitter explicitly handles an absent points-and-premiums denominator.

Two precision qualifications are tested explicitly: math.fsum canonicalizes accepted negative zero to positive zero, as it already did for the original split; published-share comparisons need total-scale ULP bounds and a subnormal quantization allowance, since complement subtraction cannot guarantee a few relative ULPs for arbitrarily tiny leaves.

Surfaces extended:

  • Donor defaults, nonnegative fields, independently sourced nonzero overwrite guards and raw-E19200 quarantine: all three residual leaves need identical source treatment.
  • Primary QRF order/checkpoints and late transfer batches/ownership: the two new Person targets must be learned and transferred; schema 7 rejects caches of the old whole-residual investment target.
  • L0 export signal guards, Build-J cache guards, release coverage and terminal/by-origin monetary batteries: each source-backed leaf must survive export and completion checks.
  • Source JSON/YAML and source schema: declare three outputs and the CN/CP/CR columns explicitly.
  • Imputation/calibration semantics and schemas: pin all three residual shares and the points-within-points-and-premiums share, restoring float identities after canonical YAML loading.
  • Catalogs, calibration exclusions, imputation allocations, coverage inventories and compiled receipts: keep typed producer and consumer contracts consistent with the expanded surface.
  • Frozen eCPS reference metadata: document the two leaves as post-reference inputs without inventing reference numbers.
  • Operator-ordering documentation and generated concept-coverage reports: describe the split and include the pending input surface.
  • Bundle/concept regeneration tools: provide a narrow reviewed Person/float/year declaration for these two unreleased inputs and preserve the engine lock; ordinary live-engine checks remain intact.

Surfaces deliberately left alone:

  • Form 4952 election and investment-income allocation: these concern investment-interest semantics and keep only investment_interest_expense.
  • SOI fiscal mortgage target mappings: CH measures mortgage interest and excludes CN/CP/CR; comments explain the distinction.
  • Capital-gains tail variable vector: it transfers capital gains; its existing identity consumer automatically receives the expanded SOI identity.
  • Historical eCPS investment restoration list, SHA-locked stored H5 column inventories and historical experiment/journal evidence: those describe existing artifact/reference bytes and retain their original scope.

Validation includes Hypothesis random finite totals and seeded/adversarial examples across all 22 bands, exact Fraction and math.fsum conservation, nested residual grouping, finite nonnegative leaves, differential mortgage bit checks, source shares, top-band investment dominance, CP=0 exact premiums, invalid inputs and denominator rejection. Focused donor, quarantine, QRF, export, DAG, transfer, release-coverage, battery, H5-loader and Build-J tests exercise propagation. Detailed commands and results are recorded in the work report.

Invariant Tests
Exact real conservation, fsum reconstruction, nested residual sum, nonnegative finite leaves, unchanged mortgage bits, share tolerance test_three_leaf_split_exact_conservation_property, test_three_leaf_split_adversarial_totals_in_every_band in test_us_puf_e19200_decomposition.py
All 22 published proportions, investment dominance at AGI $10M+, CP=0 exact premiums test_three_leaf_split_recovers_all_published_band_proportions
Source identity binds every consumed share; zero denominator fails closed test_three_leaf_split_identity_binds_all_consumed_shares, test_three_leaf_source_loader_rejects_zero_points_and_premiums
Stored donor retains mortgage bits and four-leaf conservation test_puf_e19200_donor_preserves_splitter_mortgage_bits_and_exact_leaf_sum in test_us_puf_support.py
No overwriting independent nonzero leaves; quarantine removes all E19200 leaves Decomposition donor rejection parametrization and support quarantine test
New targets invalidate old QRF caches and survive L0 export guards QRF locked-order/stale-schema tests and L0 test_required_us_release_source_columns_enforces_e19200_residual_leaf_signal
Points/premiums are required with no invented eCPS reference shares test_e19200_new_leaves_are_required_without_reference_shares in test_release_input_coverage.py
Typed specs, batches, battery and regenerated receipts match runtime US spec-bundle, late-producer DAG, stacked-spine, multispine and calibration/imputation semantic tests

The SOI asset bytes are unchanged (c3356ae216487f365cb0e0a7ab1ba46843a6c52950b75eae1bfab9b0b80a735a). Runtime SOI identity changes to 82dcaf96e7ef78efa67a88bc556d08a1756551327e0e5b7984bcb0b97d016ca9. QRF now has 67 targets, late transfer 72 and terminal comparison 136.

Pending engine bump: the locked PolicyEngine-US 2.2.1 does not expose the two new names. uv.lock, the engine ABI lock and the engine pin are unchanged. The follow-up release bump must run tools/repin_us_pool_engine_contracts.py and regenerate the live concept surface. The current live concept-coverage check and pool ownership/consumer checks are expected to fail for those missing inputs; the work report enumerates the exact observed checks. No dataset was built, calibrated, published or promoted, and no restricted microdata was downloaded.

The expected registered engine-us failure, on both Python matrix versions, is frame engine_contract/us/test_concept_mapping_policyengine_us.py::test_committed_coverage_report_matches_the_engine (928 pending inputs versus 926 locked inputs). Its full local file has nine passes and this one expected failure.

Auxiliary checks fail when explicitly run with the US extra; their normal engine-free CI environment has no country engine:

  • Build engine_free/us/test_us_multispine_pool.py::test_every_pool_transfer_target_is_an_installed_engine_input_leaf names both missing inputs.
  • Build engine_free/us/test_us_pool_input_consumers.py::test_pool_input_has_engine_or_reviewed_non_engine_consumer[home_mortgage_points] and [mortgage_insurance_premiums] reject the unknown names. The full file has 170 passes and five expected failures, including test_receives_wic_guard_rejects_unreviewed_consumer_mutation and test_guard_rejects_reinjected_consumerless_variable[has_marketplace_health_coverage] / [medicare_part_b_premiums_reported], which encounter a missing leaf before the intended mutation.
  • Build engine_free/shared/test_release_input_coverage.py::TestShippedManifest::test_manifest_is_current and ::TestParityKnownGapRenameProjection::test_anti_rot_accepts_the_shipped_register reject missing engine inputs in the expanded manifest. The normal engine-free full file passes all 87 tests.

Focused passing checks: decomposition 39; donor/quarantine 4; QRF 2; export 5; late DAG 30; transfer 7; stacked spine 5; H5 loader 2; scorer 1; Build-J 2; release coverage 87; US spec bundle 7; calibration semantics 16; imputation semantics 4; engine-free concept mapping 146; synthetic workflow terminal-state/schema identity 4; exact legacy publication 1; full live authority projections 4; locked-US catalog contracts 4. The selected live manifest checks failed only on the two pending names. Across completed targeted runs, 549 tests passed and nine failed solely because the locked engine lacks the new inputs. Generated bundle/coverage bytechecks, all 41 inventory checks, all 49 field claims, full Ruff check and formatting of all 55 touched Python files also pass. The real-PUF storage-dependent artifact test was not run locally.

Initial remote CI exposed stale test-only legacy manifest and nested authority receipt pins; the follow-up commit refreshes those pins while preserving strict byte/live comparisons. The legacy fixture's pool and agreement bytes remain identical. Replacement CI 37667937492 remains queued for the current head as of 2026-10-07 18:56 UTC after more than 21 minutes waiting for runners. Final remote verification is pending.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 7, 2026 13:54
Preserve the mortgage splitter and conserve the residual with nested complements. Propagate all three Person leaves through source, QRF, transfer, export and typed-spec contracts, with regenerated identities and coverage.

Co-Authored-By: GPT-6.1 Sol (subfleet lane) <noreply@openai.com>
Keep strict publication and live authority comparisons current with the expanded E19200 QRF order and transfer target families. Pool fixture bytes and agreement remain unchanged.

Co-Authored-By: GPT-6.1 Sol (subfleet lane) <noreply@openai.com>

This branch has not been deployed

No deployments
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