Skip to content

Replace SSA Table 7.B1 values with SSA's own workbook - #294

Merged
MaxGhenis merged 5 commits into
mainfrom
fix/ssi-table-7b1-2024-values
Nov 10, 2026
Merged

MaxGhenis merged 5 commits into
mainfrom
fix/ssi-table-7b1-2024-values

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

ssa/ssi_table_7b1_2024 emits 208 SSI recipient and 208 SSI payment facts (cy2024) citing SSA Annual Statistical Supplement 2025, Table 7.B1. Its CSV matched the published table in 1 of 416 cells, the national payment total of 63,079,493 thousand dollars. For example:

Cell Old fact Published
All areas, recipients 7,404,820 7,423,856
California, disabled recipients 849,834 753,206
California, total payments ($000) 12,927,048 10,966,403
Idaho, total recipients 17,796 27,821

The old state payment rows summed to $63.92B and exceeded the old All-areas row of $63.08B.

This PR repoints the package at SSA's own workbook and makes all 416 facts equal the published table. It also declares SSA's column headings as the labels of the three eligibility categories.

Cause

The package came from #48 (2026-05-29). The Codex session that built it (019de035…, archived rollout JSONL, lines ~170939–171480) tried to reach ssa.gov and got 403 / Access Denied from curl, urllib and the Jina reader. pandas.read_html also failed because the environment lacked lxml.

Its web tool then opened 7b.html five times, and the transcript does not record what those calls returned. At 04:38:48 UTC the session wrote the 52 area rows into the build script as a literal tab-separated block, which matches the committed CSV in 416/416 cells.

The block matches none of these SSA tables:

  • Table 7.B1 in the 2015–2025 Supplement editions.
  • Tables 7.B3, 7.B7, 7.B8 and 7.B9 in those editions.
  • SSI Annual Statistical Report 2023/2024, all state tables.
  • SSI Recipients by State and County 2023/2024.

393 of the 416 cells need three or more digit edits to reach the published value, more than a few transcription slips would explain. The same command wrote the package metadata's "manual table extraction", and the tests #48 added pinned the invented California values.

What changes

  • Artifact. Facts now come from sheet 7.B1 of SSA's section 7.B workbook, 7b.xlsx, through the xlsx_used_range parser:
    • sha256 6363d2ef…, 52,436 bytes.
    • On 2026-09-26 we hashed, in a browser, the file ssa.gov served (Last-Modified 15 Dec 2025), and it matched this one byte for byte.
    • All six Wayback id_ captures from 2026-01-01 on match it too.
    • SSA's earlier upload of 22 May 2025 differs only in the wording of its SOURCE note.
  • Cross-check file. The package vendors a Wayback capture of the 7.B web page as files.source_capture. No fact comes from it; the tests parse it as an independent rendering of the same table. .gitattributes marks it -diff -text, so git keeps its CRLF line endings byte for byte, as the file's header comment asks of pinned source artifacts.
  • Old CSV. This PR deletes the hand-typed ssi_table_7b1_2024.csv.
  • R2. chronicle publish-raw uploaded both files to ledger-raw at their canonical content-addressed keys, and a download of each matched its sha256.
  • Selectors. Every row now has a workbook row_number/column, a row-header guard, a category-heading guard (row 4) and a column-group guard (row 3). The All-areas rows also guard the table title.
  • Labels. The xlsx parser reads cells, so no source row text labels the three ssi_category values. The package declares SSA's row-4 headings (Aged, Blind, Disabled) as their labels in dimension_value_labels. Without the declaration, build_bundle reports three missing_dimension_value_label errors.
  • Identity.
    • This PR keeps the record-set ids, value_ids, measure_ids, concepts and source_column_ids, so source_record_id, semantic_fact_key, observed_measure_key, concept_alignment_key, dimension_set_key and source_series_key stay the same for all 416 facts. No key builder reads the labels.
    • For all 416, source_release_key changes, and aggregate_fact_key with it, because the release key includes the artifact sha; any byte-level fix would change it. legacy_fact_key changes because it includes the filename, and source_cell_keys and layout.record_set_spec_hash (the selector spec hash) change too.
    • source_row_keys become empty, as for every xlsx package.
  • Period. Both record sets gain non-identity period_coverage: "December 2024" for recipients and "calendar year 2024" for payments. Period identity stays calendar_year 2024.
  • Scope. The package keeps its 52-area selection (All areas, 50 states, DC). The parser reads the workbook's Northern Mariana Islands row as source cells, the package emits no fact for it, and the manifest says so.

Invariants

tests/test_source_ssa_ssi_table_7b1.py checks these properties in 75 tests:

  1. Differential, workbook vs HTML. All 424 workbook cells (53 areas × 8) equal the HTML table, which the test parses on its own with stdlib html.parser.
  2. Fidelity, for all facts. Each of the 416 facts equals its published cell times its unit scale: ×1 for counts, ×1000 for $000.
  3. Coverage. Each of the 52 geographies has one fact per category and measure, no source_record_id repeats, and no fact covers Northern Mariana Islands.
  4. Accounting, publisher data.
    • Recipients: total == aged + blind + disabled in every row, and All areas equals the sum of the 52 areas plus NMI with no residual.
    • Payments: each row's residual lies in {−1, 0, +1} ($000), and the test pins the geographic residual vector at [−1, 0, +3, −2], consistent with footnote a.
  5. Bounds. No value falls below zero, no component exceeds its total, and no area exceeds All areas.
  6. Identity stability. A digest of the sorted (source_record_id, semantic_fact_key) pairs equals its pre-fix value.
  7. Provenance. Every fact's sha256, R2 URI and URL point at the publisher workbook, and the manifest's sha, size and R2 keys equal build_r2_key over the bytes.
  8. Guard exhaustiveness. Mutating any of the 63 guarded label cells (title, two spanners, eight category headings, 52 area labels) makes build_facts raise.
  9. Labels. The consumer rows carry no label issue, and their ssi_category labels equal the workbook's row-4 headings, which agree across the recipient (E:G) and payment (I:K) columns.

The old CSV satisfied recipient additivity row by row, so arithmetic checks alone could not catch its invented values; invariant 2 catches them.

Chronicle governance

  • Approved Chronicle agent role: ledger-source-ingestor.
  • Paths outside that role's list, as in Add the UK income facts issue 280 asks for, and one name per UK geography #282: db/data/ssa/ssi_table_7b1_2024/* (manifest and artifacts), the .gitattributes line for the vendored HTML capture, and changelog.d/294.fixed.md.
  • Deterministic checks run on the chronicle venv, Python 3.14, with the worktree on PYTHONPATH:
    • At af85c59:
      • chronicle validate-package packages/ssa/ssi_table_7b1_2024 --year 2024: valid.
      • chronicle build-suite … --year 2024: 715 source cells and 416 facts, each with cell lineage. agent_acceptance.json reports the build valid, with every check true, including full_source_document_parsed, raw_artifacts_have_r2 and selected_row_only_parser_not_used.
      • The bundle builds for 2023 and 2024 with no label, geography or suite error.
    • At fbd9d5b, on the same artifact bytes:
      • The consumer facts match the published table in all 416 values.
      • pytest tests/test_source_ssa_ssi_table_7b1.py tests/test_chronicle_source_package.py tests/test_chronicle_governance.py tests/test_chronicle_boundaries.py tests/test_chronicle_facts_only.py tests/test_chronicle_consumer_contract.py tests/test_chronicle_artifacts.py: 310 passed, and ruff check (0.14.10, as locked) found nothing.
      • chronicle publish-raw --root db/data/ssa/ssi_table_7b1_2024: both uploads succeeded, and the downloads match by sha256.
  • LLM judge verdicts at head af85c59 (record: Replace SSA Table 7.B1 values with SSA's own workbook #294 (comment); the record for fbd9d5b: Replace SSA Table 7.B1 values with SSA's own workbook #294 (comment)):
    • ledger-source-fidelity: PASS. 416/416 facts equal their publisher cells, and the merge with main leaves every value and both byte pins unchanged.
    • ledger-contract: n/a. The PR changes no schema, identity construction or contract code.
    • ledger-boundary: PASS.

Downstream in Microcosm

Microcosm main pins Chronicle's consumer feed at 41b2fb2, so nothing in Microcosm moves until a Microcosm PR re-pins it.

Microcosm's compile path binds the 51 state and DC total rows of each measure as calibration targets. It skips the national rows: the PolicyEngine/microcosm#508 adjudication made national recipients a non-binding reference, and national payments come from another SSA table. It never compiles the 312 category rows.

The published values move those 102 targets by these amounts:

Measure Min Median Max Targets moving ≥10%
Recipients −12.7% +2.5% +56.3% 17 of 51
Payments −22.9% +8.2% +57.7% 29 of 51

No target's inclusion or binding status flips. Microcosm calibrated US builds I, J, M, N, O and P (logbook/us.jsonl) on feeds that carry these 416 values.

Tests

This PR changes three existing tests:

  • The California assertions pin the published values, 753,206 recipients and $8,036,411,000 payments, in place of the invented ones.
  • The source-cell count falls from 1,672 to 715.
  • The label-year regression builds with the source_capture label key, since this PR removes the extracted_targets key.

The new module tests the invariants above. CI runs the full suite. On fbd9d5b (2026-09-27), CI failed test_build_bundle_writes_merged_consumer_contract on missing_dimension_value_label errors for these values, which the label declaration in 0e60b71 removes.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits September 27, 2026 00:59
The ssa/ssi_table_7b1_2024 package emitted 416 facts from a hand-typed
CSV that matched the published Annual Statistical Supplement 2025 Table
7.B1 in 1 of 416 cells. The Codex session that built it (2026-05-29) got
HTTP 403 from every programmatic fetch of ssa.gov and then wrote the 52
area rows into the build script as literal numbers.

The package now parses SSA's section 7.B workbook (7b.xlsx, sha256
6363d2ef..., byte-identical to the file ssa.gov serves today and to every
Wayback capture since 2026-01-01) with xlsx_used_range, sheet 7.B1.
Record-set ids, value ids, measure ids and concepts are unchanged, so
source_record_id, semantic_fact_key and observed_measure_key do not move;
415 values change. A Wayback capture of the 7.B web page is vendored as
source_capture so a new test can check every fact against an independent
rendering of the table. Recipient and payment record sets gain
non-identity period_coverage (December 2024; calendar year 2024).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
publish-raw uploaded both manifest entries to ledger-raw (verified by
downloading each object and matching its sha256) and rewrote the manifest
in its canonical line-wrapped form. No field values changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Chronicle judge record for head fbd9d5b

Three independent reviews ran on Subfleet lanes. None was the session that wrote the PR. The full records are kept locally under _reviews/ssi-7b1-fix-20260925/out/J-*.md.

ledger-source-fidelity: PASS WITH NOTES (two reviews)

Read-only review (Claude lane, no shell). It checked every one of the 416 (source_record_id, value) pairs in the rebuilt facts against the live ssa.gov read of Table 7.B1 and found 416/416 exact matches. Units (payments $000 × 1000), category→column (D:G, H:K) and area→row mappings are correct. The vendored HTML is a genuine raw Wayback id_ capture: it has no archive rewriting, and its Akamai beacon timestamp equals the capture time. Transcript lines L170943, L171355 and L171438 support the stated cause. It found no invented values.

Byte-level review (Astra lane, shell).

  • shasum -a 256 of the vendored 7b.xlsx gives 6363d2ef…31bd6, 52,436 B. The vendored HTML gives 0e88bd5e…45dc, 72,888 B. Both match the manifest and the evidence copies.
  • The review read 7.B1!A5:K58 with openpyxl 3.1.5, built the facts fresh with build_source_cells and build_facts, and compared them: 416/416 exact, 416/416 value-cell lineage, and no missing, extra or duplicate keys. Row 58 (Northern Mariana Islands) is parsed and not emitted.
  • tests/test_source_ssa_ssi_table_7b1.py: 74 passed.
  • It refuted the read-only review's note that the payment guard is written with an ASCII space. All 208 guards contain c2 a0 (NBSP), matching workbook H3, and _resolve_guard_cell compares raw values with no whitespace normalization. An ASCII-space guard raises.
  • The lane sandbox had no network, so it could not re-download from Wayback or R2. Those two checks were run by the authoring session and are not independent:
    • wrangler r2 object get of both manifest keys, then sha256: both match.
    • In-page sha256 of the live ssa.gov/.../2025/7b.xlsx on 2026-09-26: 6363d2ef….
    • Six Wayback captures of that URL from 2026-01-01 on were downloaded with CDX digests checked, and all hash to 6363d2ef….

ledger-boundary: PASS WITH NOTES

It found no reconciliation, aging, imputation, support-aware activation, solver or target construction, and no computed values stored as facts. Every fact is a single published cell, scaled only by the declared unit. period_coverage is non-identity provenance: no key builder reads it, and the semantic keys are unchanged.

Keeping the existing 52-area selection is a source-package choice, not a consumer or support-aware decision, and it does not block.

The key changes (source_release_key / aggregate_fact_key, legacy_fact_key, record_set_spec_hash, cell and row keys) follow from the new bytes and selectors under the existing key rules, so no ledger-contract-maintainer role is needed.

Its notes, and how they were addressed:

  • The PR body now names source_release_key and record_set_spec_hash.
  • git diff --stat 505e0e7..fbd9d5b lists only packages/ssa/ssi_table_7b1_2024/**, db/data/ssa/ssi_table_7b1_2024/**, .gitattributes, tests/test_source_ssa_ssi_table_7b1.py and tests/test_chronicle_source_package.py.

ledger-contract: n/a

No schema, identity-construction or contract code changed.

Follow-ups outside this PR:

  • Emit or deliberately exclude the Northern Mariana Islands row.
  • Migrate the recipient period identity to month 2024-12.
  • Sweep other packages for hand-typed values.

MaxGhenis and others added 3 commits November 9, 2026 19:37
Brings the Table 7.B1 fix up to main 8f409d6. The one conflict was
.gitattributes: keep main's list and add the vendored 7.B web page
capture as `-diff -text`, the form main now uses for pinned HTML, so a
checkout can't renormalize the bytes its sha256 pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The workbook parser reads cells, not source rows, so no row text labels
the aged, blind and disabled values any more, and build_bundle reported
three missing_dimension_value_label errors: the one failure in this PR's
first CI run (test_build_bundle_writes_merged_consumer_contract).
Declare the labels in the package, in the workbook's own column
headings, which the package's guards already check (Aged, Blind,
Disabled). Only this package emits ssi_category, so a package
declaration, not a register entry, is the remedy chronicle#376 names.

The new package test checks the consumer rows the way the bundle does,
so a missing label fails in this module instead of only in the bundle
build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The label test compared the declared labels with literal strings. It now
reads SSA's row-4 headings from the workbook's source cells, in both the
recipient and payment column groups, so a label that drifts from the
publisher's heading fails. Also add the changelog fragment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Chronicle judge record for head af85c59

Three commits follow the record for fbd9d5b above:

  • 039df0b merges main (8f409d6). Its one conflict sat in .gitattributes, where the vendored HTML capture's line now reads -diff -text. The capture has CRLF line endings, and the file's header comment asks git to keep pinned source artifacts byte for byte. Both vendored files still match their sha256 and size pins.
  • 0e60b71 declares SSA's row-4 headings (Aged, Blind, Disabled) as the labels of the three ssi_category values. CI on fbd9d5b failed test_build_bundle_writes_merged_consumer_contract on missing_dimension_value_label errors, because the xlsx parser reads no source rows and nothing else labelled those values. A new test checks the labels in the consumer rows.
  • af85c59 makes that test read the headings from the workbook's cells and adds changelog.d/294.fixed.md.

An Opus 5.5 reviewer that wrote none of these commits judged the delta:

  • ledger-source-fidelity: PASS. All 416 facts still equal their cells times the unit scale, with the same cell lineage, and the merge leaves the value digest unchanged. The three labels match the workbook's headings character for character.
  • ledger-boundary: PASS. The delta adds one git attribute, a five-line label block in SSA's wording, a test and a changelog fragment. It adds no reconciliation, imputation, target construction or computed value.
  • ledger-contract: n/a. The PR changes no schema, identity construction or contract code.

The new test passes on the package. It fails when the label block goes, when a fact loses its labels, when a declared label differs from its heading, or when the recipient and payment headings disagree.

At af85c59, validate-package and build-suite pass for 2024, with 715 source cells, 416 facts and every agent-acceptance check true. The bundle builds for 2023 and 2024 with no label, geography or suite error.

@MaxGhenis
MaxGhenis merged commit 8384cc8 into main Nov 10, 2026
3 checks passed
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