Repository navigation
Declare Chronicle consumer_fact.v5 and refuse feeds on undeclared row contracts - #1510
Merged
Merged
Conversation
Chronicle main stamps every consumer fact chronicle.consumer_fact.v5 since PolicyEngine/chronicle#377 (44eebe037): the v4 row plus an optional package_dimension_value_labels field holding a package's own wording of a dimension value. These tests run before the change, and 11 of the 20 fail on assertions: - the declared row contracts include v5, in the chronicle epoch, declared as v4 is (test_chronicle_epoch.py); - a v5 artifact built from Chronicle's sample rows loads under its full feed pin and its per-row id is accepted, and the v4 restatement of the same rows still loads (test_ledger_artifact.py); - a target compiled from the v5 row takes Chronicle's label "All returns, total", never the package's "All returns", and the v4 row compiles the same; a malformed package_dimension_value_labels on a selected fact is refused, in each of eight shapes the v5 schema refuses (test_ledger_targets.py). The fixture directory holds Chronicle's v5 schema (sha256 acd33a09...) and sample rows from 44eebe037, unchanged at Chronicle main 28064d0; a test validates the rows and the malformed shapes against that schema. The shared artifact helper gains a schema-digest argument. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chronicle main writes every consumer fact as chronicle.consumer_fact.v5 since PolicyEngine/chronicle#377 (44eebe037). Microcosm declared row contracts only up to v4, so the next re-pin of any country's feed onto Chronicle main needed this first. - chronicle_epoch declares CHRONICLE_CONSUMER_FACT_SCHEMA_VERSION_V5 in the chronicle epoch, so v5 joins ACCEPTED_CONSUMER_FACT_SCHEMA_VERSIONS beside v1 to v4, matching Chronicle's CONSUMER_FACT_ROW_CONTRACTS. - ledger_targets checks the optional package_dimension_value_labels field where it checks dimension_value_labels: on each fact a hierarchy target selects, the field, when present, must map each dimension id to an object of value id to non-empty text, the v5 schema's shape; any other shape raises LedgerHierarchyMetadataError. No code takes a label from the field, so each value keeps Chronicle's one label from dimension_value_labels and layout.groupby_value_label. - The loader is unchanged: it keeps every published field on the parsed rows, the new one included. - docs/calibration-target-hierarchy.md and docs/uk-chronicle-feed-repin.md say so; a changelog fragment records it. No feed pin moves. Closes #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1 of #1510 found that nothing outside the tests read the accepted row-contract list, so a v5 feed already loaded and the declaration refused nothing. Anthony decided to enforce it. These tests run before that change; 12 of the 36 cases fail, each on a pytest.raises that does not raise: - validate_chronicle_feed_pin, directly and through the loader, refuses an artifact whose rows and pin agree on chronicle.consumer_fact.v6, or mix it in beside v4; the message names the undeclared version, says to declare it in microcosm.build.chronicle_epoch, and lists the declared ones (test_ledger_artifact.py); - chronicle_feed_identity, which reference factors, money normalization and the transport composer use, refuses a v6, a v5-and-v6 or an arch.consumer_fact.v1 list, and a list that is a string; - the targets.compile and takeup.compare kernels refuse a v6 feed under its own pin (test_transport_target_kernels.py); - the UK and US pin loaders refuse a committed pin naming v6 (test_uk_chronicle_feed.py, test_us_chronicle_feed.py). The other 24 cases pass before the change and guard what must keep working or already holds: ledger.consumer_fact.v1 and v2 to v5 pass a full feed pin; every committed pin (UK, US, NZ, BE) and every resource restating one (the NZ and BE reference inventories and ageing files, the NZ MSD reconciliation) names declared versions and passes chronicle_feed_identity; empty package wording objects compile; a malformed entry under a dimension the target does not select is refused. The fixtures move to tests/fixtures/chronicle_consumer_fact and gain Chronicle's v4 schema (d953306d...) and its v4 sample rows from 1a3f2c4, main just before chronicle#377, which label the AGI value "All returns". The v4 label and load tests use those rows in place of v5 rows restamped as v4. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1 of #1510 found that declaring v5 changed nothing at run time: no code outside the tests read the accepted row-contract list. Anthony decided to enforce it. - chronicle_epoch adds require_accepted_consumer_fact_schema_versions and UndeclaredConsumerFactSchemaVersionError (a ValueError). It refuses a row-contract list that is not a list, or that names any version the declaration table lacks; the message names the undeclared versions, says to declare them in microcosm.build.chronicle_epoch first, and lists the declared ones. - ledger_artifact calls it in validate_chronicle_feed_pin after the exact comparisons, so a pin that differs from its artifact still reports the differing field, and in chronicle_feed_identity, which reference factors, money normalization and the transport composer use. The transport kernels reach both. A feed loaded without a full pin still carries its per-row ids unchecked, as the US fiscal-refresh feed needs. - The UK and US pin loaders call it on the committed pin. - ledger_targets: the shape check's docstring says it is stricter than the dimension_value_labels check, which reads only selected entries. - congressional_district_vintage: comments in _translated_fact_shell and _state_total_proxy_source_district_fact record that a v5 row's package wording is copied keyed by the source row's ids, and that nothing reads it. - The UK and US feed re-pin guides describe the refusal; the US guide adds the district note. The changelog fragment now says the shape check runs for hierarchy targets and how callers treat its error. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 of #1510 found that the UK and US builds hold a feed to its committed pin by digest only, and never compare the row contracts the rows carry with the pin's hand-typed list or the declared set. These tests run before the fix; 20 of 55 cases fail, each on an assertion, a pytest.raises that does not raise, or an AssertionError from a compile the check should have prevented: - UK full build (load_uk_full_target_inputs): rows on v6, rows on v4 and v5 against a v4 pin, and unstamped rows are refused before either compiler runs (test_uk_full_targets.py); - UK national role: rows on v6, rows on v5 against a v4 pin, and v6 rows of a feed admitted by --allow-unpinned-feed are refused before target compilation; - US release tool: _check_us_ledger_feed_row_contracts refuses the same cases and _main calls it between the committed-pin check and target compilation (test_us_fiscal_refresh_builder.py); - the UK national and local reference generators refuse v6 rows before writing anything (test_target_reference_authoring.py), and the UK vendoring tool, release certifier, compile-parity tool and DESNZ constituency builder call the UK row-contract checks (test_uk_chronicle_feed.py); - a foreign id such as arch.consumer_fact.v1 is refused without advice to declare it, which chronicle_epoch cannot do. The other cases pass before the fix: the full pin check already refuses rows carrying a row contract the pin leaves out; the committed-resource walk now reads every package's JSON resources at any depth; the UK full-build fixture and national-role test gain the rows' and pin's row-contract lists; the DESNZ tool test's pin lists v4. The epoch test's docstring no longer cites the old US fiscal-refresh feed as the US pin. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 of #1510 found that the UK and US builds hold a feed to its committed pin by digest only, so a re-pin that moved the digests but left the hand-typed list naming v5 would compile rows on an undeclared row contract. A feed whose pin or rows name one is now refused. - ledger_artifact: consumer_fact_schema_versions_of(rows), which LedgerConsumerArtifact.fact_schema_versions now uses, and validate_pinned_row_contracts. It refuses rows on an undeclared row contract and, given a pin's list, rows whose contracts are not exactly that list, or (for a subset) not all on it. - uk_runtime.chronicle_feed: require_uk_chronicle_feed_row_contracts (exact on the committed feed, declared-only on a feed admitted by --allow-unpinned-feed) and require_uk_chronicle_feed_subset_row_contracts. The full and national target loaders, the vendoring tool and the release certifier call the first; the national and local reference generators (which now read their facts first), the compile-parity tool and the DESNZ constituency builder call the second. The DESNZ builder loads the pin module by path, as it does the energy module, so a first run still needs no UK country spec. - build_us_fiscal_refresh_release: _check_us_ledger_feed_row_contracts, called after the committed-pin digest check; the US pin describes a bare feed, so the full feed-pin check cannot run on it. - chronicle_epoch: the refusal names an id outside Chronicle's namespaces as not a Chronicle row contract, which chronicle_epoch cannot declare, instead of advising a declaration. The subject and message no longer need verb agreement, and the redundant str/bytes test is gone. - The loader comment and docstrings, the UK and US re-pin guides and the changelog give the reasons that hold for loads without a pin (feeds loaded by path for diagnostics and experiments, and bare feeds); the old fiscal-refresh feed is no longer called the US pin. - Test fakes standing in for a loaded artifact or a pin in the UK national graph and rowwise national role tests, and the US release tool's corridor test, carry the row-contract lists the new checks read. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main re-pinned the US feed to Chronicle 41b2fb2b (#1506) and the NZ feed onto chronicle.consumer_fact.v5 (#1507). The one conflict is in docs/us-chronicle-feed-repin.md, where #1506's new re-pin section and this branch's section on re-pinning onto a newer row contract sit in the same place: both are kept, the general section first. #1506's sentence saying the US loader carries a row's stamp unchecked now adds that the release and target-parity tools check the stamps against the pin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 3 of #1510 found that tools/build_us_target_parity_manifest.py holds the feed to the committed US pin by digest, compiles it and writes committed files, but checks no row contracts. Before the fix all three cases of the new test fail on the AssertionError the stubbed manifest build raises when rows on v6, rows on v4 against a v3 pin, or rows on v3 and v4 against a v3 pin reach it. A second test covers the UK subset rule directly, and passes before and after the fix: a declared row contract the pin does not list is refused, and a strict subset of the pin's list, or rows with no stamp, pass. Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 3 of #1510 found that tools/build_us_target_parity_manifest.py holds the feed to the committed US pin by digest, compiles it and writes committed files, but checked no row contracts, so a US re-pin that left the hand-typed list stale passed its own rebuild steps. - us_runtime.chronicle_feed: require_us_chronicle_feed_row_contracts, the exact-or-declared check: rows of the committed feed must carry exactly the pin's list; rows of any other feed, declared row contracts only. - The release tool's _check_us_ledger_feed_row_contracts now calls it, and the target-parity generator calls it after its digest check, before compiling or writing. - Wording: validate_pinned_row_contracts and the UK row check say the release certifier holds its feed to the operator's digests, so it applies the exact rule only when given the committed feed. The changelog says the same, names the US generator, and lists the readers left unchecked because no reviewed pin holds their feed (the US preflight and ACS tools, the scoring and diagnostic tools, the UK high-value home stock, BRMA rent-quantile and Scotland BRMA-overlap builders, and the childcare take-up fit tools). - The UK and US re-pin guides name the certifier's rule and the generator's check; notes that said the NZ pin carries the v4 digest now say NZ is on v5 (#1507). Refs #1509 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
marked this pull request as ready for review
November 5, 2026 12:06
juaristi22
added a commit
that referenced
this pull request
Nov 5, 2026
#1510 merged into main first with the same chronicle.consumer_fact.v5 declaration this branch's re-pin made in chronicle_epoch.py, and with checks that refuse a feed whose pin or rows name an undeclared row contract. The merge keeps #1510's chronicle_epoch.py and test_chronicle_epoch.py. This branch's pin and rows both name v5, so those checks pass on it; main's other changes leave the regenerated references and receipts as they are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #1509.
Summary
Chronicle
mainwrites every consumer fact aschronicle.consumer_fact.v5since PolicyEngine/chronicle#377 merged as44eebe037on 2026-11-03. v5 is the v4 row plus one optional top-level field,package_dimension_value_labels(dimension id, then value id, then text). That field holds a source package's own wording of a value, and only where it differs from the label the row exports.Before this PR, a v5 feed would already have loaded. Microcosm's declared row-contract list (
microcosm.build.chronicle_epoch) stopped at v4, but no code outside the tests read it. Review round 1 found this, and the maintainer decided that a feed whose pin or rows name an undeclared row contract is refused, so a new Chronicle format is caught at the re-pin. Review round 2 found that the UK and US builds checked only the pin's hand-typed list, not the rows they compile; they now check the rows too. Review round 3 found the same gap in the US target-parity generator, which is fixed here as well. This head also mergesmain, which re-pinned the US feed to Chronicle41b2fb2b(#1506, still v3) and the NZ feed onto v5 (#1507); both pins pass the new checks.This PR:
CONSUMER_FACT_ROW_CONTRACTS.chronicle_feed_identityandvalidate_chronicle_feed_pin.validate_chronicle_feed_pinfor full pins (transport kernels, NZ and Belgian generators), and through the newvalidate_pinned_row_contractsin the UK and US code that holds feeds to committed pins by digest.package_dimension_value_labelson the facts a hierarchy target selects. Microcosm never takes a label from that field, so each value keeps the one label Chronicle exports.No feed pin moves in this PR. After the merge of
main, the UK and Belgian pins name v4, the US pin v3 and the NZ pin v5. Every committed pin and every resource restating one names declared row contracts, and a test walks every package's JSON resources to check that.Where Microcosm checks a feed's row contracts after this PR
ledger_artifact.validate_chronicle_feed_pin(throughload_ledger_consumer_artifact(feed_pin=…)): the transport kernels (money_kernels,target_kernelsdirectly;population_kernels,takeup_kernelsthroughcompile_target_surface), and the NZ and Belgian generatorsledger_artifact.chronicle_feed_identity: reference factors, money normalization, the transport composeruk_runtime/chronicle_feed.load_uk_chronicle_feed,us_runtime/chronicle_feed.load_us_chronicle_feeduk_runtime.full_targets),tools/vendor_uk_ledger_facts.py:require_uk_chronicle_feed_row_contracts--allow-unpinned-feed, declared onlytools/certify_uk_release_cut.py:require_uk_chronicle_feed_row_contracts--ledger-facts-sha256and--ledger-manifest-sha256, not to the committed pintools/generate_uk_target_references.py,tools/generate_uk_local_target_references.py,tools/build_uk_ledger_compile_parity_signed_differences.py,tools/build_uk_desnz_constituency_energy_facts.py:require_uk_chronicle_feed_subset_row_contractstools/build_us_fiscal_refresh_release.py(_check_us_ledger_feed_row_contracts, after_check_committed_us_ledger_feed_pin) andtools/build_us_target_parity_manifest.py(after its digest check, before compiling or writing):require_us_chronicle_feed_row_contracts--allow-unpinned-feed, declared only (the parity generator has no such flag)ledger_artifact._load_fact_rowswithout a pinThe UK reference generators now read their facts file first, so the check runs before any contract or crosswalk processing. The DESNZ builder loads the UK pin module by path, as it already loads the energy module, so a first run still needs no UK country spec.
Not changed, with the reason. None of these holds its feed to a reviewed pin, which is the rule this PR enforces; their outputs are used beside the pinned feed, whose rows the builds above check.
us_runtime/release_gate_preflightpreviews the registry from the operator's--ledger-facts-sha256, not the committed pin; the release tool itself refuses.tools/build_uk_high_value_home_stock.pyrecords the manifest's identity; a fingerprint test, which runs only when the feed is present, compares its inputs with the pinned feed;tools/build_uk_brma_rent_quantiles.pyandtools/build_uk_scotland_brma_la_overlap.pycheck each source file's digest per fact.tools/fit_uk_childcare_takeup.py,tools/fit_uk_uc_childcare_takeup.py) take an operator-supplied digest.What changed, module by module
microcosm.build.chronicle_epoch:CHRONICLE_CONSUMER_FACT_SCHEMA_VERSION_V5, declared in the chronicle epoch as v4 is.require_accepted_consumer_fact_schema_versions(versions, *, subject). It refuses a value that is not a list or tuple, and raisesUndeclaredConsumerFactSchemaVersionError(aValueError) when an entry is undeclared.microcosm.build.chronicle_epoch(DECLARED_IDENTITIES) first.arch.consumer_fact.v1: the message says it is not a Chronicle row contract and thatchronicle_epochcannot declare it.microcosm.build.ledger_artifact:validate_chronicle_feed_pinruns the check after its exact comparisons, so a pin that differs from its artifact still reports the differing field.chronicle_feed_identityruns it too.consumer_fact_schema_versions_of(rows), whichLedgerConsumerArtifact.fact_schema_versionsnow uses.validate_pinned_row_contracts(fact_schema_versions, *, pinned, subject, subset=False).fact_schema_versionsdocstring give the reasons that hold for loads without a pin.uk_runtime/chronicle_feed.py: the loader checks the committed list. Newrequire_uk_chronicle_feed_row_contractsandrequire_uk_chronicle_feed_subset_row_contracts.uk_runtime/full_targets.py: both loaders check the rows after the digest checks, before either compiler runs.us_runtime/chronicle_feed.py: the loader checks the committed list. Newrequire_us_chronicle_feed_row_contracts, the exact-or-declared check both US tools call.microcosm.build.ledger_targets:_validate_chronicle_hierarchy_labelscalls_validate_package_dimension_value_labelson each selected fact.dimension_value_labelsalike.LedgerHierarchyMetadataError, the error a missing Chronicle label raises.dimension_value_labelsreads only the selected entries and takes any non-null value as text.us_runtime/congressional_district_vintage.py: comments in_translated_fact_shelland_state_total_proxy_source_district_factrecord that both copy a source row whole. After a US re-pin onto v5, a derived district row would keep any package wording keyed by the source row's value ids; nothing reads the field.docs/calibration-target-hierarchy.md: the v5 rule under "Chronicle inheritance".docs/uk-chronicle-feed-repin.md: set the pin's list from the manifest, and which code refuses what.docs/us-chronicle-feed-repin.md: a section on re-pinning onto a newer row contract, with the release tool's and parity generator's row checks and the district note. It sits beside Re-pin the US Chronicle feed to add Federal Student Aid's loan portfolio tables #1506's new re-pin section, which the merge keeps.changelog.d/1509-chronicle-consumer-fact-v5.added.md.Where the shape check runs
It runs when Microcosm compiles a target that has a calibration hierarchy, on the facts that target selects, as the
dimension_value_labelscheck does. Callers treat its error as they treat a missing Chronicle label.target_reference_authoringre-raises it. The UK national and local compilers report the reference as unsupported, which stops the build year and is recorded for validation periods. A malformed field on a fact no target selects is not refused. The empty forms{}and{dimension: {}}, which the schema accepts, compile.The fixtures
packages/microcosm-build/tests/fixtures/chronicle_consumer_fact/holds Chronicle's own schemas and sample rows on either side of #377.SOURCE.mdrecords the paths and digests.44eebe0378cd4aa708e283837e5a4ed1f7b18ef0, unchanged atmain28064d0:consumer_fact.v5.schema.json, sha256acd33a091cd8d192be5d264e3e94c13e00afb6fd1972e57d4576ac55a54f6970;consumer_facts.v5.jsonl: IRS SOI Table 1.1 for tax year 2023. These rows label the AGI valueall"All returns, total" and keep the package's "All returns" in the new field.1a3f2c41005ed0138fd82fe08a9d87201061e198(mainjust before Stale TANF degenerate-input exclusion on main aborts every default US build before calibration (Build I fix never ported) #377):consumer_fact.v4.schema.json, sha256d953306d…, the digest the UK and Belgian pins carry;consumer_facts.v4.jsonl: the same facts as real v4 rows, labelled "All returns".example-raw-bucket, and keys are sorted. Each row file validates against its own schema.Tests: before and after each change
I ran only the tests this PR adds or modifies, by node ID, from a script that refuses an empty list, with
HF_HUB_OFFLINE=1. CI runs the rest of the suite. Paths are underpackages/microcosm-build/tests/engine_free/.Round 1 (declaring v5, and the shape check): at
3ca0435a1, 11 of 20 cases failed; at5dd751a40, all 20 passed. The failures:shared/test_chronicle_epoch.py;shared/test_ledger_artifact.py;shared/test_ledger_targets.py, eachDID NOT RAISE.Round 2 (pins naming an undeclared row contract): at
e70084b12, 12 of 36 cases failed, eachDID NOT RAISE; atdeadfb7f9, all 36 passed. The failures:chronicle_feed_identityrefusals and the not-a-list case inshared/test_ledger_artifact.py;shared/test_transport_target_kernels.py::test_target_kernels_refuse_an_undeclared_row_contract;uk/test_uk_chronicle_feed.pyand the US loader cases inus/test_us_chronicle_feed.py.Round 3 (the rows UK and US builds read, and the review's points): at
30213d718, 20 of 55 cases failed; at049b30d1a, all 55 passed. Every failure is on an assertion. These failed before the change:uk/test_uk_full_targets.py::test_full_inputs_hold_the_rows_row_contracts_to_the_pin, casesundeclared,not_in_the_pinandunstamped:DID NOT RAISE;uk/test_uk_full_targets.py::test_the_national_role_holds_the_rows_row_contracts, casesundeclared,not_in_the_pinandundeclared_unpinned_feed: the compile was reached;us/test_us_fiscal_refresh_builder.py::test_feed_row_contract_check_refuses_rows_the_pin_or_epoch_lacks, 4 cases, andtest_main_checks_the_feed_row_contracts_before_compiling_targets;shared/test_target_reference_authoring.py::test_uk_reference_generators_refuse_rows_on_an_undeclared_row_contract, the national and local generators: the v6 refusal was absent from stderr;uk/test_uk_chronicle_feed.py::test_uk_feed_tools_check_the_row_contracts_of_the_rows_they_read, 6 tools;shared/test_ledger_artifact.py::test_feed_identity_refuses_a_foreign_row_contract_without_advising_a_declaration.These passed before the change too, as guards or because the behaviour already held:
shared/test_ledger_artifact.py::test_feed_pin_refuses_rows_carrying_a_row_contract_the_pin_does_not_list: the full pin check already refused this;uk/test_uk_full_targets.pytests that use thepreparedfixture, which gained the row-contract lists;uk/test_uk_full_targets.py::test_the_national_line_drops_local_only_rows_before_reconciliation, whose fakes gained the lists;The green commit also gives row-contract lists to the fakes that stand in for a loaded artifact or a pin. That covers 6 tests in
uk/test_uk_national_graph.py, 22 inuk/test_uk_rowwise_national_role.pyand the corridor test inus/test_us_fiscal_refresh_builder.py. I ran them all locally:'skipped' == 'uploaded',UPLOAD_PENDING,staged_dataset_handover). All ten fail the same way without this change: review round 3 ran them at the round 2 head and at the merge base, and I ran three of them before this change. The cause isHF_HUB_OFFLINE=1, which the local runs set: with the hub offline,uk_runtime.rowwise_stagingkeeps a requested remote stage local and records the staged dataset as skipped, while these tests expect an upload to their stand-in hub. CI does not set the variable, and runs them.Round 4 (the US target-parity generator, after merging
main): at9cc83b496, 3 of 7 cases failed; at5fdbc1ef5, all 7 passed.us/test_us_chronicle_feed.py::test_parity_generator_holds_the_rows_row_contracts_to_the_pin, casesundeclared,not_in_the_pinandpin_lists_fewer, failed on the AssertionError the stubbed manifest build raises when the rows reach it.uk/test_uk_chronicle_feed.py::test_subset_check_requires_each_row_contract_on_the_pin(4 cases) covers the UK subset rule directly, and passes before and after: a declared row contract the pin does not list is refused; a strict subset of the pin's list, or rows with no stamp, pass.On the merged head, the committed-resource and pin tests pass with the new NZ (v5) and US (
41b2fb2b, v3) pins. So do the round 3 tests (55 cases) and the US corridor test.ruff checkandruff format --checkpass on every changed Python file. The committed UK and US pins load through the changed loaders.Environment variables
None. The changed code reads no environment variable, and no workflow changes. Running the tests locally needs only
HF_HUB_OFFLINE=1.Follow-up (not in this PR)
microcosm.calibrate.hierarchy_labelssettle spellings that Chronicle now resolves to one label per value. They may become unnecessary once those feeds are re-pinned onto v5. This PR leaves them unchanged.mainmoves its pin to["chronicle.consumer_fact.v5"]andacd33a09…, set from the artifact, with the tests that restate the pin. The UK re-pin is the next pull request.🤖 Generated with Claude Code