Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/622-uk-national-contract.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add the hardened UK national calibration contract resource with signed
registry-parity accounting, explicit closure for carried unmapped
declarations, and review-pinned selector/binding invariants for the #622 UK
target migration.
3 changes: 3 additions & 0 deletions changelog.d/622-uk-target-references.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add typed UK 2023 active-subset Ledger target references plus the shared
selector vocabulary needed to resolve Chronicle-derived selectors by
dimensions, dimension values, groupby dimension, and record-set id.
78 changes: 74 additions & 4 deletions packages/microcosm-build/src/microcosm/build/ledger_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,16 +1033,37 @@ def _unique_facts(facts: Iterable[object]) -> tuple[object, ...]:


def _fact_matches_selector(fact: object, selector: Mapping[str, object]) -> bool:
"""Return whether a consumer fact satisfies a structured reference selector."""
"""Return whether a consumer fact satisfies a structured reference selector.

A list-valued scalar key matches by membership; an empty list is refused
rather than treated as match-nothing, because it reads like match-anything.
Note ``dimensions: []`` is NOT an empty membership list — it is the
list-form dimensions selector's exact name-set match for the dimensionless
total row (see :func:`_dimensions_match`).
"""

for key, expected in selector.items():
if key == "dimensions":
if not _dimensions_match(fact, expected):
return False
continue
if key == "dimension_values":
if not _dimension_values_match(fact, expected):
return False
continue
if expected is None or expected == "":
continue
candidates = _selector_candidates(fact, str(key))
if isinstance(expected, (list, tuple)):
if not expected:
raise ValueError(
f"Ledger fact selector field {key!r} is an empty list: an "
"empty membership list matches nothing while reading like "
"match-anything. Pin at least one value or drop the field."
)
if not any(str(item) in candidates for item in expected):
return False
continue
if str(expected) not in candidates:
return False
return True
Expand Down Expand Up @@ -1084,9 +1105,9 @@ def _selector_candidates(fact: object, key: str) -> tuple[str, ...]:
return (_str_at(fact, "geography", "id"),)
if key == "entity_name":
return (_str_at(fact, "entity", "name"),)
if key == "layout_record_set_id":
if key in {"record_set_id", "layout_record_set_id"}:
return (_str_at(fact, "layout", "record_set_id"),)
if key == "layout_groupby_dimension":
if key in {"groupby_dimension", "layout_groupby_dimension"}:
return (_str_at(fact, "layout", "groupby_dimension"),)
if key == "layout_groupby_value_id":
return (_str_at(fact, "layout", "groupby_value_id"),)
Expand All @@ -1100,14 +1121,63 @@ def _selector_candidates(fact: object, key: str) -> tuple[str, ...]:


def _dimensions_match(fact: object, expected: object) -> bool:
if isinstance(expected, list):
expected_names = {str(name) for name in expected}
dimension_names = {str(key) for key in _dimensions(fact)}
return dimension_names == expected_names
if not isinstance(expected, Mapping):
raise ValueError("Ledger fact selector field 'dimensions' must be a mapping.")
raise ValueError(
"Ledger fact selector field 'dimensions' must be a mapping or list."
)
dimensions = {str(key): str(value) for key, value in _dimensions(fact).items()}
return all(
dimensions.get(str(key)) == str(value) for key, value in expected.items()
)


def _dimension_values_match(fact: object, expected: object) -> bool:
"""Match dimension-value pins with strict typed equality.

A pin whose type disagrees with the fact's dimension value (float ``5.0``
against int ``5``, ``"5"`` against ``5``) does not error — the fact simply
fails to match, so under a resolves-or-defers activation rule the target
quietly drops out of the active subset. When a target is unexpectedly
inactive, check the authoring-run membership report before suspecting the
feed. Empty pin lists are refused for the same reason as empty membership
lists in :func:`_fact_matches_selector`.
"""

if not isinstance(expected, Mapping):
raise ValueError(
"Ledger fact selector field 'dimension_values' must be a mapping."
)
dimensions = _dimensions(fact)
for key, expected_value in expected.items():
if isinstance(expected_value, list) and not expected_value:
raise ValueError(
f"dimension_values pin {key!r} is an empty list: an empty pin "
"list matches nothing while reading like match-anything. Pin "
"at least one value or drop the dimension."
)
if key not in dimensions:
return False
actual_value = dimensions[key]
if isinstance(expected_value, list):
if not any(
_strict_scalar_equal(actual_value, candidate)
for candidate in expected_value
):
return False
continue
if not _strict_scalar_equal(actual_value, expected_value):
return False
return True


def _strict_scalar_equal(actual: object, expected: object) -> bool:
return type(actual) is type(expected) and actual == expected


def _measure_concepts(fact: object) -> tuple[str, ...]:
concepts = (
_str_at(fact, "measure", "concept"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"qrf_tail_reviewed_exclusions.json",
"release_input_coverage_manifest.json",
"was_wealth_support_bounds.json",
"uk_local_target_census.json"
"uk_local_target_census.json",
"uk_national_targets.json",
"target_references.json"
]
}
257 changes: 257 additions & 0 deletions packages/microcosm-build/src/microcosm/build/uk/target_references.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
{
"country": "uk",
"description": "UK active-subset Ledger target references for the national 2023 spine. Rows are generated from uk_national_targets.json: name is the contract target_id; ledger_selector is the contract selector plus geography_level country for the first national activation; entity is from_entity, then map_to, then household; measure is bindings.policyengine.metric_name; family is the contract family; period is 2023; metadata.contract_target_id points back to the contract row. Observed values stay in Ledger facts and resolve by identity only. Deferred classes are recorded in .codex-work/membership_report.json: no fact at or before 2023, multi-fact sum resolution pending, source-projection policy pending, country-level geography ambiguity, or missing/non-finite fact values. Measures name prepared columns, not raw model variables: each reference's measure is the slash-named column the microcosm#622 UK materializer must prepare on the reference's entity table from the contract binding payload (value_variable plus filters/conditions) referenced by metadata.contract_target_id, following the US prepared-indicator-column doctrine (target_spec_from_ledger_reference: count-like facts are sums of prepared indicator columns; the JCT materializer names household columns after their targets). metadata.measure_kind records this on every row. Activation is resolve-or-defer, not resolve-or-fail: a dimension pin whose type disagrees with the fact's value (float 5.0 vs int 5) deactivates the target rather than erroring - when a target is unexpectedly inactive, check the authoring-run membership report on the PR before suspecting the feed. Empty pin or membership lists are refused at resolve time.",
"allowed_value_operations": [
"identity"
],
"target_references": [
{
"name": "isc.private_school_students",
"ledger_selector": {
"source_name": "isc",
"source_concept": "isc.pupils_at_member_schools",
"geography_level": "country"
},
"entity": "person",
"measure": "obr/private_school_students",
"family": "isc",
"period": 2023,
"metadata": {
"contract_target_id": "isc.private_school_students",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.lone_households_under_65",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "lone_households_under_65"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/lone_households_under_65",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.lone_households_under_65",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.lone_households_over_65",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "lone_households_over_65"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/lone_households_over_65",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.lone_households_over_65",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.unrelated_adult_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "unrelated_adult_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/unrelated_adult_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.unrelated_adult_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.couple_no_children_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "couple_no_children_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/couple_no_children_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.couple_no_children_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.couple_under_3_children_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "couple_under_3_children_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/couple_under_3_children_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.couple_under_3_children_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.couple_3_plus_children_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "couple_3_plus_children_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/couple_3_plus_children_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.couple_3_plus_children_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.couple_non_dependent_children_only_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "couple_non_dependent_children_only_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/couple_non_dependent_children_only_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.couple_non_dependent_children_only_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.lone_parent_dependent_children_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "lone_parent_dependent_children_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/lone_parent_dependent_children_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.lone_parent_dependent_children_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.lone_parent_non_dependent_children_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "lone_parent_non_dependent_children_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/lone_parent_non_dependent_children_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.lone_parent_non_dependent_children_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.household_composition.multi_family_households",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.families_households_table7",
"groupby_dimension": "ons.household_type",
"dimension_values": {
"household_type": "multi_family_households"
},
"geography_level": "country"
},
"entity": "household",
"measure": "ons/multi_family_households",
"family": "ons_household_composition",
"period": 2023,
"metadata": {
"contract_target_id": "ons.household_composition.multi_family_households",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.savings_interest_income",
"ledger_selector": {
"source_name": "ons",
"source_concept": "ons.ukea_haxv",
"geography_level": "country"
},
"entity": "person",
"measure": "ons/savings_interest_income",
"family": "ons_national_accounts",
"period": 2023,
"metadata": {
"contract_target_id": "ons.savings_interest_income",
"measure_kind": "prepared_column"
}
},
{
"name": "ons.public_sector_employment",
"ledger_selector": {
"source_name": "ons",
"source_measure_id": "total_public_sector",
"geography_level": "country"
},
"entity": "person",
"measure": "ons/public_sector_employment",
"family": "ons_employment",
"period": 2023,
"metadata": {
"contract_target_id": "ons.public_sector_employment",
"measure_kind": "prepared_column"
}
}
]
}
Loading
Loading