Skip to content

Census ACS 5-year SLD source packages: Utah pilot at summary levels 610/620 (microcosm#625) - #146

Open
MaxGhenis wants to merge 2 commits into
mainfrom
census-acs-sld-2024
Open

Census ACS 5-year SLD source packages: Utah pilot at summary levels 610/620 (microcosm#625)#146
MaxGhenis wants to merge 2 commits into
mainfrom
census-acs-sld-2024

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Summary

Six witnessed Census ACS 2020-2024 5-year source packages at state-legislative-district summary levels 610 (upper) / 620 (lower) — the Utah pilot of the SLD target surface for PolicyEngine/microcosm#625 (the per-district layer over the ACS local artifact):

package chamber record sets facts
census/acs_s0101_sldu_ut_2024 (age bands) upper 29 522
census/acs_s0101_sldl_ut_2024 lower 75 1,350
census/acs_b19001_sldu_ut_2024 (income brackets + total households) upper 29 493
census/acs_b19001_sldl_ut_2024 lower 75 1,275
census/acs_b19013_sldu_ut_2024 (median household income, aggregation median) upper 1 29
census/acs_b19013_sldl_ut_2024 lower 1 75

3,744 facts. The SLD geography levels were already in the core vocabulary; no core-schema change. Two commits: the pilot, then the review fix batch (see verdicts below).

Shapes. The constrained tables use the CD-family unpivoted per-district record sets so every fact's own source rows evidence its bounds: S0101 reuses the existing age parser (publisher band labels on rows); B19001 gets a sibling parser (census_acs_b19001_income_json_rows) emitting bracket labels plus explicit household_income_{lower,upper}_bound numeric keys, which the suite's row-constraint gate validates fact by fact. The unconstrained B19013 keeps the compact rectangular shape. Every measure carries an expected_column_header guard (the cell store materializes the unpivoted header at row 1), so a column edit fails selector resolution. All YAML is generated deterministically from the witnessed bytes by chronicle/sources/census_acs_sld.py; regeneration, witness-sha integrity, and the agent-acceptance gate are pinned tests.

Witnessing notes (reviewed honestly)

  • The API rejects in=state:* for SLD hierarchies; witnesses are per-state canonical queries (comma-listed FIPS works for the national generalization, National generalization of the ACS SLD packages (all states, ~6.8k districts) #138).
  • api.census.gov now hard-requires a key. Recorded source_urls are the canonical keyless queries; the fetch appends a key at request time only; the recorded sha256 is of the canonical body. No key material anywhere in the diff (grep-verified). Manifests record fetched_at.
  • publish-raw to R2 is the one pending owner step (needs ledger-raw credentials): manifests honestly carry no storage.r2 block until the upload happens, and the CI acceptance pin is exactly "clean except missing_raw_r2_link" — it tightens to empty once publish-raw runs.
  • District labels carry the Census "(2024)" boundary vintage; geography_vintage: 2024_state_legislative_districts matches the 2024 SLD BEFs the microcosm membership layer pins.
  • B19013 medians are typed aggregation: median; the microcosm consumer treats them as validation-only by doctrine.

Ledger Governance

  • Approved Ledger agent role: ledger-source-ingestor
  • Deterministic checks run:
    • source-package validation: validate-package valid for all six, exact counts pinned in tests
    • source-cell preservation: rows/cells/facts validated with spot values pinned against witnessed bytes (SLDU-01 total households 36,483; SLDL-75 median 69,342; SLDL-75 aged-85+ 1,979)
    • consumer-contract validation: blocked solely on the pending R2 publish-raw (see notes); the acceptance gate is otherwise clean and pinned in CI
    • raw-facts boundary validation: full suite green
  • LLM judge verdicts (GPT-5.6-sol, read-only, scoped to the committed pilot diff):
    • ledger-source-fidelity: fail on the pilot commit — constrained bounds not evidenced by source rows; no column-identity guards; R2 links absent. All addressed in the fix commit (evidence-bearing shapes, header guards, CI acceptance pin); the numeric review also confirmed every one of the 3,744 committed cell mappings exact.
    • ledger-target-profile: n/a (no target profiles touched)
    • ledger-contract: n/a (no schema change; alias entries + two parser-dispatch clauses)
    • ledger-boundary: pass-with-notes — nothing crosses the facts-only boundary; the notes (R2 durability, authenticated cold refetch, acquisition timestamp) are operational, with fetched_at now recorded and R2 the documented owner step.

Tests

Regeneration determinism for all six packages + manifests; witness-sha integrity; canonical-keyless URL properties; district-count pins; refusals (unsorted/duplicate districts, any negative sentinel, wrong-chamber GEO_IDs); exact validation counts; fact spot values with constraint bounds and median typing; the agent-acceptance pin; bundle skipped-source pins. Full suite green; ruff clean.

Rebased across the Chronicle rename (ledgerchronicle code identity; frozen machine surfaces untouched). Part of PolicyEngine/microcosm#625; national generalization tracked in #138.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits August 7, 2026 11:26
…ls 610/620 (populace#625)

Six witnessed packages for the state-legislative-district target surface:
S0101 age bands, B19001 household income brackets (+ total households), and
B19013 median household income, each per chamber, generated
deterministically from canonical api.census.gov per-state responses by
ledger/sources/census_acs_sld.py (regeneration is a pinned test). SLD
geography levels were already in the core vocabulary; the compact
rectangular record-set shape (row-level district geography overrides,
measure-level band constraints) follows the oep_state_level precedent.

The 2020-2024 5-year window, the 2024 district-boundary vintage, and the
per-state fetch constraint (the API rejects state wildcards for SLD
hierarchies) are declared in the packages. B19013 facts aggregate as
median — the populace consumer treats them as validation-only by doctrine.

publish-raw to R2 is the one pending step (needs ledger-raw credentials);
manifests carry no storage.r2 block until the upload actually happens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce pins

Sol cross-family review verdicts on the pilot commit: ledger-source-fidelity
FAIL (bounds not evidenced by source rows; no column-identity guards),
ledger-boundary pass-with-notes. All fidelity findings addressed:

- S0101 and B19001 switch to unpivoted per-district record sets so every
  constrained fact's own source rows evidence its bounds: S0101 reuses the
  CD-family age parser (publisher band labels on rows); B19001 gets a
  sibling parser emitting bracket labels plus explicit
  household_income_{lower,upper}_bound numeric keys (the suite's
  row-constraint gate validates them). B19013 stays compact (no
  constraints).
- Measure-level expected_column_header guards on every package (the cell
  store materializes the unpivoted header at row 1, so a column edit now
  fails selector resolution — the reviewed column-swap probe dies).
- Data rows start at cell row 2 (header row 1) — the CD package's
  numbering, now explained rather than inferred.
- Negative-sentinel rejection broadened to any negative value.
- Manifests record fetched_at (the artifacts.py acquisition convention).
- New pins: witness bytes hash-match manifests; the agent-acceptance gate
  runs in CI for all three shapes and must be clean except the recorded
  missing_raw_r2_link (publish-raw needs ledger-raw credentials; the pin
  tightens to empty once it runs).

Rebased across the Chronicle rename (code identity ledger->chronicle;
frozen machine surfaces unchanged).

Co-Authored-By: Claude Fable 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.

1 participant