Skip to content

Read calibration diagnostics schema 9, whose hierarchy labels are declared once in catalogs - #255

Merged
anth-volk merged 7 commits into
mainfrom
feat/diagnostics-schema-9-catalogs
Oct 26, 2026
Merged

anth-volk merged 7 commits into
mainfrom
feat/diagnostics-schema-9-catalogs

Conversation

@anth-volk

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

Copy link
Copy Markdown
Contributor

Fixes #254

Stacked on #253 (branch fix/release-reconciliation-continues, head 0f8321b), which targets main. #253's last commit (test wording only) was merged into this branch (9feb117) rather than rebased, so this branch was never force-pushed. The two changes are independent; this one is chained on #253 only to keep one review stack. Merge #253 first.

Companion: PolicyEngine/microcosm#1342 (issue microcosm#1341) makes Microcosm publish schema 9. This PR must be merged and deployed to production before that one merges; that PR says so too.

Problem

Schema 8 copies every provider, category, geography and dimension label onto every target row. Labels come from each source fact (Chronicle packages from different publishers) or from Microcosm builders, and Microcosm only checks that the facts inside one target agree. So one identifier can carry two labels in one published file:

Design: diagnostics schema 9

Schema 9 is schema 8 with each hierarchy label declared once, in a top-level hierarchy_catalogs object, and referred to by ID from the rows. Every other top-level field and every other row field is unchanged.

{
  "schema_version": 9,
  "hierarchy_catalogs": {
    "providers":   [{"id": "irs_soi", "label": "IRS Statistics of Income"}],
    "categories":  [{"id": "irs_soi.eitc", "label": "Earned income tax credit", "provider_id": "irs_soi"}],
    "geographies": [{"id": "0100000US", "label": "United States", "level": "country"}],
    "dimensions":  [{"id": "us:statutes/26/62#adjusted_gross_income", "label": "Adjusted gross income",
                     "values": [{"id": "under_1", "label": "No adjusted gross income"}]}]
  },
  "targets": [{
    "name": "irs_soi.eitc.under_1@2024", "target_name": "irs_soi.eitc.under_1", "...": "every other schema 8 field",
    "hierarchy": {
      "category_id": "irs_soi.eitc",
      "geography_id": "0100000US",
      "dimensions": [{"id": "us:statutes/26/62#adjusted_gross_income", "value_id": "under_1"}],
      "target": {"id": "irs_soi.eitc.under_1", "label": "EITC returns with no adjusted gross income"}
    }
  }]
}

Decisions:

  • Geographies are keyed by ID, with one label and one level. In every published US file and UK tree I read, each geography ID has exactly one level, and the dashboard already groups geography by level and ID (Group calibration tree geographies by geography ID, not label #250).
  • Dimensions list their values; a row's value_id is looked up under its own dimension, so two dimensions can both have a value all. A row's dimensions keep producer order.
  • Providers and categories move into catalogs too. Their labels have never varied, but the cost is nil once the row shape changes, and it buys two things: the file mirrors the country contracts' authoring catalogs (hierarchy.providers, hierarchy.categories), and a category names its provider once, so a category under two providers (the one refusal the schema 8 reader keeps) cannot be written at all. A row names only its category.
  • The target's own label stays on the row, as in schema 8: a target is one row, so there is nothing to share.
  • Catalogs are arrays of entries, not objects keyed by ID, so a repeated ID stays visible to a reader (a JSON object silently keeps the last duplicate key).
  • Everything else is schema 8's, so readers of numbers, names and metadata are unaffected.

In schema 9 files built from current sources, a geography or dimension value ID cannot carry two labels: Microcosm's writer (companion PR) builds the catalogs from the targets and refuses to write when the rows disagree, unless a reviewed label choice settles it.

Change

  • New module hierarchy-catalog-reader.ts (exported as @policyengine/calibration-tree/hierarchy-catalog-reader): expandHierarchyCatalogs(document) turns a schema 9 document into a copy whose rows carry the schema 8 hierarchy, with the producer's key order, and drops hierarchy_catalogs; schema_version stays 9. Any other version is returned unchanged, as the same object.
  • buildCalibration applies it before anything reads the targets. It is the only place in the dashboard that reads the raw targets array (latest-artifact.ts; releases, staging runs, staged bundles and the tree publisher all go through it), so the schema 8 reader, the tree builder, published trees, comparisons and every page read both versions through the same code.
  • targetRepresentationForSchema(9) selects the hierarchy reader; the error for other versions now says "1 through 9".
  • Refusals, each naming the row (by name) or the catalog entry: hierarchy_catalogs or one of its arrays missing, or an entry missing a field; one ID listed twice in a catalog or one value ID twice in a dimension; a category whose provider_id is not listed; a row that refers to an unlisted category, geography or dimension, or to a value its own dimension does not list (a value listed only under another dimension); a row that lists one dimension twice or is still in the schema 8 layout; a hierarchy.target.id that differs from target_name. IDs are compared after trimming whitespace, as the schema 8 reader trims them.
  • Schema 8 reading is unchanged: the expansion returns a schema 8 document as the same object, and every existing test passes unchanged except one that asserted 9 was unsupported (now 10).
  • Docs: docs/calibration-map-hierarchy.md (schema 9 section, reader table, refusals) and docs/spec-driven-countries.md.

Same trees as schema 8

A schema 9 file builds the same tree files as the equivalent schema 8 file without label variants, apart from two identity fields that come from the file's bytes: the build ID and the recorded diagnostics digest.

  • Recorded golden tree: the UK schema 8 golden release, written as schema 9 and compiled under the recorded file's identity (same source digest, so the same build ID), reproduces the recorded tree and every reader response byte for byte (schema-9-diagnostics.test.ts).
  • Through the publisher: release reconciliation publishes a schema 9 release whose uploaded files equal the schema 8 release's, apart from the build ID, the source digest, and the index's per-part digests and compressed sizes that cover them (publish-calibration-tree.test.ts).
  • Real files (throwaway checks, outside the repository):
    • The public US files 0fb05b6 (5,694 targets) and 766b7c7 (4,765), with their label variants rewritten to the first spelling, built as schema 8 and as schema 9 under one identity: all 23 and all 20 bundle files have the same SHA-256. The schema 9 files are about 8% smaller (28.6 MB against 30.9 MB, and 21.5 MB against 23.5 MB, indented as the writer writes them).
    • The schema 9 file that the companion PR's Microcosm writer produced from the real 0fb05b6 file (with its US label choices) builds the same 23 files as the schema 8 file with those three values set to the chosen labels.

Tests (red, then green)

Commit What Result
34c8481 Tests only: schema-9-diagnostics.test.ts (new, 10 tests), a schema 9 publisher test in publish-calibration-tree.test.ts, target-representation.test.ts (9 is the hierarchy reader, 10 is refused), latest-artifact.test.ts (the unsupported-version loop uses 10 instead of 9). Test support: schema9Diagnostics writes a schema 8 document in the schema 9 layout, refusing label variants as the writer does; goldenReleaseRecordFromDocument compiles a recorded release from another document under the recorded file's identity. 12 fail, 106 pass across the four test files; every failure is "Unsupported calibration diagnostics schema_version 9; this dashboard reads versions 1 through 8." (the publisher test publishes nothing for the same reason). bun run lint passes.
e49f7dd The reader, the package export, the docs; tests unchanged 118 pass, 0 fail; bun run lint passes

At e49f7dd (and again at 9feb117: package 389 pass, typecheck passes):

  • calibration-tree package: 389 pass, 0 fail (35 files), including calibration-tree-golden.test.ts 12, test-support/calibration-tree-golden-cases.test.ts 2, publish-calibration-tree-golden.test.ts 7, package-boundary.test.ts 9, calibration-tree-artifact.test.ts 16, calibration-tree.test.ts 41, hierarchy-target-reader.test.ts 7, publish-calibration-tree.test.ts 21;
  • bun test app lib components scripts (route, library and deploy-check tests): 734 pass, 0 fail;
  • bun run lint (both tsc --noEmit runs): passes.

Consumers of calibration_diagnostics.json, and schema 9

Consumer What it reads Schema 9 without changes Status
This dashboard: buildCalibration, used by the release, staging, Targets and summary routes, the comparison builders and the tree publisher rows and their hierarchy; schema_version selects the reader Refused ("Unsupported … schema_version 9") Reads schema 9 with this PR
This dashboard: deploy workflow check (deploy.yml → calibration-deployment-smoke.ts) API summaries, release and staging inventories Through buildCalibration only Unaffected beyond the above
This dashboard: evaluation_harness (Python) targets[].metadata.chronicle_record_ids Unaffected —
Microcosm: writer, release contract, UK local scorer, UK dense assembler, transport and UK graph kernels, JSON Schema (#1298) the file itself — Changed in the companion PR; the contract accepts 8 and 9
microcosm#1298's JSON Schema (calibration_diagnostics.v8.schema.json) validates a document Refuses (schema_version const 8, closed objects) The companion PR adds calibration_diagnostics.v9.schema.json
.py (policyengine.py): release certification and dataset materialization artifact paths and SHA-256 only; ignores the manifest's calibration_diagnostics declaration Unaffected —
microcosm-collector intake (open PR #12) checks role, count, size and SHA-256 only; no content handler yet Unaffected today Its planned content handler must accept schema 9 (and decide about stored schema 8 submissions)
microcosm-emitter (open PRs) sends the file as bytes Unaffected —
uk-autumn-budget-dashboard-2026 (main and open PR #63) release manifest artifact roles only Unaffected —
microcosm.institute (releases.js) target_surface.n_records / n_records Unaffected —
policyengine-scorecard (scripts, open PR #53), refundable-credit-conversion and uk-dataset-evaluation (private, scripts run by hand) per-target name, target, final_estimate, relative_error, top-level losses Unaffected; uk-dataset-evaluation's reduced copy would keep hierarchy_catalogs —
The Public API (policyengine-api), policyengine-sim-api none (the simulation gateway refuses the file as a dataset name) Unaffected —

Found with GitHub code search across the organization and by reading each repository above; private Hugging Face repositories and branches other than default and open-PR branches were not checked.

Merge and deploy order

  1. Record a release that cannot be reconciled and reconcile the rest #253, then this PR, into main; each merge deploys production. Confirm production serves this PR before step 3.
  2. microcosm#1298 (the JSON Schema), which the companion PR is based on.
  3. microcosm#1342. Microcosm merges deploy nothing, but the next release a build publishes would be schema 9, and the dashboard in production must read it by then.

Environment variables

No workflow, deployment script or configuration changes, and the changed code reads no environment variable.

Notes

  • The Calibration summary reports diagnostics_schema_version 9 and representation hierarchy for a schema 9 file; hierarchy_label_variants can list only target-label variants, since the other labels are declared once.
  • Releases already published stay schema 8 and are read exactly as before; their published trees are untouched.
  • No workflow is dispatched by this PR.

🤖 Generated with Claude Code

anth-volk and others added 2 commits October 21, 2026 23:47
Diagnostics schema 9 declares provider, category, geography and
dimension labels once, in a top-level `hierarchy_catalogs` object, and
each target row refers to them by ID; the target's own label stays on
the row. These tests describe how the dashboard should read it:

- a schema 9 file reads into exactly the rows of the equivalent
  schema 8 file, with no label variants, and reports schema version 9
  with the hierarchy representation;
- compiled under the recorded schema 8 file's identity, it builds the
  recorded UK release tree and reader responses byte for byte;
- release reconciliation publishes a schema 9 release whose uploaded
  files equal the schema 8 release's apart from the build ID and the
  source digest, which come from the file's bytes;
- a structurally inconsistent file is refused with a message naming the
  problem: missing catalogs, an ID listed twice in a catalog or in one
  dimension's values, a category whose provider is not listed, a row
  that refers to an unknown category, geography, dimension or value
  (including a value listed only under another dimension), a row that
  lists a dimension twice, a row still in the schema 8 layout, and a
  hierarchy target that is not the row's target;
- schema version 9 maps to the hierarchy reader, and 10 is refused.

Test support gains `schema9Diagnostics`, which writes a schema 8
document in the schema 9 layout the way Microcosm's writer will, and
`goldenReleaseRecordFromDocument`, which compiles a recorded release
from another document under the recorded file's identity.

Tests only. Every new test fails because the reader refuses schema 9
("Unsupported calibration diagnostics schema_version 9").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diagnostics schema 9 declares provider, category, geography and
dimension labels once, in top-level `hierarchy_catalogs`, and each
target row refers to them by ID, so one geography ID or one dimension
value ID cannot carry two labels. The target's own label stays on the
row, and every other field is the same as in schema 8.

`expandHierarchyCatalogs` (new module `hierarchy-catalog-reader`,
exported by the package) turns a schema 9 document into a copy whose
rows carry the schema 8 hierarchy, with the producer's key order and
dimension order, and drops `hierarchy_catalogs`; `schema_version` stays
9. `buildCalibration` applies it before anything reads the targets, so
the schema 8 reader, the tree builder, published trees and every route
read both versions through the same code. Any other version is passed
through as the same object, so schema 8 reading is unchanged.

A structurally inconsistent schema 9 file is refused with a message
naming the row or catalog entry: missing catalogs or fields, an ID
listed twice, a category whose provider is not listed, a reference to
an unlisted category, geography, dimension or value (a value is looked
up only under its own dimension), a dimension listed twice on one row,
a row in the schema 8 layout, or a hierarchy target that is not the
row's target.

`targetRepresentationForSchema(9)` selects the hierarchy reader. The
docs describe the schema 9 layout, the refusals and the reader table.

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

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
calibration-diagnostics Ready Ready Preview Oct 26, 2026 12:36pm UTC

Request Review

…ma-9-catalogs

Brings in #253's test-only rewording commit, so this branch stays on top
of #253 without rewriting its history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk and others added 3 commits October 22, 2026 00:17
A schema 9 row's hierarchy holds only catalog references and the target
label. Microcosm's schema 9 model and its JSON Schema forbid any other
field, but the dashboard reader ignores extra fields, so a row that also
carries an inline geography or a dimension value_label (which can only
disagree with the catalog) is read using the catalog's label without
any error.

This test fails now: both documents are accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The schema 9 reader now refuses a row hierarchy field other than
category_id, geography_id, dimensions and target, and a dimension
reference field other than id and value_id, as Microcosm's schema 9
model and JSON Schema do. An inline schema 8 label beside a catalog
reference could only disagree with the catalog. The check runs after
the required fields, so a row still in the schema 8 layout keeps
reporting its missing category_id. The refusal list in
docs/calibration-map-hierarchy.md names the new case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ma-9-catalogs

Brings in #253 up to f0d1e54. Both branches added a test at the same
place in publish-calibration-tree.test.ts; the merge keeps both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ma-9-catalogs

Brings in #253's two review commits (comments and documentation only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from fix/release-reconciliation-continues to main October 26, 2026 13:25
@anth-volk
anth-volk marked this pull request as ready for review October 26, 2026 13:27
@anth-volk
anth-volk merged commit cd0288e into main Oct 26, 2026
5 checks passed
@anth-volk
anth-volk deleted the feat/diagnostics-schema-9-catalogs branch October 26, 2026 13:27

This branch was successfully deployed

1 active deployment
Preview — e6343865 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read calibration diagnostics schema 9, whose hierarchy labels are declared once in catalogs

1 participant