Skip to content

Re-pin the US Chronicle feed so three dimension values carry one label each - #1345

Draft
anth-volk wants to merge 4 commits into
mainfrom
us-feed-agi-age-labels
Draft

anth-volk wants to merge 4 commits into
mainfrom
us-feed-agi-age-labels

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1343

Summary

The pinned US Chronicle feed labels three dimension values two ways, so the compiled US targets show one value id under two names. This PR re-pins the feed to a Chronicle commit that gives each value the one wording #1343 chose, and changes nothing else.

Dimension = value Before After Targets that change
us:statutes/26/62#adjusted_gross_income = under_1 "No adjusted gross income and deficit" (week-47 filing-season package); "No adjusted gross income" (seven other IRS SOI packages, 106 targets) "No adjusted gross income" 2
us:statutes/26/62#adjusted_gross_income = all "All returns" (Historic Table 2, Table 1.1); "All returns, total" (Tables 1.2, 1.4, 2.1, 4.3; 53 targets) "All returns, total" 47
age = 85_plus "Age 85 and over" (national Census PEP); "Age 85 plus" (state Census PEP, 51 targets) "Age 85 plus" 1

The Chronicle commit

9b91e8bc61802e5fd9d5fa19a8f991828c1025f8 on the new branch feed/us-51aa40f-agi-age-labels: the current pin 51aa40f plus three label-only commits, not Chronicle main.

  1. edb7cf1: the week-47 YAML change of Give each dimension value one Chronicle-declared label, keep the package's own wording beside it (consumer_fact.v5) chronicle#377 (6d74e42), and nothing else from that PR (not its build-bundle check).
  2. 59d9d06: soi-historic-table-2 and soi-table-1-1 relabel their all rows. Table 1.1's row had no expected_row_header, so its row-header check compared the label itself with the IRS text in cell A10; the row now states expected_row_header: All returns, so the check still reads the same cell for the same text. Matching test changes at this base: two tests that pin Table 1.1's record_set_spec_hash, the checked-in consumer fact sample built from Table 1.1's first rows, and that sample's pinned digest.
  3. 9b91e8b: census-pep-2024-national-age-sex relabels its 85_plus row.

Every package's checks of the publisher's text are unchanged: the week-47 column header, Historic Table 2's AGI_STUB 0, Table 1.1's row header, and Census PEP's row header 85 and single-year age sequence. soi-historic-table-2-state-broad-2022 keeps "All returns" for state = all (chronicle#267). That is the state dimension, which no other feed package labels, and its 2,397 targets carry one label.

Checks on the branch:

  • chronicle validate-package: valid, no warnings, for each changed package at each scope year (week 47 at 2024, Historic Table 2 at 2022, Table 1.1 at 2022 and 2023, national PEP at 2024).
  • chronicle build-suite: valid with lineage coverage 1.0 and 0 acceptance errors for the same runs. The warnings are the same as at 51aa40f.
  • Chronicle tests covering these packages: 276 passed (11 files: test_chronicle_soi_targets, _source_cells, _source_package, _suite, _harness, _consumer_contract, _core, _bundle, _facts_only, test_issue_265_non_uk_labels, test_etl_soi). The CI-only merged-bundle test was deselected; see "Unverified".

Tag: none yet. Under the #940 convention it would be microcosm-us-feed-1343-v1 on 9b91e8b, to be created after review.

Red, then green

  • Red 5769515: tests only. test_pinned_feed_targets_give_each_reviewed_value_one_label compiles the pinned feed as the release does and requires every target that selects each value to carry the chosen label. On the 51aa40f feed all three cases fail: 2 targets read "No adjusted gross income and deficit", 47 read "All returns", and 1 reads "Age 85 and over". The other feed-covering tests pass there.
  • Green 09b950e: the re-pin. With the new feed, all 245 feed-covering tests pass and none skip. That includes the three cases, test_us_chronicle_feed.py, test_release_target_parity.py, the whole of test_us_fiscal_targets.py, and the pinned-feed tests in test_us_acs_local_release_tool, _acs_local_sparse_cd, _fiscal_refresh_builder, _release_gate_preflight, _soi_congressional_district_vintage and _soi_state_agi_bands, run with MICROCOSM_US_CHRONICLE_FACTS set.

Like the other pinned-feed tests, the new test skips where the feed file is absent, as in CI. A reviewer with the feed can run it at ~/PolicyEngine/_buildh-runtime/inputs/consumer_facts_us_9b91e8b.jsonl.

What changes in this PR

  • us/chronicle_feed.json: source_commit, build text, facts_sha256 and scope_sha256. fact_row_count stays 40,178.
  • us/chronicle_feed_scope.json: only source_commit.
  • target_parity_feed_families.json and target_parity_manifest.json, regenerated with tools/build_us_target_parity_manifest.py: only the feed name and feed_sha256 change. There are still 32 compiled families, 52 reviewed exclusions and 81 feed families.
  • tools/build_us_target_parity_manifest.py: DEFAULT_FEED_NAME.
  • Test pins:
    • _CHRONICLE_COMMIT and _FACTS_SHA256 in test_us_chronicle_feed.py;
    • the national_state surface version in test_pinned_feed_national_state_surface_restores_the_fences, because the version hashes the hierarchy labels.
  • docs/us-chronicle-feed-repin.md: a new section and the pin table. changelog.d/1343-us-feed-one-label-per-value.fixed.md.
  • experiments/us-feed-agi-age-labels/compare_feeds.py and its report compare.json.

Digests

Before After
Chronicle commit 51aa40fd28e2ca81937752515e773d123fecfb71 9b91e8bc61802e5fd9d5fa19a8f991828c1025f8
Feed file consumer_facts_us_51aa40f.jsonl, 40,178 rows, 169,038,757 bytes consumer_facts_us_9b91e8b.jsonl, 40,178 rows, 169,039,583 bytes
facts_sha256 965a29ac9458edb58a05184917ee4a19772fd3dc7b4588db6eb6da5803641aac e477e04aa7c73209df2bcbf484f029a2357da8c2339217d1d0518f7a8a47bcbf
scope_sha256 f090102099b3d2c799a8f975a8156101dbb18925b526c3791a785f68944d020d 8fedefb72789ad96f83f899090bd8f37cc8e0e1b81e6aebf9a32fd9e3e9a53c4
Registry version (all 33,250 targets) dde00e855af8 b8d571e91c22
national_state surface (6,102 targets) 3fad14f85468 3aa2a0830e7d

Reproducibility:

  • tools/build_us_chronicle_feed.py --skip-artifact at 51aa40f reproduced 965a29ac… byte for byte.
  • At 9b91e8b, and at an earlier commit of the branch that differed only in Chronicle test files, it gave the same bytes, e477e04a….

Only labels change

uv run python experiments/us-feed-agi-age-labels/compare_feeds.py <51aa40f feed> <9b91e8b feed> compare.json, compiled at 2024 as #940 did (packaged CD crosswalk, age targets, Medicaid substitutions):

Feed rows, joined on aggregate_fact_key:

  • The same 40,178 keys are in both feeds. 816 rows change, and no field other than the ones below changes on any row.

    Field Rows Record sets
    dimension_value_labels[…adjusted_gross_income][under_1] 2 week-47 eitc_by_agi.count and .amount
    dimension_value_labels[…adjusted_gross_income][all] 63 ty2022.historic_table_2.us (55), ty2022.table_1_1 (4), ty2023.table_1_1 (4)
    dimension_value_labels[age][85_plus] 1 census_pep.cy2024.national_resident_population_age
    layout.groupby_value_label 66 the same rows
    layout.record_set_spec_hash (covers row labels) 816 every row of those six record sets: 605 + 80 + 80 + 19 + 16 + 16
  • Label variants in the feed. Before: the three values, plus age = age_20 and age_21 ("Age 20" against "Female 20 years", and the same for 21, from Census population projections and ACS B01001). After: only age_20 and age_21, which no compiled target selects.

Compiled targets, compared field by field on TargetSpec.to_dict():

  • Both feeds give 33,250 targets, with the same names in the same order and the same values. 50 targets change, in exactly two fields each:

    • hierarchy.dimensions[…].value_label;
    • metadata["ledger_layout_groupby_value_label"].
    Old label New label Targets
    "No adjusted gross income and deficit" "No adjusted gross income" 2
    "All returns" "All returns, total" 47
    "Age 85 and over" "Age 85 plus" 1
  • Label variants in the compiled targets. Before: those three values. After: none, so no dimension value in the compiled targets has two labels.

  • The registry and surface versions move as listed above. The surface keeps its 6,102 targets.

compare.json lists every changed feed label, every record-set hash change and all 100 changed target fields.

Interplay with #1189 and #1192

All three PRs rewrite the same pin files: chronicle_feed.json, the scope file, both parity files, DEFAULT_FEED_NAME, the test_us_chronicle_feed.py pins, and the doc. Whichever merges second conflicts in those files, and resolving the conflict as text is wrong: its feed must be rebuilt from a Chronicle commit that holds both changes.

Their Chronicle bases:

PR Pinned Chronicle commit What it adds
this one 9b91e8b = 51aa40f + edb7cf1, 59d9d06, 9b91e8b labels in four package YAMLs, plus Chronicle test pins and one fixture
#1189 cf9ece2 = 51aa40f + 2744154 (digit-text guard) + cf9ece2 (BEA B4923C package) three scope pairs
#1192 f98acf4 = c5e5bf8 + the W-2 package file a scope change; based on the pin from before #940

The commits touch disjoint files. Locally:

  • this PR's three commits cherry-pick cleanly onto cf9ece2 and onto f98acf4;
  • cf9ece2's two commits, and f98acf4's one, cherry-pick cleanly onto 9b91e8b.

None of the other PRs' packages carries these dimension values.

If this PR merges first:

If #1189 (or #1192) merges first: this PR moves its three commits onto that PR's Chronicle commit, as a new branch such as feed/us-cf9ece2-agi-age-labels. It rebuilds from main's scope, reruns compare_feeds.py against that PR's feed, expecting the same 66 label rows and 816 hash rows and nothing else, and re-measures the surface version. The feed/us-51aa40f-agi-age-labels branch would then go unused.

The risk either way: a later re-pin built on a base without these commits silently restores the old labels. CI would not catch it, because test_pinned_feed_targets_give_each_reviewed_value_one_label skips without the feed. So whoever merges second must run the pinned-feed tests locally with the new feed.

Recommended order: this PR first, then #1189, then #1192.

If #1189 is ready first, the reverse order works too, with the mechanical steps above.

Unverified

Review (2026-10-21)

No bug found in this PR or in the Chronicle branch feed/us-51aa40f-agi-age-labels:

  • An independent rebuild with tools/build_us_chronicle_feed.py reproduces facts_sha256 e477e04a… and scope_sha256 8fedefb7… byte for byte.
  • compare_feeds.py confirms that only labels change: 50 national targets change only value_label and ledger_layout_groupby_value_label.
  • Regenerated parity files match. Table 1.1's new expected_row_header checks the same IRS cell, A10.
  • Consumers:
    • loss weights use the label only for congressional-district rows;
    • the target-frame cache rebuilds once, because the registry version is in its key;
    • the dashboard matches targets by name and value_id;
    • .py doesn't read these fields.

Added by the review: a check CI runs without the feed file. The pinned-feed label test skips in CI.

  • The parity generator now records, in target_parity_feed_families.json, every dimension value the feed labels more than one way. On this feed that's only age_20 and age_21.
  • A new test reads that committed file, so it runs in CI. It fails if under_1, all or 85_plus gain a second label again, or if any other value gains one without review.
  • Why it can't be skipped: a re-pin must regenerate that file, because CI already requires it to restate the pinned feed's digest.
Commit Content Result
388699c9 Tests only 2 fail (KeyError, AttributeError)
2880b263 The generator change and the regenerated file 247 pass, 0 skipped, with the feed; without it, the 2 new tests pass and the feed tests skip; ruff passes

For whichever of #1189 or #1192 merges after this:

  • rebuild the feed from a Chronicle branch based on 9b91e8b;
  • re-measure the surface version and target counts if it adds targets;
  • explain any difference beyond its own new rows.

🤖 Generated with Claude Code

anth-volk and others added 4 commits October 22, 2026 00:00
…l each (#1343)

Tests only. On the pinned feed (Chronicle 51aa40f, facts 965a29ac...), the
compiled 2024 targets label three values two ways, and this test fails for
each:

- us:statutes/26/62#adjusted_gross_income = under_1: 2 targets (the week-47
  filing-season EITC rows) read "No adjusted gross income and deficit";
- us:statutes/26/62#adjusted_gross_income = all: 47 targets (Historic
  Table 2 and Table 1.1) read "All returns";
- age = 85_plus: 1 target (national Census PEP) reads "Age 85 and over".

Like the other pinned-feed tests, it skips where the feed file is absent,
as in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l each (#1343)

The pin moves from Chronicle 51aa40f to 9b91e8b: 51aa40f plus three
label-only commits (branch feed/us-51aa40f-agi-age-labels), not Chronicle
main.

- AGI under_1: the week-47 package's "No adjusted gross income and deficit"
  becomes "No adjusted gross income" (chronicle#377's 6d74e42, without its
  build-bundle check).
- AGI all: Historic Table 2 and Table 1.1's "All returns" becomes "All
  returns, total". Table 1.1 now states its IRS row header explicitly, so
  its row-header check still reads "All returns" in cell A10.
- age 85_plus: national Census PEP's "Age 85 and over" becomes "Age 85 plus".

Measured (experiments/us-feed-agi-age-labels/compare.json): the feed keeps
its 40,178 aggregate_fact_keys and every value, key and constraint; 816
rows in six record sets change only in label fields (66 rows) and
layout.record_set_spec_hash. Compiled at 2024 as the release does, the
33,250 targets keep their names, order and values; 50 change only in the
hierarchy value_label and metadata ledger_layout_groupby_value_label (2
under_1, 47 all, 1 85_plus). Registry dde00e855af8 -> b8d571e91c22;
national_state 6,102 targets, 3fad14f85468 -> 3aa2a0830e7d.

Pin: facts 965a29ac... -> e477e04a..., scope f0901020... -> 8fedefb7...
(only source_commit), 40,178 rows. Parity resources regenerated: only the
feed name (consumer_facts_us_9b91e8b.jsonl) and digest change, 32 compiled /
52 reviewed. Test pins, the re-pin doc and a changelog fragment updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1343)

Tests only. The label test added for #1343 compiles the pinned feed, so it
skips in CI, and a later re-pin built on a Chronicle commit without the
three label commits would bring the old labels back unnoticed. A re-pin
must regenerate target_parity_feed_families.json from its feed (CI checks
that the inventory restates the pinned digest), so the inventory is where
CI can see the labels:

- test_feed_inventory_lists_only_the_reviewed_label_variants requires the
  committed inventory to list, as dimension_value_label_variants, exactly
  the two values the feed still labels two ways (age_20 and age_21), and
  none of adjusted gross income under_1 or all or age 85_plus;
- TestFeedLabelVariants checks the generator's listing on synthetic rows,
  reading both dimension_value_labels and layout.groupby_value_label.

Both fail now: the inventory has no dimension_value_label_variants
(KeyError) and the generator has no _dimension_value_label_variants
(AttributeError).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/build_us_target_parity_manifest.py now writes, into
target_parity_feed_families.json, dimension_value_label_variants: every
(dimension, value id) the feed labels more than one way, with its labels,
read from dimension_value_labels and layout.groupby_value_label as the
ledger compiler reads them. Regenerated from consumer_facts_us_9b91e8b.jsonl
(sha256 e477e04a...), it lists only age = age_20 and age_21; the manifest
and the family counts are unchanged.

A re-pin must regenerate the inventory from its feed, because CI checks that
the inventory restates the pinned digest. So a re-pin built on a Chronicle
commit without the three #1343 label commits now lists those values again
and fails test_feed_inventory_lists_only_the_reviewed_label_variants in CI,
where the compiled-target label test skips for want of the feed. The re-pin
doc says so.

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

This branch has not been deployed

No deployments
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.

US targets label three dimension values two ways: re-pin the Chronicle feed with one label each

1 participant