Repository navigation
Pooled prior-year ASEC wages are not aged to the target year #944
Description
Activity
Found by the policyengine-scorecard reform-validation diagnosis (batch 2), PolicyEngine/policyengine-scorecard#146. Memo:
diagnosis/batch2/C.md, item C-1.
Related: #944 (same no-aging behavior, measured on wages), #646 (child-vs-total SPM anomaly), #981 (per-record donor aging), #1044 (default pool moved to income years 2023-2025).Summary
On the certified bundle us-6.2.1 (policyengine-us 2.2.1 +
populace-us-2024-spm-20260915), records from ASEC income years 2022 and 2023 carry their own-year nominal dollars into the 2024 period, while the SPM calculator prices every unit at 2024 thresholds. The 2022-income records (34.1% of child weight) therefore meet a threshold about 13.7% above the one Census used for them; the 2023-income records meet one about 5.8% above.First-order estimate (taxes and benefits not recomputed): testing each record against its own source-year threshold lowers the national 2024 SPM child rate from 15.83% to 13.24% (-2.6 pp) and the total rate from 13.06% to 11.48% (-1.6 pp). This is one component of the child excess in #646. The batch-2 decomposition of the +2.43 pp national child gap vs Census P60-287 is: populace weighting +3.1 pp, this item +2.6 pp, the policyengine-us partnership-income omission (PolicyEngine/policyengine-us#9919) +1.3 pp, other resource differences -4.5 pp (all first order).
What microcosm documents (@ 581b569)
docs/us-asec-source-pins.md:216-220- Money. The pooled person tables carry nominal dollars of each income
year, as they did for 2022-2024; no base-build stage restates them by source
year.target_agingages calibration targets, not survey records. The mean
income year of the default pool is 2024, the target year; for the old pool
it was 2023.
docs/us-spm-role-stage.md:143-144
... each of the three Build P vintages (income years 2022-2024):What the engine does
The SPM threshold year is the simulation period for every unit, whatever its source year:
spm_calculator/policyengine_adapter.py:311-313(spm-calculator 1.0.0, installed with policyengine-us 2.2.1)rows = [ bound._amounts( int(period.start.year),
Measurements on the certified H5
Check income year 2022 2023 2024 Share of child weight 34.1% 31.8% 34.1% Median of PE (market income + SS + SSI + UC) / Census SPM_TOTVAL, ASEC-channel units1.0000 0.9999 0.9999 Child-weighted median of PE threshold / Census SPM_POVTHRESHOLD1.137 1.058 1.004 Child-weighted median of PE geographic factor / Census SPM_GEOADJ0.997 0.997 1.000 PE child SPM rate 17.2% 14.6% 15.6% Census SPM_POORon the same weighted records15.4% 17.5% 16.6% - Wage inputs equal
WSAL_VALfor 100% of ASEC-channel adults and 99.9% of PUF-clone adults. - The geographic factors match Census, so the threshold gap is the year, not the county adjustment.
- Per state, the own-year-threshold effect is 0-5.5 pp (C.md, column
vint).
Why a pool centred on the target year still needs a fix
#1044 makes the default pool income years 2023-2025, whose mean is the target year. Each record is still tested against a threshold from a different year than its dollars: 2023 records against a higher one, 2025 records against a lower one. Poverty is a threshold test, so the two errors cancel only if the income density near the threshold is the same in both vintages. (Reasoning, not measured.)
Options
- Age pooled-year dollar inputs to the target year per record at build time, series by series (the per-record donor ager proposed in Age donor dollar values to the base year per record, not through weights (SCF, SIPP, PUF) #981, applied to the ASEC pool). This is also the fix Pooled prior-year ASEC wages are not aged to the target year #944 asks about.
- For the 3-year Census backtest rows (
us/state_spm_poverty_levels.json), evaluate SPM status against each record's own source-year threshold. That matches how the Census flag on the H5 is built:SPM_POOR == (SPM_RESOURCES < SPM_POVTHRESHOLD)holds for 100% of units, with each source year's own thresholds.
Acceptance test (external ground truth)
The H5 carries Census
SPM_POOR,SPM_RESOURCES,SPM_POVTHRESHOLDandsource_year, and PE and Census SPM units align exactly (0 of 59,900 PE units span more than one CensusSPM_ID). After the fix, the by-source-year gap between PE's SPM status and CensusSPM_POORon ASEC-channel records should no longer depend on the source year, within a stated tolerance.Limits
First order: benefits and taxes are computed at 2024 law on nominal income and are not recomputed. Option 1 ages income with income-growth series, not SPM-threshold growth, so its effect will differ in size from the threshold counterfactual above.
- Money. The pooled person tables carry nominal dollars of each income
Observed while joining raw ASEC records to the certified national file (
populace-us-2024-spm-20260915; see the wage-band target issue for context).For every one of the 166,321 persons, the 2024-period
payroll_tax_gross_wagesequals the raw ASECWSAL_VALfor that person's own income year (ratio 1.000 at the 10th, 50th and 90th percentiles). Records from income years 2022 (31% of weight) and 2023 (31%) therefore carry 2022 and 2023 nominal wages in the 2024 period.us_runtime/asec_pool.pyscales each year's weights to a population share and does not adjust dollar amounts; I found no other stage that ages them foremployment_income.Consequences measured on FUTA: Microcosm's Σ min(wage, $43,000) is 8.5% below the raw ASEC 2025 file at Census weights ($31.78B vs $34.74B at 0.6%), while Σ min(wage, $7,000) matches within 1.2%. Total payroll-tax wages are $10.74T vs $12.07T. Calibration to SOI/BEA/CBO aggregates compensates in total but not by wage band.
Question for the build owners: is the no-aging behaviour intended (weights absorb the vintage difference) or should pooled prior-year dollar inputs be uprated to the target year before calibration? If intended, a wage-band target would be the guard; if not, this is a fix in the pooling or source stage.