Repository navigation
Conversation
…y ID Schema 8 copies every provider, category, geography and dimension label onto every target row, and Microcosm checks only that the facts inside one target agree. Published files therefore give one identifier two labels: 51 US states as "Maine" and "ME" (766b7c7), the AGI band `under_1` as "No adjusted gross income" and "No adjusted gross income and deficit" (0fb05b6), and 88 UK areas, carried in Chronicle's `geography` dimension, as e.g. "Hartlepool UA" and "Hartlepool" (the October dense release). These tests describe schema 9, which declares each label once in top-level `hierarchy_catalogs` and has rows refer to it by ID: - test_hierarchy_catalogs.py: the published version is 9 and producers still assemble schema 8; catalogs list entries in first-use order and rows refer by ID, every other field unchanged; expanding schema 9 gives back the schema 8 model; the writer writes and reports schema 9; two labels (or two levels) for one identifier are refused, by the writer with error code `label_conflict` and no file; explicit label choices resolve a geography or dimension-value conflict, only where the rows disagree; a `geography` dimension value takes its area's label from the payload, else from the chosen geography labels; the schema 9 model refuses inconsistent documents. - test_json_schema_v9.py: a committed JSON Schema for schema 9, generated like #1298's schema 8 file, with its own list of model rules it cannot express and documents that show them; the generator tool takes `--schema-version`. - test_hierarchy_label_choices.py: UK choices use UK_GEOGRAPHY_ID_TO_LABEL and the `geography` dimension; US choices name the three values US sources word differently; the calibrate writer applies the registry country's choices, and refuses the same rows for a country without. - Release contract: schema 9 diagnostics are accepted and validated; an available declaration may say 8 or 9; a failed one may expect 9. - The UK writer passes UK choices, extended with the ONS local-authority names, and the transport kernel, the US fiscal-refresh writer, the UK local scorer and the UK dense assembler handle schema 9. - Existing tests now expect the written file to be schema 9, the in-memory payload to stay schema 8, and a non-finite value added to an already-validated model to be refused as `validation_error` (the writer validates the schema 9 document it builds). The JSON Schema test helper picks the committed schema by the document's version. Tests only. In the five changed modules that import, 21 fail and 473 pass: the scorer and the dense assembler refuse or rewrite schema 9, the US writer still writes schema 8, the v9 JSON Schema and the tool's `--schema-version` do not exist. The other seven changed modules fail to import the new names (ImportError). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lared once
Schema 9 (`CalibrationDiagnosticsV9`) is schema 8 with each provider,
category, geography and dimension label declared once in a top-level
`hierarchy_catalogs` object (arrays of entries; a category names its
provider, a dimension lists its values), and each row's `hierarchy`
referring to them by ID: `category_id`, `geography_id`,
`dimensions[].id`/`value_id`, and the target's own `{id, label}`. Every
other field is schema 8's. The model refuses an ID listed twice, a
category whose provider is not listed, and a row that refers to an
unlisted category, geography, dimension or value (looked up under its
own dimension). `CALIBRATION_DIAGNOSTICS_SCHEMA_VERSION` is now 9;
producers keep assembling schema 8
(`CALIBRATION_DIAGNOSTICS_ASSEMBLY_SCHEMA_VERSION`), and
`parse_calibration_diagnostics` reads 6 to 9.
`catalog_calibration_diagnostics` builds the catalogs in first-use order
and raises `HierarchyLabelConflictError` for every identifier the rows
label two ways (or a geography placed at two levels), unless a
`HierarchyLabelChoices` names the label (for a dimension value, only
among its reviewed spellings when those are listed); a choice applies only
where the rows disagree. `expand_calibration_diagnostics` gives back schema 8. The
canonical writer publishes schema 9, reports a refusal as
`label_conflict` with no file, and logs every conflict.
Label choices (`microcosm.calibrate.hierarchy_labels`), applied by the
calibrate writer from the registry's country:
- UK: a value of Chronicle's `geography` dimension takes its area's
label, the payload's own geography label for that code, else
`UK_GEOGRAPHY_ID_TO_LABEL`; the UK writer and the UK graph's final
validation kernel add the ONS local-authority names
(`uk_hierarchy_label_choices`). This resolves the 88 areas the October
dense release names two ways and the national releases' one.
- US: `under_1` "No adjusted gross income", `all` "All returns, total",
`age` `85_plus` "Age 85 plus", the labels chosen in review on
2026-10-21. The current feed pin still produces both spellings of each;
a US feed re-pin brings these labels from Chronicle. Each choice covers
only the two spellings Chronicle registers as reviewed variants
(chronicle#377), so a third spelling is still refused.
Every disagreement a choice settles is logged as a warning naming the
identifier, each spelling with its first target, and the label written.
The transport diagnostics kernel and the UK graph kernel publish the same
schema 9 document. The release contract, the UK local scorer and the UK
dense assembler accept schema 8 and 9 wherever they handled the current
version; an available declaration may name 8 or 9. A generated
`calibration_diagnostics.v9.schema.json` ships beside the schema 8 file,
with its own list of model rules it cannot express; the generator tool
writes or checks both, or one with `--schema-version`. The READMEs, the
agent guide and the hierarchy, transport and release docs describe
schema 9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each Belgian Chronicle package that Microcosm's Belgian targets read (SPF
Finances, ONSS, ONEM and NBB) labels the group-by value measure =
country_total its own way. The schema 9 writer refuses one value with two
labels unless a reviewed choice names the label, and Belgium has no choice,
so the transport graph's diagnostics step raises and a Belgian build fails.
The tests expect hierarchy_label_choices("be") to label the value "Country
total" (chosen in review on 2026-10-22; also the label in Chronicle's
canonical register, chronicle#377), covering exactly the four package
spellings; a Belgian file to be written with that label and one warning
naming the value, the spellings and the label; and a fifth, unreviewed
spelling to be refused. The tests of countries without choices no longer
use "be" (the no-choices list drops it; the refusal test uses "nz").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each Belgian Chronicle package Microcosm's Belgian targets read labels the
group-by value measure = country_total with its own measure name (SPF
Finances, ONSS, ONEM, NBB). The schema 9 writer refused it, and because
Belgium builds through the transport graph, whose diagnostics step raises on
a refusal, a Belgian build would have failed.
hierarchy_label_choices("be") now writes the value as "Country total", the
label chosen in review on 2026-10-22 and the one Chronicle's canonical
register gives it (chronicle#377). Like the US choices, it applies only where
the rows disagree, covers exactly the four package spellings (another is
still refused), and every use is logged as a warning. The hierarchy docs and
the changelog fragment list it beside the UK and US choices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1341
Base: #1298's branch
This targets
calibration-diagnostics-json-schema(#1298, headaa25c0926), notmain, because the JSON Schema has to be regenerated for schema 9: #1298 publishes a JSON Schema generated fromCalibrationDiagnosticsV8for consumers that check the file without importing Microcosm (the collector), and that schema refuses schema 9 (schema_versionconst 8, closed objects). This PR adds a schema 9 file beside it with the same generator. #1298's branch already containsmainat954257d, including #1336, which this PR needs (see "Tests").Because the base is not
main, CI does not run on this PR (deliberately, to limit runners). Local checks are listed below. Once #1298 merges, GitHub retargets this PR tomainand CI runs.Problem
Schema 8 copies every provider, category, geography and dimension label onto every target row. Microcosm checks that the facts inside one target agree and, since #1336, that a US registry gives each geography ID one label; nothing checks dimension values across targets, and other countries only warn. Published files therefore give one identifier two labels:
populace-us-2024-0fb05b6-4b57d15a287c-20260930T150401Z:us:statutes/26/62#adjusted_gross_incomeunder_1(106 targets / 2),all(47 / 53),age85_plus(51 / 1). The current US feed pin (Chronicle51aa40f) still produces all three.…-766b7c7-…: 51 states "Maine" / "ME" (fixed in the producer by Give each US geography one label across target sources #1336).geographywhose value ID is the lower-case GSS code, labelled with each publisher's name. The October 2026 dense releasemicrocosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbegives 88 areas two names (61 E06 unitary authorities "Hartlepool UA" / "Hartlepool", 22 Welsh W06, one each E08, S12, E12, plus constituencies E14001581 "Weston-Super-Mare" / "Weston-super-Mare" and W07000112 "Ynys Mon" / "Ynys Môn"); the October national releases give one ("Yorkshire And The Humber" / "Yorkshire and The Humber"). The geography nodes themselves have one label per ID in every current UK release.Design: diagnostics schema 9
Schema 9 (
CalibrationDiagnosticsV9) is schema 8 with each hierarchy label declared once in a top-levelhierarchy_catalogsobject and referred to by ID from the rows. Every other field is schema 8's.{ "schema_version": 9, "hierarchy_catalogs": { "providers": [{"id": "obr", "label": "Office for Budget Responsibility"}], "categories": [{"id": "obr.efo_receipts", "label": "Economic and fiscal outlook receipts", "provider_id": "obr"}], "geographies": [{"id": "K02000001", "label": "United Kingdom", "level": "country"}], "dimensions": [{"id": "obr.efo_line", "label": "Economic and fiscal outlook line", "values": [{"id": "income_tax", "label": "Income tax"}]}] }, "targets": [{ "name": "obr.income_tax@2025", "target_name": "obr.income_tax", "...": "every other schema 8 field", "hierarchy": { "category_id": "obr.efo_receipts", "geography_id": "K02000001", "dimensions": [{"id": "obr.efo_line", "value_id": "income_tax"}], "target": {"id": "obr.income_tax", "label": "Income tax receipts"} } }] }value_idis looked up under its own dimension. A row'sdimensionskeep producer order.hierarchy.providers,hierarchy.categories), and a category names its provider once, so a category under two providers cannot be written. A row names only its category.Change
microcosm.diagnostics.schema):CalibrationDiagnosticsV9,TargetDiagnosticV9,HierarchyCatalogsand its entries,CatalogHierarchyReference.CALIBRATION_DIAGNOSTICS_SCHEMA_VERSIONis now 9; producers keep assembling schema 8 (CALIBRATION_DIAGNOSTICS_ASSEMBLY_SCHEMA_VERSION);HIERARCHY_CALIBRATION_DIAGNOSTICS_SCHEMA_VERSIONSis {8, 9};parse_calibration_diagnosticsreads 6 to 9.CalibrationDiagnosticsV8is unchanged (its count checks moved into a shared function; the committed v8 JSON Schema regenerates byte for byte).microcosm.diagnostics.catalogs):catalog_calibration_diagnosticsbuilds the catalogs from the targets, in first-use order, and raisesHierarchyLabelConflictErrorlisting every identifier the rows label two ways (or a geography placed at two levels), each spelling with its first target, unless aHierarchyLabelChoicesnames the label. A choice applies only where the rows disagree, and every disagreement it settles is logged as a warning naming the identifier, the spellings and the label written.expand_calibration_diagnosticsgives back schema 8.DiagnosticsWriteFailurewith the new codelabel_conflict, writes nothing and logs every conflict (the outcome message is shortened to 500 characters). As before, a diagnostics failure does not fail the dataset build; the release manifest recordsfailed.microcosm.calibrate.hierarchy_labels), applied by the calibrate writer and the transport diagnostics kernel from the registry's country (UK, US and Belgium; see the next three sections).write_uk_calibration_diagnostics), the transport diagnostics kernel and the UK graph's final validation kernel publish the same schema 9 document; the two kernels raise on a conflict, as they already raise on an invalid schema 8 document.calibration_diagnostics.v9.schema.jsonis generated fromCalibrationDiagnosticsV9, ships in the wheel beside the v8 file (checked withuv build --wheel), and lists the model rules it cannot express (CALIBRATION_DIAGNOSTICS_V9_JSON_SCHEMA_GAPS: schema 8's, with the row-hierarchy rule replaced by catalog-identity and reference rules).tools/generate_calibration_diagnostics_json_schema.pywrites or checks both files, or one with--schema-version.docs/calibration-target-hierarchy.md(new schema 9 section), the agent guide, the transport kernel doc, the root, diagnostics, calibrate and data READMEs, andchangelog.d/1341-diagnostics-schema-9-catalogs.changed.md.What refusal means for the UK, and the handling
A catalog holds one label, so without handling the writer would refuse today's UK files: the dense release (88 values) and the national releases (1 value). UK builds would still finish, but their releases would carry
calibration_diagnostics: failed, and UK release workflows stop when diagnostics are unavailable.The least risky handling chooses the area's label from what Microcosm already treats as authoritative, only for the
geographydimension and only where the rows disagree:UK_GEOGRAPHY_ID_TO_LABEL(the UK, nations and English regions);local_authority_names.json(added by the UK build's writers).Checked on the published UK trees: all 88 dense-release values and the national one resolve; in all 89 the file's geography label equals one of the spellings, and for the 87 that the catalogs cover (86 dense, 1 national) it equals the catalog's label (for example "Hartlepool", "Kingston upon Hull, City of", "Yorkshire and The Humber"); the two constituencies, which no Microcosm catalog covers, take the file's own geography label ("Weston-super-Mare", "Ynys Môn"). The September 2026 local candidates (staging, before Microcosm gave each geography one label) still have two constituency geographies with two labels and would be refused; no current build produces them.
A UK geography (not a dimension value) with two labels is resolved only by the catalogs; a constituency there is refused, because Microcosm has no constituency name catalog.
The three US values
The current US feed pin still produces two spellings of three values, so a refusing writer would stop US national diagnostics. Each gets the label chosen in review on 2026-10-21; a US feed re-pin being prepared brings the same labels from Chronicle, after which these choices apply only to older feeds. As with Chronicle's
REVIEWED_VALUE_LABEL_VARIANTS(chronicle#377), each choice covers only the two spellings reviewed, so a third spelling is refused, and each use is logged as a warning:us:statutes/26/62#adjusted_gross_income=under_1us:statutes/26/62#adjusted_gross_income=allage=85_plusReal-file check: the public 0fb05b6 file is refused without choices (3 conflicts) and written as schema 9 with them, with one warning listing the three. The calibration dashboard (calibration-diagnostics#255) builds the same 23 tree files from that schema 9 file as from the schema 8 file with those three labels applied. The public 766b7c7 file is refused (its 51 state conflicts are what #1336 fixed in the producer).
The Belgian value
Each Belgian Chronicle package that the Belgian target contract (
be/target_references.json) reads states its national figure as the group-by valuemeasure=country_totaland labels it with its own measure name. Microcosm copies that label into each target's hierarchy (ledger_targets._inherited_dimensions), so a Belgian calibration gives one value up to four labels. Without a choice, the writer refuses it, and because Belgium builds through the transport graph, whose diagnostics step (diagnostics.calibration@1) raises on a refusal, a Belgian build would have failed, not merely recorded failed diagnostics as an earlier version of this description said.The value is now written as "Country total": the label chosen in review on 2026-10-22, and the label Chronicle's canonical register gives it (chronicle#377). Like the US choices, it applies only where the rows disagree, covers exactly the four package spellings (Chronicle registers the same four as reviewed variants, chronicle#265), so another spelling is still refused, and every use is logged as a warning:
6d74e42)measure=country_totalspf_finances/pit_2023spf_finances_pit_totalonss/contributions_2024onss_employee_contribution_totalonem_rva/unemployment_2024onem_unemployment_caseloadnbb/national_accounts_household_disposable_income_2024nbb_household_disposable_incomeAll four are written as "Country total". The two published Belgian releases carry no schema 8 hierarchy, and no Belgian build was run here; the failure and the fix are shown by tests that write a Belgian-style file through the calibrate writer, which takes its choices the same way the transport kernel does (
hierarchy_label_choices(registry country)).Other countries and new variants
geographydimension value takes its area's labelunder_1, AGIall,age85_plusmeasure=country_totalsex=female("Female", "Females", "female") andproperty_typevalues. No current UK release has them on one value ID (checked in the published trees), so nothing is chosen for them; a build that brings two of them together is refused and its warning names them.Tests (red, then green)
b08c64607test_hierarchy_catalogs.py(new),test_json_schema_v9.py(new),test_hierarchy_label_choices.py(new), and changes to the schema, JSON Schema, calibrate, UK diagnostics, transport kernel, US fiscal-refresh, UK scorer, dense assembler and release contract tests; the JSON Schema test helper picks the committed schema by the document's version.--schema-versiondo not exist. The other 7 changed modules fail to import the new names (ImportError).eb758cc51a5945f9ectest_hierarchy_label_choices.pygains the Belgian choice, a Belgian-style file written with "Country total" and one warning, and a fifth, unreviewed spelling refused; the tests of countries without choices stop usingbehierarchy_label_choices("be")is empty; the Belgian file is refused (failed, notavailable); the unreviewed spelling is refused without the "(a spelling its reviewed list does not include)" marker, because no Belgian choice exists5c6cb3116microcosm.calibrate.hierarchy_labels, the hierarchy doc and the changelog fragment; no test file changedtest_diagnostics.py,test_hierarchy_catalogs.py,test_transport_terminal_kernels.py) 96 passed, 0 failedExisting test expectations changed in the red commit, on purpose: the written file is schema 9 while the in-memory payload stays schema 8; a non-finite value added to an already-validated model is refused as
validation_error(the writer validates the schema 9 document it builds) instead ofserialization_error; the contract's message says "publishes version 9".At
5c6cb3116:ruff check .clean;ruff format --checkclean on the two changed Python files. The rest of this list was run ateb758cc51.At
eb758cc51, locally (CI does not run on this base):ruff check .clean;ruff format --checkclean on the changed files;tools/ci_test_plan.py verifyok;uv lock --checkok (uv.lockunchanged);tools/generate_calibration_diagnostics_json_schema.py --checkcurrent for both files.On #1298's previous head (
c3244bfb4, before it mergedmainwith #1336), two ACS local tests failed here: the ACS local tool still labelled state population targets "CA" beside the IRS SOI label for the same state, which schema 9 refuses. With #1336 in the base they pass. So this PR must not land on a base without #1336.Environment variables
No workflow, deployment or configuration change, and no new environment variable is read.
Notes
🤖 Generated with Claude Code