Skip to content
1 change: 1 addition & 0 deletions changelog.d/fix-raw-puf-partnership-se-fallback.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Derive `partnership_self_employment_net_earnings` from a raw PUF with the archived pipeline's Schedule SE residual instead of the sum of partnership and S corporation losses, and fail closed when a raw PUF lacks any of its eight input columns. The certified default reads the processed donor's `partnership_se_income` column and is unchanged.
16 changes: 8 additions & 8 deletions docs/evidence/spec-engine/us-f0-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1656,13 +1656,13 @@
"compiler_ir.node_slices"
],
"expected": {
"map_sha256": "bdd339868ee0cec07fa3a52a91c4bd97de707c15aeb0dc224b064be9ce04eb4b",
"protocol_sha256": "8d028354d487eadecf3bc4a950d70c95e5c44c792fe7fdf2d52c77e6d86a7886"
"map_sha256": "41a64b8820bf161e316c49e3f2b666a247b58f6f6d50dd4395b89c8f5b1399f6",
"protocol_sha256": "0e4c8a4ace5e10747065669a009691d0eee9179ca42b344f8912c5ca239ebde6"
},
"failures": [],
"observed": {
"map_sha256": "bdd339868ee0cec07fa3a52a91c4bd97de707c15aeb0dc224b064be9ce04eb4b",
"protocol_sha256": "8d028354d487eadecf3bc4a950d70c95e5c44c792fe7fdf2d52c77e6d86a7886"
"map_sha256": "41a64b8820bf161e316c49e3f2b666a247b58f6f6d50dd4395b89c8f5b1399f6",
"protocol_sha256": "0e4c8a4ace5e10747065669a009691d0eee9179ca42b344f8912c5ca239ebde6"
},
"status": "covered"
},
Expand All @@ -1677,7 +1677,7 @@
"compiler_ir.seed_stream_map"
],
"expected": {
"implementation_sha256": "8d028354d487eadecf3bc4a950d70c95e5c44c792fe7fdf2d52c77e6d86a7886",
"implementation_sha256": "0e4c8a4ace5e10747065669a009691d0eee9179ca42b344f8912c5ca239ebde6",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand All @@ -1698,7 +1698,7 @@
},
"failures": [],
"observed": {
"implementation_sha256": "8d028354d487eadecf3bc4a950d70c95e5c44c792fe7fdf2d52c77e6d86a7886",
"implementation_sha256": "0e4c8a4ace5e10747065669a009691d0eee9179ca42b344f8912c5ca239ebde6",
"protocol": "legacy-v1",
"streams": [
"build_model",
Expand Down Expand Up @@ -2599,7 +2599,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "638cd60e97a9b200f8bdfd4f92e67be986f516ac25b9a37508235963e695e640"
"spec_sha256": "3080fe938df62216dc4853c1163639817de3c537e0e932309642fbd3f7d8f266"
}
},
"report_schema_version": 3,
Expand All @@ -2609,7 +2609,7 @@
"country": "us",
"schema_id": "country_spec",
"schema_version": 1,
"spec_sha256": "638cd60e97a9b200f8bdfd4f92e67be986f516ac25b9a37508235963e695e640"
"spec_sha256": "3080fe938df62216dc4853c1163639817de3c537e0e932309642fbd3f7d8f266"
},
"status": "pass"
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@
# #1178: puf_support's source bytes attest CPS Social Security preservation.
# RNG rules and the ordered QRF targets are unchanged. microcosm#104 moves
# both again: the protocol attests microcosm.calibrate.solve.
"seed_map": "bdd339868ee0cec07fa3a52a91c4bd97de707c15aeb0dc224b064be9ce04eb4b",
"seed_protocol": "8d028354d487eadecf3bc4a950d70c95e5c44c792fe7fdf2d52c77e6d86a7886",
"seed_map": "41a64b8820bf161e316c49e3f2b666a247b58f6f6d50dd4395b89c8f5b1399f6",
"seed_protocol": "0e4c8a4ace5e10747065669a009691d0eee9179ca42b344f8912c5ca239ebde6",
"source_manifest": "f4f8bfeb79268a4043a843b47cec1d1c0e58e9f5dd6bc23417ada95446dd7b8e",
"take_up": "9522ce40f7dea569312dd7a7beb474e5a5afadd2f1e10cb5876534c3ef623d35",
"tail": "ac92829c88a1a4fb6460d61190918d5d99c6c377fc8dd8f62f02b332d09bf59c",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,23 @@ def puf_tax_detail_tail_bound_quantiles_identity() -> dict[str, float]:
}
# The omitted 13.7% E17500 share represented reported Medicare Part B premiums.
# Do not redistribute it: the engine computes its distinct singular Part B output.
# Raw-PUF inputs of the archived partnership self-employment derivation
# (:func:`_puf_partnership_se_net_earnings_from_raw_columns`). All eight are
# required: a raw PUF carrying only some of them derives nothing, so the donor
# build fails closed instead of substituting a different quantity.
_PUF_PARTNERSHIP_SE_RAW_COLUMNS = (
"E30400",
"E30500",
"E00900",
"E02100",
"E25920",
"E25940",
"E25960",
"E25980",
)
# The archived pipeline's ``SE_DEDUCTION_FACTOR``: it treats E30400 and E30500
# as already multiplied by this factor and divides it back out.
_PUF_SE_NET_EARNINGS_FACTOR = 0.9235
_PUF_PREDICTOR_PREFIX = "puf_predictor_"


Expand Down Expand Up @@ -4332,11 +4349,10 @@ def _person_source_values(
return _numeric_array(arrays["E25980"]) - _numeric_array(arrays["E25960"])
if output == "s_corp_income" and {"E26190", "E26180"}.issubset(arrays):
return _numeric_array(arrays["E26190"]) - _numeric_array(arrays["E26180"])
if output == "partnership_self_employment_net_earnings" and {
"E25960",
"E26180",
}.issubset(arrays):
return _numeric_array(arrays["E25960"]) + _numeric_array(arrays["E26180"])
if output == "partnership_self_employment_net_earnings" and set(
_PUF_PARTNERSHIP_SE_RAW_COLUMNS
).issubset(arrays):
return _puf_partnership_se_net_earnings_from_raw_columns(arrays)
if output in _PUF_MEDICAL_EXPENSE_CATEGORY_BREAKDOWNS and "E17500" in arrays:
return (
_numeric_array(arrays["E17500"])
Expand All @@ -4363,6 +4379,56 @@ def _person_source_values(
return None


def _puf_partnership_se_net_earnings_from_raw_columns(
arrays: Mapping[str, Sequence[Any]],
) -> np.ndarray:
"""Derive partnership self-employment net earnings from raw PUF columns.

This ports, operation for operation, the derivation the archived
processed-PUF pipeline used for this leaf (the retired enhanced-CPS
repository at 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe,
``datasets/puf/puf.py`` lines 723-746), which matches ``part_se`` in Yale
Budget Lab Tax-Data's ``src/process_puf.R``. It is a
residual, not a read of a Schedule K-1 box 14 field: gross self-employment
earnings less the Schedule C and Schedule F amounts.

The column meanings below are the archived pipeline's own; this module does
not check them against the PUF documentation:

* ``E30400`` and ``E30500``: the taxpayer's and the spouse's taxable
self-employment income, already multiplied by 0.9235. Their sum is
divided by that factor to recover gross self-employment earnings.
* ``E00900`` and ``E02100``: Schedule C net profit or loss and Schedule F
farm income, subtracted from the gross amount.
* ``E25940 + E25980 - E25920 - E25960``: net partnership income. A record
where it is exactly zero has no partnership activity and gets zero.

No S corporation column (``E26180``, ``E26190``) enters the derivation. A
missing value counts as zero, as the archived ``fillna(0)`` did.
``_numeric_array`` also turns a nonnumeric value into zero, where the
archived arithmetic would raise; on numeric float64 arrays the result
equals the archived formula bit for bit.

Args:
arrays: Mapping carrying every column of
``_PUF_PARTNERSHIP_SE_RAW_COLUMNS`` at one common grain.

Returns:
Partnership self-employment net earnings, one value per input row.
"""

raw = {
column: _numeric_array(arrays[column])
for column in _PUF_PARTNERSHIP_SE_RAW_COLUMNS
}
gross_se = (raw["E30400"] + raw["E30500"]) / _PUF_SE_NET_EARNINGS_FACTOR
schedule_c_f_income = raw["E00900"] + raw["E02100"]
has_partnership = (
raw["E25940"] + raw["E25980"] - raw["E25920"] - raw["E25960"]
) != 0
return np.where(has_partnership, gross_se - schedule_c_f_income, 0.0)


_TAX_UNIT_ROLE_COLUMN = "tax_unit_role_input"
_TAX_UNIT_ROLES = ("HEAD", "SPOUSE", "DEPENDENT")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_semantic_hash_has_golden_vector_and_surface_separation(tmp_path) -> Non
# Pin the domain separator, normalization rules, schema-set receipt, and
# exact normative projection as one reviewable golden vector.
assert first.spec_sha256 == (
"aadf7c8cf2b0e19e75d9dc1a7b1d8190e33af9ce62657c438029b504e839867d"
"0c0c8eea8f5d350f3a73867aebfcb82450cb606124dc85cc3772cc2eae971ddb"
)

second_root = _rich_minimal(tmp_path / "xy", note="second", store="local:b")
Expand Down
Loading
Loading