Repository navigation
Read the ASEC at-interview coverage recodes the 2022/2023 inputs lack from the pinned Census archives (#720) - #991
Merged
Conversation
…lack (#720) The pinned income-year 2022/2023 census_cps H5 inputs carry 2 of the 18 NOW_* at-interview recodes and lack A_EXPRRP, PTOTVAL, A_ENRLW and A_FTPT, which the 2024 input carries. Pooling leaves those cells NaN for two-thirds of the pool and every reader treats NaN as "no" or zero, so seven reported-coverage flags have zero reporters in the 2022/2023 vintages (the #744 gate's 14 failures), the older vintages use a derived relationship recode, and tax-unit construction sees no enrollment or total-income signal for them. The complete Census person files are already pinned build inputs (--asec-education-source; ASEC_SPM_ROLE_SOURCES). The new asec_census_person_columns module appends, per vintage and before any pooling, every reviewed column the H5 lacks, read from that year's pinned member through the SPM role stage's reader (generalised to take a column list, its default read unchanged) and joined on exact PERIDNUM. The join must be one-to-one and total; identity columns must agree after it; member columns must be integers inside their reviewed Census codes; a column the H5 already carries is verified equal and never overwritten (all eleven on 2024); every existing H5 column is checked unchanged. Each refusal is a named AsecCensusPersonColumnsError. Reviewed set (11, each with a cited build reader that a test resolves): NOW_MCAID, NOW_NONM, NOW_CHAMPVA, NOW_MIL, NOW_VACARE, NOW_OTHMT, NOW_IHSFLG, A_EXPRRP, PTOTVAL, A_ENRLW, A_FTPT. Of the 24 columns the 2026-08-23 offline fix appended, nine NOW_* have no reader, A_FAMTYP/A_FAMREL/PECOHAB are only self-checks of the same member in the SPM role stage, and LKWEEKS is already restored for 2022 from the same pinned archive. AsecSource gains census_person_source; build_us_puf_support_base.py and build_us_asec_pooled_source_base.py bind every pooled year to its pinned Census person file (the --asec-education-source mapping, else fetched and verified), so the source-construction frame, the raw-stage checkpoint and everything downstream carry the columns. Provenance (archive and member digests, columns added or verified, row counts, member code counts) is recorded per vintage in the pool metadata, and so in the source-construction receipt, the raw-stage checkpoint's source_receipt and the base summary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/us-asec-census-person-columns.md records the defect, the reviewed column table with each build reader, the refusals, the paths covered, and the 2026-09-23 source-construction evidence on route A's exact inputs: - #744 gate on derive_us_cps_carried_inputs of the raw-stage frame: route A's checkpoint (47976be) fails with 14 failures (Medicaid reporters 0 / 0 / 24,844); this branch passes (28,184 / 26,367 / 24,844, the 2026-08-23 receipt's numbers). - The eleven restored columns equal the 2026-08-23 corrected H5s in value, dtype and order; the whole raw-stage frame equals the 8/23 corrected raw-stage frame on every entity table and every shared column except the twelve columns deliberately not restored. - Structural effect: 2022/2023 use Census's A_EXPRRP (20,595 / 20,561 persons change), tax units 231,007 -> 226,102 (2024 unchanged). experiments/us-asec-720-census-person-columns holds the run script, the verification scripts and their JSON outputs, and the stage context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…720-coverage-recodes
This was referenced Sep 23, 2026
MaxGhenis
marked this pull request as ready for review
September 23, 2026 12:35
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Layer 1 of #720, built into the base stage from inputs that are already pinned. Stacked on #959 (
us-spm-role-stage), because route A (epic #956) builds on #959's role stage. This PR unblocks route A's release, which refused at the #744 gate.Root cause
The base pools three processed ASEC inputs (
us_runtime/asec_sources.py, #952):census_cps_2022.h57ccca976…,census_cps_2023.h5cb578173…andcensus_cps_2024.h5ec36604c…. The 2022 and 2023 files carry 2 of the 18NOW_*at-interview recodes and lackA_EXPRRP,PTOTVAL,A_ENRLWandA_FTPT, all of which the 2024 file carries.asec_pool.pool_asec_sourcesconcatenates the three per-year person tables, so every 2022/2023 row gets NaN in those columns, and every reader treats NaN as "no" or 0:cps_carried._fill_health_coverage_inputssets seven reported-coverage flags to False. The Add per-vintage reported-coverage signal gate (#720) #744 gate reports 14 failures, which is what refused route A.asec_pool._with_relationship_recodederivesA_EXPRRPfrom line, spouse and parent pointers instead of using the Census code.PTOTVALas 0 and finds no student enrollment.eligibility_inputssetsis_full_time_college_studentFalse for everyone in those two years.The fix
The official Census archives are already pinned build inputs:
--asec-education-sourceandspm_role_source.ASEC_SPM_ROLE_SOURCES, with the archive SHA-256 and the member's name, size and SHA-256.The new module
us_runtime/asec_census_person_columns.pyruns inasec_pool._prepare_year_input, straight after each H5 is read and before the relationship recode and the smoke-household limit. It appends every reviewed column that H5 lacks. It reads them from that year's pinned person member through the reader the SPM role stage already uses,spm_role_source.read_pinned_asec_person_columns, and joins on exactPERIDNUM.That reader now takes a column list. Its default read, checks and messages are unchanged for the role stage. New tests cover the default read from both the CSV and the archive, and every refusal on a custom column list. All existing SPM role and role-stage tests pass.
AsecSourcegainscensus_person_source.build_us_puf_support_base.pyandbuild_us_asec_pooled_source_base.pybind every pooled year to its file: the--asec-education-sourcemapping if one is given, otherwise the pinned archive fetched and verified. They resolve it the same way the SPM role stage does (resolve_asec_spm_role_source_paths).Column review
The starting point was the 24 columns in the 2026-08-23 offline fix (
receipt_720.json). A column is kept only if something in the build reads it.test_every_restored_column_cites_readers_that_really_read_itresolves every citation below and requires the cited function or constant to name the column.NOW_MCAID,NOW_NONM,NOW_CHAMPVA,NOW_MIL,NOW_VACARE,NOW_OTHMT,NOW_IHSFLGcps_carried._fill_health_coverage_inputs→ the seven reported-coverage flags the #744 gate checksA_EXPRRPasec_pool._with_relationship_recode;frame.units.MICROUNIT_REQUIRED_COLUMNS→ microunit_prepare_household_people(tax-unit head);acs_transfer._person_head_feature;us_late_producer_registry._transfer_input_inventoryPTOTVALframe.units._is_microunit_optional_column→ microunit_precompute_tax_unit_inputsA_ENRLW_is_full_time_studentA_FTPTeligibility_inputs.derive_us_eligibility_inputs_from_manifest(is_full_time_college_student)NOW_CAID,NOW_COV,NOW_DIR,NOW_MCARE,NOW_MRKS,NOW_MRKUN,NOW_PCHIP,NOW_PRIV,NOW_PUBpackages/*/srcortools/reads them. A test fails if code starts to.A_FAMTYP,A_FAMREL,PECOHABLKWEEKSweeks_unemployed.fill_asec_2022_weeks_unemployed_sourcealready restores it for 2022, from the same pinnedpppub23.csv. The 2023 and 2024 inputs carry it.Refusals (
AsecCensusPersonColumnsError, aValueError)The restoration never predicts, imputes or fills a default. It refuses when:
PH_SEQ,P_SEQ,A_LINENOorA_AGEdisagree after the join.assert.On 2024, where the H5 carries all eleven columns, the join is therefore verified on real data: 142,125 persons, all eleven columns equal.
Provenance
Each vintage's pool metadata records
census_person_columns: the source form and path, the archive URL and SHA-256, the member name, SHA-256 and size, H5, member and joined row counts, columns added, columns verified equal, and the member's code counts. It flows into the source-construction receipt, the raw-stage checkpoint'ssource_receipt, and the base summary'sbase_source.Real-data evidence (source construction only; the full base and release were not run)
Route A's exact base inputs and flags (
route-a/run-47976be6ce75/base-config.json) were run with--stage source_construction. The run took 69 s and peaked at 11.2 GB RSS. The inputs matched their pins: H5s7ccca976…,cb578173…,ec36604c…; archivesd2e00025…,cdb39cda…,318845a2…. The receipts are inexperiments/us-asec-720-census-person-columns/, and the review and results are written up indocs/us-asec-census-person-columns.md.#744 gate on
derive_us_cps_carried_inputsof the raw-stage frame:47976be6c)The other flags after the fix, 2022 / 2023: non-marketplace direct purchase 8,666 / 8,435, TRICARE 3,882 / 4,318, VA 1,271 / 1,178, IHS 687 / 651, CHAMPVA 260 / 335, other means-tested 275 / 331. All 2024 counts, and
has_esiand marketplace in every year, are unchanged.Equality with the 2026-08-23 corrected inputs:
LKWEEKS2022 is also equal, on all 146,133 rows.Structural effect (intended, but downstream should know):
A_EXPRRP. It changes for 20,595 / 20,561 persons, and code 13 (partner/roommate) now appears (4,312 / 4,387).What is and isn't covered
build_us_puf_support_base.pyrun that pools--asec-h5(staged, monolithic, or with a support-spine spec), andbuild_us_asec_pooled_source_base.py.build_us_multispine_pool.pyreads the SHA-pinnedasec_raw_stage.checkpoint.h5written by source construction, so a checkpoint built before this PR still has the hole. The Add per-vintage reported-coverage signal gate (#720) #744 gate would refuse a release built on it.--base-h5builds and the enrichment and qualification lanes, which start from published parents. ACS-spine rows come from ACS PUMS, not these H5s.is_unmarried_partner_of_household_headexclusion. That exclusion describes the inputs and is still true of them. Code 13 now appears in every vintage, but it still conflates partners and roommates.Checks run locally
ruff check ., andruff formaton the files this PR touches.tools/ci_test_groups.py --verifypasses; the new test file lands in the fast and engine groups.test_us_spine_blindnesspasses with the new module classified in_OTHER_US_RUNTIME_MODULES, the way Materialize the Census SPM independence role in US builds #959 classifiedspm_role_source. The pinned pool-tool import graph stays at 73.🤖 Generated with Claude Code