diff --git a/.gitattributes b/.gitattributes index 52fbd20..da4db6a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,13 @@ # Files whose exact Git and working-tree bytes are hashed by the release chain. ledger/official_observations.jsonl text eol=lf ledger/immutable_prefix.json text eol=lf + +# Series-catalog surfaces: byte digests bind these files (catalog embeds the +# registry digest; the registry's append-only check is a byte-prefix match), +# so line endings must survive any checkout configuration. +ledger/series_catalog.json text eol=lf +ledger/series_uuid_registry.jsonl text eol=lf +ledger/seeds/thesis_docket_series.json text eol=lf releases/manifests/*.json text eol=lf releases/anchors/*.pem text eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 665b004..23b7556 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,54 @@ jobs: - name: Install dependencies run: uv sync --locked --all-extras + - name: Series catalog current and tested + run: | + uv run --locked python scripts/build_series_catalog.py --check + uv run --locked pytest tests/test_build_series_catalog.py -q + + # The UUID registry is the append-only minting ledger behind the + # series catalog: a change may extend it, never edit or truncate it. + # --check already proves catalog/registry agreement and (via git) + # that the working tree extends HEAD; this step closes the + # cross-commit hole by proving the new head extends the comparison + # base — the PR base for pull requests, the pre-push head for pushes. + # A push whose before-sha is unfetchable (history rewrite) fails + # loudly rather than skipping. + - name: Series UUID registry append-only vs base + if: >- + github.event_name == 'pull_request' || + github.event_name == 'push' + env: + BASE_SHA: ${{ github.event_name == 'pull_request' + && github.event.pull_request.base.sha + || github.event.before }} + # New-branch pushes have an all-zero before-sha; they must still + # extend the canonical lineage's registry rather than skipping. + # Trust anchor: this fallback ref is only sound because branch + # protection on codex/thesis-ledger-facts forbids deletions and + # force pushes (verified 2026-08-02); a recreated ref would + # otherwise compare the registry to itself. + FALLBACK_REF: codex/thesis-ledger-facts + run: | + if [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then + # Fully qualified so a same-named tag can never shadow the + # protected branch. + git fetch --no-tags --depth=1 origin \ + "refs/heads/$FALLBACK_REF" + BASE_SHA=FETCH_HEAD + else + git fetch --no-tags --depth=1 origin "$BASE_SHA" + fi + if git cat-file -e "$BASE_SHA:ledger/series_uuid_registry.jsonl" \ + 2>/dev/null; then + git show "$BASE_SHA:ledger/series_uuid_registry.jsonl" \ + > /tmp/base-series-uuid-registry.jsonl + else + : > /tmp/base-series-uuid-registry.jsonl + fi + uv run --locked python scripts/build_series_catalog.py \ + --verify-registry-append-only /tmp/base-series-uuid-registry.jsonl + - name: Lint Arch surface run: > uv run ruff check @@ -58,6 +106,8 @@ jobs: micro/us/validation_dashboard.py calibration/constraints.py calibration/targets.py + scripts/build_series_catalog.py + tests/test_build_series_catalog.py tests/test_arch_facts.py tests/test_arch_namespace.py tests/test_arch_normalization.py diff --git a/.gitignore b/.gitignore index 3a04aed..bab0256 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ micro/us/*.parquet venv/ *.parquet +review-context-*.md diff --git a/ledger/seeds/thesis_docket_series.json b/ledger/seeds/thesis_docket_series.json new file mode 100644 index 0000000..08027b1 --- /dev/null +++ b/ledger/seeds/thesis_docket_series.json @@ -0,0 +1,1457 @@ +{ + "comment": "Registry for the auto-roll loop (scripts/roll_docket.py). Recurring series derive their next period from the latest published run; a series without a published run may use one reviewed seedPeriod with an exact official release date. Reviewed annual registered-query snapshots instead carry one explicit period and capture window and are never cadence-stepped. Neither seed path cadence-steps itself. The live catalog slug set is the final duplicate guard.", + "series": [ + { + "series": "us.dol.initial_claims.sa", + "cadence": "weekly", + "slug": "initial-claims-week-{period}", + "extras": { + "valueScale": 0.001, + "targetUnit": "thousands" + } + }, + { + "series": "dol.eta.continued_claims.sa", + "cadence": "weekly", + "slug": "continued-claims-week-{period}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions" + } + }, + { + "series": "bls.cpi.u.headline_mom", + "cadence": "monthly", + "slug": "us-cpi-u-mom-{month}-{year}" + }, + { + "series": "bls.cpi.u.core_mom", + "cadence": "monthly", + "slug": "us-core-cpi-mom-{month}-{year}" + }, + { + "series": "bls.ces.nonfarm_payrolls.change", + "cadence": "monthly", + "slug": "nonfarm-payrolls-{month}-{year}" + }, + { + "series": "bls.cps.unemployment_rate", + "cadence": "monthly", + "slug": "unemployment-rate-{month}-{year}" + }, + { + "series": "bls.jolts.job_openings", + "cadence": "monthly", + "slug": "jolts-openings-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions" + } + }, + { + "series": "bls.jolts.quits_rate", + "cadence": "monthly", + "slug": "jolts-quits-rate-{month}-{year}" + }, + { + "series": "bls.real_earnings.avg_hourly_mom", + "cadence": "monthly", + "slug": "us-real-avg-hourly-earnings-mom-{month}-{year}" + }, + { + "series": "bls.cps.telework_share", + "cadence": "monthly", + "slug": "us-telework-rate-{month}-{year}" + }, + { + "series": "bea.core_pce.mom", + "cadence": "monthly", + "slug": "us-core-pce-mom-{month}-{year}" + }, + { + "series": "treasury.mts.monthly_deficit", + "cadence": "monthly", + "slug": "us-mts-deficit-{month}-{year}" + }, + { + "series": "fns.wic.total_participation", + "cadence": "monthly", + "slug": "wic-participation-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions" + } + }, + { + "series": "fns.snap.total_persons", + "cadence": "monthly", + "slug": "snap-participation-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.fns.usda.gov/pd/supplemental-nutrition-assistance-program-snap", + "sourceSeriesId": "fns.snap.total_persons", + "field": "Persons", + "table": "FNS SNAP data tables, national monthly participation (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "ssa.ssi.total_recipients", + "cadence": "monthly", + "slug": "ssi-recipients-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions" + } + }, + { + "series": "eurostat.hicp.flash.yoy", + "cadence": "monthly", + "slug": "euro-flash-hicp-{month}-{year}", + "releaseCalendarUrl": "https://ec.europa.eu/eurostat/news/euro-indicators/release-calendar", + "releaseDates": { + "2026-07": "2026-07-31" + }, + "extras": { + "sourceBinding": { + "adapter": "eurostat-api", + "sourceUrl": "https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/prc_hicp_minr/M.RCH_A.TOTAL.EA21?format=JSON&lastNObservations=36", + "sourceSeriesId": "prc_hicp_minr/M.RCH_A.TOTAL.EA21", + "field": "prc_hicp_minr/M.RCH_A.TOTAL.EA21", + "table": "HICP (ECOICOP ver.2) monthly rates, prc_hicp_minr (all-items annual rate, euro area)", + "transform": { + "operation": "identity", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "eurostat.unemployment_rate", + "cadence": "monthly", + "slug": "euro-area-unemployment-rate-{month}-{year}", + "extras": { + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/une_rt_m/M.SA.TOTAL.PC_ACT.T.EA21?format=JSON", + "sourceSeriesId": "une_rt_m/M.SA.TOTAL.PC_ACT.T.EA21", + "field": "PC_ACT", + "table": "Unemployment by sex and age (une_rt_m), euro area, SA, total; first print captured between monthly releases", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "abs.cpi.all_groups.yoy", + "cadence": "monthly", + "slug": "australia-cpi-annual-rate-{month}-{year}", + "releaseCalendarUrl": "https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia", + "releaseDates": { + "2026-06": "2026-07-29", + "2026-07": "2026-08-26", + "2026-08": "2026-09-30" + }, + "extras": { + "sourceBinding": { + "adapter": "abs-data-api", + "sourceUrl": "https://data.api.abs.gov.au/rest/data/CPI/3.10001.10.50.M?lastNObservations=30&format=jsondata", + "sourceSeriesId": "CPI/3.10001.10.50.M", + "field": "CPI/3.10001.10.50.M", + "table": "Monthly Consumer Price Index (complete monthly CPI, dataflow CPI: annual change, all groups, original, weighted average of eight capital cities)", + "transform": { + "operation": "identity", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "abs.labour.unemployment_rate", + "cadence": "monthly", + "slug": "australia-unemployment-rate-{month}-{year}", + "releaseCalendarUrl": "https://www.abs.gov.au/statistics/labour/employment-and-unemployment/labour-force-australia", + "releaseDates": { + "2026-06": "2026-07-23", + "2026-07": "2026-08-20", + "2026-08": "2026-09-24" + }, + "extras": { + "sourceBinding": { + "adapter": "abs-data-api", + "sourceUrl": "https://data.api.abs.gov.au/rest/data/LF/M13.3.1599.20.AUS.M?lastNObservations=30&format=jsondata", + "sourceSeriesId": "LF/M13.3.1599.20.AUS.M", + "field": "LF/M13.3.1599.20.AUS.M", + "table": "Labour Force, Australia (dataflow LF: unemployment rate, persons, total age, seasonally adjusted, Australia)", + "transform": { + "operation": "identity", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "statcan.gdp_by_industry.monthly_growth", + "cadence": "monthly", + "slug": "canada-monthly-gdp-growth-{month}-{year}", + "releaseCalendarUrl": "https://www150.statcan.gc.ca/n1/release-diffusion/2026-eng.pdf", + "releaseDates": { + "2026-05": "2026-07-31", + "2026-06": "2026-08-28", + "2026-07": "2026-09-29", + "2026-08": "2026-10-30", + "2026-09": "2026-11-30", + "2026-10": "2026-12-23", + "2026-11": "2027-01-29", + "2026-12": "2027-03-01", + "2027-01": "2027-03-31" + }, + "extras": { + "sourceBinding": { + "adapter": "statcan-wds", + "sourceUrl": "https://www150.statcan.gc.ca/t1/wds/rest/getDataFromVectorsAndLatestNPeriods", + "sourceSeriesId": "v65201210", + "field": "v65201210", + "table": "GDP by industry, Table 36-10-0434-01 (all industries, chained 2017 dollars, SA at annual rates)", + "transform": { + "operation": "percent_change_previous_period", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "statcan.cpi.allitems.yoy", + "cadence": "monthly", + "slug": "canada-cpi-annual-rate-{month}-{year}", + "releaseCalendarUrl": "https://www150.statcan.gc.ca/n1/release-diffusion/2026-eng.pdf", + "releaseDates": { + "2026-06": "2026-07-20", + "2026-07": "2026-08-17", + "2026-08": "2026-09-14", + "2026-09": "2026-10-19", + "2026-10": "2026-11-16", + "2026-11": "2026-12-14", + "2026-12": "2027-01-18", + "2027-01": "2027-02-16", + "2027-02": "2027-03-15" + }, + "extras": { + "sourceBinding": { + "adapter": "statcan-wds", + "sourceUrl": "https://www150.statcan.gc.ca/t1/wds/rest/getDataFromVectorsAndLatestNPeriods", + "sourceSeriesId": "v41690973", + "field": "v41690973", + "table": "Consumer Price Index, Table 18-10-0004-01 (all-items, Canada)", + "transform": { + "operation": "percent_change_year_ago", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "statcan.employment_insurance.regular_beneficiaries", + "cadence": "monthly", + "slug": "canada-ei-regular-beneficiaries-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "thousands", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www150.statcan.gc.ca/t1/wds/rest/getDataFromVectorByReferencePeriodRange?vectorIds=64549350", + "sourceSeriesId": "v64549350", + "field": "v64549350", + "table": "Statistics Canada Table 14-10-0011-01, EI regular beneficiaries, Canada, seasonally adjusted (persons)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "statjp.cpi.tokyo_all_items_yoy", + "cadence": "monthly", + "slug": "japan-tokyo-cpi-annual-rate-{month}-{year}-prelim", + "extras": { + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.stat.go.jp/data/cpi/sokuhou/tsuki/index-t.html", + "sourceSeriesId": "statjp.tokyo_cpi.all_items", + "field": "0001", + "table": "2020-base CPI Tokyo ku-area mid-month preliminary; machine-readable vintage is the release's e-Stat table 1-2 workbook (all items, series code 0001), YoY computed from rounded index", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "statbel.cpi.headline_yoy", + "cadence": "monthly", + "slug": "belgium-cpi-annual-rate-{month}-{year}", + "extras": { + "country": "BE" + } + }, + { + "series": "statbel.health_index.yoy", + "cadence": "monthly", + "slug": "belgium-health-index-annual-rate-{month}-{year}", + "extras": { + "country": "BE" + } + }, + { + "series": "eurostat.unemployment_rate.belgium", + "cadence": "monthly", + "slug": "belgium-unemployment-rate-{month}-{year}", + "extras": { + "country": "BE" + } + }, + { + "series": "nbb.business_barometer.overall", + "cadence": "monthly", + "slug": "belgium-nbb-business-barometer-{month}-{year}", + "extras": { + "country": "BE", + "targetUnit": "index_points" + } + }, + { + "series": "nbb.consumer_confidence.indicator", + "cadence": "monthly", + "slug": "belgium-consumer-confidence-{month}-{year}", + "extras": { + "country": "BE", + "targetUnit": "index_points" + } + }, + { + "series": "nbb.gdp.flash_qoq", + "cadence": "quarterly", + "slug": "belgium-gdp-flash-q{quarter}-{year}", + "extras": { + "country": "BE" + } + }, + { + "series": "bls.productivity.nonfarm_qoq_prelim", + "cadence": "quarterly", + "slug": "us-nonfarm-productivity-q{quarter}-{year}-prelim" + }, + { + "series": "fed.g17.industrial_production.total_index_mom", + "cadence": "monthly", + "slug": "fed-g17-industrial-production-total-index-mom-{month}-{year}", + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=INDPRO", + "sourceSeriesId": "INDPRO", + "field": "INDPRO", + "table": "G.17 Industrial Production and Capacity Utilization, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g17.manufacturing_production_mom", + "cadence": "monthly", + "slug": "us-manufacturing-production-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.federalreserve.gov/releases/g17/release_dates.htm", + "releaseDates": { + "2026-07": "2026-08-18" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=IPMAN", + "sourceSeriesId": "IPMAN", + "field": "IPMAN", + "table": "G.17 Industrial Production and Capacity Utilization, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g17.capacity_utilization.total_industry", + "cadence": "monthly", + "slug": "fed-g17-capacity-utilization-total-industry-{month}-{year}", + "extras": { + "targetUnit": "percent", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=TCU", + "sourceSeriesId": "TCU", + "field": "TCU", + "table": "G.17 Industrial Production and Capacity Utilization, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g17.capacity_utilization.manufacturing", + "cadence": "monthly", + "slug": "us-manufacturing-capacity-utilization-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.federalreserve.gov/releases/g17/release_dates.htm", + "releaseDates": { + "2026-07": "2026-08-18" + }, + "extras": { + "targetUnit": "percent", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=MCUMFN", + "sourceSeriesId": "MCUMFN", + "field": "MCUMFN", + "table": "G.17 Industrial Production and Capacity Utilization, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.housing_starts.saar", + "cadence": "monthly", + "slug": "census-housing-starts-saar-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=HOUST", + "sourceSeriesId": "HOUST", + "field": "HOUST", + "table": "New Residential Construction, seasonally adjusted annual rates", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.housing.permits_saar", + "cadence": "monthly", + "slug": "us-building-permits-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.census.gov/construction/soc/schedule.html", + "releaseDates": { + "2026-07": "2026-08-18" + }, + "extras": { + "targetUnit": "thousands", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=PERMIT", + "sourceSeriesId": "PERMIT", + "field": "PERMIT", + "table": "New Residential Construction, seasonally adjusted annual rates", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.housing.completions_saar", + "cadence": "monthly", + "slug": "us-housing-completions-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.census.gov/construction/soc/schedule.html", + "releaseDates": { + "2026-07": "2026-08-18" + }, + "extras": { + "targetUnit": "thousands", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=COMPUTSA", + "sourceSeriesId": "COMPUTSA", + "field": "COMPUTSA", + "table": "New Residential Construction, seasonally adjusted annual rates", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.new_residential_sales.new_single_family_houses_sold_saar", + "cadence": "monthly", + "slug": "us-new-home-sales-saar-{month}-{year}", + "extras": { + "targetUnit": "thousands", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=HSN1F", + "sourceSeriesId": "HSN1F", + "field": "HSN1F", + "table": "New Residential Sales, Table 1", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.m3.durable_goods_new_orders_mom", + "cadence": "monthly", + "slug": "us-durable-goods-orders-mom-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.census.gov/manufacturing/m3/release_schedule.html", + "releaseDates": { + "2026-06": "2026-07-27" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=DGORDER", + "sourceSeriesId": "DGORDER", + "field": "DGORDER", + "table": "Advance Report on Durable Goods Manufacturers' Shipments, Inventories, and Orders", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.m3.durable_goods_shipments_mom", + "cadence": "monthly", + "slug": "us-durable-goods-shipments-mom-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.census.gov/manufacturing/m3/release_schedule.html", + "releaseDates": { + "2026-06": "2026-07-27" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=AMDMVS", + "sourceSeriesId": "AMDMVS", + "field": "AMDMVS", + "table": "Advance Report on Durable Goods Manufacturers' Shipments, Inventories, and Orders", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.construction_spending.total_mom", + "cadence": "monthly", + "slug": "us-construction-spending-mom-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.census.gov/construction/c30/release.html", + "releaseDates": { + "2026-06": "2026-08-03" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=TTLCONS", + "sourceSeriesId": "TTLCONS", + "field": "TTLCONS", + "table": "Value of Construction Put in Place Survey", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "census.mtis.total_business_inventories_level", + "cadence": "monthly", + "slug": "us-total-business-inventories-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "usd_billions", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=BUSINV", + "sourceSeriesId": "BUSINV", + "field": "BUSINV", + "table": "Manufacturing and Trade Inventories and Sales", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bea.trade.goods_services_deficit", + "cadence": "monthly", + "slug": "us-goods-services-trade-deficit-{month}-{year}", + "extras": { + "valueScale": -0.001, + "targetUnit": "usd_billions", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=BOPGSTB", + "sourceSeriesId": "BOPGSTB", + "field": "BOPGSTB", + "table": "U.S. International Trade in Goods and Services, Exhibit 1", + "transform": { + "operation": "multiply", + "factor": -0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.import_price_index.all_imports_mom", + "cadence": "monthly", + "slug": "bls-import-price-index-all-imports-mom-{month}-{year}", + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=IR", + "sourceSeriesId": "IR", + "field": "IR", + "table": "U.S. Import Price Indexes, Table 1", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.export_prices.all_commodities_mom", + "cadence": "monthly", + "slug": "us-export-prices-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/ximpim.htm", + "releaseDates": { + "2026-07": "2026-08-18" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=IQ", + "sourceSeriesId": "IQ", + "field": "IQ", + "table": "U.S. Export Price Indexes, Table 2", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.ppi.final_demand_monthly_change", + "cadence": "monthly", + "slug": "bls-ppi-final-demand-monthly-change-{month}-{year}", + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=PPIFIS", + "sourceSeriesId": "PPIFIS", + "field": "PPIFIS", + "table": "Producer Price Index, final demand, seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.eci.total_compensation_private_industry_qoq", + "cadence": "quarterly", + "slug": "us-employment-cost-index-total-compensation-q{quarter}-{year}", + "seedPeriod": "2026-Q3", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/eci.htm", + "releaseDates": { + "2026-Q2": "2026-07-31", + "2026-Q3": "2026-10-30" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=ECICOM", + "sourceSeriesId": "ECICOM", + "field": "ECICOM", + "table": "Employment Cost Index, Table 1", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.eci.private_wages_salaries_qoq", + "cadence": "quarterly", + "slug": "us-eci-private-wages-salaries-q{quarter}-{year}", + "seedPeriod": "2026-Q3", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/eci.htm", + "releaseDates": { + "2026-Q2": "2026-07-31", + "2026-Q3": "2026-10-30" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=ECIWAG", + "sourceSeriesId": "ECIWAG", + "field": "ECIWAG", + "table": "Employment Cost Index, Table 2", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.productivity.nonfarm_unit_labor_costs_qoq_prelim", + "cadence": "quarterly", + "slug": "us-unit-labor-costs-q{quarter}-{year}-prelim", + "seedPeriod": "2026-Q2", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/prod2.htm", + "releaseDates": { + "2026-Q2": "2026-08-06" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=PRS85006112", + "sourceSeriesId": "PRS85006112", + "field": "PRS85006112", + "table": "Productivity and Costs, nonfarm business sector", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g19.consumer_credit_total_annual_rate", + "cadence": "monthly", + "slug": "us-consumer-credit-annual-rate-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.federalreserve.gov/newsevents/2026-august.htm", + "releaseDates": { + "2026-06": "2026-08-07" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=TOTALSLAR", + "sourceSeriesId": "TOTALSLAR", + "field": "TOTALSLAR", + "table": "G.19 Consumer Credit, outstanding, seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g19.consumer_credit_revolving_annual_rate", + "cadence": "monthly", + "slug": "us-revolving-consumer-credit-annual-rate-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.federalreserve.gov/newsevents/2026-august.htm", + "releaseDates": { + "2026-06": "2026-08-07" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=REVOLSLAR", + "sourceSeriesId": "REVOLSLAR", + "field": "REVOLSLAR", + "table": "G.19 Consumer Credit, outstanding, seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "fed.g19.consumer_credit_nonrevolving_annual_rate", + "cadence": "monthly", + "slug": "us-nonrevolving-consumer-credit-annual-rate-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.federalreserve.gov/newsevents/2026-august.htm", + "releaseDates": { + "2026-06": "2026-08-07" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=NONREVSLAR", + "sourceSeriesId": "NONREVSLAR", + "field": "NONREVSLAR", + "table": "G.19 Consumer Credit, outstanding, seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cpi.shelter_mom", + "cadence": "monthly", + "slug": "us-cpi-shelter-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/cpi.htm", + "releaseDates": { + "2026-07": "2026-08-12" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CUSR0000SAH1", + "sourceSeriesId": "CUSR0000SAH1", + "field": "CUSR0000SAH1", + "table": "Consumer Price Index, U.S. city average, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cpi.rent_primary_residence_mom", + "cadence": "monthly", + "slug": "us-cpi-primary-rent-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/cpi.htm", + "releaseDates": { + "2026-07": "2026-08-12" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CUSR0000SEHA", + "sourceSeriesId": "CUSR0000SEHA", + "field": "CUSR0000SEHA", + "table": "Consumer Price Index, U.S. city average, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cpi.owners_equivalent_rent_mom", + "cadence": "monthly", + "slug": "us-cpi-owners-equivalent-rent-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/cpi.htm", + "releaseDates": { + "2026-07": "2026-08-12" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CUSR0000SEHC", + "sourceSeriesId": "CUSR0000SEHC", + "field": "CUSR0000SEHC", + "table": "Consumer Price Index, U.S. city average, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cpi.services_less_energy_mom", + "cadence": "monthly", + "slug": "us-cpi-services-less-energy-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/cpi.htm", + "releaseDates": { + "2026-07": "2026-08-12" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CUSR0000SASLE", + "sourceSeriesId": "CUSR0000SASLE", + "field": "CUSR0000SASLE", + "table": "Consumer Price Index, U.S. city average, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cpi.services_less_rent_shelter_mom", + "cadence": "monthly", + "slug": "us-cpi-services-less-rent-shelter-mom-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/cpi.htm", + "releaseDates": { + "2026-07": "2026-08-12" + }, + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CUSR0000SASL2RS", + "sourceSeriesId": "CUSR0000SASL2RS", + "field": "CUSR0000SASL2RS", + "table": "Consumer Price Index, U.S. city average, monthly seasonally adjusted", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.jolts.hires_rate", + "cadence": "monthly", + "slug": "jolts-hires-rate-{month}-{year}", + "seedPeriod": "2026-06", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/jolts.htm", + "releaseDates": { + "2026-06": "2026-08-04" + }, + "extras": { + "targetUnit": "percent", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=JTSHIR", + "sourceSeriesId": "JTSHIR", + "field": "JTSHIR", + "table": "JOLTS news release, Table 1", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.ces.average_hourly_earnings_private", + "cadence": "monthly", + "slug": "average-hourly-earnings-mom-{month}-{year}", + "extras": { + "targetUnit": "percent_growth", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CES0500000003", + "sourceSeriesId": "CES0500000003", + "field": "CES0500000003", + "table": "Employment Situation, Table B-3", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.lns11300000", + "cadence": "monthly", + "slug": "labor-force-participation-{month_abbr}-{year}", + "extras": { + "targetUnit": "percent", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=CIVPART", + "sourceSeriesId": "CIVPART", + "field": "CIVPART", + "table": "Employment Situation, Table A-1", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.u6_underemployment_rate", + "cadence": "monthly", + "slug": "u6-underemployment-rate-{month}-{year}", + "seedPeriod": "2026-07", + "releaseCalendarUrl": "https://www.bls.gov/schedule/news_release/empsit.htm", + "releaseDates": { + "2026-07": "2026-08-07" + }, + "extras": { + "targetUnit": "percent", + "sourceBinding": { + "adapter": "alfred-fred", + "sourceUrl": "https://alfred.stlouisfed.org/graph/alfredgraph.csv?id=U6RATE", + "sourceSeriesId": "U6RATE", + "field": "U6RATE", + "table": "Employment Situation, Table A-15", + "transform": { + "operation": "multiply", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "usaspending.dod.prime_award_obligations", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-prime-award-obligations-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 1e-09, + "targetUnit": "billions USD", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/agency/097/awards/?fiscal_year={fiscal_year}", + "sourceSeriesId": "usaspending.agency.097.awards.obligations", + "field": "obligations", + "table": "USAspending API v2, agency 097 (DoD) award summary, prime award obligations, fiscal year to date", + "transform": { + "operation": "multiply", + "factor": 1e-09 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "usaspending.dod.prime_contract_obligations", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-prime-contract-obligations-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 1e-09, + "targetUnit": "billions USD", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/agency/097/obligations_by_award_category/?fiscal_year={fiscal_year}", + "sourceSeriesId": "usaspending.agency.097.obligations_by_award_category.contracts", + "field": "results[category=contracts].aggregated_amount", + "table": "USAspending API v2, agency 097 (DoD) obligations by award category, contracts row, fiscal year to date", + "transform": { + "operation": "multiply", + "factor": 1e-09 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "usaspending.dod.new_prime_awards", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-new-prime-awards-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 1e-06, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/agency/097/awards/new/count/?fiscal_year={fiscal_year}", + "sourceSeriesId": "usaspending.agency.097.awards.new_award_count", + "field": "new_award_count", + "table": "USAspending API v2, agency 097 (DoD) new award count, fiscal year to date", + "transform": { + "operation": "multiply", + "factor": 1e-06 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "usaspending.dod.prime_award_transactions", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-prime-award-transactions-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 1e-06, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/agency/097/awards/?fiscal_year={fiscal_year}", + "sourceSeriesId": "usaspending.agency.097.awards.transaction_count", + "field": "transaction_count", + "table": "USAspending API v2, agency 097 (DoD) award summary, transaction count, fiscal year to date", + "transform": { + "operation": "multiply", + "factor": 1e-06 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "usaspending.dod.unique_prime_contract_recipients", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-unique-prime-contract-recipients-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 0.001, + "targetUnit": "thousands", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/search/spending_by_category/recipient/", + "sourceSeriesId": "usaspending.search.spending_by_category.recipient.dod.contracts.distinct", + "field": "results[].recipient_id", + "table": "USAspending API v2 advanced search, DoD prime-contract obligations grouped by recipient, fiscal year to date", + "transform": { + "operation": "count_distinct", + "requestMethod": "POST", + "fiscalYear": "{fiscal_year}", + "spendingLevel": "transactions", + "agency": { + "type": "awarding", + "tier": "toptier", + "name": "Department of Defense" + }, + "awardTypeCodes": [ + "A", + "B", + "C", + "D" + ], + "identityField": "recipient_id", + "excludeNullIdentity": true, + "pageSize": 100, + "factor": 0.001 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "usaspending.dod.small_business_contract_obligation_share", + "cadence": "annual", + "period": "FY2026", + "slug": "us-dod-small-business-contract-obligation-share-{period}", + "extras": { + "expectedReleaseWindow": { + "start": "2026-10-15", + "end": "2026-10-22" + }, + "valueScale": 1, + "targetUnit": "percent", + "sourceBinding": { + "adapter": "usaspending-api", + "sourceUrl": "https://api.usaspending.gov/api/v2/search/spending_over_time/", + "sourceSeriesId": "usaspending.search.spending_over_time.dod.contracts.small_business_obligation_share", + "field": "results[time_period.fiscal_year={fiscal_year}].aggregated_amount", + "table": "USAspending API v2 advanced search, small-business share of DoD prime-contract obligations, fiscal year to date", + "transform": { + "operation": "ratio_percent", + "requestMethod": "POST", + "fiscalYear": "{fiscal_year}", + "group": "fiscal_year", + "spendingLevel": "transactions", + "agency": { + "type": "awarding", + "tier": "toptier", + "name": "Department of Defense" + }, + "awardTypeCodes": [ + "A", + "B", + "C", + "D" + ], + "numeratorRecipientTypeNames": [ + "small_business" + ], + "denominatorRecipientTypeNames": [], + "factor": 1 + }, + "releasePolicy": "registered_query_snapshot" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.business_financial_operations", + "cadence": "monthly", + "slug": "cps-business-financial-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.business_financial_operations", + "field": "Business and financial operations occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.computer_mathematical", + "cadence": "monthly", + "slug": "cps-computer-math-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.computer_mathematical", + "field": "Computer and mathematical occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.healthcare_support", + "cadence": "monthly", + "slug": "cps-healthcare-support-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.healthcare_support", + "field": "Healthcare support occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.office_administrative_support", + "cadence": "monthly", + "slug": "cps-office-admin-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.office_administrative_support", + "field": "Office and administrative support occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.production", + "cadence": "monthly", + "slug": "cps-production-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.production", + "field": "Production occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "bls.cps.employed_people_by_occupation.transportation_material_moving", + "cadence": "monthly", + "slug": "cps-transport-material-moving-employment-{month}-{year}", + "extras": { + "valueScale": 0.001, + "targetUnit": "millions", + "sourceBinding": { + "adapter": "generic-url", + "sourceUrl": "https://www.bls.gov/web/empsit/cpseea19.htm", + "sourceSeriesId": "bls.cps.employed_people_by_occupation.transportation_material_moving", + "field": "Transportation and material moving occupations", + "table": "CPS Employment Situation Table A-19, employed persons by occupation, not seasonally adjusted (thousands)", + "transform": { + "operation": "multiply", + "factor": 0.001 + }, + "releasePolicy": "first_print" + } + } + }, + { + "series": "abs.labour.employment_change.australia", + "cadence": "monthly", + "slug": "abs-labour-employment-change-australia-{month}-{year}", + "extras": { + "valueScale": 1, + "targetUnit": "thousands", + "country": "AU", + "sourceBinding": { + "adapter": "generic-url", + "field": "LF/M3.3.1599.20.AUS.M", + "releasePolicy": "first_print", + "sourceSeriesId": "LF/M3.3.1599.20.AUS.M", + "sourceUrl": "https://data.api.abs.gov.au/rest/data/LF/M3.3.1599.20.AUS.M?lastNObservations=30&format=jsondata", + "table": "Labour Force, Australia (dataflow LF: employed persons, seasonally adjusted, Australia; month-over-month change)", + "transform": { + "factor": 1, + "operation": "identity" + } + } + } + }, + { + "series": "usda.fsa.crp.enrolled_acres_total", + "cadence": "monthly", + "slug": "us-crp-enrolled-acres-{month}-{year}", + "extras": { + "valueScale": 1, + "targetUnit": "count", + "country": "US", + "sourceBinding": { + "adapter": "fsa-crp-monthly-summary", + "sourceUrl": "https://www.fsa.usda.gov/resources/programs/conservation-reserve-program/statistics", + "sourceSeriesId": "usda.fsa.crp.enrolled_acres_total", + "field": "enrolled_acres_total", + "table": "USDA FSA Conservation Reserve Program Statistics, CRP Monthly Summary, total row", + "transform": { + "operation": "identity", + "factor": 1 + }, + "releasePolicy": "first_print" + } + } + } + ] +} diff --git a/ledger/series_catalog.json b/ledger/series_catalog.json new file mode 100644 index 0000000..a6f2581 --- /dev/null +++ b/ledger/series_catalog.json @@ -0,0 +1,6385 @@ +{ + "comment": "Canonical series catalog. One row per (concept, geography level/id/vintage, entity) identity. UUID authority is the append-only ledger/series_uuid_registry.jsonl (digest below): a uuid is minted once, inherited from the registry on every regeneration, and changes only through explicit, chained supersede/retire/revive/succeeds events recorded there (live bindings and rows stay in bijection). Consumers reference series by uuid or concept only. Regenerate with scripts/build_series_catalog.py; verify with --check. Aliases are curated identity statements (plus observed spellings of the same identity) and inherit only within one (geography, entity) slice; aliases listed in ambiguous_aliases match multiple rows and never drive inheritance. source_concepts are publisher labels — provenance, never identity. Cross-spelling and cross-vintage merges are manual curation: delete the absorbed row, alias its concept on the survivor, regenerate with --allow-remint.", + "generator_version": 3, + "observations_sha256": "63127ff427a4aa3884f54dd1ee070ab631c15ebb38f23fdc9095a45e9204109f", + "observation_rows": 168, + "docket_seed_sha256": "930424fb48c0be4c9e2ce17d4e0f2a6be886408e814c80324174a7a303fa0271", + "uuid_registry_sha256": "c4ccda3f1746ff06cf8cc17dc221ea3a82b7b16a5d719361e2025bc5d7b63356", + "suspect_segments": [], + "stripped_segments": { + "2026-05": [ + "abs.cpi_indicator.allgroups.yoy", + "estat.jp.cpi.core_exfreshfood.yoy", + "ons.cpih.annual_rate", + "statcan.36-10-0434-01.all_industries.month_to_month_percent_change", + "statcan.cpi.allitems.yoy", + "us.bea.core_pce.mom_sa", + "us.census.housing_starts.total_saar", + "us.frb.industrial_production.total.mom_sa" + ], + "2026-06": [ + "abs.cpi.all_groups.yoy", + "bls.import_price_index.all_imports_mom", + "census.housing_starts.saar", + "eurostat.ea.hicp.flash.yoy", + "fed.g17.capacity_utilization.total_industry", + "fed.g17.industrial_production.total_index_mom", + "us.bea.core_pce.mom_sa", + "us.fed.fomc.target_range_upper" + ], + "2026-06-18": [ + "boe.bank_rate" + ], + "2026-07": [ + "cms.care_compare.nursing_home_occupancy_pct", + "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us", + "eurostat.ea.hicp.flash.yoy" + ], + "2026_05": [ + "estat.jp.cpi.core_exfreshfood.yoy", + "ons.cpih.annual_rate", + "us.census.housing_starts.total_saar", + "us.frb.industrial_production.total.mom_sa" + ], + "2026_06": [ + "census.m3.durable_goods_new_orders_mom", + "census.m3.durable_goods_shipments_mom", + "us.fed.fomc.target_range_upper" + ], + "2026_06_18": [ + "boe.bank_rate" + ], + "2026_q2": [ + "bls.eci.private_wages_salaries_qoq", + "bls.eci.total_compensation_private_industry_qoq" + ], + "after_june_2026": [ + "bank_of_canada.overnight_rate", + "boj.policy_rate_guideline", + "ecb.deposit_facility_rate", + "rba.cash_rate_target" + ], + "after_mpc_june_2026": [ + "boe.bank_rate" + ], + "april_2026": [ + "census.mtis.total_business_inventories_level", + "eurostat.industrial_production.euro_area", + "ons.gdp.monthly_growth", + "statcan.building_permits.total_value_mom.canada", + "statcan.employment_insurance.regular_beneficiaries.canada", + "statcan.gdp_by_industry.monthly_growth", + "statcan.retail_trade.sales_mom.canada", + "statcan.wholesale_trade.sales_mom_exclusions.canada" + ], + "feb_2026": [ + "cms.medicaid_pi.beneficiaries_disenrolled_procedural", + "cms.medicaid_pi.beneficiaries_disenrolled_total", + "cms.medicaid_pi.beneficiaries_renewed_ex_parte", + "cms.medicaid_pi.beneficiaries_renewed_total" + ], + "february_to_april_2026": [ + "ons.labour.unemployment_rate" + ], + "fy2024": [ + "fns.snap.application_processing_timeliness_rate", + "fns.snap.overpayment_error_rate", + "fns.snap.total_payment_error_rate", + "fns.snap.underpayment_error_rate" + ], + "fy2025": [ + "fns.snap.share_jurisdictions_at_or_above_6pct", + "fns.snap.total_payment_error_rate" + ], + "june_2026": [ + "abs.labour.employment_change.australia", + "abs.labour.unemployment_rate.australia", + "bls.ces.total_nonfarm_payroll_change", + "bls.cpi.u.core_mom", + "bls.cpi.u.headline_mom", + "bls.cps.employed_people_by_occupation.business_financial_operations", + "bls.cps.employed_people_by_occupation.computer_mathematical", + "bls.cps.employed_people_by_occupation.healthcare_support", + "bls.cps.employed_people_by_occupation.office_administrative_support", + "bls.cps.employed_people_by_occupation.production", + "bls.cps.employed_people_by_occupation.transportation_material_moving", + "bls.cps.unemployment_rate", + "eurostat.hicp.all_items_annual_rate.euro_area", + "statjp.cpi.tokyo_all_items_annual_rate" + ], + "may_2026": [ + "abs.building_approvals.total_dwellings_mom.australia", + "abs.cpi.all_groups_annual_rate.australia", + "abs.labour.employment_change.australia", + "abs.labour.unemployment_rate.australia", + "bea.disposable_personal_income.level", + "bea.government_social_benefits.level", + "bea.government_social_benefits.medicaid", + "bea.government_social_benefits.medicare", + "bea.government_social_benefits.social_security", + "bea.pce.core_mom", + "bea.pce_price_index.monthly_change", + "bea.personal_current_taxes.level", + "bea.wages_and_salaries.level", + "bls.ces.average_hourly_earnings_private_monthly_change", + "bls.ces.total_nonfarm_payroll_change", + "bls.cpi.u.core_mom", + "bls.cpi.u.headline_mom", + "bls.cps.unemployment_rate", + "bls.import_price_index.all_imports_mom", + "bls.jolts.job_openings", + "bls.jolts.job_openings_total", + "bls.ppi.final_demand_monthly_change", + "census.housing_starts.saar", + "census.marts.adv44x72.monthly_change", + "eurostat.hicp.all_items_annual_rate.euro_area", + "eurostat.retail_trade.volume_mom.euro_area", + "eurostat.unemployment_rate.euro_area", + "fed.g17.capacity_utilization.total_industry", + "fed.g17.industrial_production.total_index_mom", + "ons.cpi.annual_rate", + "ons.hmrc.paye_payrolled_employees", + "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks", + "ons.retail_sales.volume_mom", + "statcan.cpi.all_items_annual_rate.canada", + "statcan.employment_insurance.regular_beneficiaries.canada", + "statcan.lfs.employment_change", + "statcan.lfs.unemployment_rate", + "statjp.cpi.all_items_annual_rate.japan", + "statjp.household_spending.real_yoy.two_or_more_person_households", + "statjp.lfs.unemployment_rate.japan", + "treasury.mts.monthly_deficit" + ], + "q1_2026": [ + "bea.real_gdp.saar.third_estimate" + ], + "week_2026-06-13": [ + "us.dol.initial_claims.sa" + ], + "week_2026-06-20": [ + "us.dol.initial_claims.sa" + ], + "week_2026-06-27": [ + "dol.eta.continued_claims.sa" + ], + "week_2026-07-04": [ + "dol.eta.continued_claims.sa", + "us.dol.initial_claims.sa" + ], + "week_2026-07-11": [ + "dol.eta.continued_claims.sa", + "us.dol.initial_claims.sa" + ], + "week_2026-07-18": [ + "dol.eta.continued_claims.sa", + "us.dol.initial_claims.sa" + ], + "week_2026-07-25": [ + "us.dol.initial_claims.sa" + ], + "week_2026_06_13": [ + "us.dol.initial_claims.sa" + ], + "week_ending_2026_06_06": [ + "dol.eta.initial_claims.sa" + ] + }, + "ambiguous_aliases": [], + "series": [ + { + "uuid": "efbb2901-f8f8-4f5b-9835-56e5cc3e62ff", + "concept": "abs.building_approvals.total_dwellings_mom.australia", + "family_patterns": [ + "abs.building_approvals.total_dwellings_mom.australia.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "abs" + ], + "aliases": [ + "abs.building_approvals.total_dwellings_mom.australia.may_2026" + ], + "source_concepts": [ + "building-approvals-australia release page" + ], + "rid_patterns": [ + "abs.building_approvals.total_dwellings_mom.australia.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "92220fc5-fd61-46f5-b02d-467948889e2d", + "concept": "abs.cpi.all_groups.yoy", + "family_patterns": [ + "abs.cpi.all_groups.yoy" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "abs" + ], + "aliases": [], + "source_concepts": [ + "CPI/3.10001.10.50.M" + ], + "rid_patterns": [ + "abs.cpi.all_groups.yoy.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "88055a40-bfac-468f-bec6-fcc768e7fc7b", + "concept": "abs.cpi.all_groups_annual_rate.australia", + "family_patterns": [ + "abs.cpi.all_groups_annual_rate.australia.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "abs" + ], + "aliases": [ + "abs.cpi.all_groups_annual_rate.australia.may_2026" + ], + "source_concepts": [ + "CPI/3.10001.10.50.M" + ], + "rid_patterns": [ + "abs.cpi.all_groups_annual_rate.australia.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "b4d4b454-c8a7-42a4-9a28-ce393b664717", + "concept": "abs.cpi_indicator.allgroups.yoy", + "family_patterns": [ + "abs.cpi_indicator.allgroups.yoy.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "abs" + ], + "aliases": [ + "abs.cpi_indicator.allgroups.yoy.2026-05" + ], + "source_concepts": [ + "CPI/3.10001.10.50.M" + ], + "rid_patterns": [ + "abs.cpi_indicator.allgroups.yoy.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "ab7e3632-e9d3-453e-bdc5-aa5a8dca77c9", + "concept": "abs.labour.employment_change.australia", + "family_patterns": [ + "abs.labour.employment_change.australia.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "person", + "role": "employed" + }, + "sources": [ + "abs" + ], + "aliases": [ + "abs.labour.employment_change.australia.june_2026", + "abs.labour.employment_change.australia.may_2026" + ], + "source_concepts": [ + "LF/M3.3.1599.20.AUS.M" + ], + "rid_patterns": [ + "abs.labour.employment_change.australia.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-06", + "observation_count": 2 + }, + { + "uuid": "443cb1bf-49d3-4e1e-9897-9556c8a8c768", + "concept": "abs.labour.unemployment_rate", + "family_patterns": [ + "abs.labour.unemployment_rate" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "980c42b6-db11-4b46-a2b5-e7cd6c67527a", + "concept": "abs.labour.unemployment_rate.australia", + "family_patterns": [ + "abs.labour.unemployment_rate.australia.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "abs" + ], + "aliases": [ + "abs.labour.unemployment_rate.australia.june_2026", + "abs.labour.unemployment_rate.australia.may_2026" + ], + "source_concepts": [ + "LF/M13.3.1599.20.AUS.M" + ], + "rid_patterns": [ + "abs.labour.unemployment_rate.australia.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-06", + "observation_count": 2 + }, + { + "uuid": "1d3d8ca7-091e-4098-9abc-38bd906e7c7a", + "concept": "bank_of_canada.overnight_rate", + "family_patterns": [ + "bank_of_canada.overnight_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "government", + "role": "overnight_target" + }, + "sources": [ + "bank_of_canada" + ], + "aliases": [ + "bank_of_canada.overnight_rate.after_june_2026" + ], + "source_concepts": [ + "bank_of_canada.overnight_rate.after_june_2026" + ], + "rid_patterns": [ + "bank_of_canada.overnight_rate.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "efc3259b-eb44-49c3-9bc6-e92fd7cf6243", + "concept": "bea.core_pce.mom", + "family_patterns": [ + "bea.core_pce.mom" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "ff8df856-08d4-4d5b-8228-e51cff7e1a03", + "concept": "bea.disposable_personal_income.level", + "family_patterns": [ + "bea.disposable_personal_income.level.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.disposable_personal_income.level.may_2026" + ], + "source_concepts": [ + "DSPI" + ], + "rid_patterns": [ + "bea.disposable_personal_income.level.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "5271091d-451c-47dc-a3af-5b1581af13ba", + "concept": "bea.government_social_benefits.level", + "family_patterns": [ + "bea.government_social_benefits.level.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.government_social_benefits.level.may_2026" + ], + "source_concepts": [ + "A063RC1" + ], + "rid_patterns": [ + "bea.government_social_benefits.level.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "ed4dcaa2-e7aa-4696-98c2-b82fa81ee5d0", + "concept": "bea.government_social_benefits.medicaid", + "family_patterns": [ + "bea.government_social_benefits.medicaid.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.government_social_benefits.medicaid.may_2026" + ], + "source_concepts": [ + "W729RC1" + ], + "rid_patterns": [ + "bea.government_social_benefits.medicaid.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "c3bd2028-88b3-48e4-b648-aca7be6c0434", + "concept": "bea.government_social_benefits.medicare", + "family_patterns": [ + "bea.government_social_benefits.medicare.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.government_social_benefits.medicare.may_2026" + ], + "source_concepts": [ + "W824RC1" + ], + "rid_patterns": [ + "bea.government_social_benefits.medicare.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "e63e1fb2-2db5-4068-80da-8caf1a3faa93", + "concept": "bea.government_social_benefits.social_security", + "family_patterns": [ + "bea.government_social_benefits.social_security.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.government_social_benefits.social_security.may_2026" + ], + "source_concepts": [ + "W823RC1" + ], + "rid_patterns": [ + "bea.government_social_benefits.social_security.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "174240ec-f8ff-417a-bf1c-bdf088268a7c", + "concept": "bea.pce.core_mom", + "family_patterns": [ + "bea.pce.core_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.pce.core_mom.may_2026" + ], + "source_concepts": [ + "PCEPILFE" + ], + "rid_patterns": [ + "bea.pce.core_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "0bcbb3b9-801b-435e-8aa8-646c0ac76445", + "concept": "bea.pce_price_index.monthly_change", + "family_patterns": [ + "bea.pce_price_index.monthly_change.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.pce_price_index.monthly_change.may_2026" + ], + "source_concepts": [ + "PCEPI" + ], + "rid_patterns": [ + "bea.pce_price_index.monthly_change.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "c05ca17a-461c-4ae4-b685-5e6ba8ff7710", + "concept": "bea.personal_current_taxes.level", + "family_patterns": [ + "bea.personal_current_taxes.level.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.personal_current_taxes.level.may_2026" + ], + "source_concepts": [ + "W055RC1" + ], + "rid_patterns": [ + "bea.personal_current_taxes.level.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "83094ec4-c890-435b-8a6a-83273c3e9077", + "concept": "bea.real_gdp.saar.third_estimate", + "family_patterns": [ + "bea.real_gdp.saar.{P}.third_estimate" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "quarter", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.real_gdp.saar.q1_2026.third_estimate" + ], + "source_concepts": [ + "A191RL1Q225SBEA" + ], + "rid_patterns": [ + "bea.real_gdp.saar.{P}.third_estimate" + ], + "first_observed_period": "2026-01", + "last_observed_period": "2026-01", + "observation_count": 1 + }, + { + "uuid": "7be75093-832c-4734-a002-a0ff19c610ce", + "concept": "bea.trade.goods_services_deficit", + "family_patterns": [ + "bea.trade.goods_services_deficit" + ], + "status": "docket-only", + "unit": "usd_billions", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "27202e84-7743-46ed-a6e8-2c907aa79a6c", + "concept": "bea.wages_and_salaries.level", + "family_patterns": [ + "bea.wages_and_salaries.level.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "bea.wages_and_salaries.level.may_2026" + ], + "source_concepts": [ + "A576RC1" + ], + "rid_patterns": [ + "bea.wages_and_salaries.level.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "0edb7ad9-0541-4bd2-9597-bffc9e73cd92", + "concept": "bls.ces.average_hourly_earnings_private_monthly_change", + "family_patterns": [ + "bls.ces.average_hourly_earnings_private_monthly_change" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "private_nonfarm_payroll_employee" + }, + "sources": [ + "bls" + ], + "aliases": [ + "bls.ces.average_hourly_earnings_private" + ], + "source_concepts": [ + "bls.ces.average_hourly_earnings_private" + ], + "rid_patterns": [ + "bls.ces.average_hourly_earnings_private.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "d8ab9f3a-aa0c-4b7f-89df-a0c9107cfdbb", + "concept": "bls.ces.nonfarm_payrolls.change", + "family_patterns": [ + "bls.ces.nonfarm_payrolls.change" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "c0515530-c56b-417d-9ead-aff423c87207", + "concept": "bls.ces.total_nonfarm_payroll_change", + "family_patterns": [ + "bls.ces.total_nonfarm_payroll_change.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_ces" + ], + "aliases": [ + "bls.ces.total_nonfarm_payroll_change.june_2026" + ], + "source_concepts": [ + "PAYEMS" + ], + "rid_patterns": [ + "bls.ces.total_nonfarm_payroll_change.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "0b4f36be-2bc7-430b-b960-5df4011347bb", + "concept": "bls.ces.total_nonfarm_payroll_change", + "family_patterns": [ + "bls.ces.total_nonfarm_payroll_change" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "nonfarm_payroll_employee" + }, + "sources": [ + "bls" + ], + "aliases": [], + "source_concepts": [ + "bls.ces.total_nonfarm_payroll_change" + ], + "rid_patterns": [ + "bls.ces.total_nonfarm_payroll_change.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "d0dc87fb-fd6d-4234-832a-869d5b2d8f3d", + "concept": "bls.cpi.owners_equivalent_rent_mom", + "family_patterns": [ + "bls.cpi.owners_equivalent_rent_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "615ac1b1-27e8-437d-bf69-86d760488dc0", + "concept": "bls.cpi.rent_primary_residence_mom", + "family_patterns": [ + "bls.cpi.rent_primary_residence_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "3bab6696-88c0-44c8-988c-737d2d00c37c", + "concept": "bls.cpi.services_less_energy_mom", + "family_patterns": [ + "bls.cpi.services_less_energy_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "975e942b-d456-480d-859f-a9ffaffcfe34", + "concept": "bls.cpi.services_less_rent_shelter_mom", + "family_patterns": [ + "bls.cpi.services_less_rent_shelter_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "4c5b4292-0bcf-4c42-9fc5-39d22e791f75", + "concept": "bls.cpi.shelter_mom", + "family_patterns": [ + "bls.cpi.shelter_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "e5e402d5-1a65-4c5d-a67a-07e3cf03cbf8", + "concept": "bls.cpi.u.core_mom", + "family_patterns": [ + "bls.cpi.u.core_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cpi" + ], + "aliases": [ + "bls.cpi.u.core_mom.june_2026" + ], + "source_concepts": [ + "CPILFESL" + ], + "rid_patterns": [ + "bls.cpi.u.core_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "d85c3c7f-874b-4caa-bfad-1ec7a473c293", + "concept": "bls.cpi.u.core_mom", + "family_patterns": [ + "bls.cpi.u.core_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "cpi_u_less_food_energy" + }, + "sources": [ + "bls" + ], + "aliases": [ + "bls.cpi.u.core_mom.may_2026" + ], + "source_concepts": [ + "bls.cpi.u.core_mom.may_2026" + ], + "rid_patterns": [ + "bls.cpi.u.core_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "70ca2ecf-4323-4453-b42f-23350cb95f22", + "concept": "bls.cpi.u.headline_mom", + "family_patterns": [ + "bls.cpi.u.headline_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cpi" + ], + "aliases": [ + "bls.cpi.u.headline_mom.june_2026" + ], + "source_concepts": [ + "CPIAUCSL" + ], + "rid_patterns": [ + "bls.cpi.u.headline_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "3e796803-a194-4c83-9dc9-27cc870ff08e", + "concept": "bls.cpi.u.headline_mom", + "family_patterns": [ + "bls.cpi.u.headline_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "cpi_u_all_items" + }, + "sources": [ + "bls" + ], + "aliases": [ + "bls.cpi.u.headline_mom.may_2026" + ], + "source_concepts": [ + "bls.cpi.u.headline_mom.may_2026" + ], + "rid_patterns": [ + "bls.cpi.u.headline_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "3cbdbc7f-3e6b-4b76-8758-57e519906bd4", + "concept": "bls.cps.employed_people_by_occupation.business_financial_operations", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.business_financial_operations.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.business_financial_operations.june_2026" + ], + "source_concepts": [ + "Business and financial operations occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.business_financial_operations.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "54f19551-6288-48e2-9fa0-17933e55d390", + "concept": "bls.cps.employed_people_by_occupation.computer_mathematical", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.computer_mathematical.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.computer_mathematical.june_2026" + ], + "source_concepts": [ + "Computer and mathematical occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.computer_mathematical.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "25457957-df59-4e16-ae3f-431b4c63e7bf", + "concept": "bls.cps.employed_people_by_occupation.healthcare_support", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.healthcare_support.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.healthcare_support.june_2026" + ], + "source_concepts": [ + "Healthcare support occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.healthcare_support.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "2316f164-f13f-4025-980c-b92cc46b4d1a", + "concept": "bls.cps.employed_people_by_occupation.office_administrative_support", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.office_administrative_support.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.office_administrative_support.june_2026" + ], + "source_concepts": [ + "Office and administrative support occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.office_administrative_support.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "3240539a-c5b8-43de-a240-cc6754fb59aa", + "concept": "bls.cps.employed_people_by_occupation.production", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.production.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.production.june_2026" + ], + "source_concepts": [ + "Production occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.production.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "e2502908-1e16-46e3-b330-4d6612e510c2", + "concept": "bls.cps.employed_people_by_occupation.transportation_material_moving", + "family_patterns": [ + "bls.cps.employed_people_by_occupation.transportation_material_moving.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.employed_people_by_occupation.transportation_material_moving.june_2026" + ], + "source_concepts": [ + "Transportation and material moving occupations" + ], + "rid_patterns": [ + "bls.cps.employed_people_by_occupation.transportation_material_moving.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "94a5a046-ea77-449a-8ba9-95ffebc9039c", + "concept": "bls.cps.telework_share", + "family_patterns": [ + "bls.cps.telework_share" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "e0be267e-d74c-4ad2-a5cd-5ac9f821e018", + "concept": "bls.cps.u6_underemployment_rate", + "family_patterns": [ + "bls.cps.u6_underemployment_rate" + ], + "status": "docket-only", + "unit": "percent", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "db2f3857-e0fa-48f8-9ab1-6c8989307fd2", + "concept": "bls.cps.unemployment_rate", + "family_patterns": [ + "bls.cps.unemployment_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_cps" + ], + "aliases": [ + "bls.cps.unemployment_rate.june_2026" + ], + "source_concepts": [ + "UNRATE" + ], + "rid_patterns": [ + "bls.cps.unemployment_rate.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "8dbbd54f-4bfd-4735-ad0d-5b55b8bb4ec5", + "concept": "bls.cps.unemployment_rate", + "family_patterns": [ + "bls.cps.unemployment_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "civilian_labor_force" + }, + "sources": [ + "bls" + ], + "aliases": [], + "source_concepts": [ + "bls.cps.unemployment_rate" + ], + "rid_patterns": [ + "bls.cps.unemployment_rate.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "6d2e1191-9e14-4719-a2bb-c3561fca49e6", + "concept": "bls.eci.private_wages_salaries_qoq", + "family_patterns": [ + "bls.eci.private_wages_salaries_qoq.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "quarter", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_eci" + ], + "aliases": [ + "bls.eci.private_wages_salaries_qoq.2026_q2" + ], + "source_concepts": [ + "ECIWAG" + ], + "rid_patterns": [ + "bls.eci.private_wages_salaries_qoq.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "2820e965-4c7b-40d2-b5b8-e03792cf3fbf", + "concept": "bls.eci.total_compensation_private_industry_qoq", + "family_patterns": [ + "bls.eci.total_compensation_private_industry_qoq.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "quarter", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_eci" + ], + "aliases": [ + "bls.eci.total_compensation_private_industry_qoq.2026_q2" + ], + "source_concepts": [ + "ECICOM" + ], + "rid_patterns": [ + "bls.eci.total_compensation_private_industry_qoq.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "7dc8b980-8f14-4581-ad7c-3963de6a42d9", + "concept": "bls.export_prices.all_commodities_mom", + "family_patterns": [ + "bls.export_prices.all_commodities_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "1d271205-0e71-4a74-9d5f-47e77577e2b2", + "concept": "bls.import_price_index.all_imports_mom", + "family_patterns": [ + "bls.import_price_index.all_imports_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_import_export_prices" + ], + "aliases": [ + "bls.import_price_index.all_imports_mom.2026-06" + ], + "source_concepts": [ + "IR" + ], + "rid_patterns": [ + "bls.import_price_index.all_imports_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "e8e6e43e-2c46-4620-a070-91d2da004b1c", + "concept": "bls.import_price_index.all_imports_mom", + "family_patterns": [ + "bls.import_price_index.all_imports_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "all_imports" + }, + "sources": [ + "bls" + ], + "aliases": [ + "bls.import_price_index.all_imports_mom.may_2026" + ], + "source_concepts": [ + "bls.import_price_index.all_imports" + ], + "rid_patterns": [ + "bls.import_price_index.all_imports_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "3bddc6d0-e581-42dc-9c13-b731a9ede21f", + "concept": "bls.jolts.hires_rate", + "family_patterns": [ + "bls.jolts.hires_rate" + ], + "status": "docket-only", + "unit": "percent", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "020dafc2-2ca3-453b-a6bc-f96d443a5f77", + "concept": "bls.jolts.job_openings", + "family_patterns": [ + "bls.jolts.job_openings.{P}" + ], + "status": "observed", + "unit": "millions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_jolts" + ], + "aliases": [ + "bls.jolts.job_openings.may_2026" + ], + "source_concepts": [ + "JTSJOL" + ], + "rid_patterns": [ + "bls.jolts.job_openings.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "1d05d94d-0288-46f0-b6a9-1fa499e85fff", + "concept": "bls.jolts.job_openings_total", + "family_patterns": [ + "bls.jolts.job_openings_total.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bls_jolts" + ], + "aliases": [ + "bls.jolts.job_openings_total.may_2026" + ], + "source_concepts": [ + "JTSJOL" + ], + "rid_patterns": [ + "bls.jolts.job_openings_total.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "e6aed5ae-2f23-413b-a600-4c5b80edeb18", + "concept": "bls.jolts.quits_rate", + "family_patterns": [ + "bls.jolts.quits_rate" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "0006af01-72ba-4e63-8d88-54500588f215", + "concept": "bls.lns11300000", + "family_patterns": [ + "bls.lns11300000" + ], + "status": "docket-only", + "unit": "percent", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "a7ec4618-1ade-4f5e-bbbd-1c0680c7fccd", + "concept": "bls.ppi.final_demand_monthly_change", + "family_patterns": [ + "bls.ppi.final_demand_monthly_change.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "ppi_final_demand" + }, + "sources": [ + "bls" + ], + "aliases": [ + "bls.ppi.final_demand_monthly_change.may_2026" + ], + "source_concepts": [ + "bls.ppi.final_demand_monthly_change.may_2026" + ], + "rid_patterns": [ + "bls.ppi.final_demand_monthly_change.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "6e671a89-65f6-4da9-b092-4e6d74a36a08", + "concept": "bls.productivity.nonfarm_qoq_prelim", + "family_patterns": [ + "bls.productivity.nonfarm_qoq_prelim" + ], + "status": "docket-only", + "unit": null, + "cadence": "quarter", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "205885c4-de46-47df-a916-253ae2a0299a", + "concept": "bls.productivity.nonfarm_unit_labor_costs_qoq_prelim", + "family_patterns": [ + "bls.productivity.nonfarm_unit_labor_costs_qoq_prelim" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "quarter", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "2e3a7126-41fd-4d88-99b6-cf4f2057c277", + "concept": "bls.real_earnings.avg_hourly_mom", + "family_patterns": [ + "bls.real_earnings.avg_hourly_mom" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "8fb890ac-38d0-430d-9a35-bbeffa0cf8ca", + "concept": "boe.bank_rate", + "family_patterns": [ + "boe.bank_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "government", + "role": "bank_rate" + }, + "sources": [ + "boe" + ], + "aliases": [ + "boe.bank_rate.2026_06_18", + "boe.bank_rate.after_mpc_june_2026" + ], + "source_concepts": [ + "boe.bank_rate" + ], + "rid_patterns": [ + "boe.bank_rate.{P}", + "boe.bank_rate.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 2 + }, + { + "uuid": "e300b533-d2f0-41b4-8cbb-59168f8c80bd", + "concept": "boj.policy_rate_guideline", + "family_patterns": [ + "boj.policy_rate_guideline.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "government", + "role": "uncollateralized_overnight_call_rate_guideline" + }, + "sources": [ + "boj" + ], + "aliases": [ + "boj.policy_rate_guideline.after_june_2026" + ], + "source_concepts": [ + "boj.guideline_uncollateralized_overnight_call_rate" + ], + "rid_patterns": [ + "boj.policy_rate_guideline.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "63222847-045a-4784-bd89-4d7878a2a6d8", + "concept": "census.construction_spending.total_mom", + "family_patterns": [ + "census.construction_spending.total_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "e16080e3-02e9-4ae1-95c6-3ded737cf697", + "concept": "census.housing.completions_saar", + "family_patterns": [ + "census.housing.completions_saar" + ], + "status": "docket-only", + "unit": "thousands", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "420c8935-70b8-4d8f-a82d-e772446adc6e", + "concept": "census.housing.permits_saar", + "family_patterns": [ + "census.housing.permits_saar" + ], + "status": "docket-only", + "unit": "thousands", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "d0212a1e-b360-460a-80a8-1cf4ad5ce427", + "concept": "census.housing_starts.saar", + "family_patterns": [ + "census.housing_starts.saar.{P}" + ], + "status": "observed", + "unit": "millions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "dwelling", + "role": "housing_start" + }, + "sources": [ + "census" + ], + "aliases": [ + "census.housing_starts.saar.may_2026" + ], + "source_concepts": [ + "census.housing_starts.saar" + ], + "rid_patterns": [ + "census.housing_starts.saar.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "8c999902-84e2-4e90-b49c-325cd535062a", + "concept": "census.housing_starts.saar", + "family_patterns": [ + "census.housing_starts.saar.{P}" + ], + "status": "observed", + "unit": "millions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "census_housing" + ], + "aliases": [ + "census.housing_starts.saar.2026-06" + ], + "source_concepts": [ + "HOUST" + ], + "rid_patterns": [ + "census.housing_starts.saar.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "294d82e8-3711-4d0d-89f6-a5a7a087f1be", + "concept": "census.m3.durable_goods_new_orders_mom", + "family_patterns": [ + "census.m3.durable_goods_new_orders_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "census_m3" + ], + "aliases": [ + "census.m3.durable_goods_new_orders_mom.2026_06" + ], + "source_concepts": [ + "DGORDER" + ], + "rid_patterns": [ + "census.m3.durable_goods_new_orders_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "5cbc4c1a-e387-4b64-bb56-67ef349fbd51", + "concept": "census.m3.durable_goods_shipments_mom", + "family_patterns": [ + "census.m3.durable_goods_shipments_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "census_m3" + ], + "aliases": [ + "census.m3.durable_goods_shipments_mom.2026_06" + ], + "source_concepts": [ + "AMDMVS" + ], + "rid_patterns": [ + "census.m3.durable_goods_shipments_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "ed3215ed-43ec-44dc-b11c-f72a098fc008", + "concept": "census.marts.adv44x72.monthly_change", + "family_patterns": [ + "census.marts.adv44x72.{P}.monthly_change" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "institutional_sector", + "role": "retail_and_food_services_sales" + }, + "sources": [ + "census" + ], + "aliases": [ + "census.marts.adv44x72.may_2026.monthly_change" + ], + "source_concepts": [ + "census.marts.advance_retail_and_food_services_sales_mom" + ], + "rid_patterns": [ + "census.marts.adv44x72.{P}.monthly_change.advance" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "26bb1475-8685-4142-8472-7bcd6d998fcc", + "concept": "census.mtis.total_business_inventories_level", + "family_patterns": [ + "census.mtis.total_business_inventories_level.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "institutional_sector", + "role": "total_business_inventory" + }, + "sources": [ + "census" + ], + "aliases": [ + "census.mtis.total_business_inventories_level.april_2026" + ], + "source_concepts": [ + "census.mtis.total_business_inventories" + ], + "rid_patterns": [ + "census.mtis.total_business_inventories_level.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "5654403e-f6c5-412e-94e0-feeda778ad81", + "concept": "census.new_residential_sales.new_single_family_houses_sold_saar", + "family_patterns": [ + "census.new_residential_sales.new_single_family_houses_sold_saar" + ], + "status": "docket-only", + "unit": "thousands", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "aafb4ff0-cb04-4f76-96de-ef0baaa7bd93", + "concept": "cms.care_compare.nursing_home_occupancy_pct", + "family_patterns": [ + "cms.care_compare.nursing_home_occupancy_pct.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "cms_provider_data" + ], + "aliases": [ + "cms.care_compare.nursing_home_occupancy_pct.2026-07" + ], + "source_concepts": [ + "Average Number of Residents per Day / Number of Certified Beds" + ], + "rid_patterns": [ + "cms.care_compare.nursing_home_occupancy_pct.{P}.first_print" + ], + "first_observed_period": "2026-07", + "last_observed_period": "2026-07", + "observation_count": 1 + }, + { + "uuid": "fc7c5e2f-90ff-4a9b-95cb-8d339fc8a89d", + "concept": "cms.medicaid_pi.beneficiaries_disenrolled_procedural", + "family_patterns": [ + "cms.medicaid_pi.beneficiaries_disenrolled_procedural" + ], + "status": "observed", + "unit": "count", + "cadence": "month", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "person", + "role": "medicaid_beneficiary" + }, + "sources": [ + "cms" + ], + "aliases": [], + "source_concepts": [ + "Beneficiaries Disenrolled for Procedural Reasons at Renewal" + ], + "rid_patterns": [ + "cms.medicaid_pi.beneficiaries_disenrolled_procedural.california.{P}.original_submission" + ], + "first_observed_period": "2026-02", + "last_observed_period": "2026-02", + "observation_count": 1 + }, + { + "uuid": "8829b01a-9f45-4c9e-b38e-d04f405f238c", + "concept": "cms.medicaid_pi.beneficiaries_disenrolled_total", + "family_patterns": [ + "cms.medicaid_pi.beneficiaries_disenrolled_total" + ], + "status": "observed", + "unit": "count", + "cadence": "month", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "person", + "role": "medicaid_beneficiary" + }, + "sources": [ + "cms" + ], + "aliases": [], + "source_concepts": [ + "Beneficiaries Disenrolled at Renewal (Total)" + ], + "rid_patterns": [ + "cms.medicaid_pi.beneficiaries_disenrolled_total.california.{P}.original_submission" + ], + "first_observed_period": "2026-02", + "last_observed_period": "2026-02", + "observation_count": 1 + }, + { + "uuid": "56baf17e-830f-40ea-82f3-aa05cbbeeea2", + "concept": "cms.medicaid_pi.beneficiaries_renewed_ex_parte", + "family_patterns": [ + "cms.medicaid_pi.beneficiaries_renewed_ex_parte" + ], + "status": "observed", + "unit": "count", + "cadence": "month", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "person", + "role": "medicaid_beneficiary" + }, + "sources": [ + "cms" + ], + "aliases": [], + "source_concepts": [ + "Beneficiaries Whose Coverage Was Renewed on an Ex Parte Basis" + ], + "rid_patterns": [ + "cms.medicaid_pi.beneficiaries_renewed_ex_parte.california.{P}.original_submission" + ], + "first_observed_period": "2026-02", + "last_observed_period": "2026-02", + "observation_count": 1 + }, + { + "uuid": "f6053113-5c2f-4832-9f54-e8ba5763f160", + "concept": "cms.medicaid_pi.beneficiaries_renewed_total", + "family_patterns": [ + "cms.medicaid_pi.beneficiaries_renewed_total" + ], + "status": "observed", + "unit": "count", + "cadence": "month", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "person", + "role": "medicaid_beneficiary" + }, + "sources": [ + "cms" + ], + "aliases": [], + "source_concepts": [ + "Beneficiaries Whose Coverage Was Renewed (Total)" + ], + "rid_patterns": [ + "cms.medicaid_pi.beneficiaries_renewed_total.california.{P}.original_submission" + ], + "first_observed_period": "2026-02", + "last_observed_period": "2026-02", + "observation_count": 1 + }, + { + "uuid": "35d1d31f-ade4-4487-a14c-bd492de01521", + "concept": "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us", + "family_patterns": [ + "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us.{P}" + ], + "status": "observed", + "unit": "ratio", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "cms_provider_data" + ], + "aliases": [ + "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us.2026-07" + ], + "source_concepts": [ + "Reported Total Nurse Staffing Hours per Resident per Day" + ], + "rid_patterns": [ + "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us.{P}.first_print" + ], + "first_observed_period": "2026-07", + "last_observed_period": "2026-07", + "observation_count": 1 + }, + { + "uuid": "38773338-f84a-466e-94cb-fce55f5e2549", + "concept": "dol.eta.continued_claims.sa", + "family_patterns": [ + "dol.eta.continued_claims.sa" + ], + "status": "observed", + "unit": "millions", + "cadence": "week_ending", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "ui_claimant" + }, + "sources": [ + "dol_eta" + ], + "aliases": [], + "source_concepts": [ + "CCSA" + ], + "rid_patterns": [ + "dol.eta.continued_claims.sa.{P}.first_print" + ], + "first_observed_period": "2026-06-27", + "last_observed_period": "2026-07-18", + "observation_count": 4 + }, + { + "uuid": "330ec66b-502c-4ddf-9978-02199bab0055", + "concept": "dol.eta.initial_claims.sa", + "family_patterns": [ + "dol.eta.initial_claims.sa.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "ui_initial_claimant" + }, + "sources": [ + "dol" + ], + "aliases": [ + "dol.eta.initial_claims.sa.week_ending_2026_06_06" + ], + "source_concepts": [ + "dol.eta.initial_claims.sa.week_ending_2026_06_06" + ], + "rid_patterns": [ + "dol.eta.initial_claims.sa.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "32b335ea-b2dd-4fba-8321-e9751df19318", + "concept": "ecb.deposit_facility_rate", + "family_patterns": [ + "ecb.deposit_facility_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "EA", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "government", + "role": "deposit_facility" + }, + "sources": [ + "ecb" + ], + "aliases": [ + "ecb.deposit_facility_rate.after_june_2026" + ], + "source_concepts": [ + "ecb.deposit_facility_rate.after_june_2026" + ], + "rid_patterns": [ + "ecb.deposit_facility_rate.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "0be25641-eae9-45b8-b0c5-218b21e23d61", + "concept": "estat.jp.cpi.core_exfreshfood.yoy", + "family_patterns": [ + "estat.jp.cpi.core_exfreshfood.yoy.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "household", + "role": "cpi_less_fresh_food" + }, + "sources": [ + "statjp" + ], + "aliases": [ + "estat.jp.cpi.core_exfreshfood.yoy.2026_05" + ], + "source_concepts": [ + "japan.cpi.all_items_less_fresh_food_yoy" + ], + "rid_patterns": [ + "estat.jp.cpi.core_exfreshfood.yoy.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "4eaed6f8-7da3-405d-8293-4b61d5cefc6f", + "concept": "eurostat.ea.hicp.flash.yoy", + "family_patterns": [ + "eurostat.ea.hicp.flash.yoy", + "eurostat.ea.hicp.flash.yoy.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA21", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.ea.hicp.flash.yoy.2026-06" + ], + "source_concepts": [ + "prc_hicp_minr/M.RCH_A.TOTAL.EA21" + ], + "rid_patterns": [ + "eurostat.ea.hicp.flash.yoy.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-07", + "observation_count": 2 + }, + { + "uuid": "815299ce-841a-4115-9af2-3b52fc036834", + "concept": "eurostat.hicp.all_items_annual_rate.euro_area", + "family_patterns": [ + "eurostat.hicp.all_items_annual_rate.euro_area.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "household", + "role": "hicp_all_items" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.hicp.all_items_annual_rate.euro_area.may_2026" + ], + "source_concepts": [ + "eurostat.hicp.all_items_annual_rate.euro_area" + ], + "rid_patterns": [ + "eurostat.hicp.all_items_annual_rate.euro_area.{P}.final_first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "74ce2da4-28ed-409a-a0f1-2a7c40dc7caf", + "concept": "eurostat.hicp.all_items_annual_rate.euro_area", + "family_patterns": [ + "eurostat.hicp.all_items_annual_rate.euro_area.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA21", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.hicp.all_items_annual_rate.euro_area.june_2026" + ], + "source_concepts": [ + "prc_hicp_minr/M.RCH_A.TOTAL.EA21" + ], + "rid_patterns": [ + "eurostat.hicp.all_items_annual_rate.euro_area.{P}.flash" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "1e84eb13-9e80-4dda-9b82-71361b886c1e", + "concept": "eurostat.hicp.flash.yoy", + "family_patterns": [ + "eurostat.hicp.flash.yoy" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "4994dab0-bc46-4dd0-9bb7-ef716dfc4c75", + "concept": "eurostat.industrial_production.euro_area", + "family_patterns": [ + "eurostat.industrial_production.euro_area.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "institutional_sector", + "role": "industrial_production" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.industrial_production.euro_area.april_2026" + ], + "source_concepts": [ + "eurostat.industrial_production.euro_area" + ], + "rid_patterns": [ + "eurostat.industrial_production.euro_area.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "16b32f44-da0e-43de-bfb2-8b5a2a883b95", + "concept": "eurostat.retail_trade.volume_mom.euro_area", + "family_patterns": [ + "eurostat.retail_trade.volume_mom.euro_area.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA21", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.retail_trade.volume_mom.euro_area.may_2026" + ], + "source_concepts": [ + "products-euro-indicators release page (euro area headline)" + ], + "rid_patterns": [ + "eurostat.retail_trade.volume_mom.euro_area.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "2c59f506-873f-45df-b6c5-588c82dcd640", + "concept": "eurostat.unemployment_rate", + "family_patterns": [ + "eurostat.unemployment_rate" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "c7f994ce-3d0b-4a28-ba5b-e286237cd14d", + "concept": "eurostat.unemployment_rate.belgium", + "family_patterns": [ + "eurostat.unemployment_rate.belgium" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "ec04c486-d8fd-4b1b-9841-884bed8d88c7", + "concept": "eurostat.unemployment_rate.euro_area", + "family_patterns": [ + "eurostat.unemployment_rate.euro_area.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "region", + "id": "EA21", + "vintage": "current", + "name": "Euro area" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "eurostat" + ], + "aliases": [ + "eurostat.unemployment_rate.euro_area.may_2026" + ], + "source_concepts": [ + "une_rt_m/M.SA.TOTAL.PC_ACT.T.EA21" + ], + "rid_patterns": [ + "eurostat.unemployment_rate.euro_area.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "40f48c12-3611-4e57-9482-055e62bf35ae", + "concept": "fed.g17.capacity_utilization.manufacturing", + "family_patterns": [ + "fed.g17.capacity_utilization.manufacturing" + ], + "status": "docket-only", + "unit": "percent", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "9668ecdb-5f61-422d-a653-8e8956257ade", + "concept": "fed.g17.capacity_utilization.total_industry", + "family_patterns": [ + "fed.g17.capacity_utilization.total_industry.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "federal_reserve_g17" + ], + "aliases": [ + "fed.g17.capacity_utilization.total_industry.2026-06" + ], + "source_concepts": [ + "TCU" + ], + "rid_patterns": [ + "fed.g17.capacity_utilization.total_industry.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "978252a9-c452-41eb-aece-323340e796fc", + "concept": "fed.g17.capacity_utilization.total_industry", + "family_patterns": [ + "fed.g17.capacity_utilization.total_industry.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "institutional_sector", + "role": "total_industry_capacity" + }, + "sources": [ + "fed" + ], + "aliases": [ + "fed.g17.capacity_utilization.total_industry.may_2026" + ], + "source_concepts": [ + "fed.g17.capacity_utilization.total_industry" + ], + "rid_patterns": [ + "fed.g17.capacity_utilization.total_industry.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "dbfd5050-e5df-40af-8922-dc8927d6363a", + "concept": "fed.g17.industrial_production.total_index_mom", + "family_patterns": [ + "fed.g17.industrial_production.total_index_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "federal_reserve_g17" + ], + "aliases": [ + "fed.g17.industrial_production.total_index_mom.2026-06" + ], + "source_concepts": [ + "INDPRO" + ], + "rid_patterns": [ + "fed.g17.industrial_production.total_index_mom.{P}.first_print" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "d5634fc4-dbe0-4203-a5b7-728708b85a14", + "concept": "fed.g17.industrial_production.total_index_mom", + "family_patterns": [ + "fed.g17.industrial_production.total_index_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "institutional_sector", + "role": "total_industrial_production" + }, + "sources": [ + "fed" + ], + "aliases": [ + "fed.g17.industrial_production.total_index_mom.may_2026" + ], + "source_concepts": [ + "fed.g17.industrial_production.total_index_mom" + ], + "rid_patterns": [ + "fed.g17.industrial_production.total_index_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "41d26958-709c-44ab-a5ab-b385157db0ac", + "concept": "fed.g17.manufacturing_production_mom", + "family_patterns": [ + "fed.g17.manufacturing_production_mom" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "bd0643c4-6716-41ae-988d-c985272e38ae", + "concept": "fed.g19.consumer_credit_nonrevolving_annual_rate", + "family_patterns": [ + "fed.g19.consumer_credit_nonrevolving_annual_rate" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "8a82c6a5-7c83-4ac8-ab5b-7dd6eaf97e8f", + "concept": "fed.g19.consumer_credit_revolving_annual_rate", + "family_patterns": [ + "fed.g19.consumer_credit_revolving_annual_rate" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "dac76c03-0630-4859-898d-b4849383d147", + "concept": "fed.g19.consumer_credit_total_annual_rate", + "family_patterns": [ + "fed.g19.consumer_credit_total_annual_rate" + ], + "status": "docket-only", + "unit": "percent_growth", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "3b195edb-1b2d-4380-8267-43084fe3cbeb", + "concept": "fns.snap.application_processing_timeliness_rate", + "family_patterns": [ + "fns.snap.application_processing_timeliness_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "household", + "role": "snap_applicant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.application_processing_timeliness_rate" + ], + "rid_patterns": [ + "fns.snap.application_processing_timeliness.california.{P}.official_release" + ], + "first_observed_period": "2024", + "last_observed_period": "2024", + "observation_count": 1 + }, + { + "uuid": "ae4cc9d0-e0f7-4de4-97bc-dfcf5afeb90e", + "concept": "fns.snap.overpayment_error_rate", + "family_patterns": [ + "fns.snap.overpayment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.overpayment_error_rate" + ], + "rid_patterns": [ + "fns.snap.overpayment_payment_error_rate.us.{P}.official_release" + ], + "first_observed_period": "2024", + "last_observed_period": "2024", + "observation_count": 1 + }, + { + "uuid": "49b9c81b-935d-4523-a05d-d2000f8064e4", + "concept": "fns.snap.share_jurisdictions_at_or_above_6pct", + "family_patterns": [ + "fns.snap.share_jurisdictions_at_or_above_6pct" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "government", + "role": "snap_administering_jurisdiction" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.share_jurisdictions_at_or_above_6pct.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "d93e4cbb-5810-4a89-80fe-3830df22e057", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.us.{P}", + "fns.snap.total_payment_error_rate.us.{P}.official_release" + ], + "first_observed_period": "2024", + "last_observed_period": "2025", + "observation_count": 2 + }, + { + "uuid": "0902c609-38ed-4b39-94bf-13aa8f5d6cbb", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US01", + "vintage": "current", + "name": "Alabama" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.al.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "3b0cdfd9-16a4-424b-99ea-dbbcedecefd7", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US02", + "vintage": "current", + "name": "Alaska" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ak.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6c19d194-6a6b-4e7a-9fb1-f98ab24e6095", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US04", + "vintage": "current", + "name": "Arizona" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.az.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6409e147-6d79-4c75-ba50-7601bd7021dd", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US05", + "vintage": "current", + "name": "Arkansas" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ar.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "93d432d3-0c95-4924-a180-c74622391117", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US06", + "vintage": "current", + "name": "California" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ca.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "32bc567b-075f-4471-a601-38f7694f1c35", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US08", + "vintage": "current", + "name": "Colorado" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.co.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "83a60dd2-560a-4f92-9613-83507b73133c", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US09", + "vintage": "current", + "name": "Connecticut" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ct.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "34714371-9105-4e7b-93dd-6f7190d15e55", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US10", + "vintage": "current", + "name": "Delaware" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.de.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "acfecec9-e491-441e-8df3-02cd52b61191", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US11", + "vintage": "current", + "name": "District of Columbia" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.dc.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "34e71c1b-05c8-48dd-8625-ddadea1b2445", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US12", + "vintage": "current", + "name": "Florida" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.fl.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "94d5fcdc-3df6-4b46-bcd0-594dfc907488", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US13", + "vintage": "current", + "name": "Georgia" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ga.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "f80db1ec-1739-496f-a6f9-47f3c18b280d", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US15", + "vintage": "current", + "name": "Hawaii" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.hi.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "5ef9bdf6-62bc-4f50-8a7e-119a1c2f3aad", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US16", + "vintage": "current", + "name": "Idaho" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.id.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "a90c5f52-5bcf-4920-8b32-c2c236b7ae28", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US17", + "vintage": "current", + "name": "Illinois" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.il.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "12413650-f9e2-4308-93c7-886ce4f3956e", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US18", + "vintage": "current", + "name": "Indiana" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.in.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "dc083c88-6ad5-4b84-b7f1-b890ff4e8561", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US19", + "vintage": "current", + "name": "Iowa" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ia.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "0189b8ff-f12e-40d1-8d8f-3dc7a5e1bffa", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US20", + "vintage": "current", + "name": "Kansas" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ks.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6362a941-a7a3-422a-970f-d650c91d18ec", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US21", + "vintage": "current", + "name": "Kentucky" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ky.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "3bab127f-53fe-4a60-a1f0-c6efaaa7dbe2", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US22", + "vintage": "current", + "name": "Louisiana" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.la.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "545502ce-7fa4-44e3-bc00-411f93df5875", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US23", + "vintage": "current", + "name": "Maine" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.me.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "75a0857b-f4da-4e5d-8000-33b3c8b8ed6e", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US24", + "vintage": "current", + "name": "Maryland" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.md.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "c3fc0369-f22e-488d-b5b7-9c75e0de4b1c", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US25", + "vintage": "current", + "name": "Massachusetts" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ma.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "e52a49f4-79f6-4c0f-b06d-7c52e788794f", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US26", + "vintage": "current", + "name": "Michigan" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.mi.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6e782a95-0fee-4f5a-b802-f061ef719fd2", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US27", + "vintage": "current", + "name": "Minnesota" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.mn.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "2247c3f5-2393-4d87-a46b-5160cd80ad26", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US28", + "vintage": "current", + "name": "Mississippi" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ms.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "4642db20-cf61-4d04-891d-7d9933c6721e", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US29", + "vintage": "current", + "name": "Missouri" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.mo.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "8022c7e2-4760-40ae-9621-6d1d52d06acc", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US30", + "vintage": "current", + "name": "Montana" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.mt.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "81f1dc85-41d3-4d12-86cb-6a93338fb4c8", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US31", + "vintage": "current", + "name": "Nebraska" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ne.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "cb4e42fd-c331-4da7-9341-4e546b9ce8cd", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US32", + "vintage": "current", + "name": "Nevada" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nv.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "ad8be026-4531-46e4-b02d-43d27f93c948", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US33", + "vintage": "current", + "name": "New Hampshire" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nh.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "c92381ef-2e2b-493b-ada9-6e9fbd3d2c2d", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US34", + "vintage": "current", + "name": "New Jersey" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nj.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "b5bb3650-78cb-435b-bb66-7f04e38bf470", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US35", + "vintage": "current", + "name": "New Mexico" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nm.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "7ab34230-19e8-4543-999f-2910c1173be5", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US36", + "vintage": "current", + "name": "New York" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ny.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "2834f2ee-480f-476b-8ae6-5f8975cf493b", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US37", + "vintage": "current", + "name": "North Carolina" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nc.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "bb079952-9000-48fd-84da-0e4f38f28964", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US38", + "vintage": "current", + "name": "North Dakota" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.nd.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "9fa7ec57-8aad-4680-a867-d6e07a1fde4d", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US39", + "vintage": "current", + "name": "Ohio" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.oh.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "21b36dca-9579-4a20-a947-beaa7afb6b7a", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US40", + "vintage": "current", + "name": "Oklahoma" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ok.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "a53e930e-3042-4967-ad17-888f29e653af", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US41", + "vintage": "current", + "name": "Oregon" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.or.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "e89d9485-e437-40c3-987e-be079dbc45eb", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US42", + "vintage": "current", + "name": "Pennsylvania" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.pa.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "bc31a283-a414-4446-87cb-48d9c8a3db28", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US44", + "vintage": "current", + "name": "Rhode Island" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ri.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "9b679b84-0d9f-44fd-86dd-3556bd131e42", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US45", + "vintage": "current", + "name": "South Carolina" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.sc.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "4b6ceb75-7ede-4ec3-a1bc-de4bdd8f0dca", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US46", + "vintage": "current", + "name": "South Dakota" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.sd.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6c9ddf7b-f037-4482-89fc-e45728169d88", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US47", + "vintage": "current", + "name": "Tennessee" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.tn.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "249143e2-bbd8-46a6-96a8-673dfbf7e14f", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US48", + "vintage": "current", + "name": "Texas" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.tx.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "25099a0a-db82-4354-aa16-dc0e7d0ee301", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US49", + "vintage": "current", + "name": "Utah" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.ut.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "1435b3de-9953-4cb5-acd8-ace0df2570ad", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US50", + "vintage": "current", + "name": "Vermont" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.vt.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "28747a0a-dd14-414c-b193-514208ba0a05", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US51", + "vintage": "current", + "name": "Virginia" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.va.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "39d52b69-8066-4028-81de-a1b938cf0630", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US53", + "vintage": "current", + "name": "Washington" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.wa.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "6dcd0d3f-5ed9-49d4-93fc-b9ec63c9f699", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US54", + "vintage": "current", + "name": "West Virginia" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.wv.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "62000494-b4fe-4a7d-b0d2-013b911e7884", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US55", + "vintage": "current", + "name": "Wisconsin" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.wi.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "b9d02bc9-68e5-4900-b30e-763f6d760a70", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US56", + "vintage": "current", + "name": "Wyoming" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.wy.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "c562b4b3-09ce-45db-b355-cd6c9d412f18", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US66", + "vintage": "current", + "name": "Guam" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.gu.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "55cbe6f5-3aad-473a-8a3d-552b2525eb5f", + "concept": "fns.snap.total_payment_error_rate", + "family_patterns": [ + "fns.snap.total_payment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "state", + "id": "0400000US78", + "vintage": "current", + "name": "Virgin Islands" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.total_payment_error_rate" + ], + "rid_patterns": [ + "fns.snap.total_payment_error_rate.vi.{P}" + ], + "first_observed_period": "2025", + "last_observed_period": "2025", + "observation_count": 1 + }, + { + "uuid": "748670af-cafa-416e-afcd-b2c303593782", + "concept": "fns.snap.total_persons", + "family_patterns": [ + "fns.snap.total_persons" + ], + "status": "docket-only", + "unit": "millions", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "3fa6210a-33a1-4a3e-bbe5-2af34f29a2b9", + "concept": "fns.snap.underpayment_error_rate", + "family_patterns": [ + "fns.snap.underpayment_error_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "fiscal_year", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "household", + "role": "snap_participant" + }, + "sources": [ + "fns" + ], + "aliases": [], + "source_concepts": [ + "fns.snap.underpayment_error_rate" + ], + "rid_patterns": [ + "fns.snap.underpayment_payment_error_rate.us.{P}.official_release" + ], + "first_observed_period": "2024", + "last_observed_period": "2024", + "observation_count": 1 + }, + { + "uuid": "5a76270d-3418-49c8-b510-a08c0df0fc8c", + "concept": "fns.wic.total_participation", + "family_patterns": [ + "fns.wic.total_participation" + ], + "status": "docket-only", + "unit": "millions", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "a3e8d1fb-5f29-40a8-ab14-a06a53ecb6af", + "concept": "nbb.business_barometer.overall", + "family_patterns": [ + "nbb.business_barometer.overall" + ], + "status": "docket-only", + "unit": "index_points", + "cadence": "month", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "64bd2834-d559-4bf2-abff-3eae6fcf6b4b", + "concept": "nbb.consumer_confidence.indicator", + "family_patterns": [ + "nbb.consumer_confidence.indicator" + ], + "status": "docket-only", + "unit": "index_points", + "cadence": "month", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "03a9d380-e5cf-45d8-b4d8-b3da28aad043", + "concept": "nbb.gdp.flash_qoq", + "family_patterns": [ + "nbb.gdp.flash_qoq" + ], + "status": "docket-only", + "unit": null, + "cadence": "quarter", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "8436c65e-a9c6-46db-a2b5-77d2cdfff5c2", + "concept": "ons.cpi.annual_rate", + "family_patterns": [ + "ons.cpi.annual_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "household", + "role": "cpi_all_items" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.cpi.annual_rate.may_2026" + ], + "source_concepts": [ + "ons.cpi.annual_rate" + ], + "rid_patterns": [ + "ons.cpi.annual_rate.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "c7d9ec49-df82-41fa-a184-5cb76379a57e", + "concept": "ons.cpih.annual_rate", + "family_patterns": [ + "ons.cpih.annual_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "household", + "role": "cpih_all_items" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.cpih.annual_rate.2026_05" + ], + "source_concepts": [ + "ons.cpih.annual_rate" + ], + "rid_patterns": [ + "ons.cpih.annual_rate.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "780bdbec-6c59-4714-826f-a3dc66f3b6ab", + "concept": "ons.gdp.monthly_growth", + "family_patterns": [ + "ons.gdp.monthly_growth.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "institutional_sector", + "role": "monthly_gdp" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.gdp.monthly_growth.april_2026" + ], + "source_concepts": [ + "ons.gdp.monthly_growth.april_2026" + ], + "rid_patterns": [ + "ons.gdp.monthly_growth.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "b608f8f5-08be-4535-bae0-98282561532e", + "concept": "ons.hmrc.paye_payrolled_employees", + "family_patterns": [ + "ons.hmrc.paye_payrolled_employees.{P}" + ], + "status": "observed", + "unit": "millions", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "person", + "role": "paye_payrolled_employee" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.hmrc.paye_payrolled_employees.may_2026" + ], + "source_concepts": [ + "ons.hmrc.paye_payrolled_employees" + ], + "rid_patterns": [ + "ons.hmrc.paye_payrolled_employees.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "15c5a134-1cbe-4990-9efa-97702cd3d4de", + "concept": "ons.labour.unemployment_rate", + "family_patterns": [ + "ons.labour.unemployment_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "person", + "role": "labour_force" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.labour.unemployment_rate.february_to_april_2026" + ], + "source_concepts": [ + "ons.labour.unemployment_rate" + ], + "rid_patterns": [ + "ons.labour.unemployment_rate.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "c5d4619d-a81e-457b-b970-b2e97145caf9", + "concept": "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks", + "family_patterns": [ + "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks.{P}" + ], + "status": "observed", + "unit": "gbp_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "United Kingdom" + }, + "entity": { + "name": "government", + "role": "public_sector_net_borrowing_ex_banks" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks.may_2026" + ], + "source_concepts": [ + "ons.pusf.j5ii" + ], + "rid_patterns": [ + "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "35138dc0-68d5-40c8-abc8-b1a97789fffa", + "concept": "ons.retail_sales.volume_mom", + "family_patterns": [ + "ons.retail_sales.volume_mom.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "GB", + "vintage": "current", + "name": "Great Britain" + }, + "entity": { + "name": "institutional_sector", + "role": "retail_sales_volume" + }, + "sources": [ + "ons" + ], + "aliases": [ + "ons.retail_sales.volume_mom.may_2026" + ], + "source_concepts": [ + "ons.retail_sales.volume_mom" + ], + "rid_patterns": [ + "ons.retail_sales.volume_mom.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "d79b97a3-405d-4ac5-a47b-7733cd0ac673", + "concept": "rba.cash_rate_target", + "family_patterns": [ + "rba.cash_rate_target.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "AU", + "vintage": "current", + "name": "Australia" + }, + "entity": { + "name": "government", + "role": "cash_rate_target" + }, + "sources": [ + "rba" + ], + "aliases": [ + "rba.cash_rate_target.after_june_2026" + ], + "source_concepts": [ + "rba.cash_rate_target" + ], + "rid_patterns": [ + "rba.cash_rate_target.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "b908460f-bc4c-4416-a7d6-75b43244b752", + "concept": "ssa.ssi.total_recipients", + "family_patterns": [ + "ssa.ssi.total_recipients" + ], + "status": "docket-only", + "unit": "millions", + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "99a7178c-5085-4ec2-9714-c4d03f10f184", + "concept": "statbel.cpi.headline_yoy", + "family_patterns": [ + "statbel.cpi.headline_yoy" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "8fc8eb86-6f04-4c6b-91ae-ca2a490a175f", + "concept": "statbel.health_index.yoy", + "family_patterns": [ + "statbel.health_index.yoy" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": { + "level": "country", + "id": "BE", + "name": null + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "987c1860-5642-494e-b9da-4034691430e5", + "concept": "statcan.36-10-0434-01.all_industries.month_to_month_percent_change", + "family_patterns": [ + "statcan.36-10-0434-01.all_industries.month_to_month_percent_change" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "statcan" + ], + "aliases": [], + "source_concepts": [ + "v65201210" + ], + "rid_patterns": [ + "statcan.36-10-0434-01.all_industries.month_to_month_percent_change.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "dcff2e47-6562-406e-95ac-7914478803e1", + "concept": "statcan.building_permits.total_value_mom.canada", + "family_patterns": [ + "statcan.building_permits.total_value_mom.canada.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "dwelling", + "role": "total_value" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.building_permits.total_value_mom.canada.april_2026" + ], + "source_concepts": [ + "statcan.building_permits.total_value_mom.canada.april_2026" + ], + "rid_patterns": [ + "statcan.building_permits.total_value_mom.canada.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "51be759e-6118-4e1c-bef0-823e40fd004b", + "concept": "statcan.cpi.all_items_annual_rate.canada", + "family_patterns": [ + "statcan.cpi.all_items_annual_rate.canada.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.cpi.all_items_annual_rate.canada.may_2026" + ], + "source_concepts": [ + "v41690973" + ], + "rid_patterns": [ + "statcan.cpi.all_items_annual_rate.canada.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "23bf4da6-874a-4b29-affb-ee68d03d2dee", + "concept": "statcan.cpi.allitems.yoy", + "family_patterns": [ + "statcan.cpi.allitems.yoy.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.cpi.allitems.yoy.2026-05" + ], + "source_concepts": [ + "v41690973" + ], + "rid_patterns": [ + "statcan.cpi.allitems.yoy.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "52fba74f-ea83-4b12-981f-3ca2c581686d", + "concept": "statcan.employment_insurance.regular_beneficiaries.canada", + "family_patterns": [ + "statcan.employment_insurance.regular_beneficiaries.canada.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "person", + "role": "ei_beneficiary" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.employment_insurance.regular_beneficiaries", + "statcan.employment_insurance.regular_beneficiaries.canada.may_2026" + ], + "source_concepts": [ + "v64549350" + ], + "rid_patterns": [ + "statcan.employment_insurance.regular_beneficiaries.canada.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "78d00eef-fa0e-4248-ae18-eef6536568d4", + "concept": "statcan.employment_insurance.regular_beneficiaries.canada", + "family_patterns": [ + "statcan.employment_insurance.regular_beneficiaries.canada.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "person", + "role": "regular_employment_insurance_beneficiary" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.employment_insurance.regular_beneficiaries.canada.april_2026" + ], + "source_concepts": [ + "statcan.employment_insurance.regular_beneficiaries" + ], + "rid_patterns": [ + "statcan.employment_insurance.regular_beneficiaries.canada.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "166d1a5a-b627-4d1f-9f79-682f28692dd5", + "concept": "statcan.gdp_by_industry.monthly_growth", + "family_patterns": [ + "statcan.gdp_by_industry.monthly_growth.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.gdp_by_industry.monthly_growth.april_2026" + ], + "source_concepts": [ + "v65201210" + ], + "rid_patterns": [ + "statcan.gdp_by_industry.monthly_growth.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "552c1dda-abe3-4f6c-86fb-473f455b5e15", + "concept": "statcan.lfs.employment_change", + "family_patterns": [ + "statcan.lfs.employment_change" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "person", + "role": "employed" + }, + "sources": [ + "statcan" + ], + "aliases": [], + "source_concepts": [ + "statcan.lfs.employment_change" + ], + "rid_patterns": [ + "statcan.lfs.employment_change.canada.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "184e9ddf-aeeb-47eb-9ea4-a60eafa878cc", + "concept": "statcan.lfs.unemployment_rate", + "family_patterns": [ + "statcan.lfs.unemployment_rate" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "person", + "role": "labour_force" + }, + "sources": [ + "statcan" + ], + "aliases": [], + "source_concepts": [ + "statcan.lfs.unemployment_rate" + ], + "rid_patterns": [ + "statcan.lfs.unemployment_rate.canada.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "42b102d3-0883-4935-843d-1a8e80434f49", + "concept": "statcan.retail_trade.sales_mom.canada", + "family_patterns": [ + "statcan.retail_trade.sales_mom.canada.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "institutional_sector", + "role": "retail_sales" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.retail_trade.sales_mom.canada.april_2026" + ], + "source_concepts": [ + "statcan.retail_trade.sales_mom" + ], + "rid_patterns": [ + "statcan.retail_trade.sales_mom.canada.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "057c368b-a4e8-42bc-ab29-5b55f4eadfd5", + "concept": "statcan.wholesale_trade.sales_mom_exclusions.canada", + "family_patterns": [ + "statcan.wholesale_trade.sales_mom_exclusions.canada.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "CA", + "vintage": "current", + "name": "Canada" + }, + "entity": { + "name": "institutional_sector", + "role": "wholesale_sales_exclusions" + }, + "sources": [ + "statcan" + ], + "aliases": [ + "statcan.wholesale_trade.sales_mom_exclusions.canada.april_2026" + ], + "source_concepts": [ + "statcan.wholesale_trade.sales_mom_exclusions" + ], + "rid_patterns": [ + "statcan.wholesale_trade.sales_mom_exclusions.canada.{P}.first_print" + ], + "first_observed_period": "2026-04", + "last_observed_period": "2026-04", + "observation_count": 1 + }, + { + "uuid": "37ef2549-b8d4-4020-8ebd-a2ceb5fe8401", + "concept": "statjp.cpi.all_items_annual_rate.japan", + "family_patterns": [ + "statjp.cpi.all_items_annual_rate.japan.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "household", + "role": "cpi_all_items" + }, + "sources": [ + "statjp" + ], + "aliases": [ + "statjp.cpi.all_items_annual_rate.japan.may_2026" + ], + "source_concepts": [ + "japan.cpi.all_items_yoy" + ], + "rid_patterns": [ + "statjp.cpi.all_items_annual_rate.japan.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "d26a3e29-c04f-4c86-825d-fba2d306e7af", + "concept": "statjp.cpi.tokyo_all_items_annual_rate", + "family_patterns": [ + "statjp.cpi.tokyo_all_items_annual_rate.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "stat_jp" + ], + "aliases": [ + "statjp.cpi.tokyo_all_items_annual_rate.june_2026" + ], + "source_concepts": [ + "e-Stat statInfId 000040461676 (series 0001, all items)" + ], + "rid_patterns": [ + "statjp.cpi.tokyo_all_items_annual_rate.{P}.preliminary" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "62e78be0-309e-49ba-8604-67abb80f1a03", + "concept": "statjp.cpi.tokyo_all_items_yoy", + "family_patterns": [ + "statjp.cpi.tokyo_all_items_yoy" + ], + "status": "docket-only", + "unit": null, + "cadence": "month", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "f3c0a92a-1af7-4afc-8578-7ac55076227d", + "concept": "statjp.household_spending.real_yoy.two_or_more_person_households", + "family_patterns": [ + "statjp.household_spending.real_yoy.two_or_more_person_households.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "stat_jp" + ], + "aliases": [ + "statjp.household_spending.real_yoy.two_or_more_person_households.may_2026" + ], + "source_concepts": [ + "stat.go.jp kakei sokuhou monthly page" + ], + "rid_patterns": [ + "statjp.household_spending.real_yoy.two_or_more_person_households.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "d15b52c7-e453-4276-bf9f-0b3362a37ecf", + "concept": "statjp.lfs.unemployment_rate.japan", + "family_patterns": [ + "statjp.lfs.unemployment_rate.japan.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "JP", + "vintage": "current", + "name": "Japan" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "stat_jp" + ], + "aliases": [ + "statjp.lfs.unemployment_rate.japan.may_2026" + ], + "source_concepts": [ + "stat.go.jp roudou sokuhou monthly page" + ], + "rid_patterns": [ + "statjp.lfs.unemployment_rate.japan.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "f471eabf-fcff-449b-bac7-60a8561b063b", + "concept": "treasury.mts.monthly_deficit", + "family_patterns": [ + "treasury.mts.monthly_deficit.{P}" + ], + "status": "observed", + "unit": "usd_billions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "government", + "role": "federal_budget_balance" + }, + "sources": [ + "treasury" + ], + "aliases": [ + "treasury.mts.monthly_deficit.may_2026" + ], + "source_concepts": [ + "treasury.mts.monthly_deficit.may_2026" + ], + "rid_patterns": [ + "treasury.mts.monthly_deficit.{P}.first_print" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "5d0b93c4-1c82-4598-869d-010d56860983", + "concept": "us.bea.core_pce.mom_sa", + "family_patterns": [ + "us.bea.core_pce.mom_sa.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "economy", + "role": "aggregate" + }, + "sources": [ + "bea" + ], + "aliases": [ + "us.bea.core_pce.mom_sa.2026-05", + "us.bea.core_pce.mom_sa.2026-06" + ], + "source_concepts": [ + "PCEPILFE" + ], + "rid_patterns": [ + "us.bea.core_pce.mom_sa.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-06", + "observation_count": 2 + }, + { + "uuid": "d542cb8d-46ef-4af5-b7e0-8d38574120c2", + "concept": "us.census.housing_starts.total_saar", + "family_patterns": [ + "us.census.housing_starts.total_saar.{P}" + ], + "status": "observed", + "unit": "millions", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "dwelling", + "role": "housing_start" + }, + "sources": [ + "census" + ], + "aliases": [ + "us.census.housing_starts.total_saar.2026_05" + ], + "source_concepts": [ + "census.housing_starts.saar" + ], + "rid_patterns": [ + "us.census.housing_starts.total_saar.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "1ad67747-d9d8-4ff9-9ebc-58206e658518", + "concept": "us.dol.initial_claims.sa", + "family_patterns": [ + "us.dol.initial_claims.sa" + ], + "status": "observed", + "unit": "thousands", + "cadence": "week_ending", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "ui_claimant" + }, + "sources": [ + "dol_eta" + ], + "aliases": [], + "source_concepts": [ + "ICSA" + ], + "rid_patterns": [ + "us.dol.initial_claims.sa.{P}" + ], + "first_observed_period": "2026-06-20", + "last_observed_period": "2026-07-25", + "observation_count": 5 + }, + { + "uuid": "830e68bb-9ed6-493c-a8a4-440ba8e53f76", + "concept": "us.dol.initial_claims.sa", + "family_patterns": [ + "us.dol.initial_claims.sa.{P}" + ], + "status": "observed", + "unit": "thousands", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "person", + "role": "ui_initial_claimant" + }, + "sources": [ + "dol" + ], + "aliases": [ + "us.dol.initial_claims.sa.week_2026_06_13" + ], + "source_concepts": [ + "dol.eta.initial_claims.sa" + ], + "rid_patterns": [ + "us.dol.initial_claims.sa.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "a4729a07-d9cd-4736-9473-2f845fd71a2d", + "concept": "us.fed.fomc.target_range_upper", + "family_patterns": [ + "us.fed.fomc.target_range_upper.{P}" + ], + "status": "observed", + "unit": "percent", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "government", + "role": "federal_funds_target_range_upper" + }, + "sources": [ + "fed" + ], + "aliases": [ + "us.fed.fomc.target_range_upper.2026_06" + ], + "source_concepts": [ + "fomc.federal_funds_target_range_upper" + ], + "rid_patterns": [ + "us.fed.fomc.target_range_upper.{P}" + ], + "first_observed_period": "2026-06", + "last_observed_period": "2026-06", + "observation_count": 1 + }, + { + "uuid": "b5a74252-bf95-488c-81f6-3deab496c477", + "concept": "us.frb.industrial_production.total.mom_sa", + "family_patterns": [ + "us.frb.industrial_production.total.mom_sa.{P}" + ], + "status": "observed", + "unit": "percent_growth", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "vintage": "current", + "name": "United States" + }, + "entity": { + "name": "institutional_sector", + "role": "total_industrial_production" + }, + "sources": [ + "fed" + ], + "aliases": [ + "us.frb.industrial_production.total.mom_sa.2026_05" + ], + "source_concepts": [ + "fed.g17.industrial_production.total_index_mom" + ], + "rid_patterns": [ + "us.frb.industrial_production.total.mom_sa.{P}" + ], + "first_observed_period": "2026-05", + "last_observed_period": "2026-05", + "observation_count": 1 + }, + { + "uuid": "8146ecb0-3ef3-49eb-b8a0-e61d15aa276e", + "concept": "usaspending.dod.new_prime_awards", + "family_patterns": [ + "usaspending.dod.new_prime_awards" + ], + "status": "docket-only", + "unit": "millions", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "a2649fd9-f382-4003-ba70-01030bc93b5e", + "concept": "usaspending.dod.prime_award_obligations", + "family_patterns": [ + "usaspending.dod.prime_award_obligations" + ], + "status": "docket-only", + "unit": "billions USD", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "82ff2d7f-04ee-4366-8fe4-b8e919e6efa6", + "concept": "usaspending.dod.prime_award_transactions", + "family_patterns": [ + "usaspending.dod.prime_award_transactions" + ], + "status": "docket-only", + "unit": "millions", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "11ea0d3d-fab9-42bb-b679-3471d4b60323", + "concept": "usaspending.dod.prime_contract_obligations", + "family_patterns": [ + "usaspending.dod.prime_contract_obligations" + ], + "status": "docket-only", + "unit": "billions USD", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "91e3b668-cb4b-4682-94a5-7ae404a6565d", + "concept": "usaspending.dod.small_business_contract_obligation_share", + "family_patterns": [ + "usaspending.dod.small_business_contract_obligation_share" + ], + "status": "docket-only", + "unit": "percent", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "96d17026-b54c-4409-98c9-339b6725fde7", + "concept": "usaspending.dod.unique_prime_contract_recipients", + "family_patterns": [ + "usaspending.dod.unique_prime_contract_recipients" + ], + "status": "docket-only", + "unit": "thousands", + "cadence": "year", + "geography": null, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + }, + { + "uuid": "4266e63f-dbf3-4c86-be8d-44315dbe32e3", + "concept": "usda.fsa.crp.enrolled_acres_total", + "family_patterns": [ + "usda.fsa.crp.enrolled_acres_total" + ], + "status": "docket-only", + "unit": "count", + "cadence": "month", + "geography": { + "level": "country", + "id": "0100000US", + "name": "United States" + }, + "entity": null, + "sources": [], + "aliases": [], + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": null, + "last_observed_period": null, + "observation_count": 0 + } + ] +} diff --git a/ledger/series_uuid_registry.jsonl b/ledger/series_uuid_registry.jsonl new file mode 100644 index 0000000..cba4249 --- /dev/null +++ b/ledger/series_uuid_registry.jsonl @@ -0,0 +1,201 @@ +{"concept": "abs.building_approvals.total_dwellings_mom.australia", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "efbb2901-f8f8-4f5b-9835-56e5cc3e62ff"} +{"concept": "abs.cpi.all_groups.yoy", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "92220fc5-fd61-46f5-b02d-467948889e2d"} +{"concept": "abs.cpi.all_groups_annual_rate.australia", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "88055a40-bfac-468f-bec6-fcc768e7fc7b"} +{"concept": "abs.cpi_indicator.allgroups.yoy", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "b4d4b454-c8a7-42a4-9a28-ce393b664717"} +{"concept": "abs.labour.employment_change.australia", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "person", "role": "employed"}, "uuid": "ab7e3632-e9d3-453e-bdc5-aa5a8dca77c9"} +{"concept": "abs.labour.unemployment_rate", "geography": null, "entity": null, "uuid": "443cb1bf-49d3-4e1e-9897-9556c8a8c768"} +{"concept": "abs.labour.unemployment_rate.australia", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "980c42b6-db11-4b46-a2b5-e7cd6c67527a"} +{"concept": "bank_of_canada.overnight_rate", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "government", "role": "overnight_target"}, "uuid": "1d3d8ca7-091e-4098-9abc-38bd906e7c7a"} +{"concept": "bea.core_pce.mom", "geography": null, "entity": null, "uuid": "efc3259b-eb44-49c3-9bc6-e92fd7cf6243"} +{"concept": "bea.disposable_personal_income.level", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "ff8df856-08d4-4d5b-8228-e51cff7e1a03"} +{"concept": "bea.government_social_benefits.level", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "5271091d-451c-47dc-a3af-5b1581af13ba"} +{"concept": "bea.government_social_benefits.medicaid", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "ed4dcaa2-e7aa-4696-98c2-b82fa81ee5d0"} +{"concept": "bea.government_social_benefits.medicare", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "c3bd2028-88b3-48e4-b648-aca7be6c0434"} +{"concept": "bea.government_social_benefits.social_security", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "e63e1fb2-2db5-4068-80da-8caf1a3faa93"} +{"concept": "bea.pce.core_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "174240ec-f8ff-417a-bf1c-bdf088268a7c"} +{"concept": "bea.pce_price_index.monthly_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "0bcbb3b9-801b-435e-8aa8-646c0ac76445"} +{"concept": "bea.personal_current_taxes.level", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "c05ca17a-461c-4ae4-b685-5e6ba8ff7710"} +{"concept": "bea.real_gdp.saar.third_estimate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "83094ec4-c890-435b-8a6a-83273c3e9077"} +{"concept": "bea.trade.goods_services_deficit", "geography": null, "entity": null, "uuid": "7be75093-832c-4734-a002-a0ff19c610ce"} +{"concept": "bea.wages_and_salaries.level", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "27202e84-7743-46ed-a6e8-2c907aa79a6c"} +{"concept": "bls.ces.average_hourly_earnings_private_monthly_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "private_nonfarm_payroll_employee"}, "uuid": "0edb7ad9-0541-4bd2-9597-bffc9e73cd92"} +{"concept": "bls.ces.nonfarm_payrolls.change", "geography": null, "entity": null, "uuid": "d8ab9f3a-aa0c-4b7f-89df-a0c9107cfdbb"} +{"concept": "bls.ces.total_nonfarm_payroll_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "c0515530-c56b-417d-9ead-aff423c87207"} +{"concept": "bls.ces.total_nonfarm_payroll_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "nonfarm_payroll_employee"}, "uuid": "0b4f36be-2bc7-430b-b960-5df4011347bb"} +{"concept": "bls.cpi.owners_equivalent_rent_mom", "geography": null, "entity": null, "uuid": "d0dc87fb-fd6d-4234-832a-869d5b2d8f3d"} +{"concept": "bls.cpi.rent_primary_residence_mom", "geography": null, "entity": null, "uuid": "615ac1b1-27e8-437d-bf69-86d760488dc0"} +{"concept": "bls.cpi.services_less_energy_mom", "geography": null, "entity": null, "uuid": "3bab6696-88c0-44c8-988c-737d2d00c37c"} +{"concept": "bls.cpi.services_less_rent_shelter_mom", "geography": null, "entity": null, "uuid": "975e942b-d456-480d-859f-a9ffaffcfe34"} +{"concept": "bls.cpi.shelter_mom", "geography": null, "entity": null, "uuid": "4c5b4292-0bcf-4c42-9fc5-39d22e791f75"} +{"concept": "bls.cpi.u.core_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "e5e402d5-1a65-4c5d-a67a-07e3cf03cbf8"} +{"concept": "bls.cpi.u.core_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "cpi_u_less_food_energy"}, "uuid": "d85c3c7f-874b-4caa-bfad-1ec7a473c293"} +{"concept": "bls.cpi.u.headline_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "70ca2ecf-4323-4453-b42f-23350cb95f22"} +{"concept": "bls.cpi.u.headline_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "cpi_u_all_items"}, "uuid": "3e796803-a194-4c83-9dc9-27cc870ff08e"} +{"concept": "bls.cps.employed_people_by_occupation.business_financial_operations", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "3cbdbc7f-3e6b-4b76-8758-57e519906bd4"} +{"concept": "bls.cps.employed_people_by_occupation.computer_mathematical", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "54f19551-6288-48e2-9fa0-17933e55d390"} +{"concept": "bls.cps.employed_people_by_occupation.healthcare_support", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "25457957-df59-4e16-ae3f-431b4c63e7bf"} +{"concept": "bls.cps.employed_people_by_occupation.office_administrative_support", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "2316f164-f13f-4025-980c-b92cc46b4d1a"} +{"concept": "bls.cps.employed_people_by_occupation.production", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "3240539a-c5b8-43de-a240-cc6754fb59aa"} +{"concept": "bls.cps.employed_people_by_occupation.transportation_material_moving", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "e2502908-1e16-46e3-b330-4d6612e510c2"} +{"concept": "bls.cps.telework_share", "geography": null, "entity": null, "uuid": "94a5a046-ea77-449a-8ba9-95ffebc9039c"} +{"concept": "bls.cps.u6_underemployment_rate", "geography": null, "entity": null, "uuid": "e0be267e-d74c-4ad2-a5cd-5ac9f821e018"} +{"concept": "bls.cps.unemployment_rate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "db2f3857-e0fa-48f8-9ab1-6c8989307fd2"} +{"concept": "bls.cps.unemployment_rate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "civilian_labor_force"}, "uuid": "8dbbd54f-4bfd-4735-ad0d-5b55b8bb4ec5"} +{"concept": "bls.eci.private_wages_salaries_qoq", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "6d2e1191-9e14-4719-a2bb-c3561fca49e6"} +{"concept": "bls.eci.total_compensation_private_industry_qoq", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "2820e965-4c7b-40d2-b5b8-e03792cf3fbf"} +{"concept": "bls.export_prices.all_commodities_mom", "geography": null, "entity": null, "uuid": "7dc8b980-8f14-4581-ad7c-3963de6a42d9"} +{"concept": "bls.import_price_index.all_imports_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "1d271205-0e71-4a74-9d5f-47e77577e2b2"} +{"concept": "bls.import_price_index.all_imports_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "all_imports"}, "uuid": "e8e6e43e-2c46-4620-a070-91d2da004b1c"} +{"concept": "bls.jolts.hires_rate", "geography": null, "entity": null, "uuid": "3bddc6d0-e581-42dc-9c13-b731a9ede21f"} +{"concept": "bls.jolts.job_openings", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "020dafc2-2ca3-453b-a6bc-f96d443a5f77"} +{"concept": "bls.jolts.job_openings_total", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "1d05d94d-0288-46f0-b6a9-1fa499e85fff"} +{"concept": "bls.jolts.quits_rate", "geography": null, "entity": null, "uuid": "e6aed5ae-2f23-413b-a600-4c5b80edeb18"} +{"concept": "bls.lns11300000", "geography": null, "entity": null, "uuid": "0006af01-72ba-4e63-8d88-54500588f215"} +{"concept": "bls.ppi.final_demand_monthly_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "ppi_final_demand"}, "uuid": "a7ec4618-1ade-4f5e-bbbd-1c0680c7fccd"} +{"concept": "bls.productivity.nonfarm_qoq_prelim", "geography": null, "entity": null, "uuid": "6e671a89-65f6-4da9-b092-4e6d74a36a08"} +{"concept": "bls.productivity.nonfarm_unit_labor_costs_qoq_prelim", "geography": null, "entity": null, "uuid": "205885c4-de46-47df-a916-253ae2a0299a"} +{"concept": "bls.real_earnings.avg_hourly_mom", "geography": null, "entity": null, "uuid": "2e3a7126-41fd-4d88-99b6-cf4f2057c277"} +{"concept": "boe.bank_rate", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "government", "role": "bank_rate"}, "uuid": "8fb890ac-38d0-430d-9a35-bbeffa0cf8ca"} +{"concept": "boj.policy_rate_guideline", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "government", "role": "uncollateralized_overnight_call_rate_guideline"}, "uuid": "e300b533-d2f0-41b4-8cbb-59168f8c80bd"} +{"concept": "census.construction_spending.total_mom", "geography": null, "entity": null, "uuid": "63222847-045a-4784-bd89-4d7878a2a6d8"} +{"concept": "census.housing.completions_saar", "geography": null, "entity": null, "uuid": "e16080e3-02e9-4ae1-95c6-3ded737cf697"} +{"concept": "census.housing.permits_saar", "geography": null, "entity": null, "uuid": "420c8935-70b8-4d8f-a82d-e772446adc6e"} +{"concept": "census.housing_starts.saar", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "dwelling", "role": "housing_start"}, "uuid": "d0212a1e-b360-460a-80a8-1cf4ad5ce427"} +{"concept": "census.housing_starts.saar", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "8c999902-84e2-4e90-b49c-325cd535062a"} +{"concept": "census.m3.durable_goods_new_orders_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "294d82e8-3711-4d0d-89f6-a5a7a087f1be"} +{"concept": "census.m3.durable_goods_shipments_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "5cbc4c1a-e387-4b64-bb56-67ef349fbd51"} +{"concept": "census.marts.adv44x72.monthly_change", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "retail_and_food_services_sales"}, "uuid": "ed3215ed-43ec-44dc-b11c-f72a098fc008"} +{"concept": "census.mtis.total_business_inventories_level", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "total_business_inventory"}, "uuid": "26bb1475-8685-4142-8472-7bcd6d998fcc"} +{"concept": "census.new_residential_sales.new_single_family_houses_sold_saar", "geography": null, "entity": null, "uuid": "5654403e-f6c5-412e-94e0-feeda778ad81"} +{"concept": "cms.care_compare.nursing_home_occupancy_pct", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "aafb4ff0-cb04-4f76-96de-ef0baaa7bd93"} +{"concept": "cms.medicaid_pi.beneficiaries_disenrolled_procedural", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "person", "role": "medicaid_beneficiary"}, "uuid": "fc7c5e2f-90ff-4a9b-95cb-8d339fc8a89d"} +{"concept": "cms.medicaid_pi.beneficiaries_disenrolled_total", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "person", "role": "medicaid_beneficiary"}, "uuid": "8829b01a-9f45-4c9e-b38e-d04f405f238c"} +{"concept": "cms.medicaid_pi.beneficiaries_renewed_ex_parte", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "person", "role": "medicaid_beneficiary"}, "uuid": "56baf17e-830f-40ea-82f3-aa05cbbeeea2"} +{"concept": "cms.medicaid_pi.beneficiaries_renewed_total", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "person", "role": "medicaid_beneficiary"}, "uuid": "f6053113-5c2f-4832-9f54-e8ba5763f160"} +{"concept": "cms.nursing_home_compare.reported_total_nurse_staffing_hprd_us", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "35d1d31f-ade4-4487-a14c-bd492de01521"} +{"concept": "dol.eta.continued_claims.sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "ui_claimant"}, "uuid": "38773338-f84a-466e-94cb-fce55f5e2549"} +{"concept": "dol.eta.initial_claims.sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "ui_initial_claimant"}, "uuid": "330ec66b-502c-4ddf-9978-02199bab0055"} +{"concept": "ecb.deposit_facility_rate", "geography": {"level": "country", "id": "EA", "vintage": "current"}, "entity": {"name": "government", "role": "deposit_facility"}, "uuid": "32b335ea-b2dd-4fba-8321-e9751df19318"} +{"concept": "estat.jp.cpi.core_exfreshfood.yoy", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "household", "role": "cpi_less_fresh_food"}, "uuid": "0be25641-eae9-45b8-b0c5-218b21e23d61"} +{"concept": "eurostat.ea.hicp.flash.yoy", "geography": {"level": "region", "id": "EA21", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "4eaed6f8-7da3-405d-8293-4b61d5cefc6f"} +{"concept": "eurostat.hicp.all_items_annual_rate.euro_area", "geography": {"level": "region", "id": "EA", "vintage": "current"}, "entity": {"name": "household", "role": "hicp_all_items"}, "uuid": "815299ce-841a-4115-9af2-3b52fc036834"} +{"concept": "eurostat.hicp.all_items_annual_rate.euro_area", "geography": {"level": "region", "id": "EA21", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "74ce2da4-28ed-409a-a0f1-2a7c40dc7caf"} +{"concept": "eurostat.hicp.flash.yoy", "geography": null, "entity": null, "uuid": "1e84eb13-9e80-4dda-9b82-71361b886c1e"} +{"concept": "eurostat.industrial_production.euro_area", "geography": {"level": "region", "id": "EA", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "industrial_production"}, "uuid": "4994dab0-bc46-4dd0-9bb7-ef716dfc4c75"} +{"concept": "eurostat.retail_trade.volume_mom.euro_area", "geography": {"level": "region", "id": "EA21", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "16b32f44-da0e-43de-bfb2-8b5a2a883b95"} +{"concept": "eurostat.unemployment_rate", "geography": null, "entity": null, "uuid": "2c59f506-873f-45df-b6c5-588c82dcd640"} +{"concept": "eurostat.unemployment_rate.belgium", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "c7f994ce-3d0b-4a28-ba5b-e286237cd14d"} +{"concept": "eurostat.unemployment_rate.euro_area", "geography": {"level": "region", "id": "EA21", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "ec04c486-d8fd-4b1b-9841-884bed8d88c7"} +{"concept": "fed.g17.capacity_utilization.manufacturing", "geography": null, "entity": null, "uuid": "40f48c12-3611-4e57-9482-055e62bf35ae"} +{"concept": "fed.g17.capacity_utilization.total_industry", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "9668ecdb-5f61-422d-a653-8e8956257ade"} +{"concept": "fed.g17.capacity_utilization.total_industry", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "total_industry_capacity"}, "uuid": "978252a9-c452-41eb-aece-323340e796fc"} +{"concept": "fed.g17.industrial_production.total_index_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "dbfd5050-e5df-40af-8922-dc8927d6363a"} +{"concept": "fed.g17.industrial_production.total_index_mom", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "total_industrial_production"}, "uuid": "d5634fc4-dbe0-4203-a5b7-728708b85a14"} +{"concept": "fed.g17.manufacturing_production_mom", "geography": null, "entity": null, "uuid": "41d26958-709c-44ab-a5ab-b385157db0ac"} +{"concept": "fed.g19.consumer_credit_nonrevolving_annual_rate", "geography": null, "entity": null, "uuid": "bd0643c4-6716-41ae-988d-c985272e38ae"} +{"concept": "fed.g19.consumer_credit_revolving_annual_rate", "geography": null, "entity": null, "uuid": "8a82c6a5-7c83-4ac8-ab5b-7dd6eaf97e8f"} +{"concept": "fed.g19.consumer_credit_total_annual_rate", "geography": null, "entity": null, "uuid": "dac76c03-0630-4859-898d-b4849383d147"} +{"concept": "fns.snap.application_processing_timeliness_rate", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "household", "role": "snap_applicant"}, "uuid": "3b195edb-1b2d-4380-8267-43084fe3cbeb"} +{"concept": "fns.snap.overpayment_error_rate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "ae4cc9d0-e0f7-4de4-97bc-dfcf5afeb90e"} +{"concept": "fns.snap.share_jurisdictions_at_or_above_6pct", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "government", "role": "snap_administering_jurisdiction"}, "uuid": "49b9c81b-935d-4523-a05d-d2000f8064e4"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "d93e4cbb-5810-4a89-80fe-3830df22e057"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US01", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "0902c609-38ed-4b39-94bf-13aa8f5d6cbb"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US02", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "3b0cdfd9-16a4-424b-99ea-dbbcedecefd7"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US04", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6c19d194-6a6b-4e7a-9fb1-f98ab24e6095"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US05", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6409e147-6d79-4c75-ba50-7601bd7021dd"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US06", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "93d432d3-0c95-4924-a180-c74622391117"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US08", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "32bc567b-075f-4471-a601-38f7694f1c35"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US09", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "83a60dd2-560a-4f92-9613-83507b73133c"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US10", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "34714371-9105-4e7b-93dd-6f7190d15e55"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US11", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "acfecec9-e491-441e-8df3-02cd52b61191"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US12", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "34e71c1b-05c8-48dd-8625-ddadea1b2445"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US13", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "94d5fcdc-3df6-4b46-bcd0-594dfc907488"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US15", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "f80db1ec-1739-496f-a6f9-47f3c18b280d"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US16", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "5ef9bdf6-62bc-4f50-8a7e-119a1c2f3aad"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US17", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "a90c5f52-5bcf-4920-8b32-c2c236b7ae28"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US18", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "12413650-f9e2-4308-93c7-886ce4f3956e"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US19", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "dc083c88-6ad5-4b84-b7f1-b890ff4e8561"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US20", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "0189b8ff-f12e-40d1-8d8f-3dc7a5e1bffa"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US21", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6362a941-a7a3-422a-970f-d650c91d18ec"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US22", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "3bab127f-53fe-4a60-a1f0-c6efaaa7dbe2"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US23", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "545502ce-7fa4-44e3-bc00-411f93df5875"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US24", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "75a0857b-f4da-4e5d-8000-33b3c8b8ed6e"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US25", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "c3fc0369-f22e-488d-b5b7-9c75e0de4b1c"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US26", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "e52a49f4-79f6-4c0f-b06d-7c52e788794f"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US27", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6e782a95-0fee-4f5a-b802-f061ef719fd2"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US28", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "2247c3f5-2393-4d87-a46b-5160cd80ad26"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US29", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "4642db20-cf61-4d04-891d-7d9933c6721e"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US30", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "8022c7e2-4760-40ae-9621-6d1d52d06acc"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US31", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "81f1dc85-41d3-4d12-86cb-6a93338fb4c8"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US32", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "cb4e42fd-c331-4da7-9341-4e546b9ce8cd"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US33", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "ad8be026-4531-46e4-b02d-43d27f93c948"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US34", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "c92381ef-2e2b-493b-ada9-6e9fbd3d2c2d"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US35", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "b5bb3650-78cb-435b-bb66-7f04e38bf470"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US36", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "7ab34230-19e8-4543-999f-2910c1173be5"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US37", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "2834f2ee-480f-476b-8ae6-5f8975cf493b"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US38", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "bb079952-9000-48fd-84da-0e4f38f28964"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US39", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "9fa7ec57-8aad-4680-a867-d6e07a1fde4d"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US40", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "21b36dca-9579-4a20-a947-beaa7afb6b7a"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US41", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "a53e930e-3042-4967-ad17-888f29e653af"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US42", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "e89d9485-e437-40c3-987e-be079dbc45eb"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US44", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "bc31a283-a414-4446-87cb-48d9c8a3db28"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US45", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "9b679b84-0d9f-44fd-86dd-3556bd131e42"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US46", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "4b6ceb75-7ede-4ec3-a1bc-de4bdd8f0dca"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US47", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6c9ddf7b-f037-4482-89fc-e45728169d88"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US48", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "249143e2-bbd8-46a6-96a8-673dfbf7e14f"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US49", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "25099a0a-db82-4354-aa16-dc0e7d0ee301"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US50", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "1435b3de-9953-4cb5-acd8-ace0df2570ad"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US51", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "28747a0a-dd14-414c-b193-514208ba0a05"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US53", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "39d52b69-8066-4028-81de-a1b938cf0630"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US54", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "6dcd0d3f-5ed9-49d4-93fc-b9ec63c9f699"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US55", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "62000494-b4fe-4a7d-b0d2-013b911e7884"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US56", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "b9d02bc9-68e5-4900-b30e-763f6d760a70"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US66", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "c562b4b3-09ce-45db-b355-cd6c9d412f18"} +{"concept": "fns.snap.total_payment_error_rate", "geography": {"level": "state", "id": "0400000US78", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "55cbe6f5-3aad-473a-8a3d-552b2525eb5f"} +{"concept": "fns.snap.total_persons", "geography": null, "entity": null, "uuid": "748670af-cafa-416e-afcd-b2c303593782"} +{"concept": "fns.snap.underpayment_error_rate", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "household", "role": "snap_participant"}, "uuid": "3fa6210a-33a1-4a3e-bbe5-2af34f29a2b9"} +{"concept": "fns.wic.total_participation", "geography": null, "entity": null, "uuid": "5a76270d-3418-49c8-b510-a08c0df0fc8c"} +{"concept": "nbb.business_barometer.overall", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "a3e8d1fb-5f29-40a8-ab14-a06a53ecb6af"} +{"concept": "nbb.consumer_confidence.indicator", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "64bd2834-d559-4bf2-abff-3eae6fcf6b4b"} +{"concept": "nbb.gdp.flash_qoq", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "03a9d380-e5cf-45d8-b4d8-b3da28aad043"} +{"concept": "ons.cpi.annual_rate", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "household", "role": "cpi_all_items"}, "uuid": "8436c65e-a9c6-46db-a2b5-77d2cdfff5c2"} +{"concept": "ons.cpih.annual_rate", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "household", "role": "cpih_all_items"}, "uuid": "c7d9ec49-df82-41fa-a184-5cb76379a57e"} +{"concept": "ons.gdp.monthly_growth", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "monthly_gdp"}, "uuid": "780bdbec-6c59-4714-826f-a3dc66f3b6ab"} +{"concept": "ons.hmrc.paye_payrolled_employees", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "person", "role": "paye_payrolled_employee"}, "uuid": "b608f8f5-08be-4535-bae0-98282561532e"} +{"concept": "ons.labour.unemployment_rate", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "person", "role": "labour_force"}, "uuid": "15c5a134-1cbe-4990-9efa-97702cd3d4de"} +{"concept": "ons.pusf.j5ii.public_sector_net_borrowing_ex_banks", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "government", "role": "public_sector_net_borrowing_ex_banks"}, "uuid": "c5d4619d-a81e-457b-b970-b2e97145caf9"} +{"concept": "ons.retail_sales.volume_mom", "geography": {"level": "country", "id": "GB", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "retail_sales_volume"}, "uuid": "35138dc0-68d5-40c8-abc8-b1a97789fffa"} +{"concept": "rba.cash_rate_target", "geography": {"level": "country", "id": "AU", "vintage": "current"}, "entity": {"name": "government", "role": "cash_rate_target"}, "uuid": "d79b97a3-405d-4ac5-a47b-7733cd0ac673"} +{"concept": "ssa.ssi.total_recipients", "geography": null, "entity": null, "uuid": "b908460f-bc4c-4416-a7d6-75b43244b752"} +{"concept": "statbel.cpi.headline_yoy", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "99a7178c-5085-4ec2-9714-c4d03f10f184"} +{"concept": "statbel.health_index.yoy", "geography": {"level": "country", "id": "BE", "vintage": null}, "entity": null, "uuid": "8fc8eb86-6f04-4c6b-91ae-ca2a490a175f"} +{"concept": "statcan.36-10-0434-01.all_industries.month_to_month_percent_change", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "987c1860-5642-494e-b9da-4034691430e5"} +{"concept": "statcan.building_permits.total_value_mom.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "dwelling", "role": "total_value"}, "uuid": "dcff2e47-6562-406e-95ac-7914478803e1"} +{"concept": "statcan.cpi.all_items_annual_rate.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "51be759e-6118-4e1c-bef0-823e40fd004b"} +{"concept": "statcan.cpi.allitems.yoy", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "23bf4da6-874a-4b29-affb-ee68d03d2dee"} +{"concept": "statcan.employment_insurance.regular_beneficiaries.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "person", "role": "ei_beneficiary"}, "uuid": "52fba74f-ea83-4b12-981f-3ca2c581686d"} +{"concept": "statcan.employment_insurance.regular_beneficiaries.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "person", "role": "regular_employment_insurance_beneficiary"}, "uuid": "78d00eef-fa0e-4248-ae18-eef6536568d4"} +{"concept": "statcan.gdp_by_industry.monthly_growth", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "166d1a5a-b627-4d1f-9f79-682f28692dd5"} +{"concept": "statcan.lfs.employment_change", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "person", "role": "employed"}, "uuid": "552c1dda-abe3-4f6c-86fb-473f455b5e15"} +{"concept": "statcan.lfs.unemployment_rate", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "person", "role": "labour_force"}, "uuid": "184e9ddf-aeeb-47eb-9ea4-a60eafa878cc"} +{"concept": "statcan.retail_trade.sales_mom.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "retail_sales"}, "uuid": "42b102d3-0883-4935-843d-1a8e80434f49"} +{"concept": "statcan.wholesale_trade.sales_mom_exclusions.canada", "geography": {"level": "country", "id": "CA", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "wholesale_sales_exclusions"}, "uuid": "057c368b-a4e8-42bc-ab29-5b55f4eadfd5"} +{"concept": "statjp.cpi.all_items_annual_rate.japan", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "household", "role": "cpi_all_items"}, "uuid": "37ef2549-b8d4-4020-8ebd-a2ceb5fe8401"} +{"concept": "statjp.cpi.tokyo_all_items_annual_rate", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "d26a3e29-c04f-4c86-825d-fba2d306e7af"} +{"concept": "statjp.cpi.tokyo_all_items_yoy", "geography": null, "entity": null, "uuid": "62e78be0-309e-49ba-8604-67abb80f1a03"} +{"concept": "statjp.household_spending.real_yoy.two_or_more_person_households", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "f3c0a92a-1af7-4afc-8578-7ac55076227d"} +{"concept": "statjp.lfs.unemployment_rate.japan", "geography": {"level": "country", "id": "JP", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "d15b52c7-e453-4276-bf9f-0b3362a37ecf"} +{"concept": "treasury.mts.monthly_deficit", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "government", "role": "federal_budget_balance"}, "uuid": "f471eabf-fcff-449b-bac7-60a8561b063b"} +{"concept": "us.bea.core_pce.mom_sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "economy", "role": "aggregate"}, "uuid": "5d0b93c4-1c82-4598-869d-010d56860983"} +{"concept": "us.census.housing_starts.total_saar", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "dwelling", "role": "housing_start"}, "uuid": "d542cb8d-46ef-4af5-b7e0-8d38574120c2"} +{"concept": "us.dol.initial_claims.sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "ui_claimant"}, "uuid": "1ad67747-d9d8-4ff9-9ebc-58206e658518"} +{"concept": "us.dol.initial_claims.sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "person", "role": "ui_initial_claimant"}, "uuid": "830e68bb-9ed6-493c-a8a4-440ba8e53f76"} +{"concept": "us.fed.fomc.target_range_upper", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "government", "role": "federal_funds_target_range_upper"}, "uuid": "a4729a07-d9cd-4736-9473-2f845fd71a2d"} +{"concept": "us.frb.industrial_production.total.mom_sa", "geography": {"level": "country", "id": "0100000US", "vintage": "current"}, "entity": {"name": "institutional_sector", "role": "total_industrial_production"}, "uuid": "b5a74252-bf95-488c-81f6-3deab496c477"} +{"concept": "usaspending.dod.new_prime_awards", "geography": null, "entity": null, "uuid": "8146ecb0-3ef3-49eb-b8a0-e61d15aa276e"} +{"concept": "usaspending.dod.prime_award_obligations", "geography": null, "entity": null, "uuid": "a2649fd9-f382-4003-ba70-01030bc93b5e"} +{"concept": "usaspending.dod.prime_award_transactions", "geography": null, "entity": null, "uuid": "82ff2d7f-04ee-4366-8fe4-b8e919e6efa6"} +{"concept": "usaspending.dod.prime_contract_obligations", "geography": null, "entity": null, "uuid": "11ea0d3d-fab9-42bb-b679-3471d4b60323"} +{"concept": "usaspending.dod.small_business_contract_obligation_share", "geography": null, "entity": null, "uuid": "91e3b668-cb4b-4682-94a5-7ae404a6565d"} +{"concept": "usaspending.dod.unique_prime_contract_recipients", "geography": null, "entity": null, "uuid": "96d17026-b54c-4409-98c9-339b6725fde7"} +{"concept": "usda.fsa.crp.enrolled_acres_total", "geography": {"level": "country", "id": "0100000US", "vintage": null}, "entity": null, "uuid": "4266e63f-dbf3-4c86-be8d-44315dbe32e3"} diff --git a/scripts/build_series_catalog.py b/scripts/build_series_catalog.py new file mode 100644 index 0000000..781e9a9 --- /dev/null +++ b/scripts/build_series_catalog.py @@ -0,0 +1,1985 @@ +#!/usr/bin/env python3 +"""Build ledger/series_catalog.json — the canonical series registry. + +The observation file records facts; this catalog records the SERIES those +facts belong to, one row per (concept, geography level/id/vintage, entity +name/role) identity — the same identity dimensions the fact ADR uses, +including the geography boundary vintage — keyed by a UUID that is minted +exactly once. Consumers (Thesis's docket, bill mappers, permalink surfaces) +refer to series by catalog UUID or concept and never mint parallel +identities. + +UUID authority is NOT this file: it is the append-only minting ledger at +``ledger/series_uuid_registry.jsonl``. Every binding the catalog has ever +shipped is a line there; the builder inherits UUIDs from the registry (then +from the existing catalog row for identities the registry has not met), and +appends a mint line for every new identity it binds. The catalog embeds the +registry digest (``uuid_registry_sha256``), ``--check`` verifies that every +catalog row agrees with its registry binding, and the registry itself may +only grow: + +* in a git checkout, the working-tree registry must extend the HEAD blob + byte-for-byte (checked on every build and ``--check``); +* in CI, ``--verify-registry-append-only BASE_FILE`` proves the PR keeps the + base branch's registry as an exact byte prefix; +* an identity may change or lose its UUID only through an explicit + ``--allow-remint --remint-note "..."`` run, which appends chained + supersede/retire lines so every identity change is a reviewable event, + never a silent regeneration. UUIDs are never reused by ordinary mints; + the one sanctioned reuse is a retire + ``succeeds`` pair (placeholder + enrichment). Catalog rows and live bindings are kept in bijection — + same identity, same UUID, both directions — so re-keying or swapping + identities cannot pass ``--check``. + +Wholesale remints therefore fail ``--check`` twice over: the reminted +catalog disagrees with the committed registry, and any registry rewrite +breaks the append-only prefix. + +Family derivation replaces period segments of ``source_record_id`` and +``measure.concept`` with ``{P}``. A dotted segment is treated as a period +segment only when it denotes the row's own declared period: either it is a +direct spelling of that period (``period_token_variants``), or it parses as +a calendar window (``fy2026``, ``2026-05``, ``2026_05_02``, ``may_2026``, +``february_to_april_2026``, ``q1_2026``, ``week_ending_2026_05_02``, +``after_mpc_june_2026``) that overlaps the row's period. Date-shaped +segments that do NOT match the row's period — a disjoint window, or an +impossible token like ``2026_13`` — are never stripped: they stay in the +identity and are reported in ``suspect_segments`` for curation. No strip +is invisible either way: every distinct stripped spelling is published in +``stripped_segments``, because a statute, cohort, or edition label that +happens to spell the row's own period is mechanically indistinguishable +from a period label — the audit list is where a curator catches that. A +malformed period (month 13, an impossible week date) is a hard error, so +corrupt metadata can never manufacture strippable tokens. + +Aliases are curated identity statements, not derived data. Observed concept +spellings of one identity become aliases automatically; everything else in +``aliases`` is hand curation and persists across regeneration. Source +labels (``measure.source_concept``) are provenance, recorded per row in +``source_concepts``, and never drive identity inheritance — a source label +may name a different series entirely (a derived share can cite its base +series). Generator versions < 3 mixed source labels into ``aliases``; +purging them (and re-adding the few that are genuine identity statements) +was a one-time curated migration of the committed catalog, reviewed line by +line in the generator-v3 change. + +Alias/concept inheritance is scoped to the SAME (geography, entity): a name +match can heal a rename within one dimension slice but can never move a +UUID across geographies or entities. The one documented exception is +docket-placeholder enrichment: a docket-only row (no observations; entity +unknown; geography absent or matching on level/id, with no contradicting +declared vintage) may be claimed by the first observed rows of that +series, which upgrade it in place, keep its UUID, and record the move as +a retire + ``succeeds`` event pair. + +Curated merges (two committed rows that are the same series) are performed +by hand: delete the absorbed row, add its concept to the survivor's +``aliases``, regenerate with ``--allow-remint --remint-note``; the absorbed +identity's observations inherit the survivor's UUID and the registry gains +a retire line for the absorbed binding (its old UUID goes dormant with +it). + +Inputs are pinned: the observation JSONL (``observations_sha256``), the +committed docket seed at ``ledger/seeds/thesis_docket_series.json`` +(``docket_seed_sha256``) — a MISSING seed is a hard error, never a silent +shrink — and the UUID registry (``uuid_registry_sha256``). Bare ``--check`` +uses the committed inputs, so CI needs no external files. + +Idempotent: same inputs + same registry + same existing catalog -> +byte-identical output and an unchanged registry. Unit or cadence conflicts +within one identity are a hard error, never a silent modal pick. UUIDs must +be canonical lowercase UUIDv4 text, and uniqueness is enforced on the +parsed 128-bit value, not the string spelling. +""" + +from __future__ import annotations + +import argparse +import calendar +import datetime as dt +import hashlib +import json +import pathlib +import re +import subprocess +import sys +import uuid as uuid_module +from collections import Counter + +ROOT = pathlib.Path(__file__).resolve().parents[1] +OBSERVATIONS = ROOT / "ledger" / "official_observations.jsonl" +CATALOG = ROOT / "ledger" / "series_catalog.json" +DOCKET_SEED = ROOT / "ledger" / "seeds" / "thesis_docket_series.json" +UUID_REGISTRY = ROOT / "ledger" / "series_uuid_registry.jsonl" + +GENERATOR_VERSION = 3 + +MONTHS_FULL = [ + "january", "february", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december", +] +# Exactly twelve, indexable by month-1. "sept" is an accepted alternate +# spelling for parsing only — generator v2 kept it inside this list, which +# silently shifted the derived abbreviations for October-December. +MONTHS_ABBREV = [ + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", +] +_MONTH_NUM: dict[str, int] = {} +for _i, _name in enumerate(MONTHS_FULL): + _MONTH_NUM[_name] = _i + 1 + _MONTH_NUM[MONTHS_ABBREV[_i]] = _i + 1 +_MONTH_NUM["sept"] = 9 +_MONTH_ALT = "|".join(sorted(_MONTH_NUM, key=len, reverse=True)) + +# Docket cadence words -> ledger period types. +CADENCE_TO_PERIOD_TYPE = { + "weekly": "week_ending", + "monthly": "month", + "quarterly": "quarter", + "annual": "year", + "fiscal_year": "fiscal_year", +} + +# ISO country codes as used by observed non-US rows; US uses the Census id. +COUNTRY_GEOGRAPHY = { + "US": {"level": "country", "id": "0100000US", "name": "United States"}, + "CA": {"level": "country", "id": "CA", "name": None}, + "GB": {"level": "country", "id": "GB", "name": None}, + "AU": {"level": "country", "id": "AU", "name": None}, + "JP": {"level": "country", "id": "JP", "name": None}, + "BE": {"level": "country", "id": "BE", "name": None}, +} + +# Wide enough to flag plausible-but-out-of-window years (1899_13, +# 2999_13, 3000_13) without tripping on catalog table ids like 0434. +_YEAR_HINT = re.compile(r"(?:1[89]\d{2}|2\d{3}|30\d{2})") + +# Calendar tokens are only meaningful in a sane modern window; anything +# outside neither strips nor crashes date arithmetic (fy0000, 0000_01, +# week_ending_0001_01_01 were previously accepted or raised). +_YEAR_MIN, _YEAR_MAX = 1900, 2999 + + +def _valid_year(year: int) -> bool: + return _YEAR_MIN <= year <= _YEAR_MAX + +_FY_RE = re.compile(r"fy(\d{4})") +_NUMERIC_DATE_RE = re.compile(r"(\d{4})[-_](\d{2})(?:[-_](\d{2}))?") +_MONTH_NAME_RE = re.compile(r"(%s)_(\d{4})" % _MONTH_ALT) +_MONTH_RANGE_RE = re.compile(r"(%s)_to_(%s)_(\d{4})" % (_MONTH_ALT, _MONTH_ALT)) +_QUARTER_RE = re.compile(r"q([1-4])_(\d{4})|(\d{4})_q([1-4])") +_WEEK_RE = re.compile(r"week_(?:ending_)?(\d{4})[-_](\d{2})[-_](\d{2})") + + +def _month_span(year: int, month: int) -> tuple[dt.date, dt.date] | None: + if not _valid_year(year) or not 1 <= month <= 12: + return None + last = calendar.monthrange(year, month)[1] + return dt.date(year, month, 1), dt.date(year, month, last) + + +def _day(year: int, month: int, day: int) -> dt.date | None: + if not _valid_year(year): + return None + try: + return dt.date(year, month, day) + except ValueError: + return None + + +def parse_period_token(segment: str) -> tuple | None: + """Parse one dotted segment as a period token. + + Returns ``("fiscal_year", year)`` or ``("span", (start, end))`` with + inclusive ``datetime.date`` bounds, or ``None`` when the segment is not + a well-formed period token. ``after_``-qualified compounds + (``after_june_2026``, ``after_mpc_june_2026``) parse as their base + token. Date-shaped strings that denote no real window — ``2026_13``, + ``2026-02-30`` — return ``None``. + + >>> parse_period_token("fy2026") + ('fiscal_year', 2026) + >>> parse_period_token("2026_05") + ('span', (datetime.date(2026, 5, 1), datetime.date(2026, 5, 31))) + >>> parse_period_token("after_mpc_june_2026") + ('span', (datetime.date(2026, 6, 1), datetime.date(2026, 6, 30))) + >>> parse_period_token("week_ending_2026_06_06") + ('span', (datetime.date(2026, 5, 31), datetime.date(2026, 6, 6))) + >>> parse_period_token("2026_13") is None + True + >>> parse_period_token("m3") is None + True + """ + if segment.startswith("after_"): + rest = segment[len("after_"):] + while rest: + parsed = parse_period_token(rest) + if parsed is not None: + return parsed + if "_" not in rest: + return None + rest = rest.split("_", 1)[1] + return None + m = _FY_RE.fullmatch(segment) + if m: + year = int(m.group(1)) + return ("fiscal_year", year) if _valid_year(year) else None + m = _WEEK_RE.fullmatch(segment) + if m: + end = _day(int(m.group(1)), int(m.group(2)), int(m.group(3))) + if end is None: + return None + return ("span", (end - dt.timedelta(days=6), end)) + m = _NUMERIC_DATE_RE.fullmatch(segment) + if m: + year, month = int(m.group(1)), int(m.group(2)) + if m.group(3) is None: + span = _month_span(year, month) + return ("span", span) if span else None + day = _day(year, month, int(m.group(3))) + return ("span", (day, day)) if day else None + m = _MONTH_RANGE_RE.fullmatch(segment) + if m: + first, last = _MONTH_NUM[m.group(1)], _MONTH_NUM[m.group(2)] + year = int(m.group(3)) + if first > last or not _valid_year(year): + return None + start = dt.date(year, first, 1) + end = _month_span(year, last)[1] + return ("span", (start, end)) + m = _MONTH_NAME_RE.fullmatch(segment) + if m: + span = _month_span(int(m.group(2)), _MONTH_NUM[m.group(1)]) + return ("span", span) if span else None + m = _QUARTER_RE.fullmatch(segment) + if m: + quarter = int(m.group(1) or m.group(4)) + year = int(m.group(2) or m.group(3)) + if not _valid_year(year): + return None + start = dt.date(year, 3 * quarter - 2, 1) + end = _month_span(year, 3 * quarter)[1] + return ("span", (start, end)) + return None + + +def is_period_segment(segment: str) -> bool: + """Whether one dotted segment parses as a real period token. + + >>> [is_period_segment(s) for s in ( + ... "2026_05", "2026-05", "feb_2026", "week_ending_2026_06_06", + ... "after_june_2026", "after_mpc_june_2026", "2026_06_18", + ... "february_to_april_2026", "week_2026-06-13", "fy2026", "2026_q2", + ... )] + [True, True, True, True, True, True, True, True, True, True, True] + >>> [is_period_segment(s) for s in ( + ... "36-10-0434-01", "g17", "adv44x72", "j5ii", "m3", "2026_13", + ... "first_print", "third_estimate", "original_submission", + ... "total_nonfarm_payroll_change", "australia", + ... )] + [False, False, False, False, False, False, False, False, False, False, False] + """ + return parse_period_token(segment) is not None + + +def period_token_variants(period: dict) -> set[str]: + """Every direct spelling of ``period`` that may appear as an id segment.""" + ptype, value = period.get("type"), period.get("value") + tokens: set[str] = set() + if value is None: + return tokens + value = str(value) + if ptype == "fiscal_year": + if value.isdigit() and _valid_year(int(value)): + tokens.add(f"fy{value}") + elif ptype == "year": + # Bare years are deliberately not in the token grammar (too + # collision-prone to strip by shape), but a segment spelling the + # row's OWN annual period is a direct variant and must strip, or + # year-suffixed annual ids split into per-year identities. + if value.isdigit() and _valid_year(int(value)): + tokens.add(value) + elif ptype == "month": + m = re.fullmatch(r"(\d{4})-(\d{2})", value) + if m: + year, month = m.group(1), int(m.group(2)) + if not 1 <= month <= 12: + return tokens + tokens.update({f"{year}-{month:02d}", f"{year}_{month:02d}"}) + tokens.add(f"{MONTHS_FULL[month - 1]}_{year}") + tokens.add(f"{MONTHS_ABBREV[month - 1]}_{year}") + elif ptype == "quarter": + m = re.fullmatch(r"(\d{4})-(\d{2})", value) + if m: + year, month = m.group(1), int(m.group(2)) + if not 1 <= month <= 12: + return tokens + quarter = (month - 1) // 3 + 1 + tokens.update({f"q{quarter}_{year}", f"{year}_q{quarter}"}) + elif ptype == "week_ending": + iso = value + tokens.update({ + f"week_{iso}", f"week_{iso.replace('-', '_')}", + f"week_ending_{iso}", f"week_ending_{iso.replace('-', '_')}", + }) + return tokens + + +def period_descriptor(period: dict | None) -> tuple | None: + """The row period as a comparable descriptor (same shapes as tokens).""" + if not period: + return None + ptype = period.get("type") + value = period.get("value") + if value is None: + return None + value = str(value) + if ptype == "fiscal_year": + if value.isdigit() and _valid_year(int(value)): + return ("fiscal_year", int(value)) + return None + if ptype == "month": + m = re.fullmatch(r"(\d{4})-(\d{2})", value) + if m: + span = _month_span(int(m.group(1)), int(m.group(2))) + return ("span", span) if span else None + return None + if ptype == "quarter": + m = re.fullmatch(r"(\d{4})-(\d{2})", value) + if m: + year, month = int(m.group(1)), int(m.group(2)) + if not _valid_year(year) or not 1 <= month <= 12: + return None + quarter = (month - 1) // 3 + 1 + start = dt.date(year, 3 * quarter - 2, 1) + return ("span", (start, _month_span(year, 3 * quarter)[1])) + return None + if ptype == "week_ending": + m = re.fullmatch(r"(\d{4})-(\d{2})-(\d{2})", value) + if m: + end = _day(int(m.group(1)), int(m.group(2)), int(m.group(3))) + if end is None: + return None + return ("span", (end - dt.timedelta(days=6), end)) + return None + if ptype == "year": + if value.isdigit() and _valid_year(int(value)): + year = int(value) + return ("span", (dt.date(year, 1, 1), dt.date(year, 12, 31))) + return None + return None + + +def _fiscal_year_span(year: int) -> tuple[dt.date, dt.date]: + # US federal fiscal year, the only fiscal calendar in the ledger today. + return dt.date(year - 1, 10, 1), dt.date(year, 9, 30) + + +def _matches_period(token: tuple, period_desc: tuple) -> bool: + """Whether a parsed token denotes the row's own period (window overlap). + + Fiscal-year tokens match a fiscal-year period only on equal years; mixed + fiscal/calendar comparisons use the US federal fiscal calendar. + """ + if token[0] == "fiscal_year" and period_desc[0] == "fiscal_year": + return token[1] == period_desc[1] + a = _fiscal_year_span(token[1]) if token[0] == "fiscal_year" else token[1] + b = ( + _fiscal_year_span(period_desc[1]) + if period_desc[0] == "fiscal_year" + else period_desc[1] + ) + return a[0] <= b[1] and b[0] <= a[1] + + +def family_pattern(identifier: str, period: dict | None = None) -> str: + """Replace segments denoting the row's own period with ``{P}``. + + A segment is stripped only when it is a direct spelling of the row + period or parses to a calendar window overlapping it. Date-shaped + segments disjoint from the row period survive (and are flagged by + ``suspect_segments``), so a statute year, cohort, or edition can never + be silently deleted from an identity. + + >>> family_pattern("bls.eci.private_wages_salaries_qoq.2026_q2.first_print", + ... {"type": "quarter", "value": "2026-04"}) + 'bls.eci.private_wages_salaries_qoq.{P}.first_print' + >>> family_pattern("census.m3.durable_goods_new_orders_mom.2026_06", + ... {"type": "month", "value": "2026-06"}) + 'census.m3.durable_goods_new_orders_mom.{P}' + >>> family_pattern("boe.bank_rate.after_mpc_june_2026", + ... {"type": "month", "value": "2026-06"}) + 'boe.bank_rate.{P}' + >>> family_pattern("dol.eta.initial_claims.sa.week_ending_2026_06_06", + ... {"type": "month", "value": "2026-06"}) + 'dol.eta.initial_claims.sa.{P}' + >>> family_pattern("agency.rate.2025_12", {"type": "month", "value": "2026-06"}) + 'agency.rate.2025_12' + >>> family_pattern("abs.cpi.all_groups.yoy", {"type": "month", "value": "2026-05"}) + 'abs.cpi.all_groups.yoy' + """ + return ".".join( + "{P}" if kind in ("derived", "overlap") else segment + for segment, kind in classify_segments(identifier, period) + ) + + +def classify_segments( + identifier: str, period: dict | None = None +) -> list[tuple[str, str]]: + """Classify each dotted segment: ``derived``, ``overlap``, or ``kept``. + + ``derived`` segments are direct spellings of the row period; + ``overlap`` segments parse to a calendar window overlapping it (these + strip too, but are additionally reported in the catalog's + ``overlap_stripped_segments`` audit list — a table, statute, cohort, or + edition label that happens to spell a window overlapping the row's own + period is mechanically indistinguishable from a period label, so every + such strip stays visible for curation); everything else is ``kept``. + """ + derived = period_token_variants(period or {}) + period_desc = period_descriptor(period) + classified: list[tuple[str, str]] = [] + for segment in identifier.split("."): + if segment in derived: + classified.append((segment, "derived")) + continue + token = parse_period_token(segment) + if ( + token is not None + and period_desc is not None + and _matches_period(token, period_desc) + ): + classified.append((segment, "overlap")) + continue + classified.append((segment, "kept")) + return classified + + +def concept_for(pattern: str) -> str: + """The human-facing canonical concept: the pattern minus placeholders.""" + return ".".join(s for s in pattern.split(".") if s != "{P}") + + +def suspect_segments(pattern: str) -> list[str]: + """Surviving segments that smell of a date — flagged, never stripped. + + Covers both period-shaped segments that contradicted the row's own + period (kept in the identity by ``family_pattern``) and free-form + year-bearing segments. + + >>> suspect_segments("agency.rate.2025_12") + ['2025_12'] + >>> suspect_segments("agency.series.mid2026wave") + ['mid2026wave'] + >>> suspect_segments("bls.eci.private_wages_salaries_qoq.{P}.first_print") + [] + """ + return [ + s for s in pattern.split(".") + if s != "{P}" and (is_period_segment(s) or _YEAR_HINT.search(s)) + ] + + +def _geo_key(geography: dict | None) -> str: + """Injective geography key: JSON-encoded, so no delimiter collisions. + + A separator-joined key would let distinct dimension values collide + (level ``a|b`` + id ``c`` vs level ``a`` + id ``b|c``); JSON encoding + escapes everything and keeps None distinct from ``"None"``. + + >>> _geo_key({"level": "a|b", "id": "c"}) == _geo_key( + ... {"level": "a", "id": "b|c"}) + False + >>> _geo_key({"level": "None"}) == _geo_key(None) + False + """ + g = geography or {} + return json.dumps([g.get("level"), g.get("id"), g.get("vintage")]) + + +def _entity_key(entity: dict | None) -> str: + e = entity or {} + return json.dumps([e.get("name"), e.get("role")]) + + +def _identity_geography(geography: dict | None) -> dict | None: + if geography is None: + return None + return { + "level": geography.get("level"), + "id": geography.get("id"), + "vintage": geography.get("vintage"), + } + + +def _identity_entity(entity: dict | None) -> dict | None: + if entity is None: + return None + return {"name": entity.get("name"), "role": entity.get("role")} + + +def canonical_uuid_problem(value: object) -> str | None: + """Why ``value`` is not a canonical lowercase UUIDv4 string, else None.""" + if not isinstance(value, str): + return f"uuid {value!r} is not a string" + try: + parsed = uuid_module.UUID(value) + except ValueError: + return f"uuid {value!r} does not parse" + if str(parsed) != value: + return f"uuid {value!r} is not canonical lowercase form ({parsed})" + if parsed.version != 4: + return f"uuid {value} is not UUIDv4" + return None + + +def build_identities(rows: list[dict]) -> dict[tuple[str, str, str], dict]: + """Group observation rows by (concept, geography, entity) identity.""" + identities: dict[tuple[str, str, str], dict] = {} + for index, row in enumerate(rows): + rid = row.get("source_record_id") + measure = row.get("measure") or {} + concept_raw = measure.get("concept") + if not isinstance(rid, str) or not isinstance(concept_raw, str): + raise SystemExit( + f"observation row {index} missing source_record_id or " + f"measure.concept: {json.dumps(row)[:200]}" + ) + period = row.get("period") or {} + for identifier in (concept_raw, rid): + reserved = _reserved_segment_problem(identifier) + if reserved: + raise SystemExit(f"observation row {index}: {reserved}") + for what, allowed in ( + ("geography", ("level", "id", "vintage", "name")), + ("entity", ("name", "role")), + ): + raw_dim = row.get(what) + if raw_dim in (None, {}): + continue + # Validate the RAW value: "" or [] must fail loudly, never + # collapse into the null identity. + domain = _dimension_problem(raw_dim, allowed, what) + if domain: + raise SystemExit(f"observation row {index}: {domain}") + if ( + period.get("value") is not None + and period_descriptor(period) is None + ): + raise SystemExit( + f"observation row {index} has a malformed period " + f"{json.dumps(period)} — refusing to derive period tokens " + "from it (fix the period type/value upstream)" + ) + pattern = family_pattern(concept_raw, period) + geography = row.get("geography") or None + entity = row.get("entity") or None + key = (concept_for(pattern), _geo_key(geography), _entity_key(entity)) + ident = identities.setdefault( + key, + { + "patterns": set(), + "concepts": set(), + "source_concepts": set(), + "rid_patterns": set(), + "suspects": set(), + "stripped": set(), + "units": Counter(), + "period_types": Counter(), + "geography": geography, + "entity": entity, + "sources": set(), + "period_values": [], + "count": 0, + }, + ) + ident["patterns"].add(pattern) + ident["concepts"].add(concept_raw) + source_concept = measure.get("source_concept") + if isinstance(source_concept, str): + ident["source_concepts"].add(source_concept) + rid_pattern = family_pattern(rid, period) + ident["rid_patterns"].add(rid_pattern) + ident["suspects"].update(suspect_segments(pattern)) + ident["suspects"].update(suspect_segments(rid_pattern)) + for identifier in (concept_raw, rid): + ident["stripped"].update( + segment + for segment, kind in classify_segments(identifier, period) + if kind in ("derived", "overlap") + ) + ident["units"][measure.get("unit")] += 1 + ident["period_types"][period.get("type")] += 1 + source = row.get("source") or {} + if isinstance(source.get("source_name"), str): + ident["sources"].add(source["source_name"]) + if period.get("value") is not None: + ident["period_values"].append(str(period["value"])) + ident["count"] += 1 + return identities + + +def _sole(counter: Counter, what: str, key: tuple) -> object: + """The single value in ``counter`` — conflicts are a hard error.""" + values = [v for v in counter if v is not None] + if len(values) > 1: + raise SystemExit( + f"{what} conflict within identity {key}: {sorted(map(str, values))} " + "— resolve by curation (split or correct upstream); the catalog " + "never picks a modal winner" + ) + return values[0] if values else None + + +class ExistingCatalog: + """Canonical-concept and curated-alias memory from the committed catalog.""" + + def __init__(self, path: pathlib.Path) -> None: + self.rows: list[dict] = [] + self.by_identity: dict[tuple[str, str, str], dict] = {} + self.by_dim: dict[tuple[str, str], list[dict]] = {} + self.docket_rows: list[dict] = [] + if not path.exists(): + return + catalog = json.loads(path.read_text(encoding="utf-8")) + for row in catalog.get("series", []): + self.rows.append(row) + key = ( + row["concept"], + _geo_key(row.get("geography")), + _entity_key(row.get("entity")), + ) + self.by_identity[key] = row + self.by_dim.setdefault((key[1], key[2]), []).append(row) + if row.get("status") == "docket-only": + self.docket_rows.append(row) + + def _row_names(self, row: dict) -> set[str]: + return {row["concept"], *row.get("aliases", [])} + + def match( + self, + key: tuple[str, str, str], + names: set[str], + geography: dict | None, + entity: dict | None, + ) -> dict | None: + """The existing row for this identity. + + Exact identity key first; else a unique concept/curated-alias hit + WITHIN the same (geography, entity) — names heal renames inside one + dimension slice, never across dimensions; else a unique docket-only + placeholder whose declared dimensions do not contradict the incoming + row (the placeholder-enrichment exception). + """ + row = self.by_identity.get(key) + if row is not None: + return row + hits: dict[str, dict] = {} + for candidate in self.by_dim.get((key[1], key[2]), []): + if names & self._row_names(candidate): + hits[candidate["uuid"]] = candidate + if len(hits) > 1: + raise SystemExit( + f"identity {key} matches multiple existing UUIDs via " + f"concept/alias names {sorted(names)}: {sorted(hits)} — " + "curate the existing rows (merge or disambiguate aliases) " + "before regenerating" + ) + if hits: + return next(iter(hits.values())) + placeholder_hits: dict[str, dict] = {} + for candidate in self.docket_rows: + if not names & self._row_names(candidate): + continue + if candidate.get("entity") is not None: + continue + cand_geo = candidate.get("geography") + if cand_geo is not None: + incoming = geography or {} + if (cand_geo.get("level"), cand_geo.get("id")) != ( + incoming.get("level"), + incoming.get("id"), + ): + continue + # A placeholder that DECLARES a boundary vintage only + # enriches observations of that vintage; an undeclared + # vintage (the docket mapping never sets one) is open. + cand_vintage = cand_geo.get("vintage") + if cand_vintage is not None and cand_vintage != ( + incoming.get("vintage") + ): + continue + placeholder_hits[candidate["uuid"]] = candidate + if len(placeholder_hits) > 1: + raise SystemExit( + f"identity {key} matches multiple docket-only placeholders " + f"via names {sorted(names)}: {sorted(placeholder_hits)} — " + "curate the seed/catalog before regenerating" + ) + if placeholder_hits: + return next(iter(placeholder_hits.values())) + return None + + +def _reserved_segment_problem(identifier: object) -> str | None: + """Why an identifier is unusable as a concept/series name, else None.""" + if not isinstance(identifier, str) or not identifier: + return f"identifier {identifier!r} must be a nonempty string" + if "{P}" in identifier.split("."): + return ( + f"identifier {identifier!r} contains the reserved placeholder " + "segment '{P}' — family patterns are derived, never supplied" + ) + return None + + +def _dimension_problem(value: object, allowed: tuple[str, ...], + what: str) -> str | None: + """Geography/entity objects: null, or known keys with nonempty/null + string values (an empty string must never be a distinct identity from + an absent field).""" + if value is None: + return None + if not isinstance(value, dict): + return f"{what} must be an object or null" + for field, field_value in value.items(): + if field not in allowed: + return f"{what}.{field} is not an identity field" + if field_value is not None and ( + not isinstance(field_value, str) or not field_value + ): + return f"{what}.{field} must be a nonempty string or null" + return None + + +def _reject_json_constants(value: str): + raise ValueError(f"JSON constant {value} is not allowed") + + +def _reject_duplicate_keys(pairs: list[tuple[str, object]]) -> dict: + """JSON object hook that refuses duplicate member names. + + Ordinary ``json.loads`` keeps the last value, so a line with two + ``uuid`` members means different things to different parsers. + """ + obj: dict = {} + for key, value in pairs: + if key in obj: + raise ValueError(f"duplicate JSON member {key!r}") + obj[key] = value + return obj + + +def _usable_note(note: object) -> bool: + """A note must say something: several words' worth of real content.""" + if not isinstance(note, str): + return False + return len(note.strip()) >= 8 and sum(c.isalnum() for c in note) >= 4 + + +class UuidRegistry: + """The append-only UUID minting ledger. + + One JSON object per line, binding one identity (concept, geography + level/id/vintage, entity name/role) to a UUID. Five event kinds, chained + per identity and never edited or removed (``--verify-registry-append- + only`` and the git-HEAD prefix check enforce growth-only): + + * mint — the identity's first line; no markers. Its UUID must be new to + the registry: ordinary mints can never reuse another binding's UUID. + * succeeds-mint — a mint carrying ``succeeds`` (the predecessor's + identity fields). The one sanctioned form of UUID reuse: the named + predecessor must already be retired holding exactly that UUID (a + docket placeholder enriched into its observed identity). + * supersede — ``supersedes`` names the previous UUID; ``note`` required; + the new UUID must differ and be new to the registry. + * retire — ``retired: true`` with the unchanged UUID; ``note`` required. + The identity left the catalog; its binding stays reserved but dormant. + * revive — ``revived: true`` with the unchanged UUID; written + automatically when a retired identity is observed again. + + Invariants ``--check`` builds on: live bindings and catalog rows are in + BIJECTION (same identity, same UUID, both directions), and live + bindings' UUIDs are unique by parsed value. Any state that re-keys, + swaps, or shares UUIDs without the explicit events above fails + validation or agreement. + """ + + def __init__(self, path: pathlib.Path, raw: bytes) -> None: + self.path = path + self.raw = raw + self.entries: list[dict] = [] + self.latest: dict[tuple[str, str, str], dict] = {} + live_by_uuid: dict[int, tuple[str, str, str]] = {} + # First owner of each 128-bit value, for the no-reuse rule. + self.uuid_owner: dict[int, tuple[str, str, str]] = {} + # Retired predecessors already consumed by a succeeds event: a + # lineage can be handed over exactly once, never forked. + self.consumed: set[tuple[str, str, str]] = set() + problems: list[str] = [] + if b"\r" in raw: + problems.append("registry must be LF-only (CR byte found)") + if raw and not raw.endswith(b"\n"): + problems.append("registry must end with a newline") + physical_lines = raw.decode("utf-8").split("\n") + if physical_lines and physical_lines[-1] == "": + physical_lines.pop() + for lineno, line in enumerate(physical_lines, start=1): + if not line.strip(): + problems.append(f"line {lineno}: blank line") + continue + try: + entry = json.loads( + line, + object_pairs_hook=_reject_duplicate_keys, + parse_constant=_reject_json_constants, + ) + except (json.JSONDecodeError, ValueError) as exc: + problems.append(f"line {lineno}: not strict JSON ({exc})") + continue + if not isinstance(entry, dict): + problems.append(f"line {lineno}: not an object") + continue + reserved = _reserved_segment_problem(entry.get("concept")) + if reserved: + problems.append(f"line {lineno}: {reserved}") + continue + for what, allowed in ( + ("geography", ("level", "id", "vintage")), + ("entity", ("name", "role")), + ): + domain = _dimension_problem(entry.get(what), allowed, what) + if domain: + problems.append(f"line {lineno}: {domain}") + succeeds_field = entry.get("succeeds") + if isinstance(succeeds_field, dict): + reserved = _reserved_segment_problem( + succeeds_field.get("concept") + ) + if reserved: + problems.append(f"line {lineno}: succeeds {reserved}") + problem = canonical_uuid_problem(entry.get("uuid")) + if problem: + problems.append(f"line {lineno}: {problem}") + continue + key = self.entry_key(entry) + parsed = uuid_module.UUID(entry["uuid"]).int + previous = self.latest.get(key) + supersedes = entry.get("supersedes") + retired = entry.get("retired") + revived = entry.get("revived") + succeeds = entry.get("succeeds") + reclaimed = entry.get("reclaimed") + markers = sum( + 1 + for marker in (supersedes, retired, revived, succeeds, + reclaimed) + if marker is not None + ) + if markers > 1: + problems.append( + f"line {lineno}: {key} mixes " + "supersede/retire/revive/succeeds markers" + ) + elif previous is None and supersedes is None and retired is None \ + and revived is None: + owner = self.uuid_owner.get(parsed) + if succeeds is not None: + succeeds_problem = self._succeeds_problem( + lineno, key, entry, succeeds + ) + if succeeds_problem: + problems.append(succeeds_problem) + elif owner is not None: + problems.append( + f"line {lineno}: mint for {key} reuses uuid " + f"{entry['uuid']} already bound to {owner} — UUID " + "reuse needs an explicit succeeds event" + ) + elif previous is None: + problems.append( + f"line {lineno}: {key} has no prior binding to " + "supersede/retire/revive/reclaim" + ) + elif reclaimed is not None: + if reclaimed is not True: + problems.append(f"line {lineno}: reclaimed must be true") + if not (self._is_retired(previous) and key in self.consumed): + problems.append( + f"line {lineno}: {key} reclaims a lineage that was " + "not handed over — reclaim exists only for keys " + "whose predecessor was consumed by a succeeds event" + ) + if parsed in self.uuid_owner: + problems.append( + f"line {lineno}: reclaim for {key} reuses uuid " + f"{entry['uuid']} (first bound to " + f"{self.uuid_owner[parsed]}) — a reclaimed identity " + "is a NEW series and mints fresh" + ) + if not _usable_note(entry.get("note")): + problems.append( + f"line {lineno}: reclaim for {key} requires a " + "substantive note" + ) + elif supersedes is not None: + if self._is_retired(previous): + problems.append( + f"line {lineno}: {key} supersedes a retired binding " + "(revive it first)" + ) + if supersedes != previous["uuid"]: + problems.append( + f"line {lineno}: {key} supersedes {supersedes} but " + f"prior binding is {previous['uuid']}" + ) + if entry["uuid"] == supersedes: + problems.append( + f"line {lineno}: supersede for {key} is a no-op " + "(uuid equals supersedes)" + ) + elif parsed in self.uuid_owner: + # Never revisit a historical value, even for the same + # identity: U1 -> U2 -> U1 cycles would let the final + # map and the identity anchor "return to normal" while + # hiding the excursion. + problems.append( + f"line {lineno}: supersede for {key} recycles uuid " + f"{entry['uuid']} (first bound to " + f"{self.uuid_owner[parsed]}) — superseding UUIDs " + "must be new to the registry" + ) + if not _usable_note(entry.get("note")): + problems.append( + f"line {lineno}: supersede for {key} requires a " + "substantive note" + ) + elif retired is not None: + if retired is not True: + problems.append(f"line {lineno}: retired must be true") + if self._is_retired(previous): + problems.append( + f"line {lineno}: {key} is already retired" + ) + if entry["uuid"] != previous["uuid"]: + problems.append( + f"line {lineno}: retire for {key} must keep uuid " + f"{previous['uuid']}" + ) + if not _usable_note(entry.get("note")): + problems.append( + f"line {lineno}: retire for {key} requires a " + "substantive note" + ) + elif revived is not None: + if revived is not True: + problems.append(f"line {lineno}: revived must be true") + if not self._is_retired(previous): + problems.append( + f"line {lineno}: {key} revives a live binding" + ) + if key in self.consumed: + # A handed-over lineage is terminal: reviving it would + # re-open the predecessor and recreate a banned + # U1 -> U2 -> U1 excursion through an identity detour. + problems.append( + f"line {lineno}: {key} revives a consumed " + "lineage — handed-over predecessors are terminal" + ) + if entry["uuid"] != previous["uuid"]: + problems.append( + f"line {lineno}: revive for {key} must keep uuid " + f"{previous['uuid']}" + ) + else: + problems.append( + f"line {lineno}: {key} re-binds without supersedes " + f"(prior uuid {previous['uuid']})" + ) + previous_entry = self.latest.get(key) + if previous_entry is not None: + prev_parsed = uuid_module.UUID(previous_entry["uuid"]).int + if not self._is_retired(previous_entry) and ( + live_by_uuid.get(prev_parsed) == key + ): + del live_by_uuid[prev_parsed] + self.entries.append(entry) + self.latest[key] = entry + self.uuid_owner.setdefault(parsed, key) + if not self._is_retired(entry): + other = live_by_uuid.get(parsed) + if other is not None and other != key: + problems.append( + f"line {lineno}: live bindings {other} and {key} " + f"share uuid {entry['uuid']} — uniqueness holds " + "after every event, not just at the end" + ) + live_by_uuid[parsed] = key + if problems: + raise SystemExit( + "uuid registry invalid:\n" + + "\n".join(f" {p}" for p in problems) + ) + + def _succeeds_problem( + self, lineno: int, key: tuple, entry: dict, succeeds: object + ) -> str | None: + if not isinstance(succeeds, dict): + return f"line {lineno}: succeeds must be an identity object" + unknown = set(succeeds) - {"concept", "geography", "entity"} + if unknown: + return ( + f"line {lineno}: succeeds has non-identity fields " + f"{sorted(unknown)}" + ) + for what, allowed in ( + ("geography", ("level", "id", "vintage")), + ("entity", ("name", "role")), + ): + domain = _dimension_problem( + succeeds.get(what), allowed, f"succeeds.{what}" + ) + if domain: + return f"line {lineno}: {domain}" + predecessor_key = self.entry_key(succeeds) + predecessor = self.latest.get(predecessor_key) + if predecessor is None: + return ( + f"line {lineno}: {key} succeeds unknown identity " + f"{predecessor_key}" + ) + if not self._is_retired(predecessor): + return ( + f"line {lineno}: {key} succeeds a LIVE binding " + f"{predecessor_key} — retire it first" + ) + if predecessor_key in self.consumed: + return ( + f"line {lineno}: {key} succeeds {predecessor_key}, whose " + "lineage was already handed over — a predecessor is " + "consumed exactly once, never forked" + ) + if predecessor["uuid"] != entry["uuid"]: + return ( + f"line {lineno}: {key} succeeds {predecessor_key} but " + f"carries uuid {entry['uuid']} != {predecessor['uuid']}" + ) + # succeeds exists for exactly one documented move — a docket + # placeholder enriched into its observed identity — so it must + # look like one: same concept, predecessor entity unknown, + # geography absent or matching on level/id (and vintage when + # declared). + if succeeds.get("concept") != entry["concept"]: + return ( + f"line {lineno}: {key} succeeds a different concept " + f"{succeeds.get('concept')!r} — lineage never crosses " + "concepts" + ) + if succeeds.get("entity") is not None: + return ( + f"line {lineno}: {key} succeeds an identity with a known " + "entity — only entity-less placeholders can be enriched" + ) + pred_geo = succeeds.get("geography") or None + if pred_geo is not None: + entry_geo = entry.get("geography") or {} + if (pred_geo.get("level"), pred_geo.get("id")) != ( + entry_geo.get("level"), + entry_geo.get("id"), + ): + return ( + f"line {lineno}: {key} succeeds an identity in a " + "different geography — lineage never crosses " + "level/id" + ) + pred_vintage = pred_geo.get("vintage") + if pred_vintage is not None and pred_vintage != entry_geo.get( + "vintage" + ): + return ( + f"line {lineno}: {key} succeeds an identity with a " + "conflicting geography vintage" + ) + self.consumed.add(predecessor_key) + return None + + @staticmethod + def _is_retired(entry: dict) -> bool: + return entry.get("retired") is True + + @staticmethod + def entry_key(entry: dict) -> tuple[str, str, str]: + return ( + entry["concept"], + _geo_key(entry.get("geography")), + _entity_key(entry.get("entity")), + ) + + @classmethod + def load(cls, path: pathlib.Path) -> UuidRegistry: + if not path.exists(): + raise SystemExit( + f"uuid registry missing: {path} — the registry is the " + "append-only UUID authority and must exist (create an empty " + "file only when initializing a brand-new catalog)" + ) + return cls(path, path.read_bytes()) + + def binding(self, key: tuple[str, str, str]) -> str | None: + entry = self.latest.get(key) + return entry["uuid"] if entry else None + + def is_live(self, key: tuple[str, str, str]) -> bool: + entry = self.latest.get(key) + return entry is not None and not self._is_retired(entry) + + def live_bindings(self) -> list[tuple[tuple[str, str, str], dict]]: + return [ + (key, entry) + for key, entry in sorted(self.latest.items()) + if not self._is_retired(entry) + ] + + @staticmethod + def render_entry(entry: dict) -> str: + ordered = { + "concept": entry["concept"], + "geography": entry.get("geography"), + "entity": entry.get("entity"), + "uuid": entry["uuid"], + } + if entry.get("supersedes") is not None: + ordered["supersedes"] = entry["supersedes"] + ordered["note"] = entry["note"] + elif entry.get("retired") is not None: + ordered["retired"] = True + ordered["note"] = entry["note"] + elif entry.get("revived") is not None: + ordered["revived"] = True + elif entry.get("succeeds") is not None: + ordered["succeeds"] = entry["succeeds"] + elif entry.get("reclaimed") is not None: + ordered["reclaimed"] = True + ordered["note"] = entry["note"] + return json.dumps(ordered, ensure_ascii=False, allow_nan=False) + + def stage(self, new_entries: list[dict]) -> "UuidRegistry": + """Return a new registry with entries appended and REVALIDATED. + + Revalidation reruns the entire event grammar over the staged bytes, + so no writer path can ever put an invalid chain on disk while + reporting success. + """ + addition = "".join( + self.render_entry(entry) + "\n" for entry in new_entries + ) + staged_raw = self.raw + addition.encode("utf-8") + return UuidRegistry(self.path, staged_raw) + + def write(self) -> None: + self.path.write_bytes(self.raw) + + def sha256(self) -> str: + return hashlib.sha256(self.raw).hexdigest() + + +def _registry_event( + key_concept: str, + geography: dict | None, + entity: dict | None, + row_uuid: str, + supersedes: str | None = None, +) -> dict: + return { + "concept": key_concept, + "geography": _identity_geography(geography), + "entity": _identity_entity(entity), + "uuid": row_uuid, + "supersedes": supersedes, + } + + +def build_catalog( + observations_path: pathlib.Path, + docket_path: pathlib.Path | None, + existing: ExistingCatalog, + registry: UuidRegistry, +) -> tuple[dict, dict]: + """Build the catalog and the identity plan. + + The plan records every registry-affecting outcome: ``mints`` (new + identity bindings to append), ``supersedes`` (identities whose UUID + changes — these require ``--allow-remint``), and ``dropped`` (existing + rows whose UUID would vanish from the catalog — also gated). + """ + raw = observations_path.read_bytes() + rows = [ + json.loads( + line, + object_pairs_hook=_reject_duplicate_keys, + parse_constant=_reject_json_constants, + ) + for line in raw.decode().splitlines() + if line.strip() + ] + identities = build_identities(rows) + + # Canonicalize each observed bucket through the existing catalog: an + # exact identity hit, a same-dimension curated-alias hit, or a + # docket-placeholder hit keeps BOTH the prior UUID lineage and the prior + # canonical concept (curation owns naming; observed spellings become + # aliases). Buckets landing on the same canonical identity merge. + canonical: dict[tuple[str, str, str], dict] = {} + for key in sorted(identities): + ident = identities[key] + concept, geo_key, entity_key = key + names = ident["concepts"] | {concept} + prior = existing.match(key, names, ident["geography"], ident["entity"]) + canon_concept = prior["concept"] if prior else concept + canon_key = (canon_concept, geo_key, entity_key) + bucket = canonical.setdefault( + canon_key, + { + "prior": prior, + "patterns": set(), + "concepts": set(), + "source_concepts": set(), + "rid_patterns": set(), + "suspects": set(), + "stripped": set(), + "units": Counter(), + "period_types": Counter(), + "geography": ident["geography"], + "entity": ident["entity"], + "sources": set(), + "period_values": [], + "count": 0, + }, + ) + if bucket["prior"] is None: + bucket["prior"] = prior + elif prior is not None and prior["uuid"] != bucket["prior"]["uuid"]: + raise SystemExit( + f"identity {canon_key} inherits two different UUIDs " + f"({bucket['prior']['uuid']}, {prior['uuid']}) — curate the " + "existing catalog before regenerating" + ) + bucket["patterns"] |= ident["patterns"] + bucket["concepts"] |= ident["concepts"] + bucket["source_concepts"] |= ident["source_concepts"] + bucket["rid_patterns"] |= ident["rid_patterns"] + bucket["suspects"] |= ident["suspects"] + bucket["stripped"] |= ident["stripped"] + bucket["units"] += ident["units"] + bucket["period_types"] += ident["period_types"] + bucket["sources"] |= ident["sources"] + bucket["period_values"] += ident["period_values"] + bucket["count"] += ident["count"] + + series: list[dict] = [] + used_uuids: dict[int, tuple] = {} + plan: dict[str, list] = { + "enrich_retires": [], + "mints": [], + "revives": [], + "supersedes": [], + "retire_pending": [], + "dropped": [], + } + + def claim_uuid(row_uuid: str, key: tuple) -> str: + problem = canonical_uuid_problem(row_uuid) + if problem: + raise SystemExit(f"identity {key}: {problem}") + parsed = uuid_module.UUID(row_uuid).int + if parsed in used_uuids: + raise SystemExit( + f"UUID collision: {row_uuid} claimed by both " + f"{used_uuids[parsed]} and {key} — curate the existing " + "catalog before regenerating" + ) + used_uuids[parsed] = key + return row_uuid + + def resolve_uuid( + canon_key: tuple[str, str, str], + prior: dict | None, + geography: dict | None, + entity: dict | None, + ) -> str: + binding = registry.binding(canon_key) + prior_uuid = prior["uuid"] if prior else None + if prior_uuid and binding and prior_uuid != binding: + # The catalog row disagrees with the registry: an explicit, + # gated remint (the curator edited the row's uuid on purpose). + # The replacement must be new to the registry outright. + replacement_owner = registry.uuid_owner.get( + uuid_module.UUID(prior_uuid).int + ) + if replacement_owner is not None: + raise SystemExit( + f"identity {canon_key} would supersede to uuid " + f"{prior_uuid}, which the registry already knows " + f"(first bound to {replacement_owner}) — superseding " + "UUIDs must be new" + ) + # A retired binding is revived first so the event chain stays + # valid (revives are staged before supersedes). + if not registry.is_live(canon_key): + plan["revives"].append( + dict( + _registry_event( + canon_key[0], geography, entity, binding + ), + revived=True, + ) + ) + plan["supersedes"].append( + _registry_event( + canon_key[0], geography, entity, prior_uuid, binding + ) + ) + return prior_uuid + if binding and not ( + not registry.is_live(canon_key) and canon_key in registry.consumed + ): + if not registry.is_live(canon_key): + # A retired identity is being observed again: same UUID, + # explicit revive event (no ceremony — resuming an identity + # never changes a binding). + plan["revives"].append( + dict( + _registry_event( + canon_key[0], geography, entity, binding + ), + revived=True, + ) + ) + return binding + if binding: + # The identity handed its lineage to a successor: terminal. + # Anything reappearing under the old key is a NEW series claim + # and mints fresh through an explicit reclaim event; the old + # UUID stays with its lineage. + row_uuid = str(uuid_module.uuid4()) + plan["mints"].append( + dict( + _registry_event(canon_key[0], geography, entity, row_uuid), + reclaimed=True, + note=( + "identity re-established after its lineage was " + "handed over" + ), + ) + ) + return row_uuid + row_uuid = prior_uuid if prior_uuid else str(uuid_module.uuid4()) + owner_key = registry.uuid_owner.get(uuid_module.UUID(row_uuid).int) + if owner_key is not None: + prior_own_key = ( + UuidRegistry.entry_key(prior) if prior is not None else None + ) + prior_geo = (prior or {}).get("geography") or None + geo = geography or {} + enrichment_shaped = ( + prior is not None + and prior.get("status") == "docket-only" + and prior.get("entity") is None + and ( + prior_geo is None + or ( + (prior_geo.get("level"), prior_geo.get("id")) + == (geo.get("level"), geo.get("id")) + and prior_geo.get("vintage") + in (None, geo.get("vintage")) + ) + ) + ) + if ( + enrichment_shaped + and owner_key == prior_own_key + and registry.is_live(owner_key) + ): + # Docket-placeholder enrichment: the binding MOVES to the + # observed identity via an explicit retire + succeeds pair. + # UUID continuity is preserved, so no ceremony flag needed. + plan["enrich_retires"].append( + dict( + _registry_event( + prior["concept"], + prior.get("geography"), + prior.get("entity"), + row_uuid, + ), + retired=True, + note=( + "docket placeholder enriched by first observed " + "identity" + ), + ) + ) + plan["mints"].append( + dict( + _registry_event( + canon_key[0], geography, entity, row_uuid + ), + succeeds={ + "concept": prior["concept"], + "geography": _identity_geography( + prior.get("geography") + ), + "entity": _identity_entity(prior.get("entity")), + }, + ) + ) + return row_uuid + raise SystemExit( + f"identity {canon_key} would mint uuid {row_uuid}, which " + f"the registry already binds to {owner_key} — UUID reuse " + "requires an explicit ceremony (merge via curated alias + " + "--allow-remint, or placeholder enrichment)" + ) + plan["mints"].append( + _registry_event(canon_key[0], geography, entity, row_uuid) + ) + return row_uuid + + all_suspects: set[str] = set() + stripped_map: dict[str, set[str]] = {} + for canon_key in sorted(canonical): + bucket = canonical[canon_key] + concept, _, _ = canon_key + prior = bucket["prior"] + row_uuid = claim_uuid( + resolve_uuid(canon_key, prior, bucket["geography"], bucket["entity"]), + canon_key, + ) + curated_aliases = set(prior.get("aliases", [])) if prior else set() + aliases = sorted((bucket["concepts"] | curated_aliases) - {concept}) + all_suspects.update(bucket["suspects"]) + for segment in bucket["stripped"]: + stripped_map.setdefault(segment, set()).add(concept) + series.append({ + "uuid": row_uuid, + "concept": concept, + "family_patterns": sorted(bucket["patterns"]), + "status": "observed", + "unit": _sole(bucket["units"], "unit", canon_key), + "cadence": _sole(bucket["period_types"], "cadence", canon_key), + "geography": bucket["geography"], + "entity": bucket["entity"], + "sources": sorted(bucket["sources"]), + "aliases": aliases, + "source_concepts": sorted(bucket["source_concepts"]), + "rid_patterns": sorted(bucket["rid_patterns"]), + "first_observed_period": min(bucket["period_values"], default=None), + "last_observed_period": max(bucket["period_values"], default=None), + "observation_count": bucket["count"], + }) + + docket_raw = b"" + if docket_path is not None: + docket_raw = docket_path.read_bytes() + docket = json.loads( + docket_raw.decode(), + object_pairs_hook=_reject_duplicate_keys, + parse_constant=_reject_json_constants, + ) + alias_tally = Counter(alias for row in series for alias in row["aliases"]) + name_rows: dict[str, list[dict]] = {} + for row in series: + name_rows.setdefault(row["concept"], []).append(row) + for alias in row["aliases"]: + if alias_tally[alias] == 1: + name_rows.setdefault(alias, []).append(row) + seen_docket_names: set[str] = set() + for entry in docket["series"]: + concept = entry["series"] + reserved = _reserved_segment_problem(concept) + if reserved: + raise SystemExit(f"docket entry: {reserved}") + if concept in seen_docket_names: + raise SystemExit( + f"duplicate docket series id {concept!r} — docket names " + "must be unique or claiming becomes order-dependent" + ) + seen_docket_names.add(concept) + cadence_word = entry.get("cadence") + if cadence_word not in CADENCE_TO_PERIOD_TYPE: + raise SystemExit( + f"docket cadence {cadence_word!r} for {concept} has no " + "period-type mapping; extend CADENCE_TO_PERIOD_TYPE" + ) + extras = entry.get("extras") or {} + country = extras.get("country") + geography = None + if country is not None: + geography = COUNTRY_GEOGRAPHY.get(country) + if geography is None: + raise SystemExit( + f"docket entry {concept} has country {country!r} with " + "no geography mapping; extend COUNTRY_GEOGRAPHY" + ) + # A name claim only counts within the entry's declared + # dimension: a GB observation must not swallow a US docket + # entry of the same name. + claimants = name_rows.get(concept, []) + if geography is not None: + claimants = [ + row + for row in claimants + if ( + (row.get("geography") or {}).get("level"), + (row.get("geography") or {}).get("id"), + ) == (geography["level"], geography["id"]) + ] + if claimants: + continue + key = (concept, _geo_key(geography), _entity_key(None)) + prior = existing.match(key, {concept}, geography, None) + row_uuid = claim_uuid( + resolve_uuid(key, prior, geography, None), key + ) + curated_aliases = set(prior.get("aliases", [])) if prior else set() + docket_row = { + "uuid": row_uuid, + "concept": concept, + "family_patterns": [family_pattern(concept)], + "status": "docket-only", + "unit": extras.get("targetUnit"), + "cadence": CADENCE_TO_PERIOD_TYPE[cadence_word], + "geography": geography, + "entity": None, + "sources": [], + "aliases": sorted(curated_aliases), + "source_concepts": [], + "rid_patterns": [], + "first_observed_period": None, + "last_observed_period": None, + "observation_count": 0, + } + series.append(docket_row) + name_rows.setdefault(concept, []).append(docket_row) + + series.sort(key=lambda row: ( + row["concept"], + _geo_key(row.get("geography")), + _entity_key(row.get("entity")), + )) + + # Liveness is IDENTITY-AWARE: every live registry binding must have a + # catalog row at exactly its identity carrying exactly its UUID. A + # binding losing that (row deleted, identity re-keyed, observations + # absorbed by a curated merge) needs an explicit retire — the gap that + # let re-keyed identities swap or shed UUIDs while their old values + # lingered elsewhere in the catalog is closed by matching on the pair, + # never on bare UUID membership. + row_uuid_by_key = { + (r["concept"], _geo_key(r.get("geography")), _entity_key(r.get("entity"))): + r["uuid"] + for r in series + } + new_uuids = {r["uuid"] for r in series} + planned_keys = { + UuidRegistry.entry_key(event) + for event in plan["supersedes"] + plan["enrich_retires"] + } + retire_keys: set[tuple[str, str, str]] = set() + for key, entry in registry.live_bindings(): + if row_uuid_by_key.get(key) == entry["uuid"]: + continue + if key in planned_keys: + continue + plan["retire_pending"].append( + dict( + _registry_event( + entry["concept"], + entry.get("geography"), + entry.get("entity"), + entry["uuid"], + ), + retired=True, + ) + ) + retire_keys.add(key) + + # Existing rows whose UUID would vanish without any registry binding to + # retire (handcrafted states only; every written catalog registers). + for prior_key, prior_row in sorted(existing.by_identity.items()): + if prior_key in row_uuid_by_key: + continue + if prior_row["uuid"] in new_uuids: + continue # lineage survives on another identity (merge/enrich) + if prior_key in retire_keys: + continue + plan["dropped"].append((prior_key, prior_row["uuid"])) + + for kind in ( + "enrich_retires", "mints", "revives", "supersedes", "retire_pending", + ): + plan[kind].sort( + key=lambda e: (e["concept"], _geo_key(e["geography"]), + _entity_key(e["entity"])) + ) + + alias_counts = Counter(alias for row in series for alias in row["aliases"]) + ambiguous_aliases = sorted(a for a, n in alias_counts.items() if n > 1) + + catalog = { + "comment": ( + "Canonical series catalog. One row per (concept, geography " + "level/id/vintage, entity) identity. UUID authority is the " + "append-only ledger/series_uuid_registry.jsonl (digest below): " + "a uuid is minted once, inherited from the registry on every " + "regeneration, and changes only through explicit, chained " + "supersede/retire/revive/succeeds events recorded there " + "(live bindings and rows stay in bijection). Consumers " + "reference series by uuid or concept only. Regenerate with " + "scripts/build_series_catalog.py; verify with --check. Aliases " + "are curated identity statements (plus observed spellings of " + "the same identity) and inherit only within one (geography, " + "entity) slice; aliases listed in ambiguous_aliases match " + "multiple rows and never drive inheritance. source_concepts " + "are publisher labels — provenance, never identity. Cross-" + "spelling and cross-vintage merges are manual curation: delete " + "the absorbed row, alias its concept on the survivor, " + "regenerate with --allow-remint." + ), + "generator_version": GENERATOR_VERSION, + "observations_sha256": hashlib.sha256(raw).hexdigest(), + "observation_rows": len(rows), + "docket_seed_sha256": ( + hashlib.sha256(docket_raw).hexdigest() if docket_raw else None + ), + "uuid_registry_sha256": None, + "suspect_segments": sorted(all_suspects), + "stripped_segments": { + segment: sorted(stripped_map[segment]) + for segment in sorted(stripped_map) + }, + "ambiguous_aliases": ambiguous_aliases, + "series": series, + } + return catalog, plan + + +def render(catalog: dict) -> str: + return json.dumps( + catalog, indent=2, ensure_ascii=False, allow_nan=False + ) + "\n" + + +def validate_uuids(catalog: dict) -> list[str]: + """Canonical-form, version, and parsed-value-uniqueness problems.""" + problems: list[str] = [] + seen: dict[int, str] = {} + for row in catalog.get("series", []): + value = row.get("uuid", "") + concept = row.get("concept", "?") + problem = canonical_uuid_problem(value) + if problem: + problems.append(f"{concept}: {problem}") + if not isinstance(value, str): + continue + try: + parsed = uuid_module.UUID(value).int + except ValueError: + continue + else: + parsed = uuid_module.UUID(value).int + if parsed in seen: + problems.append( + f"uuid {value} duplicates {seen[parsed]} (same 128-bit " + f"value) on {concept}" + ) + else: + seen[parsed] = concept + return problems + + +def registry_agreement_problems( + catalog: dict, registry: UuidRegistry +) -> list[str]: + """Catalog/registry disagreements, in both directions. + + Catalog rows and live registry bindings must be in BIJECTION: each + row's identity is bound to exactly its UUID, and each live binding has + a catalog row at exactly its identity with exactly its UUID. Matching + on the (identity, uuid) pair — never on bare UUID membership — is what + makes re-keyed or swapped identities loud. + """ + problems = [] + row_uuid_by_key: dict[tuple[str, str, str], str] = {} + for row in catalog.get("series", []): + key = ( + row["concept"], + _geo_key(row.get("geography")), + _entity_key(row.get("entity")), + ) + row_uuid_by_key[key] = row.get("uuid") + binding = registry.binding(key) + if binding is None: + problems.append(f"{key}: no registry binding for uuid {row['uuid']}") + elif binding != row["uuid"]: + problems.append( + f"{key}: catalog uuid {row['uuid']} != registry binding " + f"{binding}" + ) + elif not registry.is_live(key): + problems.append( + f"{key}: catalog row uses a RETIRED binding " + f"{row['uuid']} (regenerate to record the revive)" + ) + for key, entry in registry.live_bindings(): + if row_uuid_by_key.get(key) != entry["uuid"]: + problems.append( + f"{key}: live binding {entry['uuid']} has no catalog row at " + "its identity — retire or supersede it explicitly" + ) + return problems + + +def git_head_bytes(path: pathlib.Path) -> bytes | None: + """The file's committed HEAD content, or None when unavailable.""" + try: + toplevel = subprocess.run( + ["git", "-C", str(path.resolve().parent), "rev-parse", + "--show-toplevel"], + capture_output=True, text=True, check=True, + ).stdout.strip() + rel = path.resolve().relative_to(pathlib.Path(toplevel)).as_posix() + shown = subprocess.run( + ["git", "-C", toplevel, "show", f"HEAD:{rel}"], + capture_output=True, check=True, + ) + return shown.stdout + except (subprocess.CalledProcessError, FileNotFoundError, ValueError): + return None + + +def append_only_problem(base: bytes, current: bytes) -> str | None: + """Why ``current`` is not an append-only extension of ``base``.""" + if current.startswith(base): + return None + return ( + "registry is not an append-only extension of its prior committed " + "content: existing lines were edited or removed" + ) + + +def _check_registry_vs_head(registry: UuidRegistry) -> None: + head = git_head_bytes(registry.path) + if head is None: + print( + "note: no committed HEAD version of the registry to compare " + "against (new file or not a git checkout)", + file=sys.stderr, + ) + return + problem = append_only_problem(head, registry.raw) + if problem: + raise SystemExit(f"uuid registry vs HEAD: {problem}") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--observations", type=pathlib.Path, default=OBSERVATIONS) + parser.add_argument("--catalog", type=pathlib.Path, default=CATALOG) + parser.add_argument("--registry", type=pathlib.Path, default=UUID_REGISTRY) + parser.add_argument( + "--docket", + type=pathlib.Path, + default=DOCKET_SEED, + help=( + "Thesis docket seed for docket-only rows " + "(default: the committed seed; pass a path to update it)" + ), + ) + parser.add_argument( + "--check", + action="store_true", + help="fail if the committed catalog is not current for these inputs", + ) + parser.add_argument( + "--allow-remint", + action="store_true", + help=( + "permit identities to change or lose their UUID; every change " + "is recorded as a supersede event in the registry" + ), + ) + parser.add_argument( + "--remint-note", + default=None, + help="required with --allow-remint: why the identity change is right", + ) + parser.add_argument( + "--verify-registry-append-only", + type=pathlib.Path, + default=None, + metavar="BASE_FILE", + help=( + "verify the registry extends BASE_FILE byte-for-byte (CI runs " + "this against the PR base's registry), then exit" + ), + ) + args = parser.parse_args(argv) + + if args.verify_registry_append_only is not None: + registry = UuidRegistry.load(args.registry) + base = ( + args.verify_registry_append_only.read_bytes() + if args.verify_registry_append_only.exists() + else b"" + ) + problem = append_only_problem(base, registry.raw) + if problem: + sys.stderr.write(problem + "\n") + return 1 + print( + f"registry append-only vs base: ok " + f"({len(registry.entries)} entries)" + ) + return 0 + + if not args.observations.exists(): + raise SystemExit(f"observations missing: {args.observations}") + if args.docket is None or not args.docket.exists(): + raise SystemExit( + f"docket seed missing: {args.docket} — the seed is a pinned " + "input; a catalog regenerated without it silently drops every " + "docket-only series, so this is always a hard error" + ) + registry = UuidRegistry.load(args.registry) + _check_registry_vs_head(registry) + + existing = ExistingCatalog(args.catalog) + catalog, plan = build_catalog(args.observations, args.docket, existing, registry) + + identity_changes = [ + f"remint {UuidRegistry.entry_key(e)}: {e['supersedes']} -> {e['uuid']}" + for e in plan["supersedes"] + ] + [ + f"retire {UuidRegistry.entry_key(e)}: live binding {e['uuid']} " + "would leave the catalog" + for e in plan["retire_pending"] + ] + [ + f"dropped {key}: uuid {row_uuid} would vanish from the catalog" + for key, row_uuid in plan["dropped"] + ] + if not existing.rows and registry.latest: + # Without the prior catalog, curated naming and aliases are gone: a + # renamed identity re-keys away from its registry binding and would + # fresh-mint while the old binding goes dormant — a silent remint + # that survives every append-only check. Rebuilding from a bare + # registry is therefore itself a gated identity event. + identity_changes.append( + f"prior catalog missing while the registry holds " + f"{len(registry.latest)} bindings — restore " + f"{args.catalog} from git history (rebuilding without curated " + "naming/alias memory can silently re-key renamed identities)" + ) + + if args.check: + failures: list[str] = [] + if plan["mints"]: + failures.append( + f"{len(plan['mints'])} identities missing from the registry " + "(regenerate to mint them)" + ) + if plan["revives"]: + failures.append( + f"{len(plan['revives'])} retired identities observed again " + "(regenerate to record the revive events)" + ) + if plan["enrich_retires"]: + failures.append( + f"{len(plan['enrich_retires'])} docket placeholders enriched " + "by observations (regenerate to record the retire/succeeds " + "events)" + ) + failures.extend(identity_changes) + catalog["uuid_registry_sha256"] = registry.sha256() + body = render(catalog) + current = ( + args.catalog.read_text(encoding="utf-8") + if args.catalog.exists() + else "" + ) + if current != body: + failures.append( + "series_catalog.json is stale for these inputs; regenerate " + "with scripts/build_series_catalog.py" + ) + if current: + committed = json.loads(current) + for problem in validate_uuids(committed): + failures.append(f"uuid validation: {problem}") + for problem in registry_agreement_problems(committed, registry): + failures.append(f"registry agreement: {problem}") + if committed.get("docket_seed_sha256") is None: + failures.append("committed catalog has no docket seed digest") + if failures: + for failure in failures: + sys.stderr.write(failure + "\n") + return 1 + print(f"catalog current: {len(catalog['series'])} series") + return 0 + + if identity_changes and not args.allow_remint: + for change in identity_changes: + sys.stderr.write(f"identity change requires --allow-remint: " + f"{change}\n") + sys.stderr.write( + "refusing to write: existing identities would change or lose " + "their UUID; rerun with --allow-remint --remint-note '...' if " + "this is deliberate curation\n" + ) + return 1 + if args.allow_remint: + if not (args.remint_note and args.remint_note.strip()): + raise SystemExit("--allow-remint requires --remint-note") + for event in plan["supersedes"] + plan["retire_pending"]: + event["note"] = args.remint_note.strip() + + problems = validate_uuids(catalog) + if problems: + for problem in problems: + sys.stderr.write(f"uuid validation: {problem}\n") + return 1 + + registry = registry.stage( + plan["enrich_retires"] + + plan["mints"] + + plan["revives"] + + plan["supersedes"] + + plan["retire_pending"] + ) + catalog["uuid_registry_sha256"] = registry.sha256() + body = render(catalog) + + agreement = registry_agreement_problems(catalog, registry) + if agreement: + for problem in agreement: + sys.stderr.write(f"registry agreement: {problem}\n") + return 1 + + registry.write() + args.catalog.write_text(body, encoding="utf-8") + observed = sum(1 for r in catalog["series"] if r["status"] == "observed") + docket_only = sum(1 for r in catalog["series"] if r["status"] == "docket-only") + print( + f"wrote {args.catalog}: {len(catalog['series'])} series " + f"({observed} observed, {docket_only} docket-only); " + f"suspects={len(catalog['suspect_segments'])}, " + f"ambiguous_aliases={len(catalog['ambiguous_aliases'])}, " + f"minted={len(plan['mints'])}, superseded={len(plan['supersedes'])}" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_build_series_catalog.py b/tests/test_build_series_catalog.py new file mode 100644 index 0000000..c64a486 --- /dev/null +++ b/tests/test_build_series_catalog.py @@ -0,0 +1,1627 @@ +"""Regression tests for the series-catalog generator. + +Sections mirror the adversarial reviews of the first two generators: the +token-grammar findings (live period spellings, identifiers that must never +be stripped), and the v2 identity findings (wholesale reminting invisible +to --check, cross-dimension alias inheritance, source labels treated as +identity, string-keyed UUID uniqueness, silent seed loss, mismatched +period tokens silently stripped). +""" + +from __future__ import annotations + +import doctest +import json +import pathlib +import sys + +import pytest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "scripts")) + +import build_series_catalog as bsc # noqa: E402 + +PERIOD_SEGMENTS = [ + "fy2026", + "2026-05", + "2026_05", + "2026-05-02", + "2026_06_18", + "may_2026", + "feb_2026", + "sept_2026", + "q1_2026", + "2026_q2", + "week_2026-06-13", + "week_2026_06_13", + "week_ending_2026_06_06", + "february_to_april_2026", + "after_june_2026", + "after_mpc_june_2026", +] + +NON_PERIOD_SEGMENTS = [ + "36-10-0434-01", # StatCan table id + "g17", + "adv44x72", + "j5ii", + "m3", + "2026_13", # date-shaped noise: no such month + "2026-02-30", # date-shaped noise: no such day + "first_print", + "third_estimate", + "original_submission", + "australia", + "total_nonfarm_payroll_change", +] + + +@pytest.mark.parametrize("segment", PERIOD_SEGMENTS) +def test_period_segments_recognized(segment: str) -> None: + assert bsc.is_period_segment(segment), segment + + +@pytest.mark.parametrize("segment", NON_PERIOD_SEGMENTS) +def test_non_period_segments_retained(segment: str) -> None: + assert not bsc.is_period_segment(segment), segment + + +def test_abbreviated_months_index_correctly() -> None: + # Generator v2 kept "sept" inside MONTHS_ABBREV, shifting the derived + # abbreviations for October-December. + variants = bsc.period_token_variants({"type": "month", "value": "2026-10"}) + assert "oct_2026" in variants and "october_2026" in variants + variants = bsc.period_token_variants({"type": "month", "value": "2026-12"}) + assert "dec_2026" in variants + + +MONTH_2026_06 = {"type": "month", "value": "2026-06"} + + +def test_semantic_pass_strips_unseen_spelling() -> None: + # A spelling derivable from the row period is the period. + assert ( + bsc.family_pattern("agency.rate.june_2026", MONTH_2026_06) + == "agency.rate.{P}" + ) + + +@pytest.mark.parametrize( + ("identifier", "period", "expected"), + [ + # Finer-grained tokens inside the row's declared period strip. + ( + "dol.eta.initial_claims.sa.week_ending_2026_06_06", + MONTH_2026_06, + "dol.eta.initial_claims.sa.{P}", + ), + ("boe.bank_rate.2026-06-18", MONTH_2026_06, "boe.bank_rate.{P}"), + ( + "boe.bank_rate.after_mpc_june_2026", + MONTH_2026_06, + "boe.bank_rate.{P}", + ), + # A month-range whose window covers the row period strips. + ( + "ons.labour.unemployment_rate.february_to_april_2026", + {"type": "month", "value": "2026-04"}, + "ons.labour.unemployment_rate.{P}", + ), + ( + "bls.eci.private_wages_salaries_qoq.2026_q2.first_print", + {"type": "quarter", "value": "2026-04"}, + "bls.eci.private_wages_salaries_qoq.{P}.first_print", + ), + ("fns.snap.rate.fy2024", {"type": "fiscal_year", "value": 2024}, + "fns.snap.rate.{P}"), + ], +) +def test_tokens_matching_row_period_strip(identifier, period, expected) -> None: + assert bsc.family_pattern(identifier, period) == expected + + +@pytest.mark.parametrize( + ("identifier", "period"), + [ + # Disjoint from the row period: a real date, but not THIS row's. + ("agency.rate.2025_12", MONTH_2026_06), + ("agency.rate.week_ending_2026_01_03", MONTH_2026_06), + ("fns.snap.rate.fy2023", {"type": "fiscal_year", "value": 2024}), + ], +) +def test_mismatched_tokens_flagged_not_stripped(identifier, period) -> None: + pattern = bsc.family_pattern(identifier, period) + assert pattern == identifier # kept in the identity + assert bsc.suspect_segments(pattern) == [identifier.rsplit(".", 1)[1]] + + +def test_suspect_segments_flag_but_never_strip() -> None: + pattern = bsc.family_pattern("agency.series.mid2026wave") + assert pattern == "agency.series.mid2026wave" + assert bsc.suspect_segments(pattern) == ["mid2026wave"] + + +US = {"level": "country", "id": "0100000US", "vintage": "current", + "name": "United States"} +CALIFORNIA = {"level": "state", "id": "0400000US06", "vintage": "current", + "name": "California"} +BRITAIN = {"level": "country", "id": "GB", "vintage": "current", "name": None} + + +def _row( + concept: str, + *, + rid: str | None = None, + unit: str = "percent", + period: dict | None = None, + geography: dict | None = None, + entity: dict | None = None, + source_concept: str | None = None, +) -> dict: + period = period or {"type": "month", "value": "2026-05"} + return { + "value": 1.0, + "observed_at": "2026-06-01", + "period": period, + "geography": geography or dict(US), + "entity": entity or {"name": "economy", "role": "aggregate"}, + "measure": { + "concept": concept, + "unit": unit, + **({"source_concept": source_concept} if source_concept else {}), + }, + "source": {"source_name": "test"}, + "source_record_id": rid or f"{concept}.first_print", + } + + +def _registry(tmp_path: pathlib.Path, entries: list[dict] | None = None): + path = tmp_path / "registry.jsonl" + if not path.exists() or entries is not None: + body = "".join( + bsc.UuidRegistry.render_entry(e) + "\n" for e in (entries or []) + ) + path.write_text(body, encoding="utf-8") + return bsc.UuidRegistry.load(path) + + +def _build( + tmp_path: pathlib.Path, + rows: list[dict], + existing: dict | None = None, + registry_entries: list[dict] | None = None, + docket: dict | None = None, +): + observations = tmp_path / "obs.jsonl" + observations.write_text( + "".join(json.dumps(r) + "\n" for r in rows), encoding="utf-8" + ) + catalog_path = tmp_path / "catalog.json" + if existing is not None: + catalog_path.write_text(json.dumps(existing) + "\n", encoding="utf-8") + docket_path = None + if docket is not None: + docket_path = tmp_path / "seed.json" + docket_path.write_text(json.dumps(docket), encoding="utf-8") + return bsc.build_catalog( + observations, + docket_path, + bsc.ExistingCatalog(catalog_path), + _registry(tmp_path, registry_entries), + ) + + +def test_geography_splits_identity(tmp_path: pathlib.Path) -> None: + rows = [ + _row("fns.snap.error_rate"), + _row("fns.snap.error_rate", geography=dict(CALIFORNIA)), + ] + catalog, plan = _build(tmp_path, rows) + assert len(catalog["series"]) == 2 + ids = {(r["geography"] or {}).get("id") for r in catalog["series"]} + assert ids == {"0100000US", "0400000US06"} + assert len(plan["mints"]) == 2 + + +def test_geography_vintage_splits_identity(tmp_path: pathlib.Path) -> None: + v1 = {"level": "state", "id": "0400000US06", "vintage": "2020"} + v2 = {"level": "state", "id": "0400000US06", "vintage": "2024"} + rows = [ + _row("fns.snap.error_rate", geography=v1), + _row("fns.snap.error_rate", geography=v2, + period={"type": "month", "value": "2026-06"}), + ] + catalog, _ = _build(tmp_path, rows) + assert len(catalog["series"]) == 2 + forward = [r["geography"]["vintage"] for r in catalog["series"]] + catalog_reversed, _ = _build(tmp_path, list(reversed(rows))) + assert forward == [ + r["geography"]["vintage"] for r in catalog_reversed["series"] + ] + assert sorted(forward) == ["2020", "2024"] + + +def test_cross_geography_name_match_never_inherits( + tmp_path: pathlib.Path, +) -> None: + # v2 repro: a prior US row plus a later California-only observation of + # the same concept silently moved the US UUID to California. + first, _ = _build(tmp_path, [_row("fns.snap.error_rate")]) + us_uuid = first["series"][0]["uuid"] + later, plan = _build( + tmp_path, + [_row("fns.snap.error_rate", geography=dict(CALIFORNIA))], + existing=first, + ) + assert later["series"][0]["uuid"] != us_uuid + # The US identity's UUID vanishing is loud, not silent. + assert [key for key, _ in plan["dropped"]] == [ + ( + "fns.snap.error_rate", + bsc._geo_key(US), + bsc._entity_key({"name": "economy", "role": "aggregate"}), + ) + ] + + +def test_new_geography_added_incrementally(tmp_path: pathlib.Path) -> None: + # v2 failed this with an ambiguous global name match once a concept had + # several prior geographies. + states = [dict(CALIFORNIA), {"level": "state", "id": "0400000US36", + "vintage": "current"}] + rows = [_row("fns.snap.error_rate")] + [ + _row("fns.snap.error_rate", geography=g) for g in states + ] + first, first_plan = _build(tmp_path, rows) + assert len(first["series"]) == 3 + added = rows + [ + _row( + "fns.snap.error_rate", + geography={"level": "state", "id": "0400000US48", + "vintage": "current"}, + ) + ] + second, plan = _build( + tmp_path, added, existing=first, + registry_entries=first_plan["mints"], + ) + assert len(second["series"]) == 4 + old = {r["uuid"] for r in first["series"]} + assert old < {r["uuid"] for r in second["series"]} + # Only the genuinely new geography mints; the registered three inherit. + assert len(plan["mints"]) == 1 and not plan["dropped"] + assert plan["mints"][0]["geography"]["id"] == "0400000US48" + + +def test_source_concept_never_drives_inheritance( + tmp_path: pathlib.Path, +) -> None: + # v2 repro: agency.rate_b with agency.rate_a's source label silently + # became (or merged into) agency.rate_a. + first, _ = _build( + tmp_path, [_row("agency.rate_a", source_concept="OFFICIAL_SHARED")] + ) + a_uuid = first["series"][0]["uuid"] + assert first["series"][0]["aliases"] == [] + assert first["series"][0]["source_concepts"] == ["OFFICIAL_SHARED"] + solo_b, _ = _build( + tmp_path, + [_row("agency.rate_b", source_concept="OFFICIAL_SHARED")], + existing=first, + ) + assert [r["concept"] for r in solo_b["series"]] == ["agency.rate_b"] + assert solo_b["series"][0]["uuid"] != a_uuid + both, _ = _build( + tmp_path, + [ + _row("agency.rate_a", source_concept="OFFICIAL_SHARED"), + _row("agency.rate_b", source_concept="OFFICIAL_SHARED"), + ], + existing=first, + ) + concepts = [r["concept"] for r in both["series"]] + assert concepts == ["agency.rate_a", "agency.rate_b"] + assert both["series"][0]["uuid"] == a_uuid + assert both["series"][0]["observation_count"] == 1 # no silent merge + + +def test_curated_alias_inherits_within_dimensions( + tmp_path: pathlib.Path, +) -> None: + first, _ = _build(tmp_path, [_row("abs.cpi.all_groups.yoy")]) + row = first["series"][0] + row["aliases"] = ["abs.cpi_indicator.allgroups.yoy"] + merged, plan = _build( + tmp_path, + [_row("abs.cpi_indicator.allgroups.yoy")], + existing=first, + ) + assert merged["series"][0]["uuid"] == row["uuid"] + assert merged["series"][0]["concept"] == "abs.cpi.all_groups.yoy" + assert "abs.cpi_indicator.allgroups.yoy" in merged["series"][0]["aliases"] + assert not plan["dropped"] and not plan["supersedes"] + + +def test_curated_alias_does_not_inherit_across_geography( + tmp_path: pathlib.Path, +) -> None: + first, _ = _build(tmp_path, [_row("boe.bank_rate")]) + first["series"][0]["aliases"] = ["bank_rate.official"] + stolen, _ = _build( + tmp_path, + [_row("bank_rate.official", geography=dict(BRITAIN))], + existing=first, + ) + assert stolen["series"][0]["uuid"] != first["series"][0]["uuid"] + + +def test_ambiguous_alias_match_is_a_hard_error(tmp_path: pathlib.Path) -> None: + existing = { + "series": [ + { + "uuid": "11111111-1111-4111-8111-111111111111", + "concept": "a.one", + "geography": dict(US), + "entity": {"name": "economy", "role": "aggregate"}, + "aliases": ["SHARED"], + }, + { + "uuid": "22222222-2222-4222-8222-222222222222", + "concept": "a.two", + "geography": dict(US), + "entity": {"name": "economy", "role": "aggregate"}, + "aliases": ["SHARED"], + }, + ] + } + with pytest.raises(SystemExit, match="multiple existing UUIDs"): + _build(tmp_path, [_row("SHARED")], existing=existing) + + +def test_docket_placeholder_enrichment_keeps_uuid( + tmp_path: pathlib.Path, +) -> None: + placeholder_uuid = "33333333-3333-4333-8333-333333333333" + existing = { + "series": [ + { + "uuid": placeholder_uuid, + "concept": "census.m3.new_orders", + "geography": {"level": "country", "id": "0100000US", + "name": "United States"}, + "entity": None, + "aliases": [], + "status": "docket-only", + } + ] + } + catalog, plan = _build( + tmp_path, [_row("census.m3.new_orders")], existing=existing + ) + assert len(catalog["series"]) == 1 + row = catalog["series"][0] + assert row["uuid"] == placeholder_uuid + assert row["status"] == "observed" + assert row["entity"] == {"name": "economy", "role": "aggregate"} + assert not plan["dropped"] # lineage survives on the enriched identity + + +def test_docket_placeholder_never_enriches_across_country( + tmp_path: pathlib.Path, +) -> None: + existing = { + "series": [ + { + "uuid": "44444444-4444-4444-8444-444444444444", + "concept": "labour.unemployment_rate", + "geography": {"level": "country", "id": "0100000US", + "name": "United States"}, + "entity": None, + "aliases": [], + "status": "docket-only", + } + ] + } + catalog, _ = _build( + tmp_path, + [_row("labour.unemployment_rate", geography=dict(BRITAIN))], + existing=existing, + ) + observed = [r for r in catalog["series"] if r["status"] == "observed"] + assert observed[0]["uuid"] != "44444444-4444-4444-8444-444444444444" + + +def test_unit_conflict_is_a_hard_error(tmp_path: pathlib.Path) -> None: + rows = [ + _row("fed.rate", unit="percent"), + _row( + "fed.rate", + unit="index_points", + period={"type": "month", "value": "2026-06"}, + ), + ] + with pytest.raises(SystemExit, match="unit conflict"): + _build(tmp_path, rows) + + +def test_uuid_validation_requires_canonical_and_parsed_uniqueness() -> None: + base = "abcd1234-ab12-4ab1-8ab1-abcd1234abcd" + catalog = { + "series": [ + {"uuid": "not-a-uuid", "concept": "a"}, + {"uuid": base, "concept": "b"}, + {"uuid": base, "concept": "c"}, + {"uuid": base.upper(), "concept": "d"}, + {"uuid": base.replace("-", ""), "concept": "e"}, + {"uuid": "{" + base + "}", "concept": "f"}, + ] + } + problems = bsc.validate_uuids(catalog) + assert any("does not parse" in p for p in problems) + # Every non-canonical spelling is rejected AND still counted as the + # same 128-bit value. + assert sum("not canonical lowercase" in p for p in problems) == 3 + assert sum("same 128-bit value" in p for p in problems) == 4 + + +def test_registry_chain_validation(tmp_path: pathlib.Path) -> None: + path = tmp_path / "registry.jsonl" + mint = {"concept": "a.one", "geography": None, "entity": None, + "uuid": "11111111-1111-4111-8111-111111111111"} + rebind = {"concept": "a.one", "geography": None, "entity": None, + "uuid": "22222222-2222-4222-8222-222222222222"} + path.write_text( + json.dumps(mint) + "\n" + json.dumps(rebind) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="re-binds without supersedes"): + bsc.UuidRegistry.load(path) + chained = dict(rebind, supersedes=mint["uuid"], note="curated remint") + path.write_text( + json.dumps(mint) + "\n" + json.dumps(chained) + "\n", encoding="utf-8" + ) + registry = bsc.UuidRegistry.load(path) + key = ("a.one", bsc._geo_key(None), bsc._entity_key(None)) + assert registry.binding(key) == rebind["uuid"] + wrong_chain = dict( + rebind, supersedes="99999999-9999-4999-8999-999999999999", note="x" + ) + path.write_text( + json.dumps(mint) + "\n" + json.dumps(wrong_chain) + "\n", + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="prior binding is"): + bsc.UuidRegistry.load(path) + no_note = dict(rebind, supersedes=mint["uuid"]) + path.write_text( + json.dumps(mint) + "\n" + json.dumps(no_note) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="requires a substantive note"): + bsc.UuidRegistry.load(path) + + +def test_registry_append_only_check() -> None: + base = b'{"concept": "a", "uuid": "x"}\n' + assert bsc.append_only_problem(base, base) is None + assert bsc.append_only_problem(base, base + b'{"more": 1}\n') is None + assert bsc.append_only_problem(base, b'{"edited": true}\n') is not None + assert bsc.append_only_problem(base, b"") is not None + + +SEED = { + "series": [ + { + "series": "abs.labour.unemployment_rate", + "cadence": "monthly", + "extras": {"country": "AU", "targetUnit": "percent"}, + } + ] +} + + +def _repo(tmp_path: pathlib.Path, rows: list[dict], seed: dict = SEED): + (tmp_path / "obs.jsonl").write_text( + "".join(json.dumps(r) + "\n" for r in rows), encoding="utf-8" + ) + (tmp_path / "seed.json").write_text(json.dumps(seed), encoding="utf-8") + (tmp_path / "registry.jsonl").write_text("", encoding="utf-8") + return [ + "--observations", str(tmp_path / "obs.jsonl"), + "--catalog", str(tmp_path / "catalog.json"), + "--docket", str(tmp_path / "seed.json"), + "--registry", str(tmp_path / "registry.jsonl"), + ] + + +def test_main_builds_and_is_byte_idempotent( + tmp_path: pathlib.Path, capsys +) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + catalog_1 = (tmp_path / "catalog.json").read_bytes() + registry_1 = (tmp_path / "registry.jsonl").read_bytes() + assert bsc.main(argv) == 0 + assert (tmp_path / "catalog.json").read_bytes() == catalog_1 + assert (tmp_path / "registry.jsonl").read_bytes() == registry_1 + assert bsc.main(argv + ["--check"]) == 0 + assert "catalog current: 2 series" in capsys.readouterr().out + + +def test_main_missing_seed_is_a_hard_error(tmp_path: pathlib.Path) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + (tmp_path / "seed.json").unlink() + with pytest.raises(SystemExit, match="docket seed missing"): + bsc.main(argv) + with pytest.raises(SystemExit, match="docket seed missing"): + bsc.main(argv + ["--check"]) + + +def test_main_missing_registry_is_a_hard_error( + tmp_path: pathlib.Path, +) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + (tmp_path / "registry.jsonl").unlink() + with pytest.raises(SystemExit, match="uuid registry missing"): + bsc.main(argv) + + +def test_main_remint_guard_and_ceremony(tmp_path: pathlib.Path) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + catalog = json.loads((tmp_path / "catalog.json").read_text()) + observed = next( + r for r in catalog["series"] if r["status"] == "observed" + ) + old_uuid = observed["uuid"] + observed["uuid"] = "55555555-5555-4555-8555-555555555555" + (tmp_path / "catalog.json").write_text( + json.dumps(catalog, indent=2) + "\n", encoding="utf-8" + ) + registry_before = (tmp_path / "registry.jsonl").read_bytes() + # Refuses without the flag, and writes nothing. + assert bsc.main(argv) == 1 + assert (tmp_path / "registry.jsonl").read_bytes() == registry_before + # A pending remint also fails --check. + assert bsc.main(argv + ["--check"]) == 1 + with pytest.raises(SystemExit, match="requires --remint-note"): + bsc.main(argv + ["--allow-remint"]) + assert ( + bsc.main(argv + ["--allow-remint", "--remint-note", "test remint"]) + == 0 + ) + lines = (tmp_path / "registry.jsonl").read_text().splitlines() + event = json.loads(lines[-1]) + assert event["supersedes"] == old_uuid + assert event["uuid"] == "55555555-5555-4555-8555-555555555555" + assert event["note"] == "test remint" + assert bsc.main(argv + ["--check"]) == 0 + + +def test_main_dropped_identity_retires_then_revives( + tmp_path: pathlib.Path, +) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + original = json.loads((tmp_path / "catalog.json").read_text()) + docket_uuid = next( + r["uuid"] for r in original["series"] if r["status"] == "docket-only" + ) + (tmp_path / "seed.json").write_text( + json.dumps({"series": []}), encoding="utf-8" + ) + assert bsc.main(argv) == 1 # docket-only row would vanish + assert bsc.main(argv + ["--allow-remint", "--remint-note", "seed cut"]) == 0 + catalog = json.loads((tmp_path / "catalog.json").read_text()) + assert len(catalog["series"]) == 1 + # The drop is an explicit retire event; no line was edited or removed. + lines = (tmp_path / "registry.jsonl").read_text().splitlines() + assert len(lines) == 3 + retire = json.loads(lines[-1]) + assert retire["retired"] is True and retire["uuid"] == docket_uuid + assert retire["note"] == "seed cut" + assert bsc.main(argv + ["--check"]) == 0 + # Re-seeding the identity revives the SAME uuid — minted once, ever. + (tmp_path / "seed.json").write_text(json.dumps(SEED), encoding="utf-8") + assert bsc.main(argv) == 0 + catalog = json.loads((tmp_path / "catalog.json").read_text()) + revived_row = next( + r for r in catalog["series"] if r["status"] == "docket-only" + ) + assert revived_row["uuid"] == docket_uuid + lines = (tmp_path / "registry.jsonl").read_text().splitlines() + assert len(lines) == 4 + revive = json.loads(lines[-1]) + assert revive["revived"] is True and revive["uuid"] == docket_uuid + assert bsc.main(argv + ["--check"]) == 0 + + +def test_partial_catalog_deletion_cannot_silently_remint( + tmp_path: pathlib.Path, +) -> None: + # The v3-review follow-up: deleting SOME rows (not the whole catalog) + # must not let their identities re-mint silently — every live binding's + # uuid has to stay in the catalog or be explicitly retired. + argv = _repo( + tmp_path, + [_row("bls.cps.unemployment_rate"), _row("bea.real_gdp.saar")], + ) + assert bsc.main(argv) == 0 + catalog = json.loads((tmp_path / "catalog.json").read_text()) + kept = [r for r in catalog["series"] if r["concept"] != "bea.real_gdp.saar"] + removed_uuid = next( + r["uuid"] for r in catalog["series"] + if r["concept"] == "bea.real_gdp.saar" + ) + catalog["series"] = kept + (tmp_path / "catalog.json").write_text( + json.dumps(catalog, indent=2) + "\n", encoding="utf-8" + ) + # The observations still exist, so rebuilding restores the row with its + # registry uuid — but a tampered catalog alone must fail --check on the + # liveness rule before any rebuild. + problems = bsc.registry_agreement_problems( + catalog, bsc.UuidRegistry.load(tmp_path / "registry.jsonl") + ) + assert any(removed_uuid in p and "no catalog row" in p for p in problems) + assert bsc.main(argv + ["--check"]) == 1 + # Rebuild heals: the registry still holds the binding. + assert bsc.main(argv) == 0 + healed = json.loads((tmp_path / "catalog.json").read_text()) + assert any(r["uuid"] == removed_uuid for r in healed["series"]) + + +def test_dimension_keys_are_injective() -> None: + # Delimiter-joined keys let crafted values collide across fields. + assert bsc._geo_key( + {"level": "a|b", "id": "c", "vintage": None} + ) != bsc._geo_key({"level": "a", "id": "b|c", "vintage": None}) + assert bsc._geo_key({"level": "None"}) != bsc._geo_key(None) + assert bsc._entity_key( + {"name": 'x", "y', "role": None} + ) != bsc._entity_key({"name": "x", "role": "y"}) + + +def test_check_rejects_uuid_disjoint_catalog(tmp_path: pathlib.Path) -> None: + # v2 repro: two UUID-disjoint catalogs for the same inputs both passed + # --check. The registry now pins the bindings. + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + body = (tmp_path / "catalog.json").read_text() + catalog = json.loads(body) + for i, row in enumerate(catalog["series"]): + row["uuid"] = f"7777777{i}-7777-4777-8777-777777777777" + (tmp_path / "catalog.json").write_text( + json.dumps(catalog, indent=2) + "\n", encoding="utf-8" + ) + assert bsc.main(argv + ["--check"]) == 1 + + +def test_verify_registry_append_only_mode( + tmp_path: pathlib.Path, capsys +) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + registry = tmp_path / "registry.jsonl" + base = tmp_path / "base.jsonl" + base.write_bytes(registry.read_bytes()) + verify = ["--registry", str(registry), + "--verify-registry-append-only", str(base)] + assert bsc.main(verify) == 0 + assert "append-only vs base: ok" in capsys.readouterr().out + # An absent base (registry introduced by this change) is fine. + assert bsc.main( + ["--registry", str(registry), + "--verify-registry-append-only", str(tmp_path / "nope.jsonl")] + ) == 0 + # Any edit to committed lines fails. + lines = registry.read_text().splitlines() + first = json.loads(lines[0]) + first["uuid"] = "66666666-6666-4666-8666-666666666666" + registry.write_text( + "\n".join([json.dumps(first)] + lines[1:]) + "\n", encoding="utf-8" + ) + assert bsc.main(verify) == 1 + + +def test_doctests_pass() -> None: + results = doctest.testmod(bsc) + assert results.failed == 0 + + +def test_committed_catalog_is_current_and_valid() -> None: + """The committed artifact must regenerate byte-identically from the + committed inputs, agree with the committed registry binding-for-binding, + and carry canonical, parsed-unique UUIDv4s.""" + committed_text = bsc.CATALOG.read_text(encoding="utf-8") + committed = json.loads(committed_text) + registry = bsc.UuidRegistry.load(bsc.UUID_REGISTRY) + catalog, plan = bsc.build_catalog( + bsc.OBSERVATIONS, + bsc.DOCKET_SEED, + bsc.ExistingCatalog(bsc.CATALOG), + registry, + ) + assert not plan["mints"] and not plan["supersedes"] and not plan["dropped"] + catalog["uuid_registry_sha256"] = registry.sha256() + assert bsc.render(catalog) == committed_text + assert bsc.validate_uuids(committed) == [] + assert bsc.registry_agreement_problems(committed, registry) == [] + assert committed["suspect_segments"] == [] + # EVERY stripped spelling is auditable, mapped to the canonical + # concepts it touched — a statute or edition label colliding with a + # period spelling can only be caught here. + assert sorted(committed["stripped_segments"]) == [ + "2026-05", "2026-06", "2026-06-18", "2026-07", "2026_05", + "2026_06", "2026_06_18", "2026_q2", "after_june_2026", + "after_mpc_june_2026", "april_2026", "feb_2026", + "february_to_april_2026", "fy2024", "fy2025", "june_2026", + "may_2026", "q1_2026", "week_2026-06-13", "week_2026-06-20", + "week_2026-06-27", "week_2026-07-04", "week_2026-07-11", + "week_2026-07-18", "week_2026-07-25", "week_2026_06_13", + "week_ending_2026_06_06", + ] + assert committed["stripped_segments"]["after_mpc_june_2026"] == [ + "boe.bank_rate" + ] + assert all( + occurrences and occurrences == sorted(set(occurrences)) + for occurrences in committed["stripped_segments"].values() + ) + assert committed["docket_seed_sha256"] is not None + assert committed["uuid_registry_sha256"] == registry.sha256() + assert bsc.DOCKET_SEED.exists() + assert len(committed["series"]) == 201 + + +def test_rebuild_without_prior_catalog_is_gated( + tmp_path: pathlib.Path, +) -> None: + # Deleting the committed catalog loses curated naming/alias memory: a + # renamed identity would re-key away from its registry binding and + # fresh-mint silently. The builder must treat a bare-registry rebuild + # as an identity event. + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + (tmp_path / "catalog.json").unlink() + assert bsc.main(argv) == 1 + assert bsc.main( + argv + ["--allow-remint", "--remint-note", "rebuild from registry"] + ) == 0 + + +def _mint(concept: str, uuid: str, geography=None, entity=None) -> dict: + return { + "concept": concept, + "geography": geography, + "entity": entity, + "uuid": uuid, + } + + +U1 = "aaaaaaaa-1111-4111-8111-111111111111" +U2 = "bbbbbbbb-2222-4222-8222-222222222222" + + +def test_mint_never_reuses_a_bound_uuid(tmp_path: pathlib.Path) -> None: + # Third-review repro: re-key identities and swap their prior-catalog + # UUIDs — both "new" identities would previously mint the swapped + # values as ordinary mints. + registry_entries = [ + _mint("old.one", U1, entity={"name": "economy", "role": "aggregate"}), + _mint("old.two", U2, entity={"name": "economy", "role": "aggregate"}), + ] + existing = { + "series": [ + { + "uuid": U2, # swapped + "concept": "new.one", + "geography": dict(US), + "entity": {"name": "economy", "role": "aggregate"}, + "aliases": [], + "status": "observed", + }, + { + "uuid": U1, # swapped + "concept": "new.two", + "geography": dict(US), + "entity": {"name": "economy", "role": "aggregate"}, + "aliases": [], + "status": "observed", + }, + ] + } + with pytest.raises(SystemExit, match="already binds"): + _build( + tmp_path, + [_row("new.one"), _row("new.two")], + existing=existing, + registry_entries=registry_entries, + ) + + +def test_registry_rejects_forged_shared_uuid_mint( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + path.write_text( + json.dumps(_mint("a.one", U1)) + "\n" + + json.dumps(_mint("a.two", U1)) + "\n", + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="reuses uuid"): + bsc.UuidRegistry.load(path) + + +def test_registry_rejects_duplicate_json_members( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + line = ( + '{"concept": "a.one", "geography": null, "entity": null, ' + f'"uuid": "{U1}", "uuid": "{U2}"}}' + ) + path.write_text(line + "\n", encoding="utf-8") + with pytest.raises(SystemExit, match="not strict JSON"): + bsc.UuidRegistry.load(path) + + +def test_registry_requires_lf_discipline(tmp_path: pathlib.Path) -> None: + path = tmp_path / "registry.jsonl" + body = json.dumps(_mint("a.one", U1)) + path.write_bytes(body.encode()) # no trailing newline + with pytest.raises(SystemExit, match="end with a newline"): + bsc.UuidRegistry.load(path) + path.write_bytes(body.encode() + b"\r\n") + with pytest.raises(SystemExit, match="LF-only"): + bsc.UuidRegistry.load(path) + + +def test_registry_rejects_hollow_notes_and_noop_supersedes( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + mint = _mint("a.one", U1) + hollow = dict(_mint("a.one", U2), supersedes=U1, note="​") + path.write_text( + json.dumps(mint) + "\n" + json.dumps(hollow) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="substantive note"): + bsc.UuidRegistry.load(path) + noop = dict(_mint("a.one", U1), supersedes=U1, note="says nothing changed") + path.write_text( + json.dumps(mint) + "\n" + json.dumps(noop) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="no-op"): + bsc.UuidRegistry.load(path) + + +def test_agreement_is_identity_aware(tmp_path: pathlib.Path) -> None: + # Rows carrying live UUIDs under the WRONG identities must fail even + # though every UUID is "present somewhere" in the catalog. + entries = [_mint("a.one", U1), _mint("a.two", U2)] + registry = _registry(tmp_path, entries) + catalog = { + "series": [ + {"uuid": U1, "concept": "b.one", "geography": None, "entity": None}, + {"uuid": U2, "concept": "b.two", "geography": None, "entity": None}, + ] + } + problems = bsc.registry_agreement_problems(catalog, registry) + assert sum("no catalog row at its identity" in p for p in problems) == 2 + + +def test_enrichment_records_retire_and_succeeds_events( + tmp_path: pathlib.Path, +) -> None: + seed = { + "series": [ + { + "series": "census.m3.new_orders", + "cadence": "monthly", + "extras": {"country": "US", "targetUnit": "percent"}, + } + ] + } + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")], seed=seed) + assert bsc.main(argv) == 0 + catalog = json.loads((tmp_path / "catalog.json").read_text()) + placeholder_uuid = next( + r["uuid"] for r in catalog["series"] if r["status"] == "docket-only" + ) + (tmp_path / "obs.jsonl").write_text( + "".join( + json.dumps(r) + "\n" + for r in [ + _row("bls.cps.unemployment_rate"), + _row("census.m3.new_orders"), + ] + ), + encoding="utf-8", + ) + assert bsc.main(argv) == 0 # enrichment needs no ceremony flag + catalog = json.loads((tmp_path / "catalog.json").read_text()) + enriched = next( + r for r in catalog["series"] if r["concept"] == "census.m3.new_orders" + ) + assert enriched["status"] == "observed" + assert enriched["uuid"] == placeholder_uuid + lines = [ + json.loads(line) + for line in (tmp_path / "registry.jsonl").read_text().splitlines() + ] + retire = next(line for line in lines if line.get("retired")) + succeed = next(line for line in lines if line.get("succeeds")) + assert retire["uuid"] == succeed["uuid"] == placeholder_uuid + assert succeed["succeeds"]["concept"] == "census.m3.new_orders" + assert bsc.main(argv + ["--check"]) == 0 + + +def test_placeholder_with_conflicting_vintage_never_enriches( + tmp_path: pathlib.Path, +) -> None: + existing = { + "series": [ + { + "uuid": U1, + "concept": "labour.rate", + "geography": {"level": "country", "id": "0100000US", + "vintage": "2020"}, + "entity": None, + "aliases": [], + "status": "docket-only", + } + ] + } + catalog, plan = _build( + tmp_path, + [_row("labour.rate")], # arrives with vintage "current" + existing=existing, + registry_entries=[ + _mint( + "labour.rate", + U1, + geography={"level": "country", "id": "0100000US", + "vintage": "2020"}, + ) + ], + ) + observed = next(r for r in catalog["series"] if r["status"] == "observed") + assert observed["uuid"] != U1 + # The stranded placeholder binding is a gated retire, never silent. + assert [e["uuid"] for e in plan["retire_pending"]] == [U1] + + +def test_docket_claim_is_dimension_scoped(tmp_path: pathlib.Path) -> None: + gb_row = _row("boe.bank_rate", geography=dict(BRITAIN)) + us_entry = { + "series": "boe.bank_rate", + "cadence": "monthly", + "extras": {"country": "US", "targetUnit": "percent"}, + } + catalog, _ = _build(tmp_path, [gb_row], docket={"series": [us_entry]}) + assert len(catalog["series"]) == 2 # GB observed + US docket-only + statuses = { + (r["geography"] or {}).get("id"): r["status"] for r in catalog["series"] + } + assert statuses == {"GB": "observed", "0100000US": "docket-only"} + + undeclared = {"series": "boe.bank_rate", "cadence": "monthly"} + catalog, _ = _build(tmp_path, [gb_row], docket={"series": [undeclared]}) + assert len(catalog["series"]) == 1 # no country claim: GB row claims + + with pytest.raises(SystemExit, match="duplicate docket series id"): + _build( + tmp_path, + [gb_row], + docket={"series": [undeclared, dict(undeclared)]}, + ) + + +def test_statute_spelling_stripped_but_audited( + tmp_path: pathlib.Path, +) -> None: + # A statute/edition label spelling the row's own period is + # indistinguishable from a period label; it strips, but the spelling + # is published — mapped to every canonical concept it touched, so a + # second occurrence is visible rather than absorbed into a set. + catalog, _ = _build( + tmp_path, + [ + _row("agency.statute.2026_05.rate", + rid="agency.statute.2026_05.rate.first_print"), + _row("other.report.2026_05.level", + rid="other.report.2026_05.level.first_print"), + ], + ) + concepts = {r["concept"] for r in catalog["series"]} + assert concepts == {"agency.statute.rate", "other.report.level"} + assert catalog["stripped_segments"]["2026_05"] == [ + "agency.statute.rate", "other.report.level", + ] + + +@pytest.mark.parametrize( + "period", + [ + {"type": "month", "value": "2026-13"}, + {"type": "quarter", "value": "2026-13"}, + {"type": "week_ending", "value": "2026-13-40"}, + ], +) +def test_malformed_periods_are_hard_errors( + tmp_path: pathlib.Path, period: dict +) -> None: + with pytest.raises(SystemExit, match="malformed period"): + _build(tmp_path, [_row("agency.rate", period=period)]) + + +def test_remint_of_retired_identity_stages_valid_chain( + tmp_path: pathlib.Path, +) -> None: + # Third-review repro: retire an identity, then bring it back with an + # edited prior-catalog UUID under --allow-remint. The writer must + # stage revive THEN supersede (a valid chain) — and staging always + # revalidates the whole registry before anything is written. + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + assert bsc.main(argv) == 0 + (tmp_path / "seed.json").write_text( + json.dumps({"series": []}), encoding="utf-8" + ) + assert bsc.main( + argv + ["--allow-remint", "--remint-note", "seed entry cut"] + ) == 0 + (tmp_path / "seed.json").write_text(json.dumps(SEED), encoding="utf-8") + catalog = json.loads((tmp_path / "catalog.json").read_text()) + # Hand-plant a divergent uuid for the returning docket identity. + replacement = "cccccccc-3333-4333-8333-333333333333" + lines = (tmp_path / "registry.jsonl").read_text().splitlines() + retired_uuid = json.loads(lines[-1])["uuid"] + catalog["series"].append( + { + "uuid": replacement, + "concept": "abs.labour.unemployment_rate", + "geography": bsc.COUNTRY_GEOGRAPHY["AU"], + "entity": None, + "aliases": [], + "status": "docket-only", + } + ) + (tmp_path / "catalog.json").write_text( + json.dumps(catalog, indent=2) + "\n", encoding="utf-8" + ) + assert bsc.main(argv) == 1 # gated + assert ( + bsc.main(argv + ["--allow-remint", "--remint-note", "planned swap"]) + == 0 + ) + events = [ + json.loads(line) + for line in (tmp_path / "registry.jsonl").read_text().splitlines() + ] + revive = events[-2] + supersede = events[-1] + assert revive["revived"] is True and revive["uuid"] == retired_uuid + assert supersede["supersedes"] == retired_uuid + assert supersede["uuid"] == replacement + # The staged file reloads cleanly and the catalog checks green. + bsc.UuidRegistry.load(tmp_path / "registry.jsonl") + assert bsc.main(argv + ["--check"]) == 0 + + +def test_identity_uuid_map_matches_reviewed_anchor() -> None: + """The registry's introduction commit cannot be continuity-checked by + the append-only gate (there is no prior registry to extend), so the + identity->uuid map verified by the adversarial review of PR #128 is + pinned here. Changing ANY of the 201 bindings — or adding/retiring + one — must edit this constant in the same diff, making wholesale + remints impossible to slip through as regeneration noise. + + Update the constant only alongside registry events that justify it. + """ + import hashlib + + catalog = json.loads(bsc.CATALOG.read_text(encoding="utf-8")) + lines = sorted( + json.dumps( + [ + row["uuid"], + row["concept"], + bsc._geo_key(row.get("geography")), + bsc._entity_key(row.get("entity")), + ] + ) + for row in catalog["series"] + ) + digest = hashlib.sha256("\n".join(lines).encode()).hexdigest() + assert digest == ( + "2d2552e88662eb654597999ac92f750cadbed882703324c6f9ca1f1e3ac15345" + ) + + +def test_supersede_never_recycles_a_historical_uuid( + tmp_path: pathlib.Path, +) -> None: + # Fourth-review repro: U1 -> U2 -> U1 restored the original map while + # hiding the excursion in two "valid" events. + path = tmp_path / "registry.jsonl" + mint = _mint("a.one", U1) + away = dict(_mint("a.one", U2), supersedes=U1, note="planned change one") + back = dict(_mint("a.one", U1), supersedes=U2, note="planned change two") + path.write_text( + "".join(json.dumps(e) + "\n" for e in (mint, away, back)), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="recycles uuid"): + bsc.UuidRegistry.load(path) + + +def test_succeeds_lineage_is_consumed_once_and_dimension_bound( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + mint = _mint("a.one", U1) + retire = dict(_mint("a.one", U1), retired=True, note="placeholder done") + succ = {"concept": "a.one", "geography": None, "entity": None} + + def entry(concept, uuid, **kw): + return dict(_mint(concept, uuid), **kw) + + # Fork: two successors of one predecessor. + b = entry("a.one", U1, succeeds=succ, + entity={"name": "economy", "role": "aggregate"}) + b_away = dict( + entry("a.one", U2, entity={"name": "economy", "role": "aggregate"}), + supersedes=U1, note="moved along again", + ) + c = entry("a.one", U1, succeeds=succ, + entity={"name": "person", "role": "aggregate"}) + path.write_text( + "".join(json.dumps(e) + "\n" for e in (mint, retire, b, b_away, c)), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="consumed exactly once"): + bsc.UuidRegistry.load(path) + + # Cross-concept lineage transfer. + other = entry("b.two", U1, succeeds=succ) + path.write_text( + "".join(json.dumps(e) + "\n" for e in (mint, retire, other)), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="never crosses concepts"): + bsc.UuidRegistry.load(path) + + # Predecessor with a known entity is not a placeholder. + known = _mint("a.one", U1, + entity={"name": "economy", "role": "aggregate"}) + known_retire = dict(known, retired=True, note="placeholder done") + successor = entry( + "a.one", U1, + succeeds={"concept": "a.one", "geography": None, + "entity": {"name": "economy", "role": "aggregate"}}, + ) + path.write_text( + "".join( + json.dumps(e) + "\n" for e in (known, known_retire, successor) + ), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="entity-less placeholders"): + bsc.UuidRegistry.load(path) + + +def test_literal_placeholder_segment_is_reserved( + tmp_path: pathlib.Path, +) -> None: + with pytest.raises(SystemExit, match="reserved placeholder"): + _build(tmp_path, [_row("agency.statute.{P}.rate")]) + with pytest.raises(SystemExit, match="reserved placeholder"): + _build( + tmp_path, + [_row("agency.rate", rid="agency.rate.{P}.first_print")], + ) + + +@pytest.mark.parametrize( + "segment", + ["fy0000", "fy0001", "0000_01", "week_ending_0001_01_01", "q1_0000"], +) +def test_out_of_window_calendar_tokens_neither_strip_nor_crash( + segment: str, +) -> None: + assert bsc.parse_period_token(segment) is None + pattern = bsc.family_pattern( + f"agency.rate.{segment}", {"type": "month", "value": "2026-05"} + ) + assert pattern == f"agency.rate.{segment}" + + +def test_bare_year_variant_strips_only_for_annual_rows( + tmp_path: pathlib.Path, +) -> None: + rows = [ + _row("agency.annual.total.2025", + rid="agency.annual.total.2025.final", + period={"type": "year", "value": "2025"}), + _row("agency.annual.total.2026", + rid="agency.annual.total.2026.final", + period={"type": "year", "value": "2026"}), + ] + catalog, _ = _build(tmp_path, rows) + assert [r["concept"] for r in catalog["series"]] == [ + "agency.annual.total" + ] + assert catalog["series"][0]["observation_count"] == 2 + # A bare year that is NOT the row's own period never strips. + pattern = bsc.family_pattern( + "agency.annual.total.2019", {"type": "year", "value": "2025"} + ) + assert pattern == "agency.annual.total.2019" + + +def test_consumed_lineage_is_terminal(tmp_path: pathlib.Path) -> None: + # Fifth-review repro: retire A -> B succeeds A -> B moves to U2 -> + # retire B -> revive A restored the banned U1 excursion via detour. + path = tmp_path / "registry.jsonl" + succ = {"concept": "a.one", "geography": None, "entity": None} + events = [ + _mint("a.one", U1), + dict(_mint("a.one", U1), retired=True, note="placeholder done"), + dict(_mint("a.one", U1), succeeds=succ, + entity={"name": "economy", "role": "aggregate"}), + dict(_mint("a.one", U2, + entity={"name": "economy", "role": "aggregate"}), + supersedes=U1, note="moved along deliberately"), + dict(_mint("a.one", U2, + entity={"name": "economy", "role": "aggregate"}), + retired=True, note="successor done too"), + dict(_mint("a.one", U1), revived=True), + ] + path.write_text( + "".join(json.dumps(e) + "\n" for e in events), encoding="utf-8" + ) + with pytest.raises(SystemExit, match="terminal"): + bsc.UuidRegistry.load(path) + + +def test_builder_mints_fresh_for_consumed_identity( + tmp_path: pathlib.Path, +) -> None: + # Observations reappearing under a handed-over identity are a NEW + # series claim: fresh uuid, no revival of the terminal lineage. + succ = {"concept": "census.m3.new_orders", "geography": None, + "entity": None} + entries = [ + _mint("census.m3.new_orders", U1), + dict(_mint("census.m3.new_orders", U1), retired=True, + note="placeholder enriched"), + dict(_mint("census.m3.new_orders", U1), succeeds=succ, + geography={"level": "country", "id": "0100000US", + "vintage": "current"}, + entity={"name": "economy", "role": "aggregate"}), + ] + rows = [ + _row("census.m3.new_orders"), + _row("census.m3.new_orders", + geography=None if False else {"level": "country", "id": "GB", + "vintage": "current"}, + rid="census.m3.new_orders.gb.first_print"), + ] + catalog, plan = _build(tmp_path, rows, registry_entries=entries) + us_row = next( + r for r in catalog["series"] + if (r["geography"] or {}).get("id") == "0100000US" + ) + gb_row = next( + r for r in catalog["series"] + if (r["geography"] or {}).get("id") == "GB" + ) + assert us_row["uuid"] == U1 # the enriched successor identity + assert gb_row["uuid"] not in (U1, U2) # fresh, never the old lineage + assert len(plan["revives"]) == 0 + + +def test_live_uuid_uniqueness_holds_per_event_prefix( + tmp_path: pathlib.Path, +) -> None: + # Two live holders of one uuid mid-sequence must fail even if a later + # event would "fix" the final state. + path = tmp_path / "registry.jsonl" + events = [ + _mint("a.one", U1), + _mint("a.two", U2), + # a.two jumps onto U1 while a.one still holds it live... + dict(_mint("a.two", U1), supersedes=U2, note="deliberate theft"), + # ...and a.one is retired only afterwards. + dict(_mint("a.one", U1), retired=True, note="too late to matter"), + ] + path.write_text( + "".join(json.dumps(e) + "\n" for e in events), encoding="utf-8" + ) + with pytest.raises(SystemExit, match="after every event"): + bsc.UuidRegistry.load(path) + + +def test_reserved_placeholder_rejected_in_docket_and_registry( + tmp_path: pathlib.Path, +) -> None: + with pytest.raises(SystemExit, match="reserved placeholder"): + _build( + tmp_path, + [_row("agency.rate")], + docket={"series": [{"series": "agency.{P}.rate", + "cadence": "monthly"}]}, + ) + path = tmp_path / "registry.jsonl" + path.write_text( + json.dumps(_mint("agency.{P}.rate", U1)) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="reserved placeholder"): + bsc.UuidRegistry.load(path) + + +def test_registry_rejects_json_constants_and_empty_dimensions( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + nan_line = ( + '{"concept": "a.one", "geography": {"level": "country", ' + f'"id": "US", "vintage": NaN}}, "entity": null, "uuid": "{U1}"}}' + ) + path.write_text(nan_line + "\n", encoding="utf-8") + with pytest.raises(SystemExit, match="not strict JSON"): + bsc.UuidRegistry.load(path) + empty_vintage = _mint( + "a.one", U1, + geography={"level": "country", "id": "US", "vintage": ""}, + ) + path.write_text(json.dumps(empty_vintage) + "\n", encoding="utf-8") + with pytest.raises(SystemExit, match="nonempty string or null"): + bsc.UuidRegistry.load(path) + + +def test_observation_dimension_domains_are_enforced( + tmp_path: pathlib.Path, +) -> None: + bad = _row("agency.rate") + bad["geography"] = {"level": "country", "id": "0100000US", "vintage": ""} + with pytest.raises(SystemExit, match="nonempty string or null"): + _build(tmp_path, [bad]) + + +@pytest.mark.parametrize( + ("identifier", "period"), + [ + ("agency.rate.january_1899", MONTH_2026_06), + ("agency.rate.january_3000", MONTH_2026_06), + ], +) +def test_boundary_year_month_names_neither_strip_nor_crash( + identifier: str, period: dict +) -> None: + assert bsc.family_pattern(identifier, period) == identifier + + +@pytest.mark.parametrize("value", ["1899-01", "3000-01"]) +def test_out_of_window_quarter_periods_are_malformed( + tmp_path: pathlib.Path, value: str +) -> None: + with pytest.raises(SystemExit, match="malformed period"): + _build( + tmp_path, + [_row("agency.rate", period={"type": "quarter", "value": value})], + ) + + +@pytest.mark.parametrize("segment", ["1899_13", "2999_13", "3000_13"]) +def test_out_of_window_impossible_tokens_are_flagged(segment: str) -> None: + pattern = bsc.family_pattern(f"agency.rate.{segment}", MONTH_2026_06) + assert pattern == f"agency.rate.{segment}" + assert bsc.suspect_segments(pattern) == [segment] + + +def test_reclaimed_mint_grammar(tmp_path: pathlib.Path) -> None: + path = tmp_path / "registry.jsonl" + succ = {"concept": "a.one", "geography": None, "entity": None} + base = [ + _mint("a.one", U1), + dict(_mint("a.one", U1), retired=True, note="placeholder done"), + dict(_mint("a.one", U1), succeeds=succ, + entity={"name": "economy", "role": "aggregate"}), + ] + fresh = "dddddddd-4444-4444-8444-444444444444" + good = dict(_mint("a.one", fresh), reclaimed=True, + note="re-established after handover") + path.write_text( + "".join(json.dumps(e) + "\n" for e in base + [good]), + encoding="utf-8", + ) + registry = bsc.UuidRegistry.load(path) + assert registry.binding(("a.one", bsc._geo_key(None), + bsc._entity_key(None))) == fresh + # Reclaim of a merely-retired (not consumed) key is invalid. + unconsumed = [ + _mint("b.two", U2), + dict(_mint("b.two", U2), retired=True, note="ordinary retirement"), + dict(_mint("b.two", fresh), reclaimed=True, + note="not a handover case"), + ] + path.write_text( + "".join(json.dumps(e) + "\n" for e in unconsumed), encoding="utf-8" + ) + with pytest.raises(SystemExit, match="not handed over"): + bsc.UuidRegistry.load(path) + # Reclaim must mint fresh, never reuse. + reuse = dict(_mint("a.one", U1), reclaimed=True, + note="tries to take U1 back") + path.write_text( + "".join(json.dumps(e) + "\n" for e in base + [reuse]), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="mints fresh"): + bsc.UuidRegistry.load(path) + + +def test_consumed_key_reclaims_fresh_lineage(tmp_path: pathlib.Path) -> None: + # Sixth-review repro: a docket entry re-forming a handed-over key must + # take an explicit reclaim path (previously it staged a markerless + # mint that its own validator rejected). + succ = {"concept": "census.m3.new_orders", "geography": None, + "entity": None} + entries = [ + _mint("census.m3.new_orders", U1), + dict(_mint("census.m3.new_orders", U1), retired=True, + note="placeholder enriched"), + dict(_mint("census.m3.new_orders", U1), succeeds=succ, + geography={"level": "country", "id": "0100000US", + "vintage": "current"}, + entity={"name": "economy", "role": "aggregate"}), + _mint("bls.cps.unemployment_rate", U2, + geography={"level": "country", "id": "0100000US", + "vintage": "current"}, + entity={"name": "economy", "role": "aggregate"}), + ] + existing = { + "series": [ + { + "uuid": U2, + "concept": "bls.cps.unemployment_rate", + "geography": dict(US), + "entity": {"name": "economy", "role": "aggregate"}, + "aliases": [], + "status": "observed", + } + ] + } + observations = tmp_path / "obs.jsonl" + observations.write_text( + json.dumps(_row("bls.cps.unemployment_rate")) + "\n", + encoding="utf-8", + ) + docket_path = tmp_path / "seed.json" + docket_path.write_text( + json.dumps({"series": [{"series": "census.m3.new_orders", + "cadence": "monthly"}]}), + encoding="utf-8", + ) + catalog_path = tmp_path / "catalog.json" + catalog_path.write_text(json.dumps(existing) + "\n", encoding="utf-8") + registry = _registry(tmp_path, entries) + catalog, plan = bsc.build_catalog( + observations, docket_path, bsc.ExistingCatalog(catalog_path), registry + ) + docket_row = next( + r for r in catalog["series"] if r["status"] == "docket-only" + ) + assert docket_row["uuid"] != U1 # fresh lineage, old uuid stays put + reclaim = next(e for e in plan["mints"] if e.get("reclaimed")) + assert reclaim["uuid"] == docket_row["uuid"] + # The enriched identity has no row in this synthetic catalog, so its + # live binding is a gated retire — and the staged whole must reload. + staged = registry.stage( + plan["enrich_retires"] + plan["mints"] + plan["revives"] + + plan["supersedes"] + + [dict(e, note="synthetic gate approval") + for e in plan["retire_pending"]] + ) + assert staged.entries[-1] is not None + + +def test_docket_seed_rejects_json_constants(tmp_path: pathlib.Path) -> None: + argv = _repo(tmp_path, [_row("bls.cps.unemployment_rate")]) + (tmp_path / "seed.json").write_text( + '{"series": [{"series": "a.b", "cadence": "monthly", ' + '"extras": {"valueScale": NaN}}]}', + encoding="utf-8", + ) + with pytest.raises(ValueError, match="not allowed"): + bsc.main(argv) + + +def test_observation_duplicate_members_rejected( + tmp_path: pathlib.Path, +) -> None: + row = _row("agency.rate") + line = json.dumps(row) + line = line.replace( + '"geography": {', '"geography": {"level": "state", ', 1 + ) + observations = tmp_path / "obs.jsonl" + observations.write_text( + line.replace('"geography": {"level": "state", "level"', + '"geography": {"level": "state", "level"') + "\n", + encoding="utf-8", + ) + registry = _registry(tmp_path) + with pytest.raises(ValueError, match="duplicate JSON member"): + bsc.build_catalog( + observations, None, + bsc.ExistingCatalog(tmp_path / "catalog.json"), registry, + ) + + +@pytest.mark.parametrize( + ("field", "value"), + [("geography", ""), ("geography", []), ("entity", []), ("entity", "x")], +) +def test_falsey_or_nonobject_dimensions_rejected( + tmp_path: pathlib.Path, field: str, value +) -> None: + bad = _row("agency.rate") + bad[field] = value + with pytest.raises(SystemExit, match="must be an object or null"): + _build(tmp_path, [bad]) + + +def test_succeeds_schema_is_enforced(tmp_path: pathlib.Path) -> None: + path = tmp_path / "registry.jsonl" + base = [ + _mint("a.one", U1), + dict(_mint("a.one", U1), retired=True, note="placeholder done"), + ] + forged = dict( + _mint("a.one", U1, entity={"name": "economy", "role": "aggregate"}), + succeeds={"concept": "a.one", "geography": None, "entity": None, + "extra": "field"}, + ) + path.write_text( + "".join(json.dumps(e) + "\n" for e in base + [forged]), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="non-identity fields"): + bsc.UuidRegistry.load(path) + bad_geo = dict( + _mint("a.one", U1, entity={"name": "economy", "role": "aggregate"}), + succeeds={"concept": "a.one", + "geography": {"level": "country", "id": ""}, + "entity": None}, + ) + path.write_text( + "".join(json.dumps(e) + "\n" for e in base + [bad_geo]), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="nonempty string or null"): + bsc.UuidRegistry.load(path) + + +def test_registry_rejects_hidden_line_separators( + tmp_path: pathlib.Path, +) -> None: + path = tmp_path / "registry.jsonl" + two_on_one = ( + json.dumps(_mint("a.one", U1)) + + "
" + + json.dumps(_mint("a.two", U2)) + ) + path.write_text(two_on_one + "\n", encoding="utf-8") + with pytest.raises(SystemExit, match="not strict JSON"): + bsc.UuidRegistry.load(path) + with_vt = ( + json.dumps(_mint("a.one", U1)) + + "\x0b" + + json.dumps(_mint("a.two", U2)) + ) + path.write_bytes(with_vt.encode("utf-8") + b"\n") + with pytest.raises(SystemExit, match="not strict JSON"): + bsc.UuidRegistry.load(path)