diff --git a/changelog.d/nz-money-normalization.added.md b/changelog.d/nz-money-normalization.added.md new file mode 100644 index 0000000000..1e8381d4a2 --- /dev/null +++ b/changelog.d/nz-money-normalization.added.md @@ -0,0 +1,22 @@ +Add exact, declared donor money normalization: a pure module that validates a +complete money-normalization declaration (cited decimal-text or feed-replayed +rate, explicit quote, half-even rounding, donor money period, the one target +period every factor must age money to, per-family factors or explicit +identities, and quantile-mapped or pending rosters; a concept dropped in +transport can't be normalized) and applies it +with exact rationals, and a `money.normalize@1` transport kernel that replays +its factors against the authenticated feed, refuses a donor money period that +differs from the one CREATE records, and adds an integer cents companion per +column. CREATE now records the donor's `_time_period`. An opt-in preflight names +every consumed money column without exactly one producer and refuses a second +normalization along structural population inheritance, including a currency +conversion followed by normalization; cross-population evidence edges do not +change money ownership. Specs without the resource are unchanged. Factors gain +the additive `money_normalization_input` purpose. Declare New Zealand's +generated calendar-2024 donor factors with the existing factors' validity and +explicit per-profile selection. The donor period is a declared convention +covering flows and asset stocks, checked for consistency against `_time_period`; +ageing non-wage money by AWE growth is provisional until non-wage ageing is +ruled on. The normalization resource is the single source of the USD to NZD +rate. Both monetary rounding stages and receipt sums use exact fractions, reject +non-finite inputs, and check every integer companion for overflow. diff --git a/changelog.d/nz-reference-ageing-factors.added.md b/changelog.d/nz-reference-ageing-factors.added.md new file mode 100644 index 0000000000..2f8a2967d2 --- /dev/null +++ b/changelog.d/nz-reference-ageing-factors.added.md @@ -0,0 +1,15 @@ +Add declaration-driven, exact feed-derived reference ageing factors with certified +root bounds, once-only rounding, selected evidence identities and authenticated +compiler replay. Declare the reviewed NZ ageing profiles and their explicit money +and count companions while keeping the income references parked for measurement +closure. + +Retain authenticated decimal tokens only with explicit lexical-float loading for +selected factors; ordinary loads keep plain floats and their serializer and memory +behavior. Bind factor application to the selected money/count roster and preserve +count operations. Certify exact-root ties in the independent oracle and bound +supported schedules so exact receipts remain serializable. Float selector pins +match token-retaining floats while keeping booleans, integers and floats distinct. +Scan extra feed-identity fields for RuleSpec commits. Bind factor evidence to +reviewed feed identities while allowing prose-only pin edits; refuse replay when +the source commit cannot be authenticated. diff --git a/changelog.d/nz-reference-intervals.added.md b/changelog.d/nz-reference-intervals.added.md new file mode 100644 index 0000000000..34cd83efed --- /dev/null +++ b/changelog.d/nz-reference-intervals.added.md @@ -0,0 +1,24 @@ +Add exact income-band intervals: a shared, declaration-driven interval module +with no default semantics (closure, nil band, negative-income policy, first lower +cut, entity, columns and diagnostics window are all declared per table), +integer-cent source cuts, exact rational scaling that never rounds an aged cut, +universe-aware classification that refuses unresolved records or invalid amounts +before any indicator exists, and fixed-weight boundary diagnostics weighted +through `resolve_weights(entity)`. + +`scale_money_and_intervals` now scales named interval tables with the same +selected factor as their money companions and binds each scaled table to its +source and factor. Interval outputs are their own calibration binding kind. +Preparation passes the effective interval document into column preparation and +records its digest, payload and input fingerprints; the problem kernel binds a +boundary diagnostics report without changing any target. Composition supplies +each variant its own effective interval companion. Declare the NZ national +(237 bands) and age (183 bands) taxable-income tables under Microcosm's declared +convention; the 474 IRD references stay parked. + +Require the first cut explicitly. When a nil band is present, zero is nil under +either closure, so with a zero first cut the first positive band starts above +zero. Report below-first-cut mass and floored negative records, including +weighted counts and signed pre-floor amounts by destination. These diagnostics +describe fixed weights before fitting; reporting later weight changes on +unfitted records remains G8-r2c/G8-x work. diff --git a/docs/nz-calibration-targets.md b/docs/nz-calibration-targets.md index 2cc12cf60f..89df2cb487 100644 --- a/docs/nz-calibration-targets.md +++ b/docs/nz-calibration-targets.md @@ -99,6 +99,46 @@ All statistical spreadsheets on the un-gated `ird.govt.nz` CDN (HTTP 200 confirm Notes: **I1, I3, I4 are the load-bearing IRD targets.** IRD income tables are **individual-level** — joint/couple income unpublished. **FamilyBoost: no public statistics dataset confirmed** — only a modelled `Value_Type` in Treasury DistributionExplorer [UNCONFIRMED — re-check]. +### I1 taxable-income bands: a declared convention (G8-r2b, 2026-10-24) + +IRD publishes no band closure or cents semantics for the I1 tables. The +workbook introduces an "income between $0.01 and $100" band, and four label +pairs share an endpoint ($20,000, $50,000, $80,000 and $300,000: national rows +29/30, 59/60, 89/90 and 229/230; age rows 27/28, 57/58 and 87/88). Microcosm +therefore applies its own declared convention, not IRD's semantics (G8-r open +rulings 2, section 1): + +- t = max(0, x) in integer cents, with no dollar rounding; +- t == 0 is the nil band, including qualifying loss records; +- each positive band is `previous_upper < t <= upper`, and the open tail is + `t > its lower cut` (national top band over $1,000,000, age table over + $180,000); +- the ordered right-closed cuts give each shared endpoint to the lower band. + +`build/nz/reference_intervals.json` holds the two tables: the national table +(237 bands, rows 8–244, each naming its parked count and amount references) +and the age table (183 bands, rows 6–188, no references and no fit rows). +`tools/generate_nz_target_references.py` generates both from the row formulas +and checks every cut against the pinned feed's own band labels. Ageing scales +the national cuts exactly by the selected factor and keeps fractional cents: +under the central factor 1.117109, $100 becomes 11,171.09 cents and $1,000 +becomes 111,710.9 cents. Before matrix construction the calibration problem +reports, for every cut, the weighted mass within $1, and for every band the +fixed-weight change under the left-closed alternative. These diagnostics never +change a target, and publisher reclassification is unavailable, because the +published histogram does not identify mass exactly on a cut. + +The shared diagnostics also report records below a table's first lower cut, +including their weighted count and amount. Floored negative records retain +their separate count and signed pre-floor amount, grouped by destination. +These are fixed-weight, pre-fit measurements; reporting the later weight +changes on these unfitted records remains G8-r2c/G8-x work. + +The income and universe columns (`ird_taxable_income_proxy_cents`, +`ird_taxable_income_proxy_universe_status`) name the d1952 stand-in measure +rather than IRD's definition. No producer exists yet (G8-r2c), no active row +requests them, and all 474 IRD rows stay parked. + ## 3. MSD **Critical structural finding:** the **quarterly Benefit Fact Sheets are COUNT-ONLY** (every sheet opened; only hardship reports dollars). **Dollar-level data comes from the Annual Report** expenditure schedule (explicit CC BY 4.0). diff --git a/docs/nz-take-up-engine-plan.md b/docs/nz-take-up-engine-plan.md index 1569e61cf8..2e765dffff 100644 --- a/docs/nz-take-up-engine-plan.md +++ b/docs/nz-take-up-engine-plan.md @@ -129,9 +129,12 @@ tenure targets close to each region's published Census household total before the existing ERP bridge. Calibration variant V4 concentrates only the size residual onto one- and two-person households. The implementation preserves raw discrepancies and independent national diagnostics, and supplies validated size -columns before activation. G8-r2 reuses the same resolver for ageing profiles -and interval preparation; central ageing remains explicitly deferred and V1/V2 -ageing transformations remain unavailable until that work lands. +columns before activation. G8-r2 reuses the same resolver for ageing: each +low, central or high profile scales the IRD money references and, exactly, the +national taxable-income band cuts by its own feed-derived factor, and the +interval tables declare Microcosm's band convention (see +[the target inventory](nz-calibration-targets.md)). The 474 IRD references +stay parked until the B4 stand-in measure's producer lands (G8-r2c). - **Pre-calibration:** IRD wage and salary bands, MBIE bond rents by TA, Stats NZ household financial net worth, TA population, and the same MSD recipient facts as the calibration set (the take-up draws calibrate to them). diff --git a/packages/microcosm-build/src/microcosm/build/calibration_columns.py b/packages/microcosm-build/src/microcosm/build/calibration_columns.py index be3897d360..c7a986b21b 100644 --- a/packages/microcosm-build/src/microcosm/build/calibration_columns.py +++ b/packages/microcosm-build/src/microcosm/build/calibration_columns.py @@ -1,7 +1,8 @@ """Prepare simple calibration columns from an explicit, engine-free contract. Geography and canonical concepts must already be present. This helper creates -constants, validated roster counts and conjunctions of declared predicates; +constants, validated roster counts, conjunctions of declared predicates and +interval indicators classified by :mod:`microcosm.build.reference_intervals`; receipt assignment and statistical-unit construction belong to their own producers. """ @@ -15,15 +16,26 @@ import numpy as np import pandas as pd +from microcosm.build.reference_intervals import ( + interval_indicators, + validate_interval_document, +) + def prepare_calibration_columns( - tables: Mapping[str, pd.DataFrame], bindings: Mapping[str, Any] + tables: Mapping[str, pd.DataFrame], + bindings: Mapping[str, Any], + *, + intervals: Mapping[str, Any] | None = None, ) -> dict[str, pd.DataFrame]: """Return independent tables with the contract's prepared columns. All required inputs are validated before any column is produced. Existing prepared columns are recomputed, so a changed geography or age cannot retain stale indicators. Missing/unknown inputs never become all-false categories. + An interval output names its entity, an effective table of ``intervals`` + and one of its bands; every record of each named table is validated, and + any refusal raises, before an output exists. """ if bindings.get("schema_version") != 1: raise ValueError("Calibration bindings require schema_version 1.") @@ -48,6 +60,7 @@ def prepare_calibration_columns( raise ValueError(f"Calibration input {entity}.{name} needs a domain.") _validate_input(tables[entity][name], domain, f"{entity}.{name}") + interval_columns = _interval_columns(tables, columns, required, intervals) out = {entity: table.copy(deep=True) for entity, table in tables.items()} derived = _prepare_roster_counts(out, bindings.get("roster_counts", ()), required) prepared: dict[str, dict[str, object]] = {} @@ -69,6 +82,9 @@ def prepare_calibration_columns( f"Calibration output {entity}.{name} is repeated or structural." ) seen.add(key) + if "interval" in entry: + prepared.setdefault(entity, {})[name] = interval_columns[key] + continue if "constant" in entry: value = entry["constant"] if "predicates" in entry or not _finite_number(value): @@ -115,6 +131,81 @@ def prepare_calibration_columns( return out +def _interval_columns( + tables: Mapping[str, pd.DataFrame], + columns: object, + required: Mapping[str, Any], + intervals: Mapping[str, Any] | None, +) -> dict[tuple[str, str], np.ndarray]: + """Classify every named table once; refuse before any output is produced.""" + entries = [ + entry for entry in columns if isinstance(entry, Mapping) and "interval" in entry + ] + if not entries: + return {} + if intervals is None: + raise ValueError("Interval outputs need the effective interval document.") + document = validate_interval_document(intervals) + requested: dict[str, list[tuple[str, str, str]]] = {} + for entry in entries: + entity, name, binding = ( + entry.get("entity"), + entry.get("name"), + entry["interval"], + ) + if set(entry) != {"entity", "name", "interval"}: + raise ValueError( + f"Interval output {entity}.{name} declares only its entity, name " + "and interval." + ) + if not isinstance(binding, Mapping) or set(binding) != {"table", "band"}: + raise ValueError("An interval output names exactly its table and band.") + table = document.get(binding["table"]) + if table is None or binding["band"] not in table.band_ids: + raise ValueError( + f"Interval output {entity}.{name} names an undeclared table or band." + ) + if entity not in required or entity != table.entity: + raise ValueError( + f"Interval output {entity}.{name} must name its table's entity." + ) + if name != table.indicators[table.band_ids.index(binding["band"])]: + raise ValueError( + f"Interval output {entity}.{name} must be its band's declared indicator." + ) + if ( + table.declaration["source_period"] != table.declaration["target_period"] + and not table.scaled + ): + raise ValueError( + f"Interval table {table.id!r} has source-period cuts; scale it to its " + "target period before preparing indicators." + ) + requested.setdefault(table.id, []).append((entity, name, binding["band"])) + result: dict[tuple[str, str], np.ndarray] = {} + computed = {} + for table_id in requested: + table = document[table_id] + data = tables.get(table.entity) + if data is None or any( + column not in data + for column in (table.income_column, table.universe_column) + ): + raise ValueError( + f"Interval table {table_id!r} needs {table.entity}." + f"{table.income_column} and {table.entity}.{table.universe_column}." + ) + computed[table_id] = interval_indicators( + table, + data[table.income_column].tolist(), + data[table.universe_column].tolist(), + ) + for table_id, outputs in requested.items(): + for entity, name, _ in outputs: + result[entity, name] = computed[table_id][name] + return result + + def _finite_number(value: object) -> bool: return ( isinstance(value, Real) and not isinstance(value, bool) and math.isfinite(value) @@ -196,6 +287,11 @@ def _validate_input(values: pd.Series, domain: Mapping[str, Any], name: str) -> for value in values.tolist(): _identity(value) valid = True + elif domain.get("kind") == "signed_integer": + valid = all( + isinstance(value, Integral) and not isinstance(value, (bool, np.bool_)) + for value in values.tolist() + ) elif domain.get("kind") == "nonnegative_integer": valid = all( _finite_number(value) and value >= 0 and value == int(value) diff --git a/packages/microcosm-build/src/microcosm/build/ledger_artifact.py b/packages/microcosm-build/src/microcosm/build/ledger_artifact.py index da0eb1968b..c181d68e9a 100644 --- a/packages/microcosm-build/src/microcosm/build/ledger_artifact.py +++ b/packages/microcosm-build/src/microcosm/build/ledger_artifact.py @@ -64,7 +64,9 @@ "CHRONICLE_FEED_RESOURCE", "ChronicleFeedPinMismatchError", "LedgerConsumerArtifact", + "LexicalFloat", "add_ledger_artifact_args", + "chronicle_feed_identity", "load_ledger_consumer_artifact", "resolve_ledger_artifact", "validate_chronicle_feed_pin", @@ -78,6 +80,51 @@ ALLOWED_LEDGER_ASSERTIONS = frozenset(("observation", "source_projection")) DEFAULT_LEDGER_ASSERTION = "observation" CHRONICLE_FEED_RESOURCE = "chronicle_feed.json" +_FEED_IDENTITY_FIELDS = ( + "source_commit", + "facts_sha256", + "manifest_sha256", + "artifact_schema_version", + "consumer_fact_schema_versions", + "consumer_fact_schema_sha256", + "fact_row_count", +) + + +def chronicle_feed_identity(feed_pin: Mapping[str, Any]) -> dict[str, Any]: + """Select reviewed source commit and loader-verified fields, excluding prose. + + The source commit is a caller declaration unless the authenticated manifest + also carries it; the artifact loader verifies the remaining six fields. + """ + missing = sorted(set(_FEED_IDENTITY_FIELDS) - feed_pin.keys()) + if missing: + raise ValueError( + f"Chronicle factor feed identity is missing fields: {missing}." + ) + if not isinstance(feed_pin["source_commit"], str) or not feed_pin["source_commit"]: + raise ValueError("Chronicle factor feed identity requires a source commit.") + return {field: feed_pin[field] for field in _FEED_IDENTITY_FIELDS} + + +class LexicalFloat(float): + """An opt-in float retaining its authenticated JSON numeric token. + + Request ``lexical_floats=True`` when loading operands for exact arithmetic. + The default loader returns plain floats, avoiding the instance and token + overhead and preserving compatibility with serializers requiring exact + float types. Standard JSON serialization retains the same numeric bytes. + """ + + __slots__ = ("lexical_token",) + + def __new__(cls, token: str): + instance = super().__new__(cls, token) + instance.lexical_token = token + return instance + + def __reduce__(self): + return type(self), (self.lexical_token,) class ChronicleFeedPinMismatchError(ValueError): @@ -250,6 +297,7 @@ def load_ledger_consumer_artifact( expected_facts_sha256: str | None = None, expected_manifest_sha256: str | None = None, feed_pin: Mapping[str, Any] | None = None, + lexical_floats: bool = False, ) -> LedgerConsumerArtifact: """Load a Ledger consumer artifact directory or bare consumer-facts file. @@ -259,6 +307,8 @@ def load_ledger_consumer_artifact( reviewed against; any mismatch raises before facts are used. ``feed_pin`` additionally checks all six reviewed artifact fields, without replacing the manifest integrity checks or either optional hash check. + ``lexical_floats=True`` retains numeric tokens for factor verification; + otherwise JSON decimals load as plain ``float`` instances. """ artifact_path = Path(path) if not artifact_path.exists(): @@ -314,7 +364,7 @@ def load_ledger_consumer_artifact( if feed_pin is not None: artifact = LedgerConsumerArtifact( path=artifact_path, - facts=_load_fact_rows(facts_path), + facts=_load_fact_rows(facts_path, lexical_floats=lexical_floats), facts_sha256=facts_sha256, manifest=manifest, manifest_sha256=manifest_sha256, @@ -340,7 +390,7 @@ def load_ledger_consumer_artifact( if artifact is None: artifact = LedgerConsumerArtifact( path=artifact_path, - facts=_load_fact_rows(facts_path), + facts=_load_fact_rows(facts_path, lexical_floats=lexical_floats), facts_sha256=facts_sha256, manifest=manifest, manifest_sha256=manifest_sha256, @@ -389,7 +439,9 @@ def resolve_ledger_artifact(args: Namespace) -> LedgerConsumerArtifact | None: ) -def _load_fact_rows(path: Path) -> tuple[dict[str, Any], ...]: +def _load_fact_rows( + path: Path, *, lexical_floats: bool = False +) -> tuple[dict[str, Any], ...]: rows: list[dict[str, Any]] = [] with path.open() as file: for line_number, line in enumerate(file, start=1): @@ -397,7 +449,9 @@ def _load_fact_rows(path: Path) -> tuple[dict[str, Any], ...]: if not stripped: continue try: - row = json.loads(stripped) + row = json.loads( + stripped, parse_float=LexicalFloat if lexical_floats else float + ) except json.JSONDecodeError as exc: raise ValueError( f"Invalid Ledger facts JSONL row {line_number}: {exc.msg}" diff --git a/packages/microcosm-build/src/microcosm/build/ledger_targets.py b/packages/microcosm-build/src/microcosm/build/ledger_targets.py index 3af241ca1c..035f18ad8a 100644 --- a/packages/microcosm-build/src/microcosm/build/ledger_targets.py +++ b/packages/microcosm-build/src/microcosm/build/ledger_targets.py @@ -20,6 +20,7 @@ from types import MappingProxyType from typing import Any +from microcosm.build.ledger_artifact import LexicalFloat from microcosm.calibrate import ( CalibrationHierarchy, CalibrationHierarchySeed, @@ -5058,7 +5059,9 @@ def _dimension_values_match(fact: object, expected: object) -> bool: def _strict_scalar_equal(actual: object, expected: object) -> bool: - return type(actual) is type(expected) and actual == expected + actual_type = float if type(actual) is LexicalFloat else type(actual) + expected_type = float if type(expected) is LexicalFloat else type(expected) + return actual_type is expected_type and actual == expected def _measure_concepts(fact: object) -> tuple[str, ...]: diff --git a/packages/microcosm-build/src/microcosm/build/money_normalization.py b/packages/microcosm-build/src/microcosm/build/money_normalization.py new file mode 100644 index 0000000000..d23b2b79fb --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/money_normalization.py @@ -0,0 +1,768 @@ +"""Exact, declared normalization of donor money columns. + +A country declares, as spec data, how each donor money concept reaches its +destination currency and money period: one exchange rate and one feed-derived +period factor per index family, or a named quantile map that is still owed. +This module validates that declaration and applies it with exact rationals. +It holds no country, currency, rate, factor, series or date. + +For a binary64 donor value ``v``: + +- ``x`` is the shortest round-trip decimal of ``v`` (``repr``), read exactly. +- The first rounding converts currency. With the rate as the exact rational + ``num/den`` of its declared decimal, ``to_per_from`` gives + ``m1 = half_even(x * num/den * 10**digits)`` and ``from_per_to`` gives + ``m1 = half_even(x * 10**digits * den/num)``; a quoted rate is never + inverted through a rounded constant. +- The second rounding applies the selected factor ``p / 10**k`` from its + authenticated receipt: ``m2 = half_even(m1 * p / 10**k)``. +- The rewritten column is ``m2 / 10**digits`` and the integer companion + ```` is ``m2``. + +Rounding twice (currency, then factor) can differ from rounding +``x * rate * factor`` once. Each rounding moves a value by at most half a +minor unit, so ``|m2 - half_even(exact * f)| <= floor(1 + f / 2)`` minor +units, which is one minor unit for every factor below 2. + +Every normalized column maps through its concept's index family to one +declared factor, or to an explicit identity with a reason; nothing falls back +to a factor of one. Every factor ``money.normalize@1`` applies ages money to +the one declared ``target_period``: the kernel checks each selected factor +through ``money_factor_inputs``, while a direct caller of +``normalize_money_tables`` supplies its factors unchecked. Every money concept +that survives transport is declared exactly once: normalized, quantile mapped +or pending. +""" + +from __future__ import annotations + +import math +import re +from collections.abc import Iterable, Mapping +from datetime import date +from decimal import Decimal, InvalidOperation +from fractions import Fraction + +import numpy as np +import pandas as pd + +from microcosm.build.ledger_artifact import chronicle_feed_identity +from microcosm.build.ledger_targets import ( + ALLOWED_ASSERTION_POLICIES, + selector_field_is_supported, +) +from microcosm.build.reference_factors import ( + factor_document_sha256, + resolve_feed_operand, +) +from microcosm.frame.concepts import ( + CONCEPTS, + IndexFamily, + TransportRule, + Unit, + concept_for_column, +) + +__all__ = [ + "MONEY_FACTOR_PURPOSE", + "check_money_factor", + "money_column_rosters", + "money_concept_columns", + "money_factor_inputs", + "normalize_money_tables", + "normalized_column_families", + "resolve_money_rate", + "selected_money_factors", + "validate_money_normalization", +] + +#: The only factor purpose a money normalization may apply. +MONEY_FACTOR_PURPOSE = "money_normalization_input" +_QUOTES = frozenset({"to_per_from", "from_per_to"}) +_ROUNDING_MODES = frozenset({"half_even"}) +_PERIOD_TYPES = frozenset({"calendar_year"}) +_PERIOD_BASES = frozenset({"declared_convention", "authenticated"}) +_RATE_FORMS = frozenset({"decimal_text", "feed_selector"}) +_MAX_MONEY_DIGITS = 6 +_INT64_MIN = -(2**63) +_INT64_MAX = 2**63 - 1 +_FIELDS = frozenset( + { + "schema_version", + "from_currency", + "to_currency", + "rate", + "quote", + "rounding", + "donor_money_period", + "target_period", + "columns", + "index_family_factors", + "factor_profile", + "cents_suffix", + "quantile_mapped", + "pending", + } +) +_DESCRIPTIVE_FIELDS = frozenset({"description", "status", "notes"}) +_CURRENCY = re.compile(r"[A-Z]{3}") +_DECIMAL_TEXT = re.compile(r"[0-9]+(?:\.[0-9]+)?") +_SUFFIX = re.compile(r"_[a-z0-9_]+") +_ROSTERS = ("normalized", "quantile_mapped", "pending") + + +def _mapping(value: object, label: str) -> dict: + if not isinstance(value, Mapping): + raise ValueError(f"Money normalization {label} must be an object.") + return dict(value) + + +def _closed(value: object, fields: frozenset, label: str) -> dict: + row = _mapping(value, label) + unknown = sorted(row.keys() - fields) + if unknown: + raise ValueError(f"Money normalization {label} has unknown fields {unknown}.") + missing = sorted(fields - row.keys()) + if missing: + raise ValueError(f"Money normalization {label} is missing fields {missing}.") + return row + + +def _text(value: object, label: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise ValueError(f"Money normalization {label} must be nonempty text.") + return value + + +def _decimal_text(value: object, label: str) -> Fraction: + """A positive decimal written as JSON text; a JSON number is refused.""" + if not isinstance(value, str): + raise ValueError( + f"Money normalization {label} must be decimal text, never a JSON " + "number: a binary float cannot carry the declared decimal exactly." + ) + if _DECIMAL_TEXT.fullmatch(value) is None: + raise ValueError(f"Money normalization {label} must be plain decimal text.") + try: + result = Fraction(Decimal(value)) + except (InvalidOperation, ValueError) as error: + raise ValueError(f"Money normalization {label} is not decimal text.") from error + if result <= 0: + raise ValueError(f"Money normalization {label} must be positive.") + return result + + +def _rate(value: object) -> dict: + row = _mapping(value, "rate") + form = row.get("form") + if form not in _RATE_FORMS: + raise ValueError( + f"Money normalization rate form must be one of {sorted(_RATE_FORMS)}." + ) + if form == "decimal_text": + row = _closed(row, frozenset({"form", "text", "citation"}), "rate") + _decimal_text(row["text"], "rate text") + citation = _mapping(row["citation"], "rate citation") + if not {"publisher", "title", "url"} <= citation.keys() or any( + not isinstance(item, str) or not item for item in citation.values() + ): + raise ValueError( + "Money normalization rate citation needs publisher, title and url " + "as nonempty text." + ) + return row + row = _closed( + row, + frozenset( + { + "form", + "selector", + "validation", + "assertion_policy", + "feed_identity", + "expected_decimal", + } + ), + "rate", + ) + selector = _mapping(row["selector"], "rate selector") + if not selector or any(not selector_field_is_supported(key) for key in selector): + raise ValueError( + "Money normalization rate selector must use the closed selector vocabulary." + ) + _mapping(row["validation"], "rate validation") + if row["assertion_policy"] not in ALLOWED_ASSERTION_POLICIES: + raise ValueError("Unknown money normalization rate assertion policy.") + row["feed_identity"] = chronicle_feed_identity( + _mapping(row["feed_identity"], "rate feed identity") + ) + _decimal_text(row["expected_decimal"], "rate expected_decimal") + return row + + +def _period(value: object) -> dict: + row = _closed( + value, + frozenset({"type", "value", "basis", "evidence"}), + "donor_money_period", + ) + if row["type"] not in _PERIOD_TYPES: + raise ValueError( + f"Money normalization donor period type must be one of {sorted(_PERIOD_TYPES)}." + ) + if type(row["value"]) is not int or not 1 <= row["value"] <= 9999: + raise ValueError("Money normalization donor period value must be a year.") + if row["basis"] not in _PERIOD_BASES: + raise ValueError( + "Money normalization donor period basis must be one of " + f"{sorted(_PERIOD_BASES)}." + ) + evidence = row["evidence"] + if not isinstance(evidence, list) or not evidence: + raise ValueError( + "Money normalization donor period evidence must cite its basis." + ) + for citation in evidence: + _text(citation, "donor period evidence") + return row + + +def _period_coverage(period: Mapping) -> tuple[str, str]: + year = period["value"] + return date(year, 1, 1).isoformat(), date(year, 12, 31).isoformat() + + +def _money_concept(entity: object, column: object, label: str): + if not isinstance(entity, str) or not isinstance(column, str) or not column: + raise ValueError(f"Money normalization {label} must name entity columns.") + item = concept_for_column(entity, column) + if item is None or item.unit is not Unit.BASE_CURRENCY: + raise ValueError( + f"Money normalization {label} {entity}.{column} is not a money concept." + ) + return item + + +def _columns(value: object) -> dict[str, list[str]]: + columns = _mapping(value, "columns") + if not columns: + raise ValueError("Money normalization columns must name normalized columns.") + result = {} + for entity, names in columns.items(): + if ( + not isinstance(names, list) + or not names + or len(set(map(str, names))) != len(names) + ): + raise ValueError( + "Money normalization columns must be nonempty lists of distinct names." + ) + for name in names: + item = _money_concept(entity, name, "column") + if item.transport is TransportRule.DROP: + # Quantile maps and pending rows may still name a dropped + # concept; a normalizer would only fail later, at run time. + raise ValueError( + f"Money normalization column {entity}.{name} is dropped in " + "transport, so it cannot be normalized." + ) + result[entity] = list(names) + return result + + +def _roster(value: object, label: str) -> dict[str, dict[str, dict]]: + roster = _mapping(value, label) + result = {} + for entity, rows in roster.items(): + rows = _mapping(rows, f"{label} entity") + if not rows: + raise ValueError(f"Money normalization {label} lists an empty entity.") + result[entity] = {} + for column, row in rows.items(): + _money_concept(entity, column, label) + row = _closed(row, frozenset({"awaits", "reason"}), f"{label} entry") + awaits = row["awaits"] + if ( + not isinstance(awaits, list) + or not awaits + or any(not isinstance(item, str) or not item for item in awaits) + or len(set(awaits)) != len(awaits) + ): + raise ValueError( + f"Money normalization {label} entry must name the references " + "it awaits." + ) + _text(row["reason"], f"{label} reason") + result[entity][column] = row + return result + + +def _profile(value: object) -> dict | None: + if value is None: + return None + row = _closed( + value, frozenset({"dimension", "profiles", "central_profile"}), "factor_profile" + ) + _text(row["dimension"], "factor profile dimension") + profiles = row["profiles"] + if ( + not isinstance(profiles, list) + or not profiles + or any(not isinstance(name, str) or not name for name in profiles) + or len(set(profiles)) != len(profiles) + ): + raise ValueError("Money normalization factor profiles must be distinct names.") + if row["central_profile"] not in profiles: + raise ValueError("Money normalization central profile must be a profile.") + return row + + +def _family_entry(value: object, family: str, profile: dict | None) -> dict: + row = _mapping(value, f"index family {family!r}") + if "identity" in row: + row = _closed(row, frozenset({"identity", "reason"}), f"identity {family!r}") + if row["identity"] is not True: + raise ValueError( + f"Money normalization identity family {family!r} must declare " + "identity true." + ) + _text(row["reason"], f"identity reason for {family!r}") + return row + if profile is None: + row = _closed( + row, frozenset({"factor_id", "disclosure"}), f"index family {family!r}" + ) + _text(row["factor_id"], f"factor id for {family!r}") + else: + row = _closed( + row, frozenset({"factors", "disclosure"}), f"index family {family!r}" + ) + factors = _mapping(row["factors"], f"factors for {family!r}") + if set(factors) != set(profile["profiles"]): + raise ValueError( + f"Money normalization family {family!r} must name one factor for " + "every declared profile." + ) + for name in factors.values(): + _text(name, f"factor id for {family!r}") + _text(row["disclosure"], f"disclosure for {family!r}") + return row + + +def money_concept_columns() -> dict[tuple[str, str], object]: + """Every money concept column, keyed ``(entity, column)``.""" + return { + (item.entity, item.name): item + for item in CONCEPTS + if item.unit is Unit.BASE_CURRENCY + } + + +def validate_money_normalization(document: object) -> dict: + """Validate a complete declaration; every field is required. + + Descriptive ``description``, ``status`` and ``notes`` text may ride along + and is returned unchanged; nothing reads it. + """ + row = _mapping(document, "declaration") + descriptive = {key: row.pop(key) for key in _DESCRIPTIVE_FIELDS if key in row} + for key, value in descriptive.items(): + _text(value, key) + row = _closed(row, _FIELDS, "declaration") + if type(row["schema_version"]) is not int or row["schema_version"] != 1: + raise ValueError("Money normalization requires schema_version 1.") + for key in ("from_currency", "to_currency"): + if not isinstance(row[key], str) or _CURRENCY.fullmatch(row[key]) is None: + raise ValueError(f"Money normalization {key} must be an ISO 4217 code.") + if row["from_currency"] == row["to_currency"]: + raise ValueError("Money normalization converts between distinct currencies.") + row["rate"] = _rate(row["rate"]) + if row["quote"] not in _QUOTES: + raise ValueError(f"Money normalization quote must be one of {sorted(_QUOTES)}.") + rounding = _closed(row["rounding"], frozenset({"digits", "mode"}), "rounding") + if ( + type(rounding["digits"]) is not int + or not 0 <= rounding["digits"] <= _MAX_MONEY_DIGITS + ): + raise ValueError( + f"Money normalization rounding digits must be 0 to {_MAX_MONEY_DIGITS}." + ) + if rounding["mode"] not in _ROUNDING_MODES: + raise ValueError("Money normalization rounding mode must be half_even.") + row["donor_money_period"] = _period(row["donor_money_period"]) + if type(row["target_period"]) is not int or not 1 <= row["target_period"] <= 9999: + raise ValueError( + "Money normalization target_period must be the integer period label " + "its factors age money to." + ) + row["columns"] = _columns(row["columns"]) + if ( + not isinstance(row["cents_suffix"], str) + or _SUFFIX.fullmatch(row["cents_suffix"]) is None + ): + raise ValueError( + "Money normalization cents_suffix must be lowercase text starting with _." + ) + profile = _profile(row["factor_profile"]) + row["quantile_mapped"] = _roster(row["quantile_mapped"], "quantile_mapped") + row["pending"] = _roster(row["pending"], "pending") + seen: dict[tuple[str, str], str] = {} + for roster, entity, column in ( + *( + ("normalized", entity, column) + for entity, names in row["columns"].items() + for column in names + ), + *( + (name, entity, column) + for name in ("quantile_mapped", "pending") + for entity, rows in row[name].items() + for column in rows + ), + ): + key = (entity, column) + if key in seen: + raise ValueError( + f"Money column {entity}.{column} is declared both {seen[key]} and " + f"{roster}; each column has exactly one producer route." + ) + seen[key] = roster + companion = f"{column}{row['cents_suffix']}" + if concept_for_column(entity, companion) is not None: + raise ValueError( + f"Money normalization companion {entity}.{companion} collides with " + "a concept column." + ) + unclassified = sorted( + f"{entity}.{column}" + for (entity, column), item in money_concept_columns().items() + if item.transport is not TransportRule.DROP and (entity, column) not in seen + ) + if unclassified: + raise ValueError( + "Money normalization must declare every transported money concept as " + f"normalized, quantile_mapped or pending; unclassified: {unclassified}." + ) + families = _mapping(row["index_family_factors"], "index_family_factors") + used = { + money_concept_columns()[entity, column].monetary.index_family.value + for entity, names in row["columns"].items() + for column in names + } + known = {item.value for item in IndexFamily} + if set(families) - known: + raise ValueError( + f"Money normalization names unknown index families {sorted(set(families) - known)}." + ) + unmapped = sorted(used - set(families)) + if unmapped: + raise ValueError( + f"Money normalization index families {unmapped} have no declared factor " + "or explicit identity; a family is never silently left at one." + ) + unused = sorted(set(families) - used) + if unused: + raise ValueError( + f"Money normalization declares index families {unused} that no " + "normalized column uses." + ) + row["index_family_factors"] = { + family: _family_entry(entry, family, profile) + for family, entry in families.items() + } + row["factor_profile"] = profile + return {**row, **descriptive} + + +def money_column_rosters(declaration: Mapping) -> dict[tuple[str, str], tuple]: + """``(entity, column)`` to ``(route, awaits)`` for every declared column.""" + result = { + (entity, column): ("normalized", ()) + for entity, names in declaration["columns"].items() + for column in names + } + for route in ("quantile_mapped", "pending"): + for entity, rows in declaration[route].items(): + for column, row in rows.items(): + result[entity, column] = (route, tuple(row["awaits"])) + return result + + +def normalized_column_families(declaration: Mapping) -> dict[tuple[str, str], str]: + """Each normalized column's index family, read from the concept schema.""" + concepts = money_concept_columns() + return { + (entity, column): concepts[entity, column].monetary.index_family.value + for entity, names in declaration["columns"].items() + for column in names + } + + +def selected_money_factors( + declaration: Mapping, profile_selection: Mapping +) -> tuple[str | None, dict[str, str | None]]: + """The selected profile and each family's factor id (``None``: identity). + + A selection that omits the declared dimension selects its central profile, + as a scenario that leaves a knob at its central value does. + """ + selection = _mapping(profile_selection, "profile selection") + profile = declaration["factor_profile"] + chosen = None + if profile is not None: + chosen = selection.get(profile["dimension"], profile["central_profile"]) + if chosen not in profile["profiles"]: + raise ValueError( + f"Unknown money normalization profile {chosen!r} for " + f"{profile['dimension']!r}." + ) + factors = {} + for family, entry in declaration["index_family_factors"].items(): + if entry.get("identity") is True: + factors[family] = None + elif profile is None: + factors[family] = entry["factor_id"] + else: + factors[family] = entry["factors"][chosen] + return chosen, factors + + +def check_money_factor(entry: Mapping, declaration: Mapping) -> tuple[int, int]: + """Return ``(p, k)`` for a factor ``p / 10**k`` fit for this declaration. + + The factor must carry the money-normalization purpose, its source + schedule must cover exactly the declared donor money period, and it must + age money to the declared ``target_period``, so every family reaches the + same destination period. + """ + factor_declaration = entry["declaration"] + if factor_declaration["purpose"] != MONEY_FACTOR_PURPOSE: + raise ValueError( + f"Factor {factor_declaration['id']!r} has purpose " + f"{factor_declaration['purpose']!r}; money normalization applies only " + f"{MONEY_FACTOR_PURPOSE} factors." + ) + target = factor_declaration["target_period"] + if type(target) is not int or target != declaration["target_period"]: + raise ValueError( + f"Factor {factor_declaration['id']!r} ages money to {target!r}, not " + f"the declared target_period {declaration['target_period']!r}." + ) + schedule = factor_declaration["source_schedule"] + coverage = ( + schedule[0]["validation"]["period_coverage"]["start_date"], + schedule[-1]["validation"]["period_coverage"]["end_date"], + ) + if coverage != _period_coverage(declaration["donor_money_period"]): + raise ValueError( + f"Factor {factor_declaration['id']!r} source schedule covers " + f"{coverage[0]}..{coverage[1]}, not the declared donor money period." + ) + digits = entry["receipt"]["rounding"]["digits"] + rational = entry["operational_factor"] + if ( + type(digits) is not int + or rational["denominator"] != 10**digits + or type(rational["numerator"]) is not int + or rational["numerator"] <= 0 + ): + raise ValueError( + "A money factor must be a positive integer over 10**digits from its receipt." + ) + return rational["numerator"], digits + + +def resolve_money_rate( + declaration: Mapping, + facts: Iterable | None = None, + feed_identity: Mapping | None = None, +) -> tuple[Fraction, dict]: + """The exact rate and its evidence; a feed rate is replayed from facts.""" + rate = declaration["rate"] + if rate["form"] == "decimal_text": + return _decimal_text(rate["text"], "rate text"), { + "form": "decimal_text", + "text": rate["text"], + "citation": dict(rate["citation"]), + } + if facts is None or feed_identity is None: + raise ValueError("A feed-selected rate needs the authenticated feed facts.") + if rate["feed_identity"] != chronicle_feed_identity(feed_identity): + raise ValueError( + "Money rate feed identity mismatch; re-declaration is required." + ) + value, evidence = resolve_feed_operand( + {"selector": rate["selector"], "validation": rate["validation"]}, + facts, + assertion_policy=rate["assertion_policy"], + ) + if value != _decimal_text(rate["expected_decimal"], "rate expected_decimal"): + raise ValueError( + "Money rate replay differs from its declared expected decimal; " + "re-declaration is required." + ) + return value, { + "form": "feed_selector", + "text": rate["expected_decimal"], + "operand": evidence, + } + + +def _half_even(value: Fraction) -> int: + """Round an exact rational to an integer, ties to even.""" + return round(value) + + +def _shortest_decimal(value: object) -> Fraction: + """The shortest round-trip decimal of a float, read without arithmetic.""" + number = float(value) + if not math.isfinite(number): + raise ValueError("Money normalization refuses NaN and infinite amounts.") + return Fraction(Decimal(repr(number))) + + +def _rational(value: Fraction) -> dict: + return {"numerator": value.numerator, "denominator": value.denominator} + + +def _exact_decimal(value: Fraction) -> str: + """Exact decimal text of a rational whose denominator divides 10**k.""" + denominator, places = value.denominator, 0 + while denominator % 10 == 0: + denominator //= 10 + places += 1 + while denominator % 2 == 0 or denominator % 5 == 0: + factor = 2 if denominator % 2 == 0 else 5 + denominator //= factor + places += 1 + if denominator != 1: + raise ValueError("A money sum is always a terminating decimal.") + scaled = int(value * Fraction(10**places)) + sign = "-" if scaled < 0 else "" + digits = str(abs(scaled)).rjust(places + 1, "0") + return sign + (digits if not places else f"{digits[:-places]}.{digits[-places:]}") + + +def _minor_units(values: Iterable[int], digits: int) -> str: + total = sum(values) + sign = "-" if total < 0 else "" + text = str(abs(total)).rjust(digits + 1, "0") + return sign + (text if not digits else f"{text[:-digits]}.{text[-digits:]}") + + +def normalize_money_tables( + tables: Mapping[str, pd.DataFrame], + declaration: Mapping, + *, + rate: Fraction, + rate_text: str, + factors: Mapping[str, Mapping], +) -> tuple[dict[tuple[str, str], np.ndarray], list[dict]]: + """Normalize every declared column once; return arrays and per-column receipts. + + ``factors`` maps each used family to ``{"identity": True, "reason"}`` or + ``{"factor_id", "numerator", "digits", "declaration_sha256", + "receipt_sha256"}``. A table already carrying a declared companion column + was normalized before and is refused, as is a NaN, an infinite value, a + negative amount in an unsigned concept, or a minor-unit total outside int64. + """ + if not isinstance(rate, Fraction) or rate <= 0: + raise ValueError("Money normalization needs a positive exact rate.") + digits = declaration["rounding"]["digits"] + scale = 10**digits + exchange = rate if declaration["quote"] == "to_per_from" else Fraction(1) / rate + minor_exchange = exchange * Fraction(scale) + suffix = declaration["cents_suffix"] + concepts = money_concept_columns() + families = normalized_column_families(declaration) + period = dict(declaration["donor_money_period"]) + arrays: dict[tuple[str, str], np.ndarray] = {} + receipts = [] + for (entity, column), family in families.items(): + table = tables[entity] + companion = f"{column}{suffix}" + if companion in table: + raise ValueError( + f"{entity}.{column} already carries {companion}; money is normalized " + "exactly once per lineage." + ) + if column not in table: + raise ValueError(f"Money column {entity}.{column} is absent.") + factor = factors[family] + identity = factor.get("identity") is True + ratio = ( + Fraction(1) + if identity + else Fraction(factor["numerator"], 10 ** factor["digits"]) + ) + signed = concepts[entity, column].monetary.signed + before = Fraction(0) + first, second = [], [] + for value in table[column].tolist(): + amount = _shortest_decimal(value) + if amount < 0 and not signed: + raise ValueError( + f"Unsigned money column {entity}.{column} holds a negative amount." + ) + before += amount + converted = _half_even(amount * minor_exchange) + aged = _half_even(Fraction(converted) * ratio) + if any( + not _INT64_MIN <= value <= _INT64_MAX for value in (converted, aged) + ): + raise ValueError( + f"Money column {entity}.{column} exceeds int64 minor units." + ) + first.append(converted) + second.append(aged) + # Residuals are linear: sum(m1 - x*q) = sum(m1) - q*sum(x), exactly. + fx_residual = Fraction(sum(first)) - before * minor_exchange + factor_residual = Fraction(sum(second)) - Fraction(sum(first)) * ratio + arrays[entity, column] = np.array( + [float(Fraction(units, scale)) for units in second], dtype=np.float64 + ) + arrays[entity, companion] = np.array(second, dtype=np.int64) + factor_receipt = ( + {"identity": True, "reason": factor["reason"]} + if factor.get("identity") is True + else { + "factor_id": factor["factor_id"], + "declaration_sha256": factor["declaration_sha256"], + "receipt_sha256": factor["receipt_sha256"], + "operational_factor": { + "numerator": factor["numerator"], + "denominator": 10 ** factor["digits"], + }, + } + ) + receipts.append( + { + "entity": entity, + "column": column, + "cents_column": companion, + "index_family": family, + "n": len(second), + "sum_before": _exact_decimal(before), + "sum_after_fx": _minor_units(first, digits), + "sum_after": _minor_units(second, digits), + "fx_rounding_residual_minor_units": _rational(fx_residual), + "factor_rounding_residual_minor_units": _rational(factor_residual), + "factor": factor_receipt, + "rate_text": rate_text, + "quote": declaration["quote"], + "donor_money_period": period, + } + ) + return arrays, receipts + + +def money_factor_inputs(entry: Mapping, declaration: Mapping) -> dict: + """The arithmetic inputs and identities of one checked, selected factor.""" + numerator, digits = check_money_factor(entry, declaration) + return { + "factor_id": entry["declaration"]["id"], + "numerator": numerator, + "digits": digits, + "declaration_sha256": factor_document_sha256(entry["declaration"]), + "receipt_sha256": entry["receipt"]["receipt_sha256"], + } diff --git a/packages/microcosm-build/src/microcosm/build/nz/country_package.json b/packages/microcosm-build/src/microcosm/build/nz/country_package.json index 460fcef650..d243f43579 100644 --- a/packages/microcosm-build/src/microcosm/build/nz/country_package.json +++ b/packages/microcosm-build/src/microcosm/build/nz/country_package.json @@ -97,11 +97,26 @@ "kind": "legacy_json", "schema_id": "legacy_json" }, + { + "path": "money_normalization.json", + "kind": "legacy_json", + "schema_id": "legacy_json" + }, { "path": "precal_references.json", "kind": "legacy_json", "schema_id": "legacy_json" }, + { + "path": "reference_aging.json", + "kind": "legacy_json", + "schema_id": "legacy_json" + }, + { + "path": "reference_intervals.json", + "kind": "legacy_json", + "schema_id": "legacy_json" + }, { "path": "reference_inventory.json", "kind": "legacy_json", diff --git a/packages/microcosm-build/src/microcosm/build/nz/currency_bridge.json b/packages/microcosm-build/src/microcosm/build/nz/currency_bridge.json index c87a11e8fd..e2215358fe 100644 --- a/packages/microcosm-build/src/microcosm/build/nz/currency_bridge.json +++ b/packages/microcosm-build/src/microcosm/build/nz/currency_bridge.json @@ -2,29 +2,17 @@ "version": 1, "country": "nz", "method_card_row": "MC4", - "description": "The declared USD to NZD bridge for donor money concepts outside the quantile-map set (MC4 quantile-maps earnings, rent and liquid assets onto New Zealand distributions instead). It is exactly one multiplication with two-decimal rounding, to be applied before calibration and recorded in the build receipt. Aging from the donor dollar year to tax year 2026-27 is a separate declared index (MC1, MC5), not part of this bridge.", - "status": "awaiting D1 (MC4)", + "description": "The declared USD to NZD bridge for donor money concepts outside the quantile-map set (MC4 quantile-maps earnings, rent and liquid assets onto New Zealand distributions instead). It is exactly one multiplication with two-decimal rounding, applied before calibration and recorded in the build receipt. Aging from the donor dollar year to tax year 2026-27 is a separate declared index (MC1, MC5), not part of this bridge.", + "status": "Adopted: MC4 was adopted as written under d1043 (NZ hub, method card D1 as adopted, 2026-10-10; nz-hub notes/1010/method-card-D1-adopted.md). money_normalization.json is the single source of the executable rate, its quote, rounding and citation; this record points to it rather than repeating the rate.", "from_currency": "USD", "to_currency": "NZD", "operation": "multiply", - "rate": 1.654, - "rate_unit": "NZD per USD", - "rounding": { - "decimal_places": 2 + "rate": { + "resource": "money_normalization.json", + "path": [ + "rate" + ] }, "rate_year": 2024, - "rate_year_basis": "The donor dollar year: the pinned populace-us support artifact has survey period 2024 (source_stages.json artifact vintage \"2024\"; spec/vintages.yaml nz_support_pool_2024_buildp).", - "source": { - "publisher": "Internal Revenue Service", - "title": "Yearly average currency exchange rates", - "url": "https://www.irs.gov/individuals/international-taxpayers/yearly-average-currency-exchange-rates", - "table": "Yearly average exchange rates for converting foreign currencies into U.S. dollars", - "row": "New Zealand, Dollar", - "column": "2024", - "conversion_rule": "\"To convert from U.S. dollars to foreign currency, multiply the U.S. dollar amount by the applicable yearly average exchange rate in the table below.\"", - "page_last_reviewed": "2026-09-17", - "retrieved_on": "2026-10-07", - "retrieved_sha256": "8c765c8c88a6a6ad4fe325a0344ff69d2fa2ab174dcac2e8c13e1a5ea747fff8", - "retrieved_note": "SHA-256 of the HTML bytes fetched on the retrieval date; the page is re-rendered by the publisher, so it is a receipt, not a stable content key." - } + "rate_year_basis": "The donor dollar year: the pinned populace-us support artifact has survey period 2024 (source_stages.json artifact vintage \"2024\"; spec/vintages.yaml nz_support_pool_2024_buildp)." } diff --git a/packages/microcosm-build/src/microcosm/build/nz/money_normalization.json b/packages/microcosm-build/src/microcosm/build/nz/money_normalization.json new file mode 100644 index 0000000000..e6c6c2be96 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/nz/money_normalization.json @@ -0,0 +1,126 @@ +{ + "schema_version": 1, + "description": "Exact normalization of the donor's money columns to New Zealand dollars at the tax year 2026-27 money level (MC4, MC5). Each normalized column is converted once at the declared rate and then aged once by its index family's donor factor, both rounded half-even to cents. Earnings, rent and liquid assets are quantile-mapped onto New Zealand distributions instead (MC4) and are never converted at the rate.", + "status": "Declared, not active: no New Zealand transport graph reads it yet (G8-w3).", + "from_currency": "USD", + "to_currency": "NZD", + "rate": { + "form": "decimal_text", + "text": "1.654", + "citation": { + "publisher": "Internal Revenue Service", + "title": "Yearly average currency exchange rates", + "url": "https://www.irs.gov/individuals/international-taxpayers/yearly-average-currency-exchange-rates", + "table": "Yearly average exchange rates for converting foreign currencies into U.S. dollars", + "row": "New Zealand, Dollar", + "column": "2024", + "conversion_rule": "\"To convert from U.S. dollars to foreign currency, multiply the U.S. dollar amount by the applicable yearly average exchange rate in the table below.\"", + "page_last_reviewed": "2026-09-17", + "retrieved_on": "2026-10-07", + "retrieved_sha256": "8c765c8c88a6a6ad4fe325a0344ff69d2fa2ab174dcac2e8c13e1a5ea747fff8", + "retrieved_note": "SHA-256 of the HTML bytes fetched on the retrieval date; the page is re-rendered by the publisher, so it is a receipt, not a stable content key." + } + }, + "quote": "to_per_from", + "rounding": { + "digits": 2, + "mode": "half_even" + }, + "donor_money_period": { + "type": "calendar_year", + "value": 2024, + "basis": "declared_convention", + "evidence": [ + "tools/build_us_fiscal_refresh_release.py declares the module constant PERIOD = 2024, and its _dataset_from_frame passes PERIOD as time_period to USSingleYearDataset.", + "microcosm.frame.adapters.policyengine_us.PolicyEngineUSEngine._write_and_verify writes the supplied model period as a one-row integer _time_period table. This is model-period lineage evidence, not authentication of the monetary basis of donor amounts." + ] + }, + "target_period": 2027, + "columns": { + "person": [ + "nonfarm_self_employment_income", + "farm_self_employment_income", + "interest_income", + "dividend_income", + "rental_income", + "realized_capital_gains", + "private_pension_income" + ], + "household": [ + "mortgage_interest", + "mortgage_principal", + "property_tax" + ] + }, + "factor_profile": { + "dimension": "aging_index", + "profiles": [ + "low", + "central", + "high" + ], + "central_profile": "central" + }, + "index_family_factors": { + "mixed_income": { + "factors": { + "low": "donor_cy2024_to_ty2027_low", + "central": "donor_cy2024_to_ty2027_central", + "high": "donor_cy2024_to_ty2027_high" + }, + "disclosure": "Self-employment income (both columns, signed) is aged by ordinary-time average weekly earnings (AWE) growth, the AWE convention: Stats NZ QES observations through June 2026, then Treasury PREFU 2026 forecast AWE growth of 2.7% (0.5 points lower and higher in the low and high profiles); provisional pending Max's ruling on non-wage ageing (H3). No separate mixed-income index is used." + }, + "capital_income": { + "factors": { + "low": "donor_cy2024_to_ty2027_low", + "central": "donor_cy2024_to_ty2027_central", + "high": "donor_cy2024_to_ty2027_high" + }, + "disclosure": "Interest, dividends, rental income and realized capital gains are aged by AWE growth; provisional pending Max's ruling on non-wage ageing (H3). No asset-return or price index is used." + }, + "pensions": { + "factors": { + "low": "donor_cy2024_to_ty2027_low", + "central": "donor_cy2024_to_ty2027_central", + "high": "donor_cy2024_to_ty2027_high" + }, + "disclosure": "Private pension income is aged by AWE growth; provisional pending Max's ruling on non-wage ageing (H3)." + }, + "owner_housing_costs": { + "factors": { + "low": "donor_cy2024_to_ty2027_low", + "central": "donor_cy2024_to_ty2027_central", + "high": "donor_cy2024_to_ty2027_high" + }, + "disclosure": "Mortgage interest, mortgage principal and property tax are aged by AWE growth as an interim index; provisional pending Max's ruling on non-wage ageing (H3)." + } + }, + "cents_suffix": "_cents", + "quantile_mapped": { + "person": { + "employment_income": { + "awaits": [ + "P1: precal_references ird_wage_salary_people_by_band rows 7 to 241" + ], + "reason": "MC4 quantile-maps earnings onto IRD wage bands; P1 is parked until its source-period wage bridge and intervals exist (d1952 option A, earnings first)." + }, + "liquid_financial_assets": { + "awaits": [ + "P3: precal_references stats_nz_household_financial_net_worth_distribution", + "C2: an all-household liquid-asset distribution" + ], + "reason": "MC4 quantile-maps liquid assets; no all-household distribution exists yet (C2), and any interim producer is Max's call (M1)." + } + }, + "household": { + "rent": { + "awaits": [ + "C1: Census 2023 household weekly rent bands (G8-d1)" + ], + "reason": "MC4 quantile-maps rent onto the Census 2023 weekly-rent bands for renting households (C1); the MBIE bond reference P2 is parked." + } + } + }, + "pending": {}, + "notes": "The rate is the IRS 2024 yearly average for the New Zealand dollar, quoted in NZD per USD, so a donor amount is multiplied by it. Calendar-2024 money is a declared convention covering flows and asset stocks; the donor monetary basis has not been authenticated against a producing manifest. The US build's PERIOD = 2024 and the exporter's _time_period write provide model-period lineage evidence. Money normalization refuses a donor whose recorded _time_period differs from calendar 2024 (G8-w H1); this runtime check is a consistency check, not authentication. No donor file is opened to establish this declaration. Variants V1 and V2 age with their own aging_index profile (G8-w H2). public_pension_income is dropped at CREATE and is not declared." +} diff --git a/packages/microcosm-build/src/microcosm/build/nz/reference_aging.json b/packages/microcosm-build/src/microcosm/build/nz/reference_aging.json new file mode 100644 index 0000000000..34a1af66e8 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/nz/reference_aging.json @@ -0,0 +1,11992 @@ +{ + "schema_version": 1, + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "factors": [ + { + "declaration": { + "schema_version": 1, + "id": "ird_ty2024_to_ty2027_low", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1115056, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1115056, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "ird_ty2024_to_ty2027_low", + "declaration_sha256": "4325961ede06db69ec91791cc1addf49f889f277fd62791778754b28830bbcba", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "decimal_operand": "1502.27", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1502.27", + "recovered_decimal": "1502.27", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2023 quarter", + "start_date": "2023-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "decimal_operand": "1528.92", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1528.92", + "recovered_decimal": "1528.92", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2023 quarter", + "start_date": "2023-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "decimal_operand": "1556.16", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1556.16", + "recovered_decimal": "1556.16", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2023 quarter", + "start_date": "2023-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 615089, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 11, + "denominator": 500 + } + }, + "root_bounds": { + "lower": { + "numerator": 50272759932393, + "denominator": 50000000000000 + }, + "upper": { + "numerator": 100545519864787, + "denominator": 100000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 511, + "denominator": 500 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 12500000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 100000000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 19221531250000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 153772250000000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1115056, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "beb087aeff370b0de497abb10ea2558533ca9a9995b556dc1d63d70327ad3885" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "ird_ty2024_to_ty2027_central", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1117109, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1117109, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "ird_ty2024_to_ty2027_central", + "declaration_sha256": "4178ab3ea62899cad556170195b74dbb41faf7ff7cde57e298d1e62447b1b004", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "decimal_operand": "1502.27", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1502.27", + "recovered_decimal": "1502.27", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2023 quarter", + "start_date": "2023-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "decimal_operand": "1528.92", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1528.92", + "recovered_decimal": "1528.92", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2023 quarter", + "start_date": "2023-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "decimal_operand": "1556.16", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1556.16", + "recovered_decimal": "1556.16", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2023 quarter", + "start_date": "2023-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 615089, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 27, + "denominator": 1000 + } + }, + "root_bounds": { + "lower": { + "numerator": 100668271307927, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 12583533913491, + "denominator": 12500000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 1027, + "denominator": 1000 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 195312500000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 153772250000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 300336425781250000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1117109, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "6234283ef363388578f13d7833faf0c6eb0ea1743ecc66333d017e59c554a798" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "ird_ty2024_to_ty2027_high", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1119158, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1119158, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "ird_ty2024_to_ty2027_high", + "declaration_sha256": "29b91c04a391e207e627f99de7e7bdeb90fc0c5d0c1558bccc4a76c3731e801d", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "decimal_operand": "1502.27", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1502.27", + "recovered_decimal": "1502.27", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2023 quarter", + "start_date": "2023-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "decimal_operand": "1528.92", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1528.92", + "recovered_decimal": "1528.92", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2023 quarter", + "start_date": "2023-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "decimal_operand": "1556.16", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1556.16", + "recovered_decimal": "1556.16", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2023 quarter", + "start_date": "2023-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 615089, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 4, + "denominator": 125 + } + }, + "root_bounds": { + "lower": { + "numerator": 100790575349881, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 50395287674941, + "denominator": 50000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 129, + "denominator": 125 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 12500000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 153772250000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 19221531250000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1119158, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "cc047908112d30352a300efd495c5ace4a697e93d7f21dcd4f53a7cdb175db3f" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "p1_ty2026_to_ty2027_low", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1026690, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1026690, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "p1_ty2026_to_ty2027_low", + "declaration_sha256": "53b22d7900851e896cf6ac17c9ca9f9bb92d436637459b8b59c513397833813e", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "decimal_operand": "1652.72", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1652.72", + "recovered_decimal": "1652.72", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2025 quarter", + "start_date": "2025-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "decimal_operand": "1661.12", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1661.12", + "recovered_decimal": "1661.12", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2025 quarter", + "start_date": "2025-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "decimal_operand": "1681.06", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1681.06", + "recovered_decimal": "1681.06", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2025 quarter", + "start_date": "2025-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "decimal_operand": "1685.39", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1685.39", + "recovered_decimal": "1685.39", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2026 quarter", + "start_date": "2026-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 668029, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 11, + "denominator": 500 + } + }, + "root_bounds": { + "lower": { + "numerator": 50272759932393, + "denominator": 50000000000000 + }, + "upper": { + "numerator": 100545519864787, + "denominator": 100000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 511, + "denominator": 500 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 12500000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 100000000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 20875906250000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 167007250000000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1026690, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "28006b689061f41a7cfd9abf055aa3ee1ee5a5414d83fa255d36d5ee7fe326bc" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "p1_ty2026_to_ty2027_central", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1028580, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1028580, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "p1_ty2026_to_ty2027_central", + "declaration_sha256": "8507fe3d665c6a0727319765827120afda4ee36e8d970a70b61a747d8cb9210b", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "decimal_operand": "1652.72", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1652.72", + "recovered_decimal": "1652.72", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2025 quarter", + "start_date": "2025-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "decimal_operand": "1661.12", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1661.12", + "recovered_decimal": "1661.12", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2025 quarter", + "start_date": "2025-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "decimal_operand": "1681.06", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1681.06", + "recovered_decimal": "1681.06", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2025 quarter", + "start_date": "2025-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "decimal_operand": "1685.39", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1685.39", + "recovered_decimal": "1685.39", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2026 quarter", + "start_date": "2026-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 668029, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 27, + "denominator": 1000 + } + }, + "root_bounds": { + "lower": { + "numerator": 100668271307927, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 12583533913491, + "denominator": 12500000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 1027, + "denominator": 1000 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 195312500000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 167007250000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 326186035156250000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1028580, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "43e1914b11969709ea4f0df7fc9a8280f82f15bf186b4e107f986da0a7871c53" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "p1_ty2026_to_ty2027_high", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1030467, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1030467, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "p1_ty2026_to_ty2027_high", + "declaration_sha256": "b2c2c0b631861fa6c3975dfb6d326b49ca60b81057abc5bb8581dd0c0bca1ce9", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2026, + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "decimal_operand": "1652.72", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1652.72", + "recovered_decimal": "1652.72", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2025 quarter", + "start_date": "2025-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "decimal_operand": "1661.12", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1661.12", + "recovered_decimal": "1661.12", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2025 quarter", + "start_date": "2025-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "decimal_operand": "1681.06", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1681.06", + "recovered_decimal": "1681.06", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2025 quarter", + "start_date": "2025-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "decimal_operand": "1685.39", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1685.39", + "recovered_decimal": "1685.39", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2026 quarter", + "start_date": "2026-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 668029, + "denominator": 400 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 4, + "denominator": 125 + } + }, + "root_bounds": { + "lower": { + "numerator": 100790575349881, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 50395287674941, + "denominator": 50000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 129, + "denominator": 125 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 12500000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 167007250000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 20875906250000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1030467, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "5d1b1006938c2628d7f2d4344643270c2477f01cd0d313bfa323834ff8e54cb2" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "observed_ty2026_over_ty2024", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "diagnostic", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2026, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": null, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1086069, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ] + }, + "operational_factor": { + "numerator": 1086069, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "observed_ty2026_over_ty2024", + "declaration_sha256": "dae0d44f673fb8e437c4787b3c0d6d6c142412c2fbb29e03f1702a6940a56316", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": 2024, + "target_period": 2026, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:cfbce1343aa646fce0411631", + "decimal_operand": "1502.27", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1502.27", + "recovered_decimal": "1502.27", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-04-01", + "end_date": "2023-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2023 quarter", + "start_date": "2023-04-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:f9f4f127d080fe2f06617947", + "decimal_operand": "1528.92", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1528.92", + "recovered_decimal": "1528.92", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-07-01", + "end_date": "2023-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2023 quarter", + "start_date": "2023-07-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2023-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2023q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:fb5938810b7b37ae8534e5f7", + "decimal_operand": "1556.16", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1556.16", + "recovered_decimal": "1556.16", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2023-10-01", + "end_date": "2023-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2023-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2023-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2023 quarter", + "start_date": "2023-10-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:86ce22ba42a6e2a36b889f9b", + "decimal_operand": "1652.72", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1652.72", + "recovered_decimal": "1652.72", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-04-01", + "end_date": "2025-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2025 quarter", + "start_date": "2025-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:434e7b2736b4b16589034c72", + "decimal_operand": "1661.12", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1661.12", + "recovered_decimal": "1661.12", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-07-01", + "end_date": "2025-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2025 quarter", + "start_date": "2025-07-01" + }, + "status": "observed", + "sequence": 1, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2025-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2025q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:bc87e53157eaf53c846879dc", + "decimal_operand": "1681.06", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1681.06", + "recovered_decimal": "1681.06", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2025-10-01", + "end_date": "2025-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2025-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2025-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2025 quarter", + "start_date": "2025-10-01" + }, + "status": "observed", + "sequence": 2, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:78e735569814069f8066ba9f", + "decimal_operand": "1685.39", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1685.39", + "recovered_decimal": "1685.39", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-01-01", + "end_date": "2026-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2026 quarter", + "start_date": "2026-01-01" + }, + "status": "observed", + "sequence": 3, + "weight": "1", + "projection_offset": 0 + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 615089, + "denominator": 400 + }, + "growth": null, + "root_bounds": { + "lower": { + "numerator": 1, + "denominator": 1 + }, + "upper": { + "numerator": 1, + "denominator": 1 + }, + "exact": true, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 1, + "denominator": 1 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 668029, + "denominator": 400 + }, + "upper": { + "numerator": 668029, + "denominator": 400 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 668029, + "denominator": 615089 + }, + "upper": { + "numerator": 668029, + "denominator": 615089 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1086069, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026" + ], + "decision_references": [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2" + ], + "receipt_sha256": "6487bd1664ce1e691faaa0771e9835da5437243b06a5c7d749888380a76cd995" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "donor_cy2024_to_ty2027_low", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "money_normalization_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1077090, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ] + }, + "operational_factor": { + "numerator": 1077090, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "donor_cy2024_to_ty2027_low", + "declaration_sha256": "814ca4fd84c53431bd05e4db9127a6567923f73f56a149a685424f75a14bcdfe", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "decimal_operand": "1586.32", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1586.32", + "recovered_decimal": "1586.32", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2024 quarter", + "start_date": "2024-04-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "decimal_operand": "1595.11", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1595.11", + "recovered_decimal": "1595.11", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2024 quarter", + "start_date": "2024-07-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "decimal_operand": "1622.73", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1622.73", + "recovered_decimal": "1622.73", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2024 quarter", + "start_date": "2024-10-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 63677, + "denominator": 40 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "-0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 11, + "denominator": 500 + } + }, + "root_bounds": { + "lower": { + "numerator": 50272759932393, + "denominator": 50000000000000 + }, + "upper": { + "numerator": 100545519864787, + "denominator": 100000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 511, + "denominator": 500 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 12500000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 100000000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 21433087509261019469100089110351817978329164269, + "denominator": 19899062500000000000000000000000000000000000000 + }, + "upper": { + "numerator": 171464700074090725365853333139754513013059781351, + "denominator": 159192500000000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1077090, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ], + "receipt_sha256": "e6a3adeec37249a70142c7801bc031915a0f40d9521b91c567fbf4a11e616ada" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "donor_cy2024_to_ty2027_central", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "money_normalization_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1079073, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ] + }, + "operational_factor": { + "numerator": 1079073, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "donor_cy2024_to_ty2027_central", + "declaration_sha256": "cb026f66df5cc07468a0c517e348b399082f0648b81856b4ca888587ba8d9d25", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "decimal_operand": "1586.32", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1586.32", + "recovered_decimal": "1586.32", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2024 quarter", + "start_date": "2024-04-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "decimal_operand": "1595.11", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1595.11", + "recovered_decimal": "1595.11", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2024 quarter", + "start_date": "2024-07-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "decimal_operand": "1622.73", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1622.73", + "recovered_decimal": "1622.73", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2024 quarter", + "start_date": "2024-10-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 63677, + "denominator": 40 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 27, + "denominator": 1000 + } + }, + "root_bounds": { + "lower": { + "numerator": 100668271307927, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 12583533913491, + "denominator": 12500000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 1027, + "denominator": 1000 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 195312500000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 171780381168748188591006079321988113950935096211, + "denominator": 159192500000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 335508556970216332809143747140199880884173607, + "denominator": 310922851562500000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1079073, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ], + "receipt_sha256": "ce934982c900e9996911b0a8d5b0de52d567c99124d33b597ea9fb481df45db1" + } + }, + { + "declaration": { + "schema_version": 1, + "id": "donor_cy2024_to_ty2027_high", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "money_normalization_input", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 0, + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 1, + "projection_offset": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 2, + "projection_offset": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "weight": "1", + "sequence": 3, + "projection_offset": 3 + } + ], + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ] + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "expected_operational_factor": { + "numerator": 1081052, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ] + }, + "operational_factor": { + "numerator": 1081052, + "denominator": 1000000 + }, + "receipt": { + "schema_version": 1, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "factor_id": "donor_cy2024_to_ty2027_high", + "declaration_sha256": "2dea33aa96b4a553780bf9f7b361b48a1487470c710a080b79330e8db3793bbd", + "feed_identity": { + "source_commit": "8f4cfd573c0d6f580d9c51cd67e8f449a42ed174", + "facts_sha256": "03f5ea0fe60e6dc93a135ab32cd5cd6ffb56388e702a4734cdb515a956e36b18", + "manifest_sha256": "1ea2d1eeb122275a9ce7f861ca3e20e21dd695772c04ce9d71114168e92fe053", + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": [ + "chronicle.consumer_fact.v4" + ], + "consumer_fact_schema_sha256": "d953306d6f01fbc64db4372d0262d3012ee590269f8a25b276b18dca0626c6a0", + "fact_row_count": 12368 + }, + "source_period": "CY2024", + "target_period": 2027, + "source_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:854e91ec1dd95e396659ad8e", + "decimal_operand": "1563.54", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1563.54", + "recovered_decimal": "1563.54", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_vintage": "stats_nz_qes_june_2025", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q1" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2025/2025/25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30/quarterly-employment-survey-june-2025-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2025-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30", + "source_size_bytes": 135784, + "source_table": "Quarterly Employment Survey June 2025 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2025-quarter/Download-data/quarterly-employment-survey-june-2025-quarter.xlsx", + "vintage": "stats_nz_qes_june_2025" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-03-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Mar 2024 quarter", + "start_date": "2024-01-01" + }, + "weight": "1", + "sequence": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:c07fd9ce3137fa20386bafe0", + "decimal_operand": "1586.32", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1586.32", + "recovered_decimal": "1586.32", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-04-01", + "end_date": "2024-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2024 quarter", + "start_date": "2024-04-01" + }, + "weight": "1", + "sequence": 1 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:54a0eeb64049ba4c15e397bf", + "decimal_operand": "1595.11", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1595.11", + "recovered_decimal": "1595.11", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-07-01", + "end_date": "2024-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q3" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-09-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Sep 2024 quarter", + "start_date": "2024-07-01" + }, + "weight": "1", + "sequence": 2 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2024-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2024q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:7fc35ba9682ceb04504f2ef9", + "decimal_operand": "1622.73", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1622.73", + "recovered_decimal": "1622.73", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2024-10-01", + "end_date": "2024-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2024-Q4" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2024-12-31", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Dec 2024 quarter", + "start_date": "2024-10-01" + }, + "weight": "1", + "sequence": 3 + } + ], + "target_schedule": [ + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q2", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q2", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "assertion": "observation" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2", + "decimal_operand": "1700.68", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "1700.68", + "recovered_decimal": "1700.68", + "decimal_values_equal": true + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-04-01", + "end_date": "2026-06-30" + }, + "source_column_id": "SBSZ9A" + }, + "period": { + "type": "quarter", + "value": "2026-Q2" + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/stats_nz/qes_average_weekly_earnings_june_2026/2026/1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97/quarterly-employment-survey-june-2026-quarter.xlsx", + "source_file": "quarterly-employment-survey-june-2026-quarter.xlsx", + "source_name": "stats_nz", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_size_bytes": 124556, + "source_table": "Quarterly Employment Survey June 2026 quarter, Table 7: Average weekly earnings for FTEs, by sector", + "url": "https://www.stats.govt.nz/assets/Uploads/Labour-market-statistics/Labour-market-statistics-June-2026-quarter/Download-data/quarterly-employment-survey-june-2026-quarter.xlsx", + "vintage": "stats_nz_qes_june_2026" + }, + "unit": "nzd_per_week", + "assertion": "observation", + "provenance_class": "survey_aggregate", + "entity_role": "full_time_equivalent_employee", + "period_coverage": { + "basis": "calendar", + "end_date": "2026-06-30", + "notes": "QES reference period is the pay week ending on or before the 20th of the quarter’s middle month (hub-supplied source-period definition); the fact period is the published quarter, not the pay week. Actual, not seasonally adjusted. No annual averaging or period alignment.", + "source_period_label": "Jun 2026 quarter", + "start_date": "2026-04-01" + }, + "status": "observed", + "sequence": 0, + "weight": "1", + "projection_offset": 0 + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q3", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q3", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2026-09-30" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 1, + "weight": "1", + "projection_offset": 1, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2026-Q4", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2026q4", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2026-10-01", + "end_date": "2026-12-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 2, + "weight": "1", + "projection_offset": 2, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + }, + { + "selector": { + "source_name": "stats_nz", + "source_measure_id": "SBSZ9A", + "source_concept": "stats_nz.qes.average_weekly_earnings.ordinary_time", + "entity_name": "person", + "period_type": "quarter", + "period_value": "2027-Q1", + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "stats_nz.qes.awe.2027q1", + "dimensions": [ + "earnings_component", + "earnings_denominator", + "earnings_sector", + "earnings_series_reference", + "seasonal_adjustment" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "earnings_denominator": "fte", + "earnings_sector": "all_sectors", + "earnings_series_reference": "QEMQ.SBSZ9A", + "seasonal_adjustment": "actual" + }, + "assertion": "observation" + }, + "validation": { + "unit": "nzd_per_week", + "source_sha256": "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97", + "source_vintage": "stats_nz_qes_june_2026", + "entity_role": "full_time_equivalent_employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-03-31" + }, + "source_column_id": "SBSZ9A" + }, + "status": "projected", + "sequence": 3, + "weight": "1", + "projection_offset": 3, + "projection_anchor": { + "aggregate_fact_key": "ledger.aggregate_fact.v2:91583e3f39d11b8b4b2923d2" + } + } + ], + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "denominator_mean": { + "numerator": 63677, + "denominator": 40 + }, + "growth": { + "selector": { + "source_name": "treasury", + "source_measure_id": "average_weekly_earnings_annual_percentage_change", + "source_concept": "treasury.prefu26.average_weekly_earnings_annual_percentage_change", + "entity_name": "government", + "period_type": "fiscal_year", + "period_value": 2026, + "geography_level": "country", + "geography_id": "NZ", + "geography_vintage": "current", + "record_set_id": "treasury.prefu26.average_weekly_earnings.fy2026", + "dimensions": [ + "earnings_component", + "publication_status" + ], + "dimension_values": { + "earnings_component": "ordinary_time", + "publication_status": "Forecast" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "assertion": "source_projection", + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts" + }, + "aggregate_fact_key": "ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25", + "decimal_operand": "2.7", + "numeric_token_proof": { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "2.7", + "recovered_decimal": "2.7", + "decimal_values_equal": true + }, + "validation": { + "unit": "percent", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_vintage": "prefu_2026_09_29", + "entity_role": "forecaster", + "provenance_class": "model_output", + "assertion": "source_projection", + "period_coverage": { + "start_date": "2026-07-01", + "end_date": "2027-06-30" + }, + "source_column_id": "year_ending_30_june_2027" + }, + "period": { + "type": "fiscal_year", + "value": 2026 + }, + "source": { + "extracted_at": "2026-10-22", + "extraction_method": "CellSelectorSpec and SourceRecordSpec resolved from cells", + "method_notes": "Each fact carries source_record_id, source_cell_keys, and source_row_keys when available.", + "raw_r2_bucket": "ledger-raw", + "raw_r2_key": "raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "raw_r2_uri": "r2://ledger-raw/raw/nz/treasury/treasury-prefu26-economic-assumptions/2026/e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9/prefu26-charts-data.xlsx", + "source_file": "prefu26-charts-data.xlsx", + "source_name": "treasury", + "source_sha256": "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9", + "source_size_bytes": 664670, + "source_table": "PREFU 2026 Table 4.1 - Key economic assumptions for fiscal forecasts", + "url": "https://www.treasury.govt.nz/sites/default/files/2026-09/prefu26-charts-data.xlsx", + "vintage": "prefu_2026_09_29" + }, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "entity_role": "forecaster", + "period_coverage": { + "basis": "fiscal", + "end_date": "2027-06-30", + "notes": "Publisher Year ending 30 June 2027; Chronicle opening-year fiscal_year:2026. Publisher column status is Forecast. Footnote 3 (Table 4.1!C21:D21): Ordinary time. Footnote 4 (C22:D22): Measures are actuals except for real GDP and nominal GDP. 2026 earnings are therefore actuals despite the Forecast4 column heading; only 2027–2031 earnings are forecasts. The published annual percentage change is retained unchanged; no annual average, level, factor or period alignment is calculated.", + "source_period_label": "Year ending 30 June 2027", + "start_date": "2026-07-01" + }, + "unit_multiplier": "0.01", + "scenario_delta": "0.5", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + "effective_growth": { + "numerator": 4, + "denominator": 125 + } + }, + "root_bounds": { + "lower": { + "numerator": 100790575349881, + "denominator": 100000000000000 + }, + "upper": { + "numerator": 50395287674941, + "denominator": 50000000000000 + }, + "exact": false, + "decimal_places": 14, + "degree": 4, + "radicand": { + "numerator": 129, + "denominator": 125 + } + }, + "numerator_mean_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 100000000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 12500000000000000000000000000000000000000000 + } + }, + "unrounded_factor_bounds": { + "lower": { + "numerator": 172095423947015521933465483088234150187075944797, + "denominator": 159192500000000000000000000000000000000000000000 + }, + "upper": { + "numerator": 21511927993377262490437471966978882670435826057, + "denominator": 19899062500000000000000000000000000000000000000 + } + }, + "rounding": { + "digits": 6, + "mode": "nearest_half_even" + }, + "operational_factor": { + "numerator": 1081052, + "denominator": 1000000 + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23" + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + "Declared calendar-2024 money convention (H1), covering flows and asset stocks; the recorded _time_period is a consistency check, not authentication" + ], + "decision_references": [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1" + ], + "receipt_sha256": "a26a1e5d24afca6c15b33bc043379c8d96c497a8c30080c89c3c682427d24cbc" + } + } + ] +} diff --git a/packages/microcosm-build/src/microcosm/build/nz/reference_intervals.json b/packages/microcosm-build/src/microcosm/build/nz/reference_intervals.json new file mode 100644 index 0000000000..991a9b4984 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/nz/reference_intervals.json @@ -0,0 +1,3464 @@ +{ + "schema_version": 1, + "country": "nz", + "description": "Generated by tools/generate_nz_target_references.py from the row formulas and checked against the pinned feed's own band labels.", + "disclosure": "IRD publishes no band closure or cents semantics for these tables. The cuts, closure, nil band and integer-cent arithmetic are Microcosm's declared convention (G8-r open rulings 2, section 1, 23 October 2026), not IRD's own semantics: t = max(0, x) in integer cents with no dollar rounding; t == 0 is the nil band, including qualifying loss records; each positive band is previous_upper < t <= upper; and the open tail is t > its lower cut. Four published label pairs share an endpoint ($20,000, $50,000, $80,000 and $300,000: national rows 29/30, 59/60, 89/90 and 229/230; age rows 27/28, 57/58 and 87/88); the ordered right-closed cuts assign each shared endpoint to the lower band. Ageing scales every finite cut exactly by the selected factor and keeps fractional cents. The income and universe columns name the d1952 stand-in measure rather than IRD's definition; no producer exists yet (G8-r2c) and no active row requests them. Boundary diagnostics report weighted mass within $1 of every cut and the fixed-weight change under the left-closed alternative; publisher reclassification is unavailable.", + "tables": [ + { + "id": "ird_taxable_income_national_ty2024", + "entity": "person", + "currency": "NZD", + "input_unit": "integer_cent", + "income_column": "ird_taxable_income_proxy_cents", + "universe_column": "ird_taxable_income_proxy_universe_status", + "source_period": 2024, + "target_period": 2027, + "positive_band_closure": "right", + "nil_band": "present", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": 0, + "source_locator": "Income tables 2001-24!BE8:BE244", + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left" + }, + "bands": [ + { + "id": "row_8", + "kind": "nil", + "indicator": "taxable_income_band_row_8_indicator", + "source_label": "nil", + "count_reference": "ird_taxable_income_people_by_band__row_8", + "amount_reference": "ird_taxable_income_total_by_band__row_8" + }, + { + "id": "row_9", + "kind": "positive", + "upper_cent": 10000, + "indicator": "taxable_income_band_row_9_indicator", + "source_label": "$0.01 - $100", + "count_reference": "ird_taxable_income_people_by_band__row_9", + "amount_reference": "ird_taxable_income_total_by_band__row_9" + }, + { + "id": "row_10", + "kind": "positive", + "upper_cent": 100000, + "indicator": "taxable_income_band_row_10_indicator", + "source_label": "$101 - $1,000", + "count_reference": "ird_taxable_income_people_by_band__row_10", + "amount_reference": "ird_taxable_income_total_by_band__row_10" + }, + { + "id": "row_11", + "kind": "positive", + "upper_cent": 200000, + "indicator": "taxable_income_band_row_11_indicator", + "source_label": "$1,001 - $2,000", + "count_reference": "ird_taxable_income_people_by_band__row_11", + "amount_reference": "ird_taxable_income_total_by_band__row_11" + }, + { + "id": "row_12", + "kind": "positive", + "upper_cent": 300000, + "indicator": "taxable_income_band_row_12_indicator", + "source_label": "$2,001 - $3,000", + "count_reference": "ird_taxable_income_people_by_band__row_12", + "amount_reference": "ird_taxable_income_total_by_band__row_12" + }, + { + "id": "row_13", + "kind": "positive", + "upper_cent": 400000, + "indicator": "taxable_income_band_row_13_indicator", + "source_label": "$3,001 - $4,000", + "count_reference": "ird_taxable_income_people_by_band__row_13", + "amount_reference": "ird_taxable_income_total_by_band__row_13" + }, + { + "id": "row_14", + "kind": "positive", + "upper_cent": 500000, + "indicator": "taxable_income_band_row_14_indicator", + "source_label": "$4,001 - $5,000", + "count_reference": "ird_taxable_income_people_by_band__row_14", + "amount_reference": "ird_taxable_income_total_by_band__row_14" + }, + { + "id": "row_15", + "kind": "positive", + "upper_cent": 600000, + "indicator": "taxable_income_band_row_15_indicator", + "source_label": "$5,001 - $6,000", + "count_reference": "ird_taxable_income_people_by_band__row_15", + "amount_reference": "ird_taxable_income_total_by_band__row_15" + }, + { + "id": "row_16", + "kind": "positive", + "upper_cent": 700000, + "indicator": "taxable_income_band_row_16_indicator", + "source_label": "$6,001 - $7,000", + "count_reference": "ird_taxable_income_people_by_band__row_16", + "amount_reference": "ird_taxable_income_total_by_band__row_16" + }, + { + "id": "row_17", + "kind": "positive", + "upper_cent": 800000, + "indicator": "taxable_income_band_row_17_indicator", + "source_label": "$7,001 - $8,000", + "count_reference": "ird_taxable_income_people_by_band__row_17", + "amount_reference": "ird_taxable_income_total_by_band__row_17" + }, + { + "id": "row_18", + "kind": "positive", + "upper_cent": 900000, + "indicator": "taxable_income_band_row_18_indicator", + "source_label": "$8,001 - $9,000", + "count_reference": "ird_taxable_income_people_by_band__row_18", + "amount_reference": "ird_taxable_income_total_by_band__row_18" + }, + { + "id": "row_19", + "kind": "positive", + "upper_cent": 1000000, + "indicator": "taxable_income_band_row_19_indicator", + "source_label": "$9,001 - $10,000", + "count_reference": "ird_taxable_income_people_by_band__row_19", + "amount_reference": "ird_taxable_income_total_by_band__row_19" + }, + { + "id": "row_20", + "kind": "positive", + "upper_cent": 1100000, + "indicator": "taxable_income_band_row_20_indicator", + "source_label": "$10,001 - $11,000", + "count_reference": "ird_taxable_income_people_by_band__row_20", + "amount_reference": "ird_taxable_income_total_by_band__row_20" + }, + { + "id": "row_21", + "kind": "positive", + "upper_cent": 1200000, + "indicator": "taxable_income_band_row_21_indicator", + "source_label": "$11,001 - $12,000", + "count_reference": "ird_taxable_income_people_by_band__row_21", + "amount_reference": "ird_taxable_income_total_by_band__row_21" + }, + { + "id": "row_22", + "kind": "positive", + "upper_cent": 1300000, + "indicator": "taxable_income_band_row_22_indicator", + "source_label": "$12,001 - $13,000", + "count_reference": "ird_taxable_income_people_by_band__row_22", + "amount_reference": "ird_taxable_income_total_by_band__row_22" + }, + { + "id": "row_23", + "kind": "positive", + "upper_cent": 1400000, + "indicator": "taxable_income_band_row_23_indicator", + "source_label": "$13,001 - $14,000", + "count_reference": "ird_taxable_income_people_by_band__row_23", + "amount_reference": "ird_taxable_income_total_by_band__row_23" + }, + { + "id": "row_24", + "kind": "positive", + "upper_cent": 1500000, + "indicator": "taxable_income_band_row_24_indicator", + "source_label": "$14,001 - $15,000", + "count_reference": "ird_taxable_income_people_by_band__row_24", + "amount_reference": "ird_taxable_income_total_by_band__row_24" + }, + { + "id": "row_25", + "kind": "positive", + "upper_cent": 1600000, + "indicator": "taxable_income_band_row_25_indicator", + "source_label": "$15,001 - $16,000", + "count_reference": "ird_taxable_income_people_by_band__row_25", + "amount_reference": "ird_taxable_income_total_by_band__row_25" + }, + { + "id": "row_26", + "kind": "positive", + "upper_cent": 1700000, + "indicator": "taxable_income_band_row_26_indicator", + "source_label": "$16,001 - $17,000", + "count_reference": "ird_taxable_income_people_by_band__row_26", + "amount_reference": "ird_taxable_income_total_by_band__row_26" + }, + { + "id": "row_27", + "kind": "positive", + "upper_cent": 1800000, + "indicator": "taxable_income_band_row_27_indicator", + "source_label": "$17,001 - $18,000", + "count_reference": "ird_taxable_income_people_by_band__row_27", + "amount_reference": "ird_taxable_income_total_by_band__row_27" + }, + { + "id": "row_28", + "kind": "positive", + "upper_cent": 1900000, + "indicator": "taxable_income_band_row_28_indicator", + "source_label": "$18,001 - $19,000", + "count_reference": "ird_taxable_income_people_by_band__row_28", + "amount_reference": "ird_taxable_income_total_by_band__row_28" + }, + { + "id": "row_29", + "kind": "positive", + "upper_cent": 2000000, + "indicator": "taxable_income_band_row_29_indicator", + "source_label": "$19,001 - $20,000", + "count_reference": "ird_taxable_income_people_by_band__row_29", + "amount_reference": "ird_taxable_income_total_by_band__row_29" + }, + { + "id": "row_30", + "kind": "positive", + "upper_cent": 2100000, + "indicator": "taxable_income_band_row_30_indicator", + "source_label": "$20,000 - $21,000", + "count_reference": "ird_taxable_income_people_by_band__row_30", + "amount_reference": "ird_taxable_income_total_by_band__row_30" + }, + { + "id": "row_31", + "kind": "positive", + "upper_cent": 2200000, + "indicator": "taxable_income_band_row_31_indicator", + "source_label": "$21,001 - $22,000", + "count_reference": "ird_taxable_income_people_by_band__row_31", + "amount_reference": "ird_taxable_income_total_by_band__row_31" + }, + { + "id": "row_32", + "kind": "positive", + "upper_cent": 2300000, + "indicator": "taxable_income_band_row_32_indicator", + "source_label": "$22,001 - $23,000", + "count_reference": "ird_taxable_income_people_by_band__row_32", + "amount_reference": "ird_taxable_income_total_by_band__row_32" + }, + { + "id": "row_33", + "kind": "positive", + "upper_cent": 2400000, + "indicator": "taxable_income_band_row_33_indicator", + "source_label": "$23,001 - $24,000", + "count_reference": "ird_taxable_income_people_by_band__row_33", + "amount_reference": "ird_taxable_income_total_by_band__row_33" + }, + { + "id": "row_34", + "kind": "positive", + "upper_cent": 2500000, + "indicator": "taxable_income_band_row_34_indicator", + "source_label": "$24,001 - $25,000", + "count_reference": "ird_taxable_income_people_by_band__row_34", + "amount_reference": "ird_taxable_income_total_by_band__row_34" + }, + { + "id": "row_35", + "kind": "positive", + "upper_cent": 2600000, + "indicator": "taxable_income_band_row_35_indicator", + "source_label": "$25,001 - $26,000", + "count_reference": "ird_taxable_income_people_by_band__row_35", + "amount_reference": "ird_taxable_income_total_by_band__row_35" + }, + { + "id": "row_36", + "kind": "positive", + "upper_cent": 2700000, + "indicator": "taxable_income_band_row_36_indicator", + "source_label": "$26,001 - $27,000", + "count_reference": "ird_taxable_income_people_by_band__row_36", + "amount_reference": "ird_taxable_income_total_by_band__row_36" + }, + { + "id": "row_37", + "kind": "positive", + "upper_cent": 2800000, + "indicator": "taxable_income_band_row_37_indicator", + "source_label": "$27,001 - $28,000", + "count_reference": "ird_taxable_income_people_by_band__row_37", + "amount_reference": "ird_taxable_income_total_by_band__row_37" + }, + { + "id": "row_38", + "kind": "positive", + "upper_cent": 2900000, + "indicator": "taxable_income_band_row_38_indicator", + "source_label": "$28,001 - $29,000", + "count_reference": "ird_taxable_income_people_by_band__row_38", + "amount_reference": "ird_taxable_income_total_by_band__row_38" + }, + { + "id": "row_39", + "kind": "positive", + "upper_cent": 3000000, + "indicator": "taxable_income_band_row_39_indicator", + "source_label": "$29,001 - $30,000", + "count_reference": "ird_taxable_income_people_by_band__row_39", + "amount_reference": "ird_taxable_income_total_by_band__row_39" + }, + { + "id": "row_40", + "kind": "positive", + "upper_cent": 3100000, + "indicator": "taxable_income_band_row_40_indicator", + "source_label": "$30,001 - $31,000", + "count_reference": "ird_taxable_income_people_by_band__row_40", + "amount_reference": "ird_taxable_income_total_by_band__row_40" + }, + { + "id": "row_41", + "kind": "positive", + "upper_cent": 3200000, + "indicator": "taxable_income_band_row_41_indicator", + "source_label": "$31,001 - $32,000", + "count_reference": "ird_taxable_income_people_by_band__row_41", + "amount_reference": "ird_taxable_income_total_by_band__row_41" + }, + { + "id": "row_42", + "kind": "positive", + "upper_cent": 3300000, + "indicator": "taxable_income_band_row_42_indicator", + "source_label": "$32,001 - $33,000", + "count_reference": "ird_taxable_income_people_by_band__row_42", + "amount_reference": "ird_taxable_income_total_by_band__row_42" + }, + { + "id": "row_43", + "kind": "positive", + "upper_cent": 3400000, + "indicator": "taxable_income_band_row_43_indicator", + "source_label": "$33,001 - $34,000", + "count_reference": "ird_taxable_income_people_by_band__row_43", + "amount_reference": "ird_taxable_income_total_by_band__row_43" + }, + { + "id": "row_44", + "kind": "positive", + "upper_cent": 3500000, + "indicator": "taxable_income_band_row_44_indicator", + "source_label": "$34,001 - $35,000", + "count_reference": "ird_taxable_income_people_by_band__row_44", + "amount_reference": "ird_taxable_income_total_by_band__row_44" + }, + { + "id": "row_45", + "kind": "positive", + "upper_cent": 3600000, + "indicator": "taxable_income_band_row_45_indicator", + "source_label": "$35,001 - $36,000", + "count_reference": "ird_taxable_income_people_by_band__row_45", + "amount_reference": "ird_taxable_income_total_by_band__row_45" + }, + { + "id": "row_46", + "kind": "positive", + "upper_cent": 3700000, + "indicator": "taxable_income_band_row_46_indicator", + "source_label": "$36,001 - $37,000", + "count_reference": "ird_taxable_income_people_by_band__row_46", + "amount_reference": "ird_taxable_income_total_by_band__row_46" + }, + { + "id": "row_47", + "kind": "positive", + "upper_cent": 3800000, + "indicator": "taxable_income_band_row_47_indicator", + "source_label": "$37,001 - $38,000", + "count_reference": "ird_taxable_income_people_by_band__row_47", + "amount_reference": "ird_taxable_income_total_by_band__row_47" + }, + { + "id": "row_48", + "kind": "positive", + "upper_cent": 3900000, + "indicator": "taxable_income_band_row_48_indicator", + "source_label": "$38,001 - $39,000", + "count_reference": "ird_taxable_income_people_by_band__row_48", + "amount_reference": "ird_taxable_income_total_by_band__row_48" + }, + { + "id": "row_49", + "kind": "positive", + "upper_cent": 4000000, + "indicator": "taxable_income_band_row_49_indicator", + "source_label": "$39,001 - $40,000", + "count_reference": "ird_taxable_income_people_by_band__row_49", + "amount_reference": "ird_taxable_income_total_by_band__row_49" + }, + { + "id": "row_50", + "kind": "positive", + "upper_cent": 4100000, + "indicator": "taxable_income_band_row_50_indicator", + "source_label": "$40,001 - $41,000", + "count_reference": "ird_taxable_income_people_by_band__row_50", + "amount_reference": "ird_taxable_income_total_by_band__row_50" + }, + { + "id": "row_51", + "kind": "positive", + "upper_cent": 4200000, + "indicator": "taxable_income_band_row_51_indicator", + "source_label": "$41,001 - $42,000", + "count_reference": "ird_taxable_income_people_by_band__row_51", + "amount_reference": "ird_taxable_income_total_by_band__row_51" + }, + { + "id": "row_52", + "kind": "positive", + "upper_cent": 4300000, + "indicator": "taxable_income_band_row_52_indicator", + "source_label": "$42,001 - $43,000", + "count_reference": "ird_taxable_income_people_by_band__row_52", + "amount_reference": "ird_taxable_income_total_by_band__row_52" + }, + { + "id": "row_53", + "kind": "positive", + "upper_cent": 4400000, + "indicator": "taxable_income_band_row_53_indicator", + "source_label": "$43,001 - $44,000", + "count_reference": "ird_taxable_income_people_by_band__row_53", + "amount_reference": "ird_taxable_income_total_by_band__row_53" + }, + { + "id": "row_54", + "kind": "positive", + "upper_cent": 4500000, + "indicator": "taxable_income_band_row_54_indicator", + "source_label": "$44,001 - $45,000", + "count_reference": "ird_taxable_income_people_by_band__row_54", + "amount_reference": "ird_taxable_income_total_by_band__row_54" + }, + { + "id": "row_55", + "kind": "positive", + "upper_cent": 4600000, + "indicator": "taxable_income_band_row_55_indicator", + "source_label": "$45,001 - $46,000", + "count_reference": "ird_taxable_income_people_by_band__row_55", + "amount_reference": "ird_taxable_income_total_by_band__row_55" + }, + { + "id": "row_56", + "kind": "positive", + "upper_cent": 4700000, + "indicator": "taxable_income_band_row_56_indicator", + "source_label": "$46,001 - $47,000", + "count_reference": "ird_taxable_income_people_by_band__row_56", + "amount_reference": "ird_taxable_income_total_by_band__row_56" + }, + { + "id": "row_57", + "kind": "positive", + "upper_cent": 4800000, + "indicator": "taxable_income_band_row_57_indicator", + "source_label": "$47,001 - $48,000", + "count_reference": "ird_taxable_income_people_by_band__row_57", + "amount_reference": "ird_taxable_income_total_by_band__row_57" + }, + { + "id": "row_58", + "kind": "positive", + "upper_cent": 4900000, + "indicator": "taxable_income_band_row_58_indicator", + "source_label": "$48,001 - $49,000", + "count_reference": "ird_taxable_income_people_by_band__row_58", + "amount_reference": "ird_taxable_income_total_by_band__row_58" + }, + { + "id": "row_59", + "kind": "positive", + "upper_cent": 5000000, + "indicator": "taxable_income_band_row_59_indicator", + "source_label": "$49,001 - $50,000", + "count_reference": "ird_taxable_income_people_by_band__row_59", + "amount_reference": "ird_taxable_income_total_by_band__row_59" + }, + { + "id": "row_60", + "kind": "positive", + "upper_cent": 5100000, + "indicator": "taxable_income_band_row_60_indicator", + "source_label": "$50,000 - $51,000", + "count_reference": "ird_taxable_income_people_by_band__row_60", + "amount_reference": "ird_taxable_income_total_by_band__row_60" + }, + { + "id": "row_61", + "kind": "positive", + "upper_cent": 5200000, + "indicator": "taxable_income_band_row_61_indicator", + "source_label": "$51,001 - $52,000", + "count_reference": "ird_taxable_income_people_by_band__row_61", + "amount_reference": "ird_taxable_income_total_by_band__row_61" + }, + { + "id": "row_62", + "kind": "positive", + "upper_cent": 5300000, + "indicator": "taxable_income_band_row_62_indicator", + "source_label": "$52,001 - $53,000", + "count_reference": "ird_taxable_income_people_by_band__row_62", + "amount_reference": "ird_taxable_income_total_by_band__row_62" + }, + { + "id": "row_63", + "kind": "positive", + "upper_cent": 5400000, + "indicator": "taxable_income_band_row_63_indicator", + "source_label": "$53,001 - $54,000", + "count_reference": "ird_taxable_income_people_by_band__row_63", + "amount_reference": "ird_taxable_income_total_by_band__row_63" + }, + { + "id": "row_64", + "kind": "positive", + "upper_cent": 5500000, + "indicator": "taxable_income_band_row_64_indicator", + "source_label": "$54,001 - $55,000", + "count_reference": "ird_taxable_income_people_by_band__row_64", + "amount_reference": "ird_taxable_income_total_by_band__row_64" + }, + { + "id": "row_65", + "kind": "positive", + "upper_cent": 5600000, + "indicator": "taxable_income_band_row_65_indicator", + "source_label": "$55,001 - $56,000", + "count_reference": "ird_taxable_income_people_by_band__row_65", + "amount_reference": "ird_taxable_income_total_by_band__row_65" + }, + { + "id": "row_66", + "kind": "positive", + "upper_cent": 5700000, + "indicator": "taxable_income_band_row_66_indicator", + "source_label": "$56,001 - $57,000", + "count_reference": "ird_taxable_income_people_by_band__row_66", + "amount_reference": "ird_taxable_income_total_by_band__row_66" + }, + { + "id": "row_67", + "kind": "positive", + "upper_cent": 5800000, + "indicator": "taxable_income_band_row_67_indicator", + "source_label": "$57,001 - $58,000", + "count_reference": "ird_taxable_income_people_by_band__row_67", + "amount_reference": "ird_taxable_income_total_by_band__row_67" + }, + { + "id": "row_68", + "kind": "positive", + "upper_cent": 5900000, + "indicator": "taxable_income_band_row_68_indicator", + "source_label": "$58,001 - $59,000", + "count_reference": "ird_taxable_income_people_by_band__row_68", + "amount_reference": "ird_taxable_income_total_by_band__row_68" + }, + { + "id": "row_69", + "kind": "positive", + "upper_cent": 6000000, + "indicator": "taxable_income_band_row_69_indicator", + "source_label": "$59,001 - $60,000", + "count_reference": "ird_taxable_income_people_by_band__row_69", + "amount_reference": "ird_taxable_income_total_by_band__row_69" + }, + { + "id": "row_70", + "kind": "positive", + "upper_cent": 6100000, + "indicator": "taxable_income_band_row_70_indicator", + "source_label": "$60,001 - $61,000", + "count_reference": "ird_taxable_income_people_by_band__row_70", + "amount_reference": "ird_taxable_income_total_by_band__row_70" + }, + { + "id": "row_71", + "kind": "positive", + "upper_cent": 6200000, + "indicator": "taxable_income_band_row_71_indicator", + "source_label": "$61,001 - $62,000", + "count_reference": "ird_taxable_income_people_by_band__row_71", + "amount_reference": "ird_taxable_income_total_by_band__row_71" + }, + { + "id": "row_72", + "kind": "positive", + "upper_cent": 6300000, + "indicator": "taxable_income_band_row_72_indicator", + "source_label": "$62,001 - $63,000", + "count_reference": "ird_taxable_income_people_by_band__row_72", + "amount_reference": "ird_taxable_income_total_by_band__row_72" + }, + { + "id": "row_73", + "kind": "positive", + "upper_cent": 6400000, + "indicator": "taxable_income_band_row_73_indicator", + "source_label": "$63,001 - $64,000", + "count_reference": "ird_taxable_income_people_by_band__row_73", + "amount_reference": "ird_taxable_income_total_by_band__row_73" + }, + { + "id": "row_74", + "kind": "positive", + "upper_cent": 6500000, + "indicator": "taxable_income_band_row_74_indicator", + "source_label": "$64,001 - $65,000", + "count_reference": "ird_taxable_income_people_by_band__row_74", + "amount_reference": "ird_taxable_income_total_by_band__row_74" + }, + { + "id": "row_75", + "kind": "positive", + "upper_cent": 6600000, + "indicator": "taxable_income_band_row_75_indicator", + "source_label": "$65,001 - $66,000", + "count_reference": "ird_taxable_income_people_by_band__row_75", + "amount_reference": "ird_taxable_income_total_by_band__row_75" + }, + { + "id": "row_76", + "kind": "positive", + "upper_cent": 6700000, + "indicator": "taxable_income_band_row_76_indicator", + "source_label": "$66,001 - $67,000", + "count_reference": "ird_taxable_income_people_by_band__row_76", + "amount_reference": "ird_taxable_income_total_by_band__row_76" + }, + { + "id": "row_77", + "kind": "positive", + "upper_cent": 6800000, + "indicator": "taxable_income_band_row_77_indicator", + "source_label": "$67,001 - $68,000", + "count_reference": "ird_taxable_income_people_by_band__row_77", + "amount_reference": "ird_taxable_income_total_by_band__row_77" + }, + { + "id": "row_78", + "kind": "positive", + "upper_cent": 6900000, + "indicator": "taxable_income_band_row_78_indicator", + "source_label": "$68,001 - $69,000", + "count_reference": "ird_taxable_income_people_by_band__row_78", + "amount_reference": "ird_taxable_income_total_by_band__row_78" + }, + { + "id": "row_79", + "kind": "positive", + "upper_cent": 7000000, + "indicator": "taxable_income_band_row_79_indicator", + "source_label": "$69,001 - $70,000", + "count_reference": "ird_taxable_income_people_by_band__row_79", + "amount_reference": "ird_taxable_income_total_by_band__row_79" + }, + { + "id": "row_80", + "kind": "positive", + "upper_cent": 7100000, + "indicator": "taxable_income_band_row_80_indicator", + "source_label": "$70,001 - $71,000", + "count_reference": "ird_taxable_income_people_by_band__row_80", + "amount_reference": "ird_taxable_income_total_by_band__row_80" + }, + { + "id": "row_81", + "kind": "positive", + "upper_cent": 7200000, + "indicator": "taxable_income_band_row_81_indicator", + "source_label": "$71,001 - $72,000", + "count_reference": "ird_taxable_income_people_by_band__row_81", + "amount_reference": "ird_taxable_income_total_by_band__row_81" + }, + { + "id": "row_82", + "kind": "positive", + "upper_cent": 7300000, + "indicator": "taxable_income_band_row_82_indicator", + "source_label": "$72,001 - $73,000", + "count_reference": "ird_taxable_income_people_by_band__row_82", + "amount_reference": "ird_taxable_income_total_by_band__row_82" + }, + { + "id": "row_83", + "kind": "positive", + "upper_cent": 7400000, + "indicator": "taxable_income_band_row_83_indicator", + "source_label": "$73,001 - $74,000", + "count_reference": "ird_taxable_income_people_by_band__row_83", + "amount_reference": "ird_taxable_income_total_by_band__row_83" + }, + { + "id": "row_84", + "kind": "positive", + "upper_cent": 7500000, + "indicator": "taxable_income_band_row_84_indicator", + "source_label": "$74,001 - $75,000", + "count_reference": "ird_taxable_income_people_by_band__row_84", + "amount_reference": "ird_taxable_income_total_by_band__row_84" + }, + { + "id": "row_85", + "kind": "positive", + "upper_cent": 7600000, + "indicator": "taxable_income_band_row_85_indicator", + "source_label": "$75,001 - $76,000", + "count_reference": "ird_taxable_income_people_by_band__row_85", + "amount_reference": "ird_taxable_income_total_by_band__row_85" + }, + { + "id": "row_86", + "kind": "positive", + "upper_cent": 7700000, + "indicator": "taxable_income_band_row_86_indicator", + "source_label": "$76,001 - $77,000", + "count_reference": "ird_taxable_income_people_by_band__row_86", + "amount_reference": "ird_taxable_income_total_by_band__row_86" + }, + { + "id": "row_87", + "kind": "positive", + "upper_cent": 7800000, + "indicator": "taxable_income_band_row_87_indicator", + "source_label": "$77,001 - $78,000", + "count_reference": "ird_taxable_income_people_by_band__row_87", + "amount_reference": "ird_taxable_income_total_by_band__row_87" + }, + { + "id": "row_88", + "kind": "positive", + "upper_cent": 7900000, + "indicator": "taxable_income_band_row_88_indicator", + "source_label": "$78,001 - $79,000", + "count_reference": "ird_taxable_income_people_by_band__row_88", + "amount_reference": "ird_taxable_income_total_by_band__row_88" + }, + { + "id": "row_89", + "kind": "positive", + "upper_cent": 8000000, + "indicator": "taxable_income_band_row_89_indicator", + "source_label": "$79,001 - $80,000", + "count_reference": "ird_taxable_income_people_by_band__row_89", + "amount_reference": "ird_taxable_income_total_by_band__row_89" + }, + { + "id": "row_90", + "kind": "positive", + "upper_cent": 8100000, + "indicator": "taxable_income_band_row_90_indicator", + "source_label": "$80,000 - $81,000", + "count_reference": "ird_taxable_income_people_by_band__row_90", + "amount_reference": "ird_taxable_income_total_by_band__row_90" + }, + { + "id": "row_91", + "kind": "positive", + "upper_cent": 8200000, + "indicator": "taxable_income_band_row_91_indicator", + "source_label": "$81,001 - $82,000", + "count_reference": "ird_taxable_income_people_by_band__row_91", + "amount_reference": "ird_taxable_income_total_by_band__row_91" + }, + { + "id": "row_92", + "kind": "positive", + "upper_cent": 8300000, + "indicator": "taxable_income_band_row_92_indicator", + "source_label": "$82,001 - $83,000", + "count_reference": "ird_taxable_income_people_by_band__row_92", + "amount_reference": "ird_taxable_income_total_by_band__row_92" + }, + { + "id": "row_93", + "kind": "positive", + "upper_cent": 8400000, + "indicator": "taxable_income_band_row_93_indicator", + "source_label": "$83,001 - $84,000", + "count_reference": "ird_taxable_income_people_by_band__row_93", + "amount_reference": "ird_taxable_income_total_by_band__row_93" + }, + { + "id": "row_94", + "kind": "positive", + "upper_cent": 8500000, + "indicator": "taxable_income_band_row_94_indicator", + "source_label": "$84,001 - $85,000", + "count_reference": "ird_taxable_income_people_by_band__row_94", + "amount_reference": "ird_taxable_income_total_by_band__row_94" + }, + { + "id": "row_95", + "kind": "positive", + "upper_cent": 8600000, + "indicator": "taxable_income_band_row_95_indicator", + "source_label": "$85,001 - $86,000", + "count_reference": "ird_taxable_income_people_by_band__row_95", + "amount_reference": "ird_taxable_income_total_by_band__row_95" + }, + { + "id": "row_96", + "kind": "positive", + "upper_cent": 8700000, + "indicator": "taxable_income_band_row_96_indicator", + "source_label": "$86,001 - $87,000", + "count_reference": "ird_taxable_income_people_by_band__row_96", + "amount_reference": "ird_taxable_income_total_by_band__row_96" + }, + { + "id": "row_97", + "kind": "positive", + "upper_cent": 8800000, + "indicator": "taxable_income_band_row_97_indicator", + "source_label": "$87,001 - $88,000", + "count_reference": "ird_taxable_income_people_by_band__row_97", + "amount_reference": "ird_taxable_income_total_by_band__row_97" + }, + { + "id": "row_98", + "kind": "positive", + "upper_cent": 8900000, + "indicator": "taxable_income_band_row_98_indicator", + "source_label": "$88,001 - $89,000", + "count_reference": "ird_taxable_income_people_by_band__row_98", + "amount_reference": "ird_taxable_income_total_by_band__row_98" + }, + { + "id": "row_99", + "kind": "positive", + "upper_cent": 9000000, + "indicator": "taxable_income_band_row_99_indicator", + "source_label": "$89,001 - $90,000", + "count_reference": "ird_taxable_income_people_by_band__row_99", + "amount_reference": "ird_taxable_income_total_by_band__row_99" + }, + { + "id": "row_100", + "kind": "positive", + "upper_cent": 9100000, + "indicator": "taxable_income_band_row_100_indicator", + "source_label": "$90,001 - $91,000", + "count_reference": "ird_taxable_income_people_by_band__row_100", + "amount_reference": "ird_taxable_income_total_by_band__row_100" + }, + { + "id": "row_101", + "kind": "positive", + "upper_cent": 9200000, + "indicator": "taxable_income_band_row_101_indicator", + "source_label": "$91,001 - $92,000", + "count_reference": "ird_taxable_income_people_by_band__row_101", + "amount_reference": "ird_taxable_income_total_by_band__row_101" + }, + { + "id": "row_102", + "kind": "positive", + "upper_cent": 9300000, + "indicator": "taxable_income_band_row_102_indicator", + "source_label": "$92,001 - $93,000", + "count_reference": "ird_taxable_income_people_by_band__row_102", + "amount_reference": "ird_taxable_income_total_by_band__row_102" + }, + { + "id": "row_103", + "kind": "positive", + "upper_cent": 9400000, + "indicator": "taxable_income_band_row_103_indicator", + "source_label": "$93,001 - $94,000", + "count_reference": "ird_taxable_income_people_by_band__row_103", + "amount_reference": "ird_taxable_income_total_by_band__row_103" + }, + { + "id": "row_104", + "kind": "positive", + "upper_cent": 9500000, + "indicator": "taxable_income_band_row_104_indicator", + "source_label": "$94,001 - $95,000", + "count_reference": "ird_taxable_income_people_by_band__row_104", + "amount_reference": "ird_taxable_income_total_by_band__row_104" + }, + { + "id": "row_105", + "kind": "positive", + "upper_cent": 9600000, + "indicator": "taxable_income_band_row_105_indicator", + "source_label": "$95,001 - $96,000", + "count_reference": "ird_taxable_income_people_by_band__row_105", + "amount_reference": "ird_taxable_income_total_by_band__row_105" + }, + { + "id": "row_106", + "kind": "positive", + "upper_cent": 9700000, + "indicator": "taxable_income_band_row_106_indicator", + "source_label": "$96,001 - $97,000", + "count_reference": "ird_taxable_income_people_by_band__row_106", + "amount_reference": "ird_taxable_income_total_by_band__row_106" + }, + { + "id": "row_107", + "kind": "positive", + "upper_cent": 9800000, + "indicator": "taxable_income_band_row_107_indicator", + "source_label": "$97,001 - $98,000", + "count_reference": "ird_taxable_income_people_by_band__row_107", + "amount_reference": "ird_taxable_income_total_by_band__row_107" + }, + { + "id": "row_108", + "kind": "positive", + "upper_cent": 9900000, + "indicator": "taxable_income_band_row_108_indicator", + "source_label": "$98,001 - $99,000", + "count_reference": "ird_taxable_income_people_by_band__row_108", + "amount_reference": "ird_taxable_income_total_by_band__row_108" + }, + { + "id": "row_109", + "kind": "positive", + "upper_cent": 10000000, + "indicator": "taxable_income_band_row_109_indicator", + "source_label": "$99,001 - $100,000", + "count_reference": "ird_taxable_income_people_by_band__row_109", + "amount_reference": "ird_taxable_income_total_by_band__row_109" + }, + { + "id": "row_110", + "kind": "positive", + "upper_cent": 10100000, + "indicator": "taxable_income_band_row_110_indicator", + "source_label": "$100,001 - $101,000", + "count_reference": "ird_taxable_income_people_by_band__row_110", + "amount_reference": "ird_taxable_income_total_by_band__row_110" + }, + { + "id": "row_111", + "kind": "positive", + "upper_cent": 10200000, + "indicator": "taxable_income_band_row_111_indicator", + "source_label": "$101,001 - $102,000", + "count_reference": "ird_taxable_income_people_by_band__row_111", + "amount_reference": "ird_taxable_income_total_by_band__row_111" + }, + { + "id": "row_112", + "kind": "positive", + "upper_cent": 10300000, + "indicator": "taxable_income_band_row_112_indicator", + "source_label": "$102,001 - $103,000", + "count_reference": "ird_taxable_income_people_by_band__row_112", + "amount_reference": "ird_taxable_income_total_by_band__row_112" + }, + { + "id": "row_113", + "kind": "positive", + "upper_cent": 10400000, + "indicator": "taxable_income_band_row_113_indicator", + "source_label": "$103,001 - $104,000", + "count_reference": "ird_taxable_income_people_by_band__row_113", + "amount_reference": "ird_taxable_income_total_by_band__row_113" + }, + { + "id": "row_114", + "kind": "positive", + "upper_cent": 10500000, + "indicator": "taxable_income_band_row_114_indicator", + "source_label": "$104,001 - $105,000", + "count_reference": "ird_taxable_income_people_by_band__row_114", + "amount_reference": "ird_taxable_income_total_by_band__row_114" + }, + { + "id": "row_115", + "kind": "positive", + "upper_cent": 10600000, + "indicator": "taxable_income_band_row_115_indicator", + "source_label": "$105,001 - $106,000", + "count_reference": "ird_taxable_income_people_by_band__row_115", + "amount_reference": "ird_taxable_income_total_by_band__row_115" + }, + { + "id": "row_116", + "kind": "positive", + "upper_cent": 10700000, + "indicator": "taxable_income_band_row_116_indicator", + "source_label": "$106,001 - $107,000", + "count_reference": "ird_taxable_income_people_by_band__row_116", + "amount_reference": "ird_taxable_income_total_by_band__row_116" + }, + { + "id": "row_117", + "kind": "positive", + "upper_cent": 10800000, + "indicator": "taxable_income_band_row_117_indicator", + "source_label": "$107,001 - $108,000", + "count_reference": "ird_taxable_income_people_by_band__row_117", + "amount_reference": "ird_taxable_income_total_by_band__row_117" + }, + { + "id": "row_118", + "kind": "positive", + "upper_cent": 10900000, + "indicator": "taxable_income_band_row_118_indicator", + "source_label": "$108,001 - $109,000", + "count_reference": "ird_taxable_income_people_by_band__row_118", + "amount_reference": "ird_taxable_income_total_by_band__row_118" + }, + { + "id": "row_119", + "kind": "positive", + "upper_cent": 11000000, + "indicator": "taxable_income_band_row_119_indicator", + "source_label": "$109,001 - $110,000", + "count_reference": "ird_taxable_income_people_by_band__row_119", + "amount_reference": "ird_taxable_income_total_by_band__row_119" + }, + { + "id": "row_120", + "kind": "positive", + "upper_cent": 11100000, + "indicator": "taxable_income_band_row_120_indicator", + "source_label": "$110,001 - $111,000", + "count_reference": "ird_taxable_income_people_by_band__row_120", + "amount_reference": "ird_taxable_income_total_by_band__row_120" + }, + { + "id": "row_121", + "kind": "positive", + "upper_cent": 11200000, + "indicator": "taxable_income_band_row_121_indicator", + "source_label": "$111,001 - $112,000", + "count_reference": "ird_taxable_income_people_by_band__row_121", + "amount_reference": "ird_taxable_income_total_by_band__row_121" + }, + { + "id": "row_122", + "kind": "positive", + "upper_cent": 11300000, + "indicator": "taxable_income_band_row_122_indicator", + "source_label": "$112,001 - $113,000", + "count_reference": "ird_taxable_income_people_by_band__row_122", + "amount_reference": "ird_taxable_income_total_by_band__row_122" + }, + { + "id": "row_123", + "kind": "positive", + "upper_cent": 11400000, + "indicator": "taxable_income_band_row_123_indicator", + "source_label": "$113,001 - $114,000", + "count_reference": "ird_taxable_income_people_by_band__row_123", + "amount_reference": "ird_taxable_income_total_by_band__row_123" + }, + { + "id": "row_124", + "kind": "positive", + "upper_cent": 11500000, + "indicator": "taxable_income_band_row_124_indicator", + "source_label": "$114,001 - $115,000", + "count_reference": "ird_taxable_income_people_by_band__row_124", + "amount_reference": "ird_taxable_income_total_by_band__row_124" + }, + { + "id": "row_125", + "kind": "positive", + "upper_cent": 11600000, + "indicator": "taxable_income_band_row_125_indicator", + "source_label": "$115,001 - $116,000", + "count_reference": "ird_taxable_income_people_by_band__row_125", + "amount_reference": "ird_taxable_income_total_by_band__row_125" + }, + { + "id": "row_126", + "kind": "positive", + "upper_cent": 11700000, + "indicator": "taxable_income_band_row_126_indicator", + "source_label": "$116,001 - $117,000", + "count_reference": "ird_taxable_income_people_by_band__row_126", + "amount_reference": "ird_taxable_income_total_by_band__row_126" + }, + { + "id": "row_127", + "kind": "positive", + "upper_cent": 11800000, + "indicator": "taxable_income_band_row_127_indicator", + "source_label": "$117,001 - $118,000", + "count_reference": "ird_taxable_income_people_by_band__row_127", + "amount_reference": "ird_taxable_income_total_by_band__row_127" + }, + { + "id": "row_128", + "kind": "positive", + "upper_cent": 11900000, + "indicator": "taxable_income_band_row_128_indicator", + "source_label": "$118,001 - $119,000", + "count_reference": "ird_taxable_income_people_by_band__row_128", + "amount_reference": "ird_taxable_income_total_by_band__row_128" + }, + { + "id": "row_129", + "kind": "positive", + "upper_cent": 12000000, + "indicator": "taxable_income_band_row_129_indicator", + "source_label": "$119,001 - $120,000", + "count_reference": "ird_taxable_income_people_by_band__row_129", + "amount_reference": "ird_taxable_income_total_by_band__row_129" + }, + { + "id": "row_130", + "kind": "positive", + "upper_cent": 12100000, + "indicator": "taxable_income_band_row_130_indicator", + "source_label": "$120,001 - $121,000", + "count_reference": "ird_taxable_income_people_by_band__row_130", + "amount_reference": "ird_taxable_income_total_by_band__row_130" + }, + { + "id": "row_131", + "kind": "positive", + "upper_cent": 12200000, + "indicator": "taxable_income_band_row_131_indicator", + "source_label": "$121,001 - $122,000", + "count_reference": "ird_taxable_income_people_by_band__row_131", + "amount_reference": "ird_taxable_income_total_by_band__row_131" + }, + { + "id": "row_132", + "kind": "positive", + "upper_cent": 12300000, + "indicator": "taxable_income_band_row_132_indicator", + "source_label": "$122,001 - $123,000", + "count_reference": "ird_taxable_income_people_by_band__row_132", + "amount_reference": "ird_taxable_income_total_by_band__row_132" + }, + { + "id": "row_133", + "kind": "positive", + "upper_cent": 12400000, + "indicator": "taxable_income_band_row_133_indicator", + "source_label": "$123,001 - $124,000", + "count_reference": "ird_taxable_income_people_by_band__row_133", + "amount_reference": "ird_taxable_income_total_by_band__row_133" + }, + { + "id": "row_134", + "kind": "positive", + "upper_cent": 12500000, + "indicator": "taxable_income_band_row_134_indicator", + "source_label": "$124,001 - $125,000", + "count_reference": "ird_taxable_income_people_by_band__row_134", + "amount_reference": "ird_taxable_income_total_by_band__row_134" + }, + { + "id": "row_135", + "kind": "positive", + "upper_cent": 12600000, + "indicator": "taxable_income_band_row_135_indicator", + "source_label": "$125,001 - $126,000", + "count_reference": "ird_taxable_income_people_by_band__row_135", + "amount_reference": "ird_taxable_income_total_by_band__row_135" + }, + { + "id": "row_136", + "kind": "positive", + "upper_cent": 12700000, + "indicator": "taxable_income_band_row_136_indicator", + "source_label": "$126,001 - $127,000", + "count_reference": "ird_taxable_income_people_by_band__row_136", + "amount_reference": "ird_taxable_income_total_by_band__row_136" + }, + { + "id": "row_137", + "kind": "positive", + "upper_cent": 12800000, + "indicator": "taxable_income_band_row_137_indicator", + "source_label": "$127,001 - $128,000", + "count_reference": "ird_taxable_income_people_by_band__row_137", + "amount_reference": "ird_taxable_income_total_by_band__row_137" + }, + { + "id": "row_138", + "kind": "positive", + "upper_cent": 12900000, + "indicator": "taxable_income_band_row_138_indicator", + "source_label": "$128,001 - $129,000", + "count_reference": "ird_taxable_income_people_by_band__row_138", + "amount_reference": "ird_taxable_income_total_by_band__row_138" + }, + { + "id": "row_139", + "kind": "positive", + "upper_cent": 13000000, + "indicator": "taxable_income_band_row_139_indicator", + "source_label": "$129,001 - $130,000", + "count_reference": "ird_taxable_income_people_by_band__row_139", + "amount_reference": "ird_taxable_income_total_by_band__row_139" + }, + { + "id": "row_140", + "kind": "positive", + "upper_cent": 13100000, + "indicator": "taxable_income_band_row_140_indicator", + "source_label": "$130,001 - $131,000", + "count_reference": "ird_taxable_income_people_by_band__row_140", + "amount_reference": "ird_taxable_income_total_by_band__row_140" + }, + { + "id": "row_141", + "kind": "positive", + "upper_cent": 13200000, + "indicator": "taxable_income_band_row_141_indicator", + "source_label": "$131,001 - $132,000", + "count_reference": "ird_taxable_income_people_by_band__row_141", + "amount_reference": "ird_taxable_income_total_by_band__row_141" + }, + { + "id": "row_142", + "kind": "positive", + "upper_cent": 13300000, + "indicator": "taxable_income_band_row_142_indicator", + "source_label": "$132,001 - $133,000", + "count_reference": "ird_taxable_income_people_by_band__row_142", + "amount_reference": "ird_taxable_income_total_by_band__row_142" + }, + { + "id": "row_143", + "kind": "positive", + "upper_cent": 13400000, + "indicator": "taxable_income_band_row_143_indicator", + "source_label": "$133,001 - $134,000", + "count_reference": "ird_taxable_income_people_by_band__row_143", + "amount_reference": "ird_taxable_income_total_by_band__row_143" + }, + { + "id": "row_144", + "kind": "positive", + "upper_cent": 13500000, + "indicator": "taxable_income_band_row_144_indicator", + "source_label": "$134,001 - $135,000", + "count_reference": "ird_taxable_income_people_by_band__row_144", + "amount_reference": "ird_taxable_income_total_by_band__row_144" + }, + { + "id": "row_145", + "kind": "positive", + "upper_cent": 13600000, + "indicator": "taxable_income_band_row_145_indicator", + "source_label": "$135,001 - $136,000", + "count_reference": "ird_taxable_income_people_by_band__row_145", + "amount_reference": "ird_taxable_income_total_by_band__row_145" + }, + { + "id": "row_146", + "kind": "positive", + "upper_cent": 13700000, + "indicator": "taxable_income_band_row_146_indicator", + "source_label": "$136,001 - $137,000", + "count_reference": "ird_taxable_income_people_by_band__row_146", + "amount_reference": "ird_taxable_income_total_by_band__row_146" + }, + { + "id": "row_147", + "kind": "positive", + "upper_cent": 13800000, + "indicator": "taxable_income_band_row_147_indicator", + "source_label": "$137,001 - $138,000", + "count_reference": "ird_taxable_income_people_by_band__row_147", + "amount_reference": "ird_taxable_income_total_by_band__row_147" + }, + { + "id": "row_148", + "kind": "positive", + "upper_cent": 13900000, + "indicator": "taxable_income_band_row_148_indicator", + "source_label": "$138,001 - $139,000", + "count_reference": "ird_taxable_income_people_by_band__row_148", + "amount_reference": "ird_taxable_income_total_by_band__row_148" + }, + { + "id": "row_149", + "kind": "positive", + "upper_cent": 14000000, + "indicator": "taxable_income_band_row_149_indicator", + "source_label": "$139,001 - $140,000", + "count_reference": "ird_taxable_income_people_by_band__row_149", + "amount_reference": "ird_taxable_income_total_by_band__row_149" + }, + { + "id": "row_150", + "kind": "positive", + "upper_cent": 14100000, + "indicator": "taxable_income_band_row_150_indicator", + "source_label": "$140,001 - $141,000", + "count_reference": "ird_taxable_income_people_by_band__row_150", + "amount_reference": "ird_taxable_income_total_by_band__row_150" + }, + { + "id": "row_151", + "kind": "positive", + "upper_cent": 14200000, + "indicator": "taxable_income_band_row_151_indicator", + "source_label": "$141,001 - $142,000", + "count_reference": "ird_taxable_income_people_by_band__row_151", + "amount_reference": "ird_taxable_income_total_by_band__row_151" + }, + { + "id": "row_152", + "kind": "positive", + "upper_cent": 14300000, + "indicator": "taxable_income_band_row_152_indicator", + "source_label": "$142,001 - $143,000", + "count_reference": "ird_taxable_income_people_by_band__row_152", + "amount_reference": "ird_taxable_income_total_by_band__row_152" + }, + { + "id": "row_153", + "kind": "positive", + "upper_cent": 14400000, + "indicator": "taxable_income_band_row_153_indicator", + "source_label": "$143,001 - $144,000", + "count_reference": "ird_taxable_income_people_by_band__row_153", + "amount_reference": "ird_taxable_income_total_by_band__row_153" + }, + { + "id": "row_154", + "kind": "positive", + "upper_cent": 14500000, + "indicator": "taxable_income_band_row_154_indicator", + "source_label": "$144,001 - $145,000", + "count_reference": "ird_taxable_income_people_by_band__row_154", + "amount_reference": "ird_taxable_income_total_by_band__row_154" + }, + { + "id": "row_155", + "kind": "positive", + "upper_cent": 14600000, + "indicator": "taxable_income_band_row_155_indicator", + "source_label": "$145,001 - $146,000", + "count_reference": "ird_taxable_income_people_by_band__row_155", + "amount_reference": "ird_taxable_income_total_by_band__row_155" + }, + { + "id": "row_156", + "kind": "positive", + "upper_cent": 14700000, + "indicator": "taxable_income_band_row_156_indicator", + "source_label": "$146,001 - $147,000", + "count_reference": "ird_taxable_income_people_by_band__row_156", + "amount_reference": "ird_taxable_income_total_by_band__row_156" + }, + { + "id": "row_157", + "kind": "positive", + "upper_cent": 14800000, + "indicator": "taxable_income_band_row_157_indicator", + "source_label": "$147,001 - $148,000", + "count_reference": "ird_taxable_income_people_by_band__row_157", + "amount_reference": "ird_taxable_income_total_by_band__row_157" + }, + { + "id": "row_158", + "kind": "positive", + "upper_cent": 14900000, + "indicator": "taxable_income_band_row_158_indicator", + "source_label": "$148,001 - $149,000", + "count_reference": "ird_taxable_income_people_by_band__row_158", + "amount_reference": "ird_taxable_income_total_by_band__row_158" + }, + { + "id": "row_159", + "kind": "positive", + "upper_cent": 15000000, + "indicator": "taxable_income_band_row_159_indicator", + "source_label": "$149,001 - $150,000", + "count_reference": "ird_taxable_income_people_by_band__row_159", + "amount_reference": "ird_taxable_income_total_by_band__row_159" + }, + { + "id": "row_160", + "kind": "positive", + "upper_cent": 15100000, + "indicator": "taxable_income_band_row_160_indicator", + "source_label": "$150,001 - $151,000", + "count_reference": "ird_taxable_income_people_by_band__row_160", + "amount_reference": "ird_taxable_income_total_by_band__row_160" + }, + { + "id": "row_161", + "kind": "positive", + "upper_cent": 15200000, + "indicator": "taxable_income_band_row_161_indicator", + "source_label": "$151,001 - $152,000", + "count_reference": "ird_taxable_income_people_by_band__row_161", + "amount_reference": "ird_taxable_income_total_by_band__row_161" + }, + { + "id": "row_162", + "kind": "positive", + "upper_cent": 15300000, + "indicator": "taxable_income_band_row_162_indicator", + "source_label": "$152,001 - $153,000", + "count_reference": "ird_taxable_income_people_by_band__row_162", + "amount_reference": "ird_taxable_income_total_by_band__row_162" + }, + { + "id": "row_163", + "kind": "positive", + "upper_cent": 15400000, + "indicator": "taxable_income_band_row_163_indicator", + "source_label": "$153,001 - $154,000", + "count_reference": "ird_taxable_income_people_by_band__row_163", + "amount_reference": "ird_taxable_income_total_by_band__row_163" + }, + { + "id": "row_164", + "kind": "positive", + "upper_cent": 15500000, + "indicator": "taxable_income_band_row_164_indicator", + "source_label": "$154,001 - $155,000", + "count_reference": "ird_taxable_income_people_by_band__row_164", + "amount_reference": "ird_taxable_income_total_by_band__row_164" + }, + { + "id": "row_165", + "kind": "positive", + "upper_cent": 15600000, + "indicator": "taxable_income_band_row_165_indicator", + "source_label": "$155,001 - $156,000", + "count_reference": "ird_taxable_income_people_by_band__row_165", + "amount_reference": "ird_taxable_income_total_by_band__row_165" + }, + { + "id": "row_166", + "kind": "positive", + "upper_cent": 15700000, + "indicator": "taxable_income_band_row_166_indicator", + "source_label": "$156,001 - $157,000", + "count_reference": "ird_taxable_income_people_by_band__row_166", + "amount_reference": "ird_taxable_income_total_by_band__row_166" + }, + { + "id": "row_167", + "kind": "positive", + "upper_cent": 15800000, + "indicator": "taxable_income_band_row_167_indicator", + "source_label": "$157,001 - $158,000", + "count_reference": "ird_taxable_income_people_by_band__row_167", + "amount_reference": "ird_taxable_income_total_by_band__row_167" + }, + { + "id": "row_168", + "kind": "positive", + "upper_cent": 15900000, + "indicator": "taxable_income_band_row_168_indicator", + "source_label": "$158,001 - $159,000", + "count_reference": "ird_taxable_income_people_by_band__row_168", + "amount_reference": "ird_taxable_income_total_by_band__row_168" + }, + { + "id": "row_169", + "kind": "positive", + "upper_cent": 16000000, + "indicator": "taxable_income_band_row_169_indicator", + "source_label": "$159,001 - $160,000", + "count_reference": "ird_taxable_income_people_by_band__row_169", + "amount_reference": "ird_taxable_income_total_by_band__row_169" + }, + { + "id": "row_170", + "kind": "positive", + "upper_cent": 16100000, + "indicator": "taxable_income_band_row_170_indicator", + "source_label": "$160,001 - $161,000", + "count_reference": "ird_taxable_income_people_by_band__row_170", + "amount_reference": "ird_taxable_income_total_by_band__row_170" + }, + { + "id": "row_171", + "kind": "positive", + "upper_cent": 16200000, + "indicator": "taxable_income_band_row_171_indicator", + "source_label": "$161,001 - $162,000", + "count_reference": "ird_taxable_income_people_by_band__row_171", + "amount_reference": "ird_taxable_income_total_by_band__row_171" + }, + { + "id": "row_172", + "kind": "positive", + "upper_cent": 16300000, + "indicator": "taxable_income_band_row_172_indicator", + "source_label": "$162,001 - $163,000", + "count_reference": "ird_taxable_income_people_by_band__row_172", + "amount_reference": "ird_taxable_income_total_by_band__row_172" + }, + { + "id": "row_173", + "kind": "positive", + "upper_cent": 16400000, + "indicator": "taxable_income_band_row_173_indicator", + "source_label": "$163,001 - $164,000", + "count_reference": "ird_taxable_income_people_by_band__row_173", + "amount_reference": "ird_taxable_income_total_by_band__row_173" + }, + { + "id": "row_174", + "kind": "positive", + "upper_cent": 16500000, + "indicator": "taxable_income_band_row_174_indicator", + "source_label": "$164,001 - $165,000", + "count_reference": "ird_taxable_income_people_by_band__row_174", + "amount_reference": "ird_taxable_income_total_by_band__row_174" + }, + { + "id": "row_175", + "kind": "positive", + "upper_cent": 16600000, + "indicator": "taxable_income_band_row_175_indicator", + "source_label": "$165,001 - $166,000", + "count_reference": "ird_taxable_income_people_by_band__row_175", + "amount_reference": "ird_taxable_income_total_by_band__row_175" + }, + { + "id": "row_176", + "kind": "positive", + "upper_cent": 16700000, + "indicator": "taxable_income_band_row_176_indicator", + "source_label": "$166,001 - $167,000", + "count_reference": "ird_taxable_income_people_by_band__row_176", + "amount_reference": "ird_taxable_income_total_by_band__row_176" + }, + { + "id": "row_177", + "kind": "positive", + "upper_cent": 16800000, + "indicator": "taxable_income_band_row_177_indicator", + "source_label": "$167,001 - $168,000", + "count_reference": "ird_taxable_income_people_by_band__row_177", + "amount_reference": "ird_taxable_income_total_by_band__row_177" + }, + { + "id": "row_178", + "kind": "positive", + "upper_cent": 16900000, + "indicator": "taxable_income_band_row_178_indicator", + "source_label": "$168,001 - $169,000", + "count_reference": "ird_taxable_income_people_by_band__row_178", + "amount_reference": "ird_taxable_income_total_by_band__row_178" + }, + { + "id": "row_179", + "kind": "positive", + "upper_cent": 17000000, + "indicator": "taxable_income_band_row_179_indicator", + "source_label": "$169,001 - $170,000", + "count_reference": "ird_taxable_income_people_by_band__row_179", + "amount_reference": "ird_taxable_income_total_by_band__row_179" + }, + { + "id": "row_180", + "kind": "positive", + "upper_cent": 17100000, + "indicator": "taxable_income_band_row_180_indicator", + "source_label": "$170,001 - $171,000", + "count_reference": "ird_taxable_income_people_by_band__row_180", + "amount_reference": "ird_taxable_income_total_by_band__row_180" + }, + { + "id": "row_181", + "kind": "positive", + "upper_cent": 17200000, + "indicator": "taxable_income_band_row_181_indicator", + "source_label": "$171,001 - $172,000", + "count_reference": "ird_taxable_income_people_by_band__row_181", + "amount_reference": "ird_taxable_income_total_by_band__row_181" + }, + { + "id": "row_182", + "kind": "positive", + "upper_cent": 17300000, + "indicator": "taxable_income_band_row_182_indicator", + "source_label": "$172,001 - $173,000", + "count_reference": "ird_taxable_income_people_by_band__row_182", + "amount_reference": "ird_taxable_income_total_by_band__row_182" + }, + { + "id": "row_183", + "kind": "positive", + "upper_cent": 17400000, + "indicator": "taxable_income_band_row_183_indicator", + "source_label": "$173,001 - $174,000", + "count_reference": "ird_taxable_income_people_by_band__row_183", + "amount_reference": "ird_taxable_income_total_by_band__row_183" + }, + { + "id": "row_184", + "kind": "positive", + "upper_cent": 17500000, + "indicator": "taxable_income_band_row_184_indicator", + "source_label": "$174,001 - $175,000", + "count_reference": "ird_taxable_income_people_by_band__row_184", + "amount_reference": "ird_taxable_income_total_by_band__row_184" + }, + { + "id": "row_185", + "kind": "positive", + "upper_cent": 17600000, + "indicator": "taxable_income_band_row_185_indicator", + "source_label": "$175,001 - $176,000", + "count_reference": "ird_taxable_income_people_by_band__row_185", + "amount_reference": "ird_taxable_income_total_by_band__row_185" + }, + { + "id": "row_186", + "kind": "positive", + "upper_cent": 17700000, + "indicator": "taxable_income_band_row_186_indicator", + "source_label": "$176,001 - $177,000", + "count_reference": "ird_taxable_income_people_by_band__row_186", + "amount_reference": "ird_taxable_income_total_by_band__row_186" + }, + { + "id": "row_187", + "kind": "positive", + "upper_cent": 17800000, + "indicator": "taxable_income_band_row_187_indicator", + "source_label": "$177,001 - $178,000", + "count_reference": "ird_taxable_income_people_by_band__row_187", + "amount_reference": "ird_taxable_income_total_by_band__row_187" + }, + { + "id": "row_188", + "kind": "positive", + "upper_cent": 17900000, + "indicator": "taxable_income_band_row_188_indicator", + "source_label": "$178,001 - $179,000", + "count_reference": "ird_taxable_income_people_by_band__row_188", + "amount_reference": "ird_taxable_income_total_by_band__row_188" + }, + { + "id": "row_189", + "kind": "positive", + "upper_cent": 18000000, + "indicator": "taxable_income_band_row_189_indicator", + "source_label": "$179,001 - $180,000", + "count_reference": "ird_taxable_income_people_by_band__row_189", + "amount_reference": "ird_taxable_income_total_by_band__row_189" + }, + { + "id": "row_190", + "kind": "positive", + "upper_cent": 18100000, + "indicator": "taxable_income_band_row_190_indicator", + "source_label": "$180,001 - $181,000", + "count_reference": "ird_taxable_income_people_by_band__row_190", + "amount_reference": "ird_taxable_income_total_by_band__row_190" + }, + { + "id": "row_191", + "kind": "positive", + "upper_cent": 18200000, + "indicator": "taxable_income_band_row_191_indicator", + "source_label": "$181,001 - $182,000", + "count_reference": "ird_taxable_income_people_by_band__row_191", + "amount_reference": "ird_taxable_income_total_by_band__row_191" + }, + { + "id": "row_192", + "kind": "positive", + "upper_cent": 18300000, + "indicator": "taxable_income_band_row_192_indicator", + "source_label": "$182,001 - $183,000", + "count_reference": "ird_taxable_income_people_by_band__row_192", + "amount_reference": "ird_taxable_income_total_by_band__row_192" + }, + { + "id": "row_193", + "kind": "positive", + "upper_cent": 18400000, + "indicator": "taxable_income_band_row_193_indicator", + "source_label": "$183,001 - $184,000", + "count_reference": "ird_taxable_income_people_by_band__row_193", + "amount_reference": "ird_taxable_income_total_by_band__row_193" + }, + { + "id": "row_194", + "kind": "positive", + "upper_cent": 18500000, + "indicator": "taxable_income_band_row_194_indicator", + "source_label": "$184,001 - $185,000", + "count_reference": "ird_taxable_income_people_by_band__row_194", + "amount_reference": "ird_taxable_income_total_by_band__row_194" + }, + { + "id": "row_195", + "kind": "positive", + "upper_cent": 18600000, + "indicator": "taxable_income_band_row_195_indicator", + "source_label": "$185,001 - $186,000", + "count_reference": "ird_taxable_income_people_by_band__row_195", + "amount_reference": "ird_taxable_income_total_by_band__row_195" + }, + { + "id": "row_196", + "kind": "positive", + "upper_cent": 18700000, + "indicator": "taxable_income_band_row_196_indicator", + "source_label": "$186,001 - $187,000", + "count_reference": "ird_taxable_income_people_by_band__row_196", + "amount_reference": "ird_taxable_income_total_by_band__row_196" + }, + { + "id": "row_197", + "kind": "positive", + "upper_cent": 18800000, + "indicator": "taxable_income_band_row_197_indicator", + "source_label": "$187,001 - $188,000", + "count_reference": "ird_taxable_income_people_by_band__row_197", + "amount_reference": "ird_taxable_income_total_by_band__row_197" + }, + { + "id": "row_198", + "kind": "positive", + "upper_cent": 18900000, + "indicator": "taxable_income_band_row_198_indicator", + "source_label": "$188,001 - $189,000", + "count_reference": "ird_taxable_income_people_by_band__row_198", + "amount_reference": "ird_taxable_income_total_by_band__row_198" + }, + { + "id": "row_199", + "kind": "positive", + "upper_cent": 19000000, + "indicator": "taxable_income_band_row_199_indicator", + "source_label": "$189,001 - $190,000", + "count_reference": "ird_taxable_income_people_by_band__row_199", + "amount_reference": "ird_taxable_income_total_by_band__row_199" + }, + { + "id": "row_200", + "kind": "positive", + "upper_cent": 19100000, + "indicator": "taxable_income_band_row_200_indicator", + "source_label": "$190,001 - $191,000", + "count_reference": "ird_taxable_income_people_by_band__row_200", + "amount_reference": "ird_taxable_income_total_by_band__row_200" + }, + { + "id": "row_201", + "kind": "positive", + "upper_cent": 19200000, + "indicator": "taxable_income_band_row_201_indicator", + "source_label": "$191,001 - $192,000", + "count_reference": "ird_taxable_income_people_by_band__row_201", + "amount_reference": "ird_taxable_income_total_by_band__row_201" + }, + { + "id": "row_202", + "kind": "positive", + "upper_cent": 19300000, + "indicator": "taxable_income_band_row_202_indicator", + "source_label": "$192,001 - $193,000", + "count_reference": "ird_taxable_income_people_by_band__row_202", + "amount_reference": "ird_taxable_income_total_by_band__row_202" + }, + { + "id": "row_203", + "kind": "positive", + "upper_cent": 19400000, + "indicator": "taxable_income_band_row_203_indicator", + "source_label": "$193,001 - $194,000", + "count_reference": "ird_taxable_income_people_by_band__row_203", + "amount_reference": "ird_taxable_income_total_by_band__row_203" + }, + { + "id": "row_204", + "kind": "positive", + "upper_cent": 19500000, + "indicator": "taxable_income_band_row_204_indicator", + "source_label": "$194,001 - $195,000", + "count_reference": "ird_taxable_income_people_by_band__row_204", + "amount_reference": "ird_taxable_income_total_by_band__row_204" + }, + { + "id": "row_205", + "kind": "positive", + "upper_cent": 19600000, + "indicator": "taxable_income_band_row_205_indicator", + "source_label": "$195,001 - $196,000", + "count_reference": "ird_taxable_income_people_by_band__row_205", + "amount_reference": "ird_taxable_income_total_by_band__row_205" + }, + { + "id": "row_206", + "kind": "positive", + "upper_cent": 19700000, + "indicator": "taxable_income_band_row_206_indicator", + "source_label": "$196,001 - $197,000", + "count_reference": "ird_taxable_income_people_by_band__row_206", + "amount_reference": "ird_taxable_income_total_by_band__row_206" + }, + { + "id": "row_207", + "kind": "positive", + "upper_cent": 19800000, + "indicator": "taxable_income_band_row_207_indicator", + "source_label": "$197,001 - $198,000", + "count_reference": "ird_taxable_income_people_by_band__row_207", + "amount_reference": "ird_taxable_income_total_by_band__row_207" + }, + { + "id": "row_208", + "kind": "positive", + "upper_cent": 19900000, + "indicator": "taxable_income_band_row_208_indicator", + "source_label": "$198,001 - $199,000", + "count_reference": "ird_taxable_income_people_by_band__row_208", + "amount_reference": "ird_taxable_income_total_by_band__row_208" + }, + { + "id": "row_209", + "kind": "positive", + "upper_cent": 20000000, + "indicator": "taxable_income_band_row_209_indicator", + "source_label": "$199,001 - $200,000", + "count_reference": "ird_taxable_income_people_by_band__row_209", + "amount_reference": "ird_taxable_income_total_by_band__row_209" + }, + { + "id": "row_210", + "kind": "positive", + "upper_cent": 20500000, + "indicator": "taxable_income_band_row_210_indicator", + "source_label": "$200,001 - $205,000", + "count_reference": "ird_taxable_income_people_by_band__row_210", + "amount_reference": "ird_taxable_income_total_by_band__row_210" + }, + { + "id": "row_211", + "kind": "positive", + "upper_cent": 21000000, + "indicator": "taxable_income_band_row_211_indicator", + "source_label": "$205,001 - $210,000", + "count_reference": "ird_taxable_income_people_by_band__row_211", + "amount_reference": "ird_taxable_income_total_by_band__row_211" + }, + { + "id": "row_212", + "kind": "positive", + "upper_cent": 21500000, + "indicator": "taxable_income_band_row_212_indicator", + "source_label": "$210,001 - $215,000", + "count_reference": "ird_taxable_income_people_by_band__row_212", + "amount_reference": "ird_taxable_income_total_by_band__row_212" + }, + { + "id": "row_213", + "kind": "positive", + "upper_cent": 22000000, + "indicator": "taxable_income_band_row_213_indicator", + "source_label": "$215,001 - $220,000", + "count_reference": "ird_taxable_income_people_by_band__row_213", + "amount_reference": "ird_taxable_income_total_by_band__row_213" + }, + { + "id": "row_214", + "kind": "positive", + "upper_cent": 22500000, + "indicator": "taxable_income_band_row_214_indicator", + "source_label": "$220,001 - $225,000", + "count_reference": "ird_taxable_income_people_by_band__row_214", + "amount_reference": "ird_taxable_income_total_by_band__row_214" + }, + { + "id": "row_215", + "kind": "positive", + "upper_cent": 23000000, + "indicator": "taxable_income_band_row_215_indicator", + "source_label": "$225,001 - $230,000", + "count_reference": "ird_taxable_income_people_by_band__row_215", + "amount_reference": "ird_taxable_income_total_by_band__row_215" + }, + { + "id": "row_216", + "kind": "positive", + "upper_cent": 23500000, + "indicator": "taxable_income_band_row_216_indicator", + "source_label": "$230,001 - $235,000", + "count_reference": "ird_taxable_income_people_by_band__row_216", + "amount_reference": "ird_taxable_income_total_by_band__row_216" + }, + { + "id": "row_217", + "kind": "positive", + "upper_cent": 24000000, + "indicator": "taxable_income_band_row_217_indicator", + "source_label": "$235,001 - $240,000", + "count_reference": "ird_taxable_income_people_by_band__row_217", + "amount_reference": "ird_taxable_income_total_by_band__row_217" + }, + { + "id": "row_218", + "kind": "positive", + "upper_cent": 24500000, + "indicator": "taxable_income_band_row_218_indicator", + "source_label": "$240,001 - $245,000", + "count_reference": "ird_taxable_income_people_by_band__row_218", + "amount_reference": "ird_taxable_income_total_by_band__row_218" + }, + { + "id": "row_219", + "kind": "positive", + "upper_cent": 25000000, + "indicator": "taxable_income_band_row_219_indicator", + "source_label": "$245,001 - $250,000", + "count_reference": "ird_taxable_income_people_by_band__row_219", + "amount_reference": "ird_taxable_income_total_by_band__row_219" + }, + { + "id": "row_220", + "kind": "positive", + "upper_cent": 25500000, + "indicator": "taxable_income_band_row_220_indicator", + "source_label": "$250,001 - $255,000", + "count_reference": "ird_taxable_income_people_by_band__row_220", + "amount_reference": "ird_taxable_income_total_by_band__row_220" + }, + { + "id": "row_221", + "kind": "positive", + "upper_cent": 26000000, + "indicator": "taxable_income_band_row_221_indicator", + "source_label": "$255,001 - $260,000", + "count_reference": "ird_taxable_income_people_by_band__row_221", + "amount_reference": "ird_taxable_income_total_by_band__row_221" + }, + { + "id": "row_222", + "kind": "positive", + "upper_cent": 26500000, + "indicator": "taxable_income_band_row_222_indicator", + "source_label": "$260,001 - $265,000", + "count_reference": "ird_taxable_income_people_by_band__row_222", + "amount_reference": "ird_taxable_income_total_by_band__row_222" + }, + { + "id": "row_223", + "kind": "positive", + "upper_cent": 27000000, + "indicator": "taxable_income_band_row_223_indicator", + "source_label": "$265,001 - $270,000", + "count_reference": "ird_taxable_income_people_by_band__row_223", + "amount_reference": "ird_taxable_income_total_by_band__row_223" + }, + { + "id": "row_224", + "kind": "positive", + "upper_cent": 27500000, + "indicator": "taxable_income_band_row_224_indicator", + "source_label": "$270,001 - $275,000", + "count_reference": "ird_taxable_income_people_by_band__row_224", + "amount_reference": "ird_taxable_income_total_by_band__row_224" + }, + { + "id": "row_225", + "kind": "positive", + "upper_cent": 28000000, + "indicator": "taxable_income_band_row_225_indicator", + "source_label": "$275,001 - $280,000", + "count_reference": "ird_taxable_income_people_by_band__row_225", + "amount_reference": "ird_taxable_income_total_by_band__row_225" + }, + { + "id": "row_226", + "kind": "positive", + "upper_cent": 28500000, + "indicator": "taxable_income_band_row_226_indicator", + "source_label": "$280,001 - $285,000", + "count_reference": "ird_taxable_income_people_by_band__row_226", + "amount_reference": "ird_taxable_income_total_by_band__row_226" + }, + { + "id": "row_227", + "kind": "positive", + "upper_cent": 29000000, + "indicator": "taxable_income_band_row_227_indicator", + "source_label": "$285,001 - $290,000", + "count_reference": "ird_taxable_income_people_by_band__row_227", + "amount_reference": "ird_taxable_income_total_by_band__row_227" + }, + { + "id": "row_228", + "kind": "positive", + "upper_cent": 29500000, + "indicator": "taxable_income_band_row_228_indicator", + "source_label": "$290,001 - $295,000", + "count_reference": "ird_taxable_income_people_by_band__row_228", + "amount_reference": "ird_taxable_income_total_by_band__row_228" + }, + { + "id": "row_229", + "kind": "positive", + "upper_cent": 30000000, + "indicator": "taxable_income_band_row_229_indicator", + "source_label": "$295,001 - $300,000", + "count_reference": "ird_taxable_income_people_by_band__row_229", + "amount_reference": "ird_taxable_income_total_by_band__row_229" + }, + { + "id": "row_230", + "kind": "positive", + "upper_cent": 35000000, + "indicator": "taxable_income_band_row_230_indicator", + "source_label": "$300,000 - $350,000", + "count_reference": "ird_taxable_income_people_by_band__row_230", + "amount_reference": "ird_taxable_income_total_by_band__row_230" + }, + { + "id": "row_231", + "kind": "positive", + "upper_cent": 40000000, + "indicator": "taxable_income_band_row_231_indicator", + "source_label": "$350,001 - $400,000", + "count_reference": "ird_taxable_income_people_by_band__row_231", + "amount_reference": "ird_taxable_income_total_by_band__row_231" + }, + { + "id": "row_232", + "kind": "positive", + "upper_cent": 45000000, + "indicator": "taxable_income_band_row_232_indicator", + "source_label": "$400,001 - $450,000", + "count_reference": "ird_taxable_income_people_by_band__row_232", + "amount_reference": "ird_taxable_income_total_by_band__row_232" + }, + { + "id": "row_233", + "kind": "positive", + "upper_cent": 50000000, + "indicator": "taxable_income_band_row_233_indicator", + "source_label": "$450,001 - $500,000", + "count_reference": "ird_taxable_income_people_by_band__row_233", + "amount_reference": "ird_taxable_income_total_by_band__row_233" + }, + { + "id": "row_234", + "kind": "positive", + "upper_cent": 55000000, + "indicator": "taxable_income_band_row_234_indicator", + "source_label": "$500,001 - $550,000", + "count_reference": "ird_taxable_income_people_by_band__row_234", + "amount_reference": "ird_taxable_income_total_by_band__row_234" + }, + { + "id": "row_235", + "kind": "positive", + "upper_cent": 60000000, + "indicator": "taxable_income_band_row_235_indicator", + "source_label": "$550,001 - $600,000", + "count_reference": "ird_taxable_income_people_by_band__row_235", + "amount_reference": "ird_taxable_income_total_by_band__row_235" + }, + { + "id": "row_236", + "kind": "positive", + "upper_cent": 65000000, + "indicator": "taxable_income_band_row_236_indicator", + "source_label": "$600,001 - $650,000", + "count_reference": "ird_taxable_income_people_by_band__row_236", + "amount_reference": "ird_taxable_income_total_by_band__row_236" + }, + { + "id": "row_237", + "kind": "positive", + "upper_cent": 70000000, + "indicator": "taxable_income_band_row_237_indicator", + "source_label": "$650,001 - $700,000", + "count_reference": "ird_taxable_income_people_by_band__row_237", + "amount_reference": "ird_taxable_income_total_by_band__row_237" + }, + { + "id": "row_238", + "kind": "positive", + "upper_cent": 75000000, + "indicator": "taxable_income_band_row_238_indicator", + "source_label": "$700,001 - $750,000", + "count_reference": "ird_taxable_income_people_by_band__row_238", + "amount_reference": "ird_taxable_income_total_by_band__row_238" + }, + { + "id": "row_239", + "kind": "positive", + "upper_cent": 80000000, + "indicator": "taxable_income_band_row_239_indicator", + "source_label": "$750,001 - $800,000", + "count_reference": "ird_taxable_income_people_by_band__row_239", + "amount_reference": "ird_taxable_income_total_by_band__row_239" + }, + { + "id": "row_240", + "kind": "positive", + "upper_cent": 85000000, + "indicator": "taxable_income_band_row_240_indicator", + "source_label": "$800,001 - $850,000", + "count_reference": "ird_taxable_income_people_by_band__row_240", + "amount_reference": "ird_taxable_income_total_by_band__row_240" + }, + { + "id": "row_241", + "kind": "positive", + "upper_cent": 90000000, + "indicator": "taxable_income_band_row_241_indicator", + "source_label": "$850,001 - $900,000", + "count_reference": "ird_taxable_income_people_by_band__row_241", + "amount_reference": "ird_taxable_income_total_by_band__row_241" + }, + { + "id": "row_242", + "kind": "positive", + "upper_cent": 95000000, + "indicator": "taxable_income_band_row_242_indicator", + "source_label": "$900,001 - $950,000", + "count_reference": "ird_taxable_income_people_by_band__row_242", + "amount_reference": "ird_taxable_income_total_by_band__row_242" + }, + { + "id": "row_243", + "kind": "positive", + "upper_cent": 100000000, + "indicator": "taxable_income_band_row_243_indicator", + "source_label": "$950,001 - $1,000,000", + "count_reference": "ird_taxable_income_people_by_band__row_243", + "amount_reference": "ird_taxable_income_total_by_band__row_243" + }, + { + "id": "row_244", + "kind": "positive", + "upper_cent": null, + "indicator": "taxable_income_band_row_244_indicator", + "source_label": "Over $1,000,000", + "count_reference": "ird_taxable_income_people_by_band__row_244", + "amount_reference": "ird_taxable_income_total_by_band__row_244" + } + ], + "description": "National TY2024 taxable-income bands, rows 8-244: one nil band, 235 finite bands and the open tail over $1,000,000. Every band names its parked count and amount references." + }, + { + "id": "ird_taxable_income_age_ty2024", + "entity": "person", + "currency": "NZD", + "input_unit": "integer_cent", + "income_column": "ird_taxable_income_proxy_cents", + "universe_column": "ird_taxable_income_proxy_universe_status", + "source_period": 2024, + "target_period": 2027, + "positive_band_closure": "right", + "nil_band": "present", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": 0, + "source_locator": "A6:A188", + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left" + }, + "bands": [ + { + "id": "row_6", + "kind": "nil", + "indicator": "taxable_income_age_table_band_row_6_indicator", + "source_label": "nil" + }, + { + "id": "row_7", + "kind": "positive", + "upper_cent": 10000, + "indicator": "taxable_income_age_table_band_row_7_indicator", + "source_label": "$0.01 - $100" + }, + { + "id": "row_8", + "kind": "positive", + "upper_cent": 100000, + "indicator": "taxable_income_age_table_band_row_8_indicator", + "source_label": "$101 - $1,000" + }, + { + "id": "row_9", + "kind": "positive", + "upper_cent": 200000, + "indicator": "taxable_income_age_table_band_row_9_indicator", + "source_label": "$1,001 - $2,000" + }, + { + "id": "row_10", + "kind": "positive", + "upper_cent": 300000, + "indicator": "taxable_income_age_table_band_row_10_indicator", + "source_label": "$2,001 - $3,000" + }, + { + "id": "row_11", + "kind": "positive", + "upper_cent": 400000, + "indicator": "taxable_income_age_table_band_row_11_indicator", + "source_label": "$3,001 - $4,000" + }, + { + "id": "row_12", + "kind": "positive", + "upper_cent": 500000, + "indicator": "taxable_income_age_table_band_row_12_indicator", + "source_label": "$4,001 - $5,000" + }, + { + "id": "row_13", + "kind": "positive", + "upper_cent": 600000, + "indicator": "taxable_income_age_table_band_row_13_indicator", + "source_label": "$5,001 - $6,000" + }, + { + "id": "row_14", + "kind": "positive", + "upper_cent": 700000, + "indicator": "taxable_income_age_table_band_row_14_indicator", + "source_label": "$6,001 - $7,000" + }, + { + "id": "row_15", + "kind": "positive", + "upper_cent": 800000, + "indicator": "taxable_income_age_table_band_row_15_indicator", + "source_label": "$7,001 - $8,000" + }, + { + "id": "row_16", + "kind": "positive", + "upper_cent": 900000, + "indicator": "taxable_income_age_table_band_row_16_indicator", + "source_label": "$8,001 - $9,000" + }, + { + "id": "row_17", + "kind": "positive", + "upper_cent": 1000000, + "indicator": "taxable_income_age_table_band_row_17_indicator", + "source_label": "$9,001 - $10,000" + }, + { + "id": "row_18", + "kind": "positive", + "upper_cent": 1100000, + "indicator": "taxable_income_age_table_band_row_18_indicator", + "source_label": "$10,001 - $11,000" + }, + { + "id": "row_19", + "kind": "positive", + "upper_cent": 1200000, + "indicator": "taxable_income_age_table_band_row_19_indicator", + "source_label": "$11,001 - $12,000" + }, + { + "id": "row_20", + "kind": "positive", + "upper_cent": 1300000, + "indicator": "taxable_income_age_table_band_row_20_indicator", + "source_label": "$12,001 - $13,000" + }, + { + "id": "row_21", + "kind": "positive", + "upper_cent": 1400000, + "indicator": "taxable_income_age_table_band_row_21_indicator", + "source_label": "$13,001 - $14,000" + }, + { + "id": "row_22", + "kind": "positive", + "upper_cent": 1500000, + "indicator": "taxable_income_age_table_band_row_22_indicator", + "source_label": "$14,001 - $15,000" + }, + { + "id": "row_23", + "kind": "positive", + "upper_cent": 1600000, + "indicator": "taxable_income_age_table_band_row_23_indicator", + "source_label": "$15,001 - $16,000" + }, + { + "id": "row_24", + "kind": "positive", + "upper_cent": 1700000, + "indicator": "taxable_income_age_table_band_row_24_indicator", + "source_label": "$16,001 - $17,000" + }, + { + "id": "row_25", + "kind": "positive", + "upper_cent": 1800000, + "indicator": "taxable_income_age_table_band_row_25_indicator", + "source_label": "$17,001 - $18,000" + }, + { + "id": "row_26", + "kind": "positive", + "upper_cent": 1900000, + "indicator": "taxable_income_age_table_band_row_26_indicator", + "source_label": "$18,001 - $19,000" + }, + { + "id": "row_27", + "kind": "positive", + "upper_cent": 2000000, + "indicator": "taxable_income_age_table_band_row_27_indicator", + "source_label": "$19,001 - $20,000" + }, + { + "id": "row_28", + "kind": "positive", + "upper_cent": 2100000, + "indicator": "taxable_income_age_table_band_row_28_indicator", + "source_label": "$20,000 - $21,000" + }, + { + "id": "row_29", + "kind": "positive", + "upper_cent": 2200000, + "indicator": "taxable_income_age_table_band_row_29_indicator", + "source_label": "$21,001 - $22,000" + }, + { + "id": "row_30", + "kind": "positive", + "upper_cent": 2300000, + "indicator": "taxable_income_age_table_band_row_30_indicator", + "source_label": "$22,001 - $23,000" + }, + { + "id": "row_31", + "kind": "positive", + "upper_cent": 2400000, + "indicator": "taxable_income_age_table_band_row_31_indicator", + "source_label": "$23,001 - $24,000" + }, + { + "id": "row_32", + "kind": "positive", + "upper_cent": 2500000, + "indicator": "taxable_income_age_table_band_row_32_indicator", + "source_label": "$24,001 - $25,000" + }, + { + "id": "row_33", + "kind": "positive", + "upper_cent": 2600000, + "indicator": "taxable_income_age_table_band_row_33_indicator", + "source_label": "$25,001 - $26,000" + }, + { + "id": "row_34", + "kind": "positive", + "upper_cent": 2700000, + "indicator": "taxable_income_age_table_band_row_34_indicator", + "source_label": "$26,001 - $27,000" + }, + { + "id": "row_35", + "kind": "positive", + "upper_cent": 2800000, + "indicator": "taxable_income_age_table_band_row_35_indicator", + "source_label": "$27,001 - $28,000" + }, + { + "id": "row_36", + "kind": "positive", + "upper_cent": 2900000, + "indicator": "taxable_income_age_table_band_row_36_indicator", + "source_label": "$28,001 - $29,000" + }, + { + "id": "row_37", + "kind": "positive", + "upper_cent": 3000000, + "indicator": "taxable_income_age_table_band_row_37_indicator", + "source_label": "$29,001 - $30,000" + }, + { + "id": "row_38", + "kind": "positive", + "upper_cent": 3100000, + "indicator": "taxable_income_age_table_band_row_38_indicator", + "source_label": "$30,001 - $31,000" + }, + { + "id": "row_39", + "kind": "positive", + "upper_cent": 3200000, + "indicator": "taxable_income_age_table_band_row_39_indicator", + "source_label": "$31,001 - $32,000" + }, + { + "id": "row_40", + "kind": "positive", + "upper_cent": 3300000, + "indicator": "taxable_income_age_table_band_row_40_indicator", + "source_label": "$32,001 - $33,000" + }, + { + "id": "row_41", + "kind": "positive", + "upper_cent": 3400000, + "indicator": "taxable_income_age_table_band_row_41_indicator", + "source_label": "$33,001 - $34,000" + }, + { + "id": "row_42", + "kind": "positive", + "upper_cent": 3500000, + "indicator": "taxable_income_age_table_band_row_42_indicator", + "source_label": "$34,001 - $35,000" + }, + { + "id": "row_43", + "kind": "positive", + "upper_cent": 3600000, + "indicator": "taxable_income_age_table_band_row_43_indicator", + "source_label": "$35,001 - $36,000" + }, + { + "id": "row_44", + "kind": "positive", + "upper_cent": 3700000, + "indicator": "taxable_income_age_table_band_row_44_indicator", + "source_label": "$36,001 - $37,000" + }, + { + "id": "row_45", + "kind": "positive", + "upper_cent": 3800000, + "indicator": "taxable_income_age_table_band_row_45_indicator", + "source_label": "$37,001 - $38,000" + }, + { + "id": "row_46", + "kind": "positive", + "upper_cent": 3900000, + "indicator": "taxable_income_age_table_band_row_46_indicator", + "source_label": "$38,001 - $39,000" + }, + { + "id": "row_47", + "kind": "positive", + "upper_cent": 4000000, + "indicator": "taxable_income_age_table_band_row_47_indicator", + "source_label": "$39,001 - $40,000" + }, + { + "id": "row_48", + "kind": "positive", + "upper_cent": 4100000, + "indicator": "taxable_income_age_table_band_row_48_indicator", + "source_label": "$40,001 - $41,000" + }, + { + "id": "row_49", + "kind": "positive", + "upper_cent": 4200000, + "indicator": "taxable_income_age_table_band_row_49_indicator", + "source_label": "$41,001 - $42,000" + }, + { + "id": "row_50", + "kind": "positive", + "upper_cent": 4300000, + "indicator": "taxable_income_age_table_band_row_50_indicator", + "source_label": "$42,001 - $43,000" + }, + { + "id": "row_51", + "kind": "positive", + "upper_cent": 4400000, + "indicator": "taxable_income_age_table_band_row_51_indicator", + "source_label": "$43,001 - $44,000" + }, + { + "id": "row_52", + "kind": "positive", + "upper_cent": 4500000, + "indicator": "taxable_income_age_table_band_row_52_indicator", + "source_label": "$44,001 - $45,000" + }, + { + "id": "row_53", + "kind": "positive", + "upper_cent": 4600000, + "indicator": "taxable_income_age_table_band_row_53_indicator", + "source_label": "$45,001 - $46,000" + }, + { + "id": "row_54", + "kind": "positive", + "upper_cent": 4700000, + "indicator": "taxable_income_age_table_band_row_54_indicator", + "source_label": "$46,001 - $47,000" + }, + { + "id": "row_55", + "kind": "positive", + "upper_cent": 4800000, + "indicator": "taxable_income_age_table_band_row_55_indicator", + "source_label": "$47,001 - $48,000" + }, + { + "id": "row_56", + "kind": "positive", + "upper_cent": 4900000, + "indicator": "taxable_income_age_table_band_row_56_indicator", + "source_label": "$48,001 - $49,000" + }, + { + "id": "row_57", + "kind": "positive", + "upper_cent": 5000000, + "indicator": "taxable_income_age_table_band_row_57_indicator", + "source_label": "$49,001 - $50,000" + }, + { + "id": "row_58", + "kind": "positive", + "upper_cent": 5100000, + "indicator": "taxable_income_age_table_band_row_58_indicator", + "source_label": "$50,000 - $51,000" + }, + { + "id": "row_59", + "kind": "positive", + "upper_cent": 5200000, + "indicator": "taxable_income_age_table_band_row_59_indicator", + "source_label": "$51,001 - $52,000" + }, + { + "id": "row_60", + "kind": "positive", + "upper_cent": 5300000, + "indicator": "taxable_income_age_table_band_row_60_indicator", + "source_label": "$52,001 - $53,000" + }, + { + "id": "row_61", + "kind": "positive", + "upper_cent": 5400000, + "indicator": "taxable_income_age_table_band_row_61_indicator", + "source_label": "$53,001 - $54,000" + }, + { + "id": "row_62", + "kind": "positive", + "upper_cent": 5500000, + "indicator": "taxable_income_age_table_band_row_62_indicator", + "source_label": "$54,001 - $55,000" + }, + { + "id": "row_63", + "kind": "positive", + "upper_cent": 5600000, + "indicator": "taxable_income_age_table_band_row_63_indicator", + "source_label": "$55,001 - $56,000" + }, + { + "id": "row_64", + "kind": "positive", + "upper_cent": 5700000, + "indicator": "taxable_income_age_table_band_row_64_indicator", + "source_label": "$56,001 - $57,000" + }, + { + "id": "row_65", + "kind": "positive", + "upper_cent": 5800000, + "indicator": "taxable_income_age_table_band_row_65_indicator", + "source_label": "$57,001 - $58,000" + }, + { + "id": "row_66", + "kind": "positive", + "upper_cent": 5900000, + "indicator": "taxable_income_age_table_band_row_66_indicator", + "source_label": "$58,001 - $59,000" + }, + { + "id": "row_67", + "kind": "positive", + "upper_cent": 6000000, + "indicator": "taxable_income_age_table_band_row_67_indicator", + "source_label": "$59,001 - $60,000" + }, + { + "id": "row_68", + "kind": "positive", + "upper_cent": 6100000, + "indicator": "taxable_income_age_table_band_row_68_indicator", + "source_label": "$60,001 - $61,000" + }, + { + "id": "row_69", + "kind": "positive", + "upper_cent": 6200000, + "indicator": "taxable_income_age_table_band_row_69_indicator", + "source_label": "$61,001 - $62,000" + }, + { + "id": "row_70", + "kind": "positive", + "upper_cent": 6300000, + "indicator": "taxable_income_age_table_band_row_70_indicator", + "source_label": "$62,001 - $63,000" + }, + { + "id": "row_71", + "kind": "positive", + "upper_cent": 6400000, + "indicator": "taxable_income_age_table_band_row_71_indicator", + "source_label": "$63,001 - $64,000" + }, + { + "id": "row_72", + "kind": "positive", + "upper_cent": 6500000, + "indicator": "taxable_income_age_table_band_row_72_indicator", + "source_label": "$64,001 - $65,000" + }, + { + "id": "row_73", + "kind": "positive", + "upper_cent": 6600000, + "indicator": "taxable_income_age_table_band_row_73_indicator", + "source_label": "$65,001 - $66,000" + }, + { + "id": "row_74", + "kind": "positive", + "upper_cent": 6700000, + "indicator": "taxable_income_age_table_band_row_74_indicator", + "source_label": "$66,001 - $67,000" + }, + { + "id": "row_75", + "kind": "positive", + "upper_cent": 6800000, + "indicator": "taxable_income_age_table_band_row_75_indicator", + "source_label": "$67,001 - $68,000" + }, + { + "id": "row_76", + "kind": "positive", + "upper_cent": 6900000, + "indicator": "taxable_income_age_table_band_row_76_indicator", + "source_label": "$68,001 - $69,000" + }, + { + "id": "row_77", + "kind": "positive", + "upper_cent": 7000000, + "indicator": "taxable_income_age_table_band_row_77_indicator", + "source_label": "$69,001 - $70,000" + }, + { + "id": "row_78", + "kind": "positive", + "upper_cent": 7100000, + "indicator": "taxable_income_age_table_band_row_78_indicator", + "source_label": "$70,001 - $71,000" + }, + { + "id": "row_79", + "kind": "positive", + "upper_cent": 7200000, + "indicator": "taxable_income_age_table_band_row_79_indicator", + "source_label": "$71,001 - $72,000" + }, + { + "id": "row_80", + "kind": "positive", + "upper_cent": 7300000, + "indicator": "taxable_income_age_table_band_row_80_indicator", + "source_label": "$72,001 - $73,000" + }, + { + "id": "row_81", + "kind": "positive", + "upper_cent": 7400000, + "indicator": "taxable_income_age_table_band_row_81_indicator", + "source_label": "$73,001 - $74,000" + }, + { + "id": "row_82", + "kind": "positive", + "upper_cent": 7500000, + "indicator": "taxable_income_age_table_band_row_82_indicator", + "source_label": "$74,001 - $75,000" + }, + { + "id": "row_83", + "kind": "positive", + "upper_cent": 7600000, + "indicator": "taxable_income_age_table_band_row_83_indicator", + "source_label": "$75,001 - $76,000" + }, + { + "id": "row_84", + "kind": "positive", + "upper_cent": 7700000, + "indicator": "taxable_income_age_table_band_row_84_indicator", + "source_label": "$76,001 - $77,000" + }, + { + "id": "row_85", + "kind": "positive", + "upper_cent": 7800000, + "indicator": "taxable_income_age_table_band_row_85_indicator", + "source_label": "$77,001 - $78,000" + }, + { + "id": "row_86", + "kind": "positive", + "upper_cent": 7900000, + "indicator": "taxable_income_age_table_band_row_86_indicator", + "source_label": "$78,001 - $79,000" + }, + { + "id": "row_87", + "kind": "positive", + "upper_cent": 8000000, + "indicator": "taxable_income_age_table_band_row_87_indicator", + "source_label": "$79,001 - $80,000" + }, + { + "id": "row_88", + "kind": "positive", + "upper_cent": 8100000, + "indicator": "taxable_income_age_table_band_row_88_indicator", + "source_label": "$80,000 - $81,000" + }, + { + "id": "row_89", + "kind": "positive", + "upper_cent": 8200000, + "indicator": "taxable_income_age_table_band_row_89_indicator", + "source_label": "$81,001 - $82,000" + }, + { + "id": "row_90", + "kind": "positive", + "upper_cent": 8300000, + "indicator": "taxable_income_age_table_band_row_90_indicator", + "source_label": "$82,001 - $83,000" + }, + { + "id": "row_91", + "kind": "positive", + "upper_cent": 8400000, + "indicator": "taxable_income_age_table_band_row_91_indicator", + "source_label": "$83,001 - $84,000" + }, + { + "id": "row_92", + "kind": "positive", + "upper_cent": 8500000, + "indicator": "taxable_income_age_table_band_row_92_indicator", + "source_label": "$84,001 - $85,000" + }, + { + "id": "row_93", + "kind": "positive", + "upper_cent": 8600000, + "indicator": "taxable_income_age_table_band_row_93_indicator", + "source_label": "$85,001 - $86,000" + }, + { + "id": "row_94", + "kind": "positive", + "upper_cent": 8700000, + "indicator": "taxable_income_age_table_band_row_94_indicator", + "source_label": "$86,001 - $87,000" + }, + { + "id": "row_95", + "kind": "positive", + "upper_cent": 8800000, + "indicator": "taxable_income_age_table_band_row_95_indicator", + "source_label": "$87,001 - $88,000" + }, + { + "id": "row_96", + "kind": "positive", + "upper_cent": 8900000, + "indicator": "taxable_income_age_table_band_row_96_indicator", + "source_label": "$88,001 - $89,000" + }, + { + "id": "row_97", + "kind": "positive", + "upper_cent": 9000000, + "indicator": "taxable_income_age_table_band_row_97_indicator", + "source_label": "$89,001 - $90,000" + }, + { + "id": "row_98", + "kind": "positive", + "upper_cent": 9100000, + "indicator": "taxable_income_age_table_band_row_98_indicator", + "source_label": "$90,001 - $91,000" + }, + { + "id": "row_99", + "kind": "positive", + "upper_cent": 9200000, + "indicator": "taxable_income_age_table_band_row_99_indicator", + "source_label": "$91,001 - $92,000" + }, + { + "id": "row_100", + "kind": "positive", + "upper_cent": 9300000, + "indicator": "taxable_income_age_table_band_row_100_indicator", + "source_label": "$92,001 - $93,000" + }, + { + "id": "row_101", + "kind": "positive", + "upper_cent": 9400000, + "indicator": "taxable_income_age_table_band_row_101_indicator", + "source_label": "$93,001 - $94,000" + }, + { + "id": "row_102", + "kind": "positive", + "upper_cent": 9500000, + "indicator": "taxable_income_age_table_band_row_102_indicator", + "source_label": "$94,001 - $95,000" + }, + { + "id": "row_103", + "kind": "positive", + "upper_cent": 9600000, + "indicator": "taxable_income_age_table_band_row_103_indicator", + "source_label": "$95,001 - $96,000" + }, + { + "id": "row_104", + "kind": "positive", + "upper_cent": 9700000, + "indicator": "taxable_income_age_table_band_row_104_indicator", + "source_label": "$96,001 - $97,000" + }, + { + "id": "row_105", + "kind": "positive", + "upper_cent": 9800000, + "indicator": "taxable_income_age_table_band_row_105_indicator", + "source_label": "$97,001 - $98,000" + }, + { + "id": "row_106", + "kind": "positive", + "upper_cent": 9900000, + "indicator": "taxable_income_age_table_band_row_106_indicator", + "source_label": "$98,001 - $99,000" + }, + { + "id": "row_107", + "kind": "positive", + "upper_cent": 10000000, + "indicator": "taxable_income_age_table_band_row_107_indicator", + "source_label": "$99,001 - $100,000" + }, + { + "id": "row_108", + "kind": "positive", + "upper_cent": 10100000, + "indicator": "taxable_income_age_table_band_row_108_indicator", + "source_label": "$100,001 - $101,000" + }, + { + "id": "row_109", + "kind": "positive", + "upper_cent": 10200000, + "indicator": "taxable_income_age_table_band_row_109_indicator", + "source_label": "$101,001 - $102,000" + }, + { + "id": "row_110", + "kind": "positive", + "upper_cent": 10300000, + "indicator": "taxable_income_age_table_band_row_110_indicator", + "source_label": "$102,001 - $103,000" + }, + { + "id": "row_111", + "kind": "positive", + "upper_cent": 10400000, + "indicator": "taxable_income_age_table_band_row_111_indicator", + "source_label": "$103,001 - $104,000" + }, + { + "id": "row_112", + "kind": "positive", + "upper_cent": 10500000, + "indicator": "taxable_income_age_table_band_row_112_indicator", + "source_label": "$104,001 - $105,000" + }, + { + "id": "row_113", + "kind": "positive", + "upper_cent": 10600000, + "indicator": "taxable_income_age_table_band_row_113_indicator", + "source_label": "$105,001 - $106,000" + }, + { + "id": "row_114", + "kind": "positive", + "upper_cent": 10700000, + "indicator": "taxable_income_age_table_band_row_114_indicator", + "source_label": "$106,001 - $107,000" + }, + { + "id": "row_115", + "kind": "positive", + "upper_cent": 10800000, + "indicator": "taxable_income_age_table_band_row_115_indicator", + "source_label": "$107,001 - $108,000" + }, + { + "id": "row_116", + "kind": "positive", + "upper_cent": 10900000, + "indicator": "taxable_income_age_table_band_row_116_indicator", + "source_label": "$108,001 - $109,000" + }, + { + "id": "row_117", + "kind": "positive", + "upper_cent": 11000000, + "indicator": "taxable_income_age_table_band_row_117_indicator", + "source_label": "$109,001 - $110,000" + }, + { + "id": "row_118", + "kind": "positive", + "upper_cent": 11100000, + "indicator": "taxable_income_age_table_band_row_118_indicator", + "source_label": "$110,001 - $111,000" + }, + { + "id": "row_119", + "kind": "positive", + "upper_cent": 11200000, + "indicator": "taxable_income_age_table_band_row_119_indicator", + "source_label": "$111,001 - $112,000" + }, + { + "id": "row_120", + "kind": "positive", + "upper_cent": 11300000, + "indicator": "taxable_income_age_table_band_row_120_indicator", + "source_label": "$112,001 - $113,000" + }, + { + "id": "row_121", + "kind": "positive", + "upper_cent": 11400000, + "indicator": "taxable_income_age_table_band_row_121_indicator", + "source_label": "$113,001 - $114,000" + }, + { + "id": "row_122", + "kind": "positive", + "upper_cent": 11500000, + "indicator": "taxable_income_age_table_band_row_122_indicator", + "source_label": "$114,001 - $115,000" + }, + { + "id": "row_123", + "kind": "positive", + "upper_cent": 11600000, + "indicator": "taxable_income_age_table_band_row_123_indicator", + "source_label": "$115,001 - $116,000" + }, + { + "id": "row_124", + "kind": "positive", + "upper_cent": 11700000, + "indicator": "taxable_income_age_table_band_row_124_indicator", + "source_label": "$116,001 - $117,000" + }, + { + "id": "row_125", + "kind": "positive", + "upper_cent": 11800000, + "indicator": "taxable_income_age_table_band_row_125_indicator", + "source_label": "$117,001 - $118,000" + }, + { + "id": "row_126", + "kind": "positive", + "upper_cent": 11900000, + "indicator": "taxable_income_age_table_band_row_126_indicator", + "source_label": "$118,001 - $119,000" + }, + { + "id": "row_127", + "kind": "positive", + "upper_cent": 12000000, + "indicator": "taxable_income_age_table_band_row_127_indicator", + "source_label": "$119,001 - $120,000" + }, + { + "id": "row_128", + "kind": "positive", + "upper_cent": 12100000, + "indicator": "taxable_income_age_table_band_row_128_indicator", + "source_label": "$120,001 - $121,000" + }, + { + "id": "row_129", + "kind": "positive", + "upper_cent": 12200000, + "indicator": "taxable_income_age_table_band_row_129_indicator", + "source_label": "$121,001 - $122,000" + }, + { + "id": "row_130", + "kind": "positive", + "upper_cent": 12300000, + "indicator": "taxable_income_age_table_band_row_130_indicator", + "source_label": "$122,001 - $123,000" + }, + { + "id": "row_131", + "kind": "positive", + "upper_cent": 12400000, + "indicator": "taxable_income_age_table_band_row_131_indicator", + "source_label": "$123,001 - $124,000" + }, + { + "id": "row_132", + "kind": "positive", + "upper_cent": 12500000, + "indicator": "taxable_income_age_table_band_row_132_indicator", + "source_label": "$124,001 - $125,000" + }, + { + "id": "row_133", + "kind": "positive", + "upper_cent": 12600000, + "indicator": "taxable_income_age_table_band_row_133_indicator", + "source_label": "$125,001 - $126,000" + }, + { + "id": "row_134", + "kind": "positive", + "upper_cent": 12700000, + "indicator": "taxable_income_age_table_band_row_134_indicator", + "source_label": "$126,001 - $127,000" + }, + { + "id": "row_135", + "kind": "positive", + "upper_cent": 12800000, + "indicator": "taxable_income_age_table_band_row_135_indicator", + "source_label": "$127,001 - $128,000" + }, + { + "id": "row_136", + "kind": "positive", + "upper_cent": 12900000, + "indicator": "taxable_income_age_table_band_row_136_indicator", + "source_label": "$128,001 - $129,000" + }, + { + "id": "row_137", + "kind": "positive", + "upper_cent": 13000000, + "indicator": "taxable_income_age_table_band_row_137_indicator", + "source_label": "$129,001 - $130,000" + }, + { + "id": "row_138", + "kind": "positive", + "upper_cent": 13100000, + "indicator": "taxable_income_age_table_band_row_138_indicator", + "source_label": "$130,001 - $131,000" + }, + { + "id": "row_139", + "kind": "positive", + "upper_cent": 13200000, + "indicator": "taxable_income_age_table_band_row_139_indicator", + "source_label": "$131,001 - $132,000" + }, + { + "id": "row_140", + "kind": "positive", + "upper_cent": 13300000, + "indicator": "taxable_income_age_table_band_row_140_indicator", + "source_label": "$132,001 - $133,000" + }, + { + "id": "row_141", + "kind": "positive", + "upper_cent": 13400000, + "indicator": "taxable_income_age_table_band_row_141_indicator", + "source_label": "$133,001 - $134,000" + }, + { + "id": "row_142", + "kind": "positive", + "upper_cent": 13500000, + "indicator": "taxable_income_age_table_band_row_142_indicator", + "source_label": "$134,001 - $135,000" + }, + { + "id": "row_143", + "kind": "positive", + "upper_cent": 13600000, + "indicator": "taxable_income_age_table_band_row_143_indicator", + "source_label": "$135,001 - $136,000" + }, + { + "id": "row_144", + "kind": "positive", + "upper_cent": 13700000, + "indicator": "taxable_income_age_table_band_row_144_indicator", + "source_label": "$136,001 - $137,000" + }, + { + "id": "row_145", + "kind": "positive", + "upper_cent": 13800000, + "indicator": "taxable_income_age_table_band_row_145_indicator", + "source_label": "$137,001 - $138,000" + }, + { + "id": "row_146", + "kind": "positive", + "upper_cent": 13900000, + "indicator": "taxable_income_age_table_band_row_146_indicator", + "source_label": "$138,001 - $139,000" + }, + { + "id": "row_147", + "kind": "positive", + "upper_cent": 14000000, + "indicator": "taxable_income_age_table_band_row_147_indicator", + "source_label": "$139,001 - $140,000" + }, + { + "id": "row_148", + "kind": "positive", + "upper_cent": 14100000, + "indicator": "taxable_income_age_table_band_row_148_indicator", + "source_label": "$140,001 - $141,000" + }, + { + "id": "row_149", + "kind": "positive", + "upper_cent": 14200000, + "indicator": "taxable_income_age_table_band_row_149_indicator", + "source_label": "$141,001 - $142,000" + }, + { + "id": "row_150", + "kind": "positive", + "upper_cent": 14300000, + "indicator": "taxable_income_age_table_band_row_150_indicator", + "source_label": "$142,001 - $143,000" + }, + { + "id": "row_151", + "kind": "positive", + "upper_cent": 14400000, + "indicator": "taxable_income_age_table_band_row_151_indicator", + "source_label": "$143,001 - $144,000" + }, + { + "id": "row_152", + "kind": "positive", + "upper_cent": 14500000, + "indicator": "taxable_income_age_table_band_row_152_indicator", + "source_label": "$144,001 - $145,000" + }, + { + "id": "row_153", + "kind": "positive", + "upper_cent": 14600000, + "indicator": "taxable_income_age_table_band_row_153_indicator", + "source_label": "$145,001 - $146,000" + }, + { + "id": "row_154", + "kind": "positive", + "upper_cent": 14700000, + "indicator": "taxable_income_age_table_band_row_154_indicator", + "source_label": "$146,001 - $147,000" + }, + { + "id": "row_155", + "kind": "positive", + "upper_cent": 14800000, + "indicator": "taxable_income_age_table_band_row_155_indicator", + "source_label": "$147,001 - $148,000" + }, + { + "id": "row_156", + "kind": "positive", + "upper_cent": 14900000, + "indicator": "taxable_income_age_table_band_row_156_indicator", + "source_label": "$148,001 - $149,000" + }, + { + "id": "row_157", + "kind": "positive", + "upper_cent": 15000000, + "indicator": "taxable_income_age_table_band_row_157_indicator", + "source_label": "$149,001 - $150,000" + }, + { + "id": "row_158", + "kind": "positive", + "upper_cent": 15100000, + "indicator": "taxable_income_age_table_band_row_158_indicator", + "source_label": "$150,001 - $151,000" + }, + { + "id": "row_159", + "kind": "positive", + "upper_cent": 15200000, + "indicator": "taxable_income_age_table_band_row_159_indicator", + "source_label": "$151,001 - $152,000" + }, + { + "id": "row_160", + "kind": "positive", + "upper_cent": 15300000, + "indicator": "taxable_income_age_table_band_row_160_indicator", + "source_label": "$152,001 - $153,000" + }, + { + "id": "row_161", + "kind": "positive", + "upper_cent": 15400000, + "indicator": "taxable_income_age_table_band_row_161_indicator", + "source_label": "$153,001 - $154,000" + }, + { + "id": "row_162", + "kind": "positive", + "upper_cent": 15500000, + "indicator": "taxable_income_age_table_band_row_162_indicator", + "source_label": "$154,001 - $155,000" + }, + { + "id": "row_163", + "kind": "positive", + "upper_cent": 15600000, + "indicator": "taxable_income_age_table_band_row_163_indicator", + "source_label": "$155,001 - $156,000" + }, + { + "id": "row_164", + "kind": "positive", + "upper_cent": 15700000, + "indicator": "taxable_income_age_table_band_row_164_indicator", + "source_label": "$156,001 - $157,000" + }, + { + "id": "row_165", + "kind": "positive", + "upper_cent": 15800000, + "indicator": "taxable_income_age_table_band_row_165_indicator", + "source_label": "$157,001 - $158,000" + }, + { + "id": "row_166", + "kind": "positive", + "upper_cent": 15900000, + "indicator": "taxable_income_age_table_band_row_166_indicator", + "source_label": "$158,001 - $159,000" + }, + { + "id": "row_167", + "kind": "positive", + "upper_cent": 16000000, + "indicator": "taxable_income_age_table_band_row_167_indicator", + "source_label": "$159,001 - $160,000" + }, + { + "id": "row_168", + "kind": "positive", + "upper_cent": 16100000, + "indicator": "taxable_income_age_table_band_row_168_indicator", + "source_label": "$160,001 - $161,000" + }, + { + "id": "row_169", + "kind": "positive", + "upper_cent": 16200000, + "indicator": "taxable_income_age_table_band_row_169_indicator", + "source_label": "$161,001 - $162,000" + }, + { + "id": "row_170", + "kind": "positive", + "upper_cent": 16300000, + "indicator": "taxable_income_age_table_band_row_170_indicator", + "source_label": "$162,001 - $163,000" + }, + { + "id": "row_171", + "kind": "positive", + "upper_cent": 16400000, + "indicator": "taxable_income_age_table_band_row_171_indicator", + "source_label": "$163,001 - $164,000" + }, + { + "id": "row_172", + "kind": "positive", + "upper_cent": 16500000, + "indicator": "taxable_income_age_table_band_row_172_indicator", + "source_label": "$164,001 - $165,000" + }, + { + "id": "row_173", + "kind": "positive", + "upper_cent": 16600000, + "indicator": "taxable_income_age_table_band_row_173_indicator", + "source_label": "$165,001 - $166,000" + }, + { + "id": "row_174", + "kind": "positive", + "upper_cent": 16700000, + "indicator": "taxable_income_age_table_band_row_174_indicator", + "source_label": "$166,001 - $167,000" + }, + { + "id": "row_175", + "kind": "positive", + "upper_cent": 16800000, + "indicator": "taxable_income_age_table_band_row_175_indicator", + "source_label": "$167,001 - $168,000" + }, + { + "id": "row_176", + "kind": "positive", + "upper_cent": 16900000, + "indicator": "taxable_income_age_table_band_row_176_indicator", + "source_label": "$168,001 - $169,000" + }, + { + "id": "row_177", + "kind": "positive", + "upper_cent": 17000000, + "indicator": "taxable_income_age_table_band_row_177_indicator", + "source_label": "$169,001 - $170,000" + }, + { + "id": "row_178", + "kind": "positive", + "upper_cent": 17100000, + "indicator": "taxable_income_age_table_band_row_178_indicator", + "source_label": "$170,001 - $171,000" + }, + { + "id": "row_179", + "kind": "positive", + "upper_cent": 17200000, + "indicator": "taxable_income_age_table_band_row_179_indicator", + "source_label": "$171,001 - $172,000" + }, + { + "id": "row_180", + "kind": "positive", + "upper_cent": 17300000, + "indicator": "taxable_income_age_table_band_row_180_indicator", + "source_label": "$172,001 - $173,000" + }, + { + "id": "row_181", + "kind": "positive", + "upper_cent": 17400000, + "indicator": "taxable_income_age_table_band_row_181_indicator", + "source_label": "$173,001 - $174,000" + }, + { + "id": "row_182", + "kind": "positive", + "upper_cent": 17500000, + "indicator": "taxable_income_age_table_band_row_182_indicator", + "source_label": "$174,001 - $175,000" + }, + { + "id": "row_183", + "kind": "positive", + "upper_cent": 17600000, + "indicator": "taxable_income_age_table_band_row_183_indicator", + "source_label": "$175,001 - $176,000" + }, + { + "id": "row_184", + "kind": "positive", + "upper_cent": 17700000, + "indicator": "taxable_income_age_table_band_row_184_indicator", + "source_label": "$176,001 - $177,000" + }, + { + "id": "row_185", + "kind": "positive", + "upper_cent": 17800000, + "indicator": "taxable_income_age_table_band_row_185_indicator", + "source_label": "$177,001 - $178,000" + }, + { + "id": "row_186", + "kind": "positive", + "upper_cent": 17900000, + "indicator": "taxable_income_age_table_band_row_186_indicator", + "source_label": "$178,001 - $179,000" + }, + { + "id": "row_187", + "kind": "positive", + "upper_cent": 18000000, + "indicator": "taxable_income_age_table_band_row_187_indicator", + "source_label": "$179,001 - $180,000" + }, + { + "id": "row_188", + "kind": "positive", + "upper_cent": null, + "indicator": "taxable_income_age_table_band_row_188_indicator", + "source_label": "Over $180,000" + } + ], + "description": "Age-by-income TY2024 taxable-income bands, rows 6-188: one nil band, 181 finite bands and the open tail over $180,000. Declared and validated only; no reference companions and no fit rows." + } + ] +} diff --git a/packages/microcosm-build/src/microcosm/build/nz/reference_inventory.json b/packages/microcosm-build/src/microcosm/build/nz/reference_inventory.json index bc33e7a400..cb5c0e7635 100644 --- a/packages/microcosm-build/src/microcosm/build/nz/reference_inventory.json +++ b/packages/microcosm-build/src/microcosm/build/nz/reference_inventory.json @@ -36,7 +36,7 @@ "active": 64, "requires_reference_profile_resolution": 170, "requires_census_family_nucleus_standard": 51, - "requires_g8r2_taxable_income_contract_intervals_and_ageing": 474, + "requires_prerequisites": 474, "requires_stratified_receipt_assignment": 12, "requires_receipt_binding": 1, "requires_unsupported_veterans_pension": 1 @@ -14290,7 +14290,7 @@ }, { "id": "qes_prefu_and_b5_ageing", - "reason": "G8-r2; QES/PREFU rows are present in the pinned artifact but unused by this residual-allocation task." + "reason": "QES/PREFU feed rows support the declared factor calculations in reference_aging.json; they never become direct calibration references or carried target values." }, { "id": "wff_tripwires", diff --git a/packages/microcosm-build/src/microcosm/build/nz/reference_profiles.json b/packages/microcosm-build/src/microcosm/build/nz/reference_profiles.json index 4fe3ebf5f4..a00eaf9637 100644 --- a/packages/microcosm-build/src/microcosm/build/nz/reference_profiles.json +++ b/packages/microcosm-build/src/microcosm/build/nz/reference_profiles.json @@ -1,5 +1,7 @@ { "schema_version": 1, + "factor_context_resource": "reference_aging.json", + "interval_resource": "reference_intervals.json", "dimensions": { "census_residual_allocation": { "default": "mar", @@ -70601,17 +70603,1475 @@ "aging_index": { "default": "central", "profiles": { - "central": { - "transformations": [], - "deferred": true - }, "low": { - "available": false, - "transformations": [] + "transformations": [ + { + "operation": "scale_money_and_intervals", + "factor_id": "ird_ty2024_to_ty2027_low", + "reference_names": [ + "ird_taxable_income_total_by_band__row_8", + "ird_taxable_income_total_by_band__row_9", + "ird_taxable_income_total_by_band__row_10", + "ird_taxable_income_total_by_band__row_11", + "ird_taxable_income_total_by_band__row_12", + "ird_taxable_income_total_by_band__row_13", + "ird_taxable_income_total_by_band__row_14", + "ird_taxable_income_total_by_band__row_15", + "ird_taxable_income_total_by_band__row_16", + "ird_taxable_income_total_by_band__row_17", + "ird_taxable_income_total_by_band__row_18", + "ird_taxable_income_total_by_band__row_19", + "ird_taxable_income_total_by_band__row_20", + "ird_taxable_income_total_by_band__row_21", + "ird_taxable_income_total_by_band__row_22", + "ird_taxable_income_total_by_band__row_23", + "ird_taxable_income_total_by_band__row_24", + "ird_taxable_income_total_by_band__row_25", + "ird_taxable_income_total_by_band__row_26", + "ird_taxable_income_total_by_band__row_27", + "ird_taxable_income_total_by_band__row_28", + "ird_taxable_income_total_by_band__row_29", + "ird_taxable_income_total_by_band__row_30", + "ird_taxable_income_total_by_band__row_31", + "ird_taxable_income_total_by_band__row_32", + "ird_taxable_income_total_by_band__row_33", + "ird_taxable_income_total_by_band__row_34", + "ird_taxable_income_total_by_band__row_35", + "ird_taxable_income_total_by_band__row_36", + "ird_taxable_income_total_by_band__row_37", + "ird_taxable_income_total_by_band__row_38", + "ird_taxable_income_total_by_band__row_39", + "ird_taxable_income_total_by_band__row_40", + "ird_taxable_income_total_by_band__row_41", + "ird_taxable_income_total_by_band__row_42", + "ird_taxable_income_total_by_band__row_43", + "ird_taxable_income_total_by_band__row_44", + "ird_taxable_income_total_by_band__row_45", + "ird_taxable_income_total_by_band__row_46", + "ird_taxable_income_total_by_band__row_47", + "ird_taxable_income_total_by_band__row_48", + "ird_taxable_income_total_by_band__row_49", + "ird_taxable_income_total_by_band__row_50", + "ird_taxable_income_total_by_band__row_51", + "ird_taxable_income_total_by_band__row_52", + "ird_taxable_income_total_by_band__row_53", + "ird_taxable_income_total_by_band__row_54", + "ird_taxable_income_total_by_band__row_55", + "ird_taxable_income_total_by_band__row_56", + "ird_taxable_income_total_by_band__row_57", + "ird_taxable_income_total_by_band__row_58", + "ird_taxable_income_total_by_band__row_59", + "ird_taxable_income_total_by_band__row_60", + "ird_taxable_income_total_by_band__row_61", + "ird_taxable_income_total_by_band__row_62", + "ird_taxable_income_total_by_band__row_63", + "ird_taxable_income_total_by_band__row_64", + "ird_taxable_income_total_by_band__row_65", + "ird_taxable_income_total_by_band__row_66", + "ird_taxable_income_total_by_band__row_67", + "ird_taxable_income_total_by_band__row_68", + "ird_taxable_income_total_by_band__row_69", + "ird_taxable_income_total_by_band__row_70", + "ird_taxable_income_total_by_band__row_71", + "ird_taxable_income_total_by_band__row_72", + "ird_taxable_income_total_by_band__row_73", + "ird_taxable_income_total_by_band__row_74", + "ird_taxable_income_total_by_band__row_75", + "ird_taxable_income_total_by_band__row_76", + "ird_taxable_income_total_by_band__row_77", + "ird_taxable_income_total_by_band__row_78", + "ird_taxable_income_total_by_band__row_79", + "ird_taxable_income_total_by_band__row_80", + "ird_taxable_income_total_by_band__row_81", + "ird_taxable_income_total_by_band__row_82", + "ird_taxable_income_total_by_band__row_83", + "ird_taxable_income_total_by_band__row_84", + "ird_taxable_income_total_by_band__row_85", + "ird_taxable_income_total_by_band__row_86", + "ird_taxable_income_total_by_band__row_87", + "ird_taxable_income_total_by_band__row_88", + "ird_taxable_income_total_by_band__row_89", + "ird_taxable_income_total_by_band__row_90", + "ird_taxable_income_total_by_band__row_91", + "ird_taxable_income_total_by_band__row_92", + "ird_taxable_income_total_by_band__row_93", + "ird_taxable_income_total_by_band__row_94", + "ird_taxable_income_total_by_band__row_95", + "ird_taxable_income_total_by_band__row_96", + "ird_taxable_income_total_by_band__row_97", + "ird_taxable_income_total_by_band__row_98", + "ird_taxable_income_total_by_band__row_99", + "ird_taxable_income_total_by_band__row_100", + "ird_taxable_income_total_by_band__row_101", + "ird_taxable_income_total_by_band__row_102", + "ird_taxable_income_total_by_band__row_103", + "ird_taxable_income_total_by_band__row_104", + "ird_taxable_income_total_by_band__row_105", + "ird_taxable_income_total_by_band__row_106", + "ird_taxable_income_total_by_band__row_107", + "ird_taxable_income_total_by_band__row_108", + "ird_taxable_income_total_by_band__row_109", + "ird_taxable_income_total_by_band__row_110", + "ird_taxable_income_total_by_band__row_111", + "ird_taxable_income_total_by_band__row_112", + "ird_taxable_income_total_by_band__row_113", + "ird_taxable_income_total_by_band__row_114", + "ird_taxable_income_total_by_band__row_115", + "ird_taxable_income_total_by_band__row_116", + "ird_taxable_income_total_by_band__row_117", + "ird_taxable_income_total_by_band__row_118", + "ird_taxable_income_total_by_band__row_119", + "ird_taxable_income_total_by_band__row_120", + "ird_taxable_income_total_by_band__row_121", + "ird_taxable_income_total_by_band__row_122", + "ird_taxable_income_total_by_band__row_123", + "ird_taxable_income_total_by_band__row_124", + "ird_taxable_income_total_by_band__row_125", + "ird_taxable_income_total_by_band__row_126", + "ird_taxable_income_total_by_band__row_127", + "ird_taxable_income_total_by_band__row_128", + "ird_taxable_income_total_by_band__row_129", + "ird_taxable_income_total_by_band__row_130", + "ird_taxable_income_total_by_band__row_131", + "ird_taxable_income_total_by_band__row_132", + "ird_taxable_income_total_by_band__row_133", + "ird_taxable_income_total_by_band__row_134", + "ird_taxable_income_total_by_band__row_135", + "ird_taxable_income_total_by_band__row_136", + "ird_taxable_income_total_by_band__row_137", + "ird_taxable_income_total_by_band__row_138", + "ird_taxable_income_total_by_band__row_139", + "ird_taxable_income_total_by_band__row_140", + "ird_taxable_income_total_by_band__row_141", + "ird_taxable_income_total_by_band__row_142", + "ird_taxable_income_total_by_band__row_143", + "ird_taxable_income_total_by_band__row_144", + "ird_taxable_income_total_by_band__row_145", + "ird_taxable_income_total_by_band__row_146", + "ird_taxable_income_total_by_band__row_147", + "ird_taxable_income_total_by_band__row_148", + "ird_taxable_income_total_by_band__row_149", + "ird_taxable_income_total_by_band__row_150", + "ird_taxable_income_total_by_band__row_151", + "ird_taxable_income_total_by_band__row_152", + "ird_taxable_income_total_by_band__row_153", + "ird_taxable_income_total_by_band__row_154", + "ird_taxable_income_total_by_band__row_155", + "ird_taxable_income_total_by_band__row_156", + "ird_taxable_income_total_by_band__row_157", + "ird_taxable_income_total_by_band__row_158", + "ird_taxable_income_total_by_band__row_159", + "ird_taxable_income_total_by_band__row_160", + "ird_taxable_income_total_by_band__row_161", + "ird_taxable_income_total_by_band__row_162", + "ird_taxable_income_total_by_band__row_163", + "ird_taxable_income_total_by_band__row_164", + "ird_taxable_income_total_by_band__row_165", + "ird_taxable_income_total_by_band__row_166", + "ird_taxable_income_total_by_band__row_167", + "ird_taxable_income_total_by_band__row_168", + "ird_taxable_income_total_by_band__row_169", + "ird_taxable_income_total_by_band__row_170", + "ird_taxable_income_total_by_band__row_171", + "ird_taxable_income_total_by_band__row_172", + "ird_taxable_income_total_by_band__row_173", + "ird_taxable_income_total_by_band__row_174", + "ird_taxable_income_total_by_band__row_175", + "ird_taxable_income_total_by_band__row_176", + "ird_taxable_income_total_by_band__row_177", + "ird_taxable_income_total_by_band__row_178", + "ird_taxable_income_total_by_band__row_179", + "ird_taxable_income_total_by_band__row_180", + "ird_taxable_income_total_by_band__row_181", + "ird_taxable_income_total_by_band__row_182", + "ird_taxable_income_total_by_band__row_183", + "ird_taxable_income_total_by_band__row_184", + "ird_taxable_income_total_by_band__row_185", + "ird_taxable_income_total_by_band__row_186", + "ird_taxable_income_total_by_band__row_187", + "ird_taxable_income_total_by_band__row_188", + "ird_taxable_income_total_by_band__row_189", + "ird_taxable_income_total_by_band__row_190", + "ird_taxable_income_total_by_band__row_191", + "ird_taxable_income_total_by_band__row_192", + "ird_taxable_income_total_by_band__row_193", + "ird_taxable_income_total_by_band__row_194", + "ird_taxable_income_total_by_band__row_195", + "ird_taxable_income_total_by_band__row_196", + "ird_taxable_income_total_by_band__row_197", + "ird_taxable_income_total_by_band__row_198", + "ird_taxable_income_total_by_band__row_199", + "ird_taxable_income_total_by_band__row_200", + "ird_taxable_income_total_by_band__row_201", + "ird_taxable_income_total_by_band__row_202", + "ird_taxable_income_total_by_band__row_203", + "ird_taxable_income_total_by_band__row_204", + "ird_taxable_income_total_by_band__row_205", + "ird_taxable_income_total_by_band__row_206", + "ird_taxable_income_total_by_band__row_207", + "ird_taxable_income_total_by_band__row_208", + "ird_taxable_income_total_by_band__row_209", + "ird_taxable_income_total_by_band__row_210", + "ird_taxable_income_total_by_band__row_211", + "ird_taxable_income_total_by_band__row_212", + "ird_taxable_income_total_by_band__row_213", + "ird_taxable_income_total_by_band__row_214", + "ird_taxable_income_total_by_band__row_215", + "ird_taxable_income_total_by_band__row_216", + "ird_taxable_income_total_by_band__row_217", + "ird_taxable_income_total_by_band__row_218", + "ird_taxable_income_total_by_band__row_219", + "ird_taxable_income_total_by_band__row_220", + "ird_taxable_income_total_by_band__row_221", + "ird_taxable_income_total_by_band__row_222", + "ird_taxable_income_total_by_band__row_223", + "ird_taxable_income_total_by_band__row_224", + "ird_taxable_income_total_by_band__row_225", + "ird_taxable_income_total_by_band__row_226", + "ird_taxable_income_total_by_band__row_227", + "ird_taxable_income_total_by_band__row_228", + "ird_taxable_income_total_by_band__row_229", + "ird_taxable_income_total_by_band__row_230", + "ird_taxable_income_total_by_band__row_231", + "ird_taxable_income_total_by_band__row_232", + "ird_taxable_income_total_by_band__row_233", + "ird_taxable_income_total_by_band__row_234", + "ird_taxable_income_total_by_band__row_235", + "ird_taxable_income_total_by_band__row_236", + "ird_taxable_income_total_by_band__row_237", + "ird_taxable_income_total_by_band__row_238", + "ird_taxable_income_total_by_band__row_239", + "ird_taxable_income_total_by_band__row_240", + "ird_taxable_income_total_by_band__row_241", + "ird_taxable_income_total_by_band__row_242", + "ird_taxable_income_total_by_band__row_243", + "ird_taxable_income_total_by_band__row_244" + ], + "count_reference_names": [ + "ird_taxable_income_people_by_band__row_8", + "ird_taxable_income_people_by_band__row_9", + "ird_taxable_income_people_by_band__row_10", + "ird_taxable_income_people_by_band__row_11", + "ird_taxable_income_people_by_band__row_12", + "ird_taxable_income_people_by_band__row_13", + "ird_taxable_income_people_by_band__row_14", + "ird_taxable_income_people_by_band__row_15", + "ird_taxable_income_people_by_band__row_16", + "ird_taxable_income_people_by_band__row_17", + "ird_taxable_income_people_by_band__row_18", + "ird_taxable_income_people_by_band__row_19", + "ird_taxable_income_people_by_band__row_20", + "ird_taxable_income_people_by_band__row_21", + "ird_taxable_income_people_by_band__row_22", + "ird_taxable_income_people_by_band__row_23", + "ird_taxable_income_people_by_band__row_24", + "ird_taxable_income_people_by_band__row_25", + "ird_taxable_income_people_by_band__row_26", + "ird_taxable_income_people_by_band__row_27", + "ird_taxable_income_people_by_band__row_28", + "ird_taxable_income_people_by_band__row_29", + "ird_taxable_income_people_by_band__row_30", + "ird_taxable_income_people_by_band__row_31", + "ird_taxable_income_people_by_band__row_32", + "ird_taxable_income_people_by_band__row_33", + "ird_taxable_income_people_by_band__row_34", + "ird_taxable_income_people_by_band__row_35", + "ird_taxable_income_people_by_band__row_36", + "ird_taxable_income_people_by_band__row_37", + "ird_taxable_income_people_by_band__row_38", + "ird_taxable_income_people_by_band__row_39", + "ird_taxable_income_people_by_band__row_40", + "ird_taxable_income_people_by_band__row_41", + "ird_taxable_income_people_by_band__row_42", + "ird_taxable_income_people_by_band__row_43", + "ird_taxable_income_people_by_band__row_44", + "ird_taxable_income_people_by_band__row_45", + "ird_taxable_income_people_by_band__row_46", + "ird_taxable_income_people_by_band__row_47", + "ird_taxable_income_people_by_band__row_48", + "ird_taxable_income_people_by_band__row_49", + "ird_taxable_income_people_by_band__row_50", + "ird_taxable_income_people_by_band__row_51", + "ird_taxable_income_people_by_band__row_52", + "ird_taxable_income_people_by_band__row_53", + "ird_taxable_income_people_by_band__row_54", + "ird_taxable_income_people_by_band__row_55", + "ird_taxable_income_people_by_band__row_56", + "ird_taxable_income_people_by_band__row_57", + "ird_taxable_income_people_by_band__row_58", + "ird_taxable_income_people_by_band__row_59", + "ird_taxable_income_people_by_band__row_60", + "ird_taxable_income_people_by_band__row_61", + "ird_taxable_income_people_by_band__row_62", + "ird_taxable_income_people_by_band__row_63", + "ird_taxable_income_people_by_band__row_64", + "ird_taxable_income_people_by_band__row_65", + "ird_taxable_income_people_by_band__row_66", + "ird_taxable_income_people_by_band__row_67", + "ird_taxable_income_people_by_band__row_68", + "ird_taxable_income_people_by_band__row_69", + "ird_taxable_income_people_by_band__row_70", + "ird_taxable_income_people_by_band__row_71", + "ird_taxable_income_people_by_band__row_72", + "ird_taxable_income_people_by_band__row_73", + "ird_taxable_income_people_by_band__row_74", + "ird_taxable_income_people_by_band__row_75", + "ird_taxable_income_people_by_band__row_76", + "ird_taxable_income_people_by_band__row_77", + "ird_taxable_income_people_by_band__row_78", + "ird_taxable_income_people_by_band__row_79", + "ird_taxable_income_people_by_band__row_80", + "ird_taxable_income_people_by_band__row_81", + "ird_taxable_income_people_by_band__row_82", + "ird_taxable_income_people_by_band__row_83", + "ird_taxable_income_people_by_band__row_84", + "ird_taxable_income_people_by_band__row_85", + "ird_taxable_income_people_by_band__row_86", + "ird_taxable_income_people_by_band__row_87", + "ird_taxable_income_people_by_band__row_88", + "ird_taxable_income_people_by_band__row_89", + "ird_taxable_income_people_by_band__row_90", + "ird_taxable_income_people_by_band__row_91", + "ird_taxable_income_people_by_band__row_92", + "ird_taxable_income_people_by_band__row_93", + "ird_taxable_income_people_by_band__row_94", + "ird_taxable_income_people_by_band__row_95", + "ird_taxable_income_people_by_band__row_96", + "ird_taxable_income_people_by_band__row_97", + "ird_taxable_income_people_by_band__row_98", + "ird_taxable_income_people_by_band__row_99", + "ird_taxable_income_people_by_band__row_100", + "ird_taxable_income_people_by_band__row_101", + "ird_taxable_income_people_by_band__row_102", + "ird_taxable_income_people_by_band__row_103", + "ird_taxable_income_people_by_band__row_104", + "ird_taxable_income_people_by_band__row_105", + "ird_taxable_income_people_by_band__row_106", + "ird_taxable_income_people_by_band__row_107", + "ird_taxable_income_people_by_band__row_108", + "ird_taxable_income_people_by_band__row_109", + "ird_taxable_income_people_by_band__row_110", + "ird_taxable_income_people_by_band__row_111", + "ird_taxable_income_people_by_band__row_112", + "ird_taxable_income_people_by_band__row_113", + "ird_taxable_income_people_by_band__row_114", + "ird_taxable_income_people_by_band__row_115", + "ird_taxable_income_people_by_band__row_116", + "ird_taxable_income_people_by_band__row_117", + "ird_taxable_income_people_by_band__row_118", + "ird_taxable_income_people_by_band__row_119", + "ird_taxable_income_people_by_band__row_120", + "ird_taxable_income_people_by_band__row_121", + "ird_taxable_income_people_by_band__row_122", + "ird_taxable_income_people_by_band__row_123", + "ird_taxable_income_people_by_band__row_124", + "ird_taxable_income_people_by_band__row_125", + "ird_taxable_income_people_by_band__row_126", + "ird_taxable_income_people_by_band__row_127", + "ird_taxable_income_people_by_band__row_128", + "ird_taxable_income_people_by_band__row_129", + "ird_taxable_income_people_by_band__row_130", + "ird_taxable_income_people_by_band__row_131", + "ird_taxable_income_people_by_band__row_132", + "ird_taxable_income_people_by_band__row_133", + "ird_taxable_income_people_by_band__row_134", + "ird_taxable_income_people_by_band__row_135", + "ird_taxable_income_people_by_band__row_136", + "ird_taxable_income_people_by_band__row_137", + "ird_taxable_income_people_by_band__row_138", + "ird_taxable_income_people_by_band__row_139", + "ird_taxable_income_people_by_band__row_140", + "ird_taxable_income_people_by_band__row_141", + "ird_taxable_income_people_by_band__row_142", + "ird_taxable_income_people_by_band__row_143", + "ird_taxable_income_people_by_band__row_144", + "ird_taxable_income_people_by_band__row_145", + "ird_taxable_income_people_by_band__row_146", + "ird_taxable_income_people_by_band__row_147", + "ird_taxable_income_people_by_band__row_148", + "ird_taxable_income_people_by_band__row_149", + "ird_taxable_income_people_by_band__row_150", + "ird_taxable_income_people_by_band__row_151", + "ird_taxable_income_people_by_band__row_152", + "ird_taxable_income_people_by_band__row_153", + "ird_taxable_income_people_by_band__row_154", + "ird_taxable_income_people_by_band__row_155", + "ird_taxable_income_people_by_band__row_156", + "ird_taxable_income_people_by_band__row_157", + "ird_taxable_income_people_by_band__row_158", + "ird_taxable_income_people_by_band__row_159", + "ird_taxable_income_people_by_band__row_160", + "ird_taxable_income_people_by_band__row_161", + "ird_taxable_income_people_by_band__row_162", + "ird_taxable_income_people_by_band__row_163", + "ird_taxable_income_people_by_band__row_164", + "ird_taxable_income_people_by_band__row_165", + "ird_taxable_income_people_by_band__row_166", + "ird_taxable_income_people_by_band__row_167", + "ird_taxable_income_people_by_band__row_168", + "ird_taxable_income_people_by_band__row_169", + "ird_taxable_income_people_by_band__row_170", + "ird_taxable_income_people_by_band__row_171", + "ird_taxable_income_people_by_band__row_172", + "ird_taxable_income_people_by_band__row_173", + "ird_taxable_income_people_by_band__row_174", + "ird_taxable_income_people_by_band__row_175", + "ird_taxable_income_people_by_band__row_176", + "ird_taxable_income_people_by_band__row_177", + "ird_taxable_income_people_by_band__row_178", + "ird_taxable_income_people_by_band__row_179", + "ird_taxable_income_people_by_band__row_180", + "ird_taxable_income_people_by_band__row_181", + "ird_taxable_income_people_by_band__row_182", + "ird_taxable_income_people_by_band__row_183", + "ird_taxable_income_people_by_band__row_184", + "ird_taxable_income_people_by_band__row_185", + "ird_taxable_income_people_by_band__row_186", + "ird_taxable_income_people_by_band__row_187", + "ird_taxable_income_people_by_band__row_188", + "ird_taxable_income_people_by_band__row_189", + "ird_taxable_income_people_by_band__row_190", + "ird_taxable_income_people_by_band__row_191", + "ird_taxable_income_people_by_band__row_192", + "ird_taxable_income_people_by_band__row_193", + "ird_taxable_income_people_by_band__row_194", + "ird_taxable_income_people_by_band__row_195", + "ird_taxable_income_people_by_band__row_196", + "ird_taxable_income_people_by_band__row_197", + "ird_taxable_income_people_by_band__row_198", + "ird_taxable_income_people_by_band__row_199", + "ird_taxable_income_people_by_band__row_200", + "ird_taxable_income_people_by_band__row_201", + "ird_taxable_income_people_by_band__row_202", + "ird_taxable_income_people_by_band__row_203", + "ird_taxable_income_people_by_band__row_204", + "ird_taxable_income_people_by_band__row_205", + "ird_taxable_income_people_by_band__row_206", + "ird_taxable_income_people_by_band__row_207", + "ird_taxable_income_people_by_band__row_208", + "ird_taxable_income_people_by_band__row_209", + "ird_taxable_income_people_by_band__row_210", + "ird_taxable_income_people_by_band__row_211", + "ird_taxable_income_people_by_band__row_212", + "ird_taxable_income_people_by_band__row_213", + "ird_taxable_income_people_by_band__row_214", + "ird_taxable_income_people_by_band__row_215", + "ird_taxable_income_people_by_band__row_216", + "ird_taxable_income_people_by_band__row_217", + "ird_taxable_income_people_by_band__row_218", + "ird_taxable_income_people_by_band__row_219", + "ird_taxable_income_people_by_band__row_220", + "ird_taxable_income_people_by_band__row_221", + "ird_taxable_income_people_by_band__row_222", + "ird_taxable_income_people_by_band__row_223", + "ird_taxable_income_people_by_band__row_224", + "ird_taxable_income_people_by_band__row_225", + "ird_taxable_income_people_by_band__row_226", + "ird_taxable_income_people_by_band__row_227", + "ird_taxable_income_people_by_band__row_228", + "ird_taxable_income_people_by_band__row_229", + "ird_taxable_income_people_by_band__row_230", + "ird_taxable_income_people_by_band__row_231", + "ird_taxable_income_people_by_band__row_232", + "ird_taxable_income_people_by_band__row_233", + "ird_taxable_income_people_by_band__row_234", + "ird_taxable_income_people_by_band__row_235", + "ird_taxable_income_people_by_band__row_236", + "ird_taxable_income_people_by_band__row_237", + "ird_taxable_income_people_by_band__row_238", + "ird_taxable_income_people_by_band__row_239", + "ird_taxable_income_people_by_band__row_240", + "ird_taxable_income_people_by_band__row_241", + "ird_taxable_income_people_by_band__row_242", + "ird_taxable_income_people_by_band__row_243", + "ird_taxable_income_people_by_band__row_244" + ], + "interval_table_ids": [ + "ird_taxable_income_national_ty2024" + ], + "required_measurement_binding": "ird_taxable_income_ty2027_v1" + } + ] + }, + "central": { + "transformations": [ + { + "operation": "scale_money_and_intervals", + "factor_id": "ird_ty2024_to_ty2027_central", + "reference_names": [ + "ird_taxable_income_total_by_band__row_8", + "ird_taxable_income_total_by_band__row_9", + "ird_taxable_income_total_by_band__row_10", + "ird_taxable_income_total_by_band__row_11", + "ird_taxable_income_total_by_band__row_12", + "ird_taxable_income_total_by_band__row_13", + "ird_taxable_income_total_by_band__row_14", + "ird_taxable_income_total_by_band__row_15", + "ird_taxable_income_total_by_band__row_16", + "ird_taxable_income_total_by_band__row_17", + "ird_taxable_income_total_by_band__row_18", + "ird_taxable_income_total_by_band__row_19", + "ird_taxable_income_total_by_band__row_20", + "ird_taxable_income_total_by_band__row_21", + "ird_taxable_income_total_by_band__row_22", + "ird_taxable_income_total_by_band__row_23", + "ird_taxable_income_total_by_band__row_24", + "ird_taxable_income_total_by_band__row_25", + "ird_taxable_income_total_by_band__row_26", + "ird_taxable_income_total_by_band__row_27", + "ird_taxable_income_total_by_band__row_28", + "ird_taxable_income_total_by_band__row_29", + "ird_taxable_income_total_by_band__row_30", + "ird_taxable_income_total_by_band__row_31", + "ird_taxable_income_total_by_band__row_32", + "ird_taxable_income_total_by_band__row_33", + "ird_taxable_income_total_by_band__row_34", + "ird_taxable_income_total_by_band__row_35", + "ird_taxable_income_total_by_band__row_36", + "ird_taxable_income_total_by_band__row_37", + "ird_taxable_income_total_by_band__row_38", + "ird_taxable_income_total_by_band__row_39", + "ird_taxable_income_total_by_band__row_40", + "ird_taxable_income_total_by_band__row_41", + "ird_taxable_income_total_by_band__row_42", + "ird_taxable_income_total_by_band__row_43", + "ird_taxable_income_total_by_band__row_44", + "ird_taxable_income_total_by_band__row_45", + "ird_taxable_income_total_by_band__row_46", + "ird_taxable_income_total_by_band__row_47", + "ird_taxable_income_total_by_band__row_48", + "ird_taxable_income_total_by_band__row_49", + "ird_taxable_income_total_by_band__row_50", + "ird_taxable_income_total_by_band__row_51", + "ird_taxable_income_total_by_band__row_52", + "ird_taxable_income_total_by_band__row_53", + "ird_taxable_income_total_by_band__row_54", + "ird_taxable_income_total_by_band__row_55", + "ird_taxable_income_total_by_band__row_56", + "ird_taxable_income_total_by_band__row_57", + "ird_taxable_income_total_by_band__row_58", + "ird_taxable_income_total_by_band__row_59", + "ird_taxable_income_total_by_band__row_60", + "ird_taxable_income_total_by_band__row_61", + "ird_taxable_income_total_by_band__row_62", + "ird_taxable_income_total_by_band__row_63", + "ird_taxable_income_total_by_band__row_64", + "ird_taxable_income_total_by_band__row_65", + "ird_taxable_income_total_by_band__row_66", + "ird_taxable_income_total_by_band__row_67", + "ird_taxable_income_total_by_band__row_68", + "ird_taxable_income_total_by_band__row_69", + "ird_taxable_income_total_by_band__row_70", + "ird_taxable_income_total_by_band__row_71", + "ird_taxable_income_total_by_band__row_72", + "ird_taxable_income_total_by_band__row_73", + "ird_taxable_income_total_by_band__row_74", + "ird_taxable_income_total_by_band__row_75", + "ird_taxable_income_total_by_band__row_76", + "ird_taxable_income_total_by_band__row_77", + "ird_taxable_income_total_by_band__row_78", + "ird_taxable_income_total_by_band__row_79", + "ird_taxable_income_total_by_band__row_80", + "ird_taxable_income_total_by_band__row_81", + "ird_taxable_income_total_by_band__row_82", + "ird_taxable_income_total_by_band__row_83", + "ird_taxable_income_total_by_band__row_84", + "ird_taxable_income_total_by_band__row_85", + "ird_taxable_income_total_by_band__row_86", + "ird_taxable_income_total_by_band__row_87", + "ird_taxable_income_total_by_band__row_88", + "ird_taxable_income_total_by_band__row_89", + "ird_taxable_income_total_by_band__row_90", + "ird_taxable_income_total_by_band__row_91", + "ird_taxable_income_total_by_band__row_92", + "ird_taxable_income_total_by_band__row_93", + "ird_taxable_income_total_by_band__row_94", + "ird_taxable_income_total_by_band__row_95", + "ird_taxable_income_total_by_band__row_96", + "ird_taxable_income_total_by_band__row_97", + "ird_taxable_income_total_by_band__row_98", + "ird_taxable_income_total_by_band__row_99", + "ird_taxable_income_total_by_band__row_100", + "ird_taxable_income_total_by_band__row_101", + "ird_taxable_income_total_by_band__row_102", + "ird_taxable_income_total_by_band__row_103", + "ird_taxable_income_total_by_band__row_104", + "ird_taxable_income_total_by_band__row_105", + "ird_taxable_income_total_by_band__row_106", + "ird_taxable_income_total_by_band__row_107", + "ird_taxable_income_total_by_band__row_108", + "ird_taxable_income_total_by_band__row_109", + "ird_taxable_income_total_by_band__row_110", + "ird_taxable_income_total_by_band__row_111", + "ird_taxable_income_total_by_band__row_112", + "ird_taxable_income_total_by_band__row_113", + "ird_taxable_income_total_by_band__row_114", + "ird_taxable_income_total_by_band__row_115", + "ird_taxable_income_total_by_band__row_116", + "ird_taxable_income_total_by_band__row_117", + "ird_taxable_income_total_by_band__row_118", + "ird_taxable_income_total_by_band__row_119", + "ird_taxable_income_total_by_band__row_120", + "ird_taxable_income_total_by_band__row_121", + "ird_taxable_income_total_by_band__row_122", + "ird_taxable_income_total_by_band__row_123", + "ird_taxable_income_total_by_band__row_124", + "ird_taxable_income_total_by_band__row_125", + "ird_taxable_income_total_by_band__row_126", + "ird_taxable_income_total_by_band__row_127", + "ird_taxable_income_total_by_band__row_128", + "ird_taxable_income_total_by_band__row_129", + "ird_taxable_income_total_by_band__row_130", + "ird_taxable_income_total_by_band__row_131", + "ird_taxable_income_total_by_band__row_132", + "ird_taxable_income_total_by_band__row_133", + "ird_taxable_income_total_by_band__row_134", + "ird_taxable_income_total_by_band__row_135", + "ird_taxable_income_total_by_band__row_136", + "ird_taxable_income_total_by_band__row_137", + "ird_taxable_income_total_by_band__row_138", + "ird_taxable_income_total_by_band__row_139", + "ird_taxable_income_total_by_band__row_140", + "ird_taxable_income_total_by_band__row_141", + "ird_taxable_income_total_by_band__row_142", + "ird_taxable_income_total_by_band__row_143", + "ird_taxable_income_total_by_band__row_144", + "ird_taxable_income_total_by_band__row_145", + "ird_taxable_income_total_by_band__row_146", + "ird_taxable_income_total_by_band__row_147", + "ird_taxable_income_total_by_band__row_148", + "ird_taxable_income_total_by_band__row_149", + "ird_taxable_income_total_by_band__row_150", + "ird_taxable_income_total_by_band__row_151", + "ird_taxable_income_total_by_band__row_152", + "ird_taxable_income_total_by_band__row_153", + "ird_taxable_income_total_by_band__row_154", + "ird_taxable_income_total_by_band__row_155", + "ird_taxable_income_total_by_band__row_156", + "ird_taxable_income_total_by_band__row_157", + "ird_taxable_income_total_by_band__row_158", + "ird_taxable_income_total_by_band__row_159", + "ird_taxable_income_total_by_band__row_160", + "ird_taxable_income_total_by_band__row_161", + "ird_taxable_income_total_by_band__row_162", + "ird_taxable_income_total_by_band__row_163", + "ird_taxable_income_total_by_band__row_164", + "ird_taxable_income_total_by_band__row_165", + "ird_taxable_income_total_by_band__row_166", + "ird_taxable_income_total_by_band__row_167", + "ird_taxable_income_total_by_band__row_168", + "ird_taxable_income_total_by_band__row_169", + "ird_taxable_income_total_by_band__row_170", + "ird_taxable_income_total_by_band__row_171", + "ird_taxable_income_total_by_band__row_172", + "ird_taxable_income_total_by_band__row_173", + "ird_taxable_income_total_by_band__row_174", + "ird_taxable_income_total_by_band__row_175", + "ird_taxable_income_total_by_band__row_176", + "ird_taxable_income_total_by_band__row_177", + "ird_taxable_income_total_by_band__row_178", + "ird_taxable_income_total_by_band__row_179", + "ird_taxable_income_total_by_band__row_180", + "ird_taxable_income_total_by_band__row_181", + "ird_taxable_income_total_by_band__row_182", + "ird_taxable_income_total_by_band__row_183", + "ird_taxable_income_total_by_band__row_184", + "ird_taxable_income_total_by_band__row_185", + "ird_taxable_income_total_by_band__row_186", + "ird_taxable_income_total_by_band__row_187", + "ird_taxable_income_total_by_band__row_188", + "ird_taxable_income_total_by_band__row_189", + "ird_taxable_income_total_by_band__row_190", + "ird_taxable_income_total_by_band__row_191", + "ird_taxable_income_total_by_band__row_192", + "ird_taxable_income_total_by_band__row_193", + "ird_taxable_income_total_by_band__row_194", + "ird_taxable_income_total_by_band__row_195", + "ird_taxable_income_total_by_band__row_196", + "ird_taxable_income_total_by_band__row_197", + "ird_taxable_income_total_by_band__row_198", + "ird_taxable_income_total_by_band__row_199", + "ird_taxable_income_total_by_band__row_200", + "ird_taxable_income_total_by_band__row_201", + "ird_taxable_income_total_by_band__row_202", + "ird_taxable_income_total_by_band__row_203", + "ird_taxable_income_total_by_band__row_204", + "ird_taxable_income_total_by_band__row_205", + "ird_taxable_income_total_by_band__row_206", + "ird_taxable_income_total_by_band__row_207", + "ird_taxable_income_total_by_band__row_208", + "ird_taxable_income_total_by_band__row_209", + "ird_taxable_income_total_by_band__row_210", + "ird_taxable_income_total_by_band__row_211", + "ird_taxable_income_total_by_band__row_212", + "ird_taxable_income_total_by_band__row_213", + "ird_taxable_income_total_by_band__row_214", + "ird_taxable_income_total_by_band__row_215", + "ird_taxable_income_total_by_band__row_216", + "ird_taxable_income_total_by_band__row_217", + "ird_taxable_income_total_by_band__row_218", + "ird_taxable_income_total_by_band__row_219", + "ird_taxable_income_total_by_band__row_220", + "ird_taxable_income_total_by_band__row_221", + "ird_taxable_income_total_by_band__row_222", + "ird_taxable_income_total_by_band__row_223", + "ird_taxable_income_total_by_band__row_224", + "ird_taxable_income_total_by_band__row_225", + "ird_taxable_income_total_by_band__row_226", + "ird_taxable_income_total_by_band__row_227", + "ird_taxable_income_total_by_band__row_228", + "ird_taxable_income_total_by_band__row_229", + "ird_taxable_income_total_by_band__row_230", + "ird_taxable_income_total_by_band__row_231", + "ird_taxable_income_total_by_band__row_232", + "ird_taxable_income_total_by_band__row_233", + "ird_taxable_income_total_by_band__row_234", + "ird_taxable_income_total_by_band__row_235", + "ird_taxable_income_total_by_band__row_236", + "ird_taxable_income_total_by_band__row_237", + "ird_taxable_income_total_by_band__row_238", + "ird_taxable_income_total_by_band__row_239", + "ird_taxable_income_total_by_band__row_240", + "ird_taxable_income_total_by_band__row_241", + "ird_taxable_income_total_by_band__row_242", + "ird_taxable_income_total_by_band__row_243", + "ird_taxable_income_total_by_band__row_244" + ], + "count_reference_names": [ + "ird_taxable_income_people_by_band__row_8", + "ird_taxable_income_people_by_band__row_9", + "ird_taxable_income_people_by_band__row_10", + "ird_taxable_income_people_by_band__row_11", + "ird_taxable_income_people_by_band__row_12", + "ird_taxable_income_people_by_band__row_13", + "ird_taxable_income_people_by_band__row_14", + "ird_taxable_income_people_by_band__row_15", + "ird_taxable_income_people_by_band__row_16", + "ird_taxable_income_people_by_band__row_17", + "ird_taxable_income_people_by_band__row_18", + "ird_taxable_income_people_by_band__row_19", + "ird_taxable_income_people_by_band__row_20", + "ird_taxable_income_people_by_band__row_21", + "ird_taxable_income_people_by_band__row_22", + "ird_taxable_income_people_by_band__row_23", + "ird_taxable_income_people_by_band__row_24", + "ird_taxable_income_people_by_band__row_25", + "ird_taxable_income_people_by_band__row_26", + "ird_taxable_income_people_by_band__row_27", + "ird_taxable_income_people_by_band__row_28", + "ird_taxable_income_people_by_band__row_29", + "ird_taxable_income_people_by_band__row_30", + "ird_taxable_income_people_by_band__row_31", + "ird_taxable_income_people_by_band__row_32", + "ird_taxable_income_people_by_band__row_33", + "ird_taxable_income_people_by_band__row_34", + "ird_taxable_income_people_by_band__row_35", + "ird_taxable_income_people_by_band__row_36", + "ird_taxable_income_people_by_band__row_37", + "ird_taxable_income_people_by_band__row_38", + "ird_taxable_income_people_by_band__row_39", + "ird_taxable_income_people_by_band__row_40", + "ird_taxable_income_people_by_band__row_41", + "ird_taxable_income_people_by_band__row_42", + "ird_taxable_income_people_by_band__row_43", + "ird_taxable_income_people_by_band__row_44", + "ird_taxable_income_people_by_band__row_45", + "ird_taxable_income_people_by_band__row_46", + "ird_taxable_income_people_by_band__row_47", + "ird_taxable_income_people_by_band__row_48", + "ird_taxable_income_people_by_band__row_49", + "ird_taxable_income_people_by_band__row_50", + "ird_taxable_income_people_by_band__row_51", + "ird_taxable_income_people_by_band__row_52", + "ird_taxable_income_people_by_band__row_53", + "ird_taxable_income_people_by_band__row_54", + "ird_taxable_income_people_by_band__row_55", + "ird_taxable_income_people_by_band__row_56", + "ird_taxable_income_people_by_band__row_57", + "ird_taxable_income_people_by_band__row_58", + "ird_taxable_income_people_by_band__row_59", + "ird_taxable_income_people_by_band__row_60", + "ird_taxable_income_people_by_band__row_61", + "ird_taxable_income_people_by_band__row_62", + "ird_taxable_income_people_by_band__row_63", + "ird_taxable_income_people_by_band__row_64", + "ird_taxable_income_people_by_band__row_65", + "ird_taxable_income_people_by_band__row_66", + "ird_taxable_income_people_by_band__row_67", + "ird_taxable_income_people_by_band__row_68", + "ird_taxable_income_people_by_band__row_69", + "ird_taxable_income_people_by_band__row_70", + "ird_taxable_income_people_by_band__row_71", + "ird_taxable_income_people_by_band__row_72", + "ird_taxable_income_people_by_band__row_73", + "ird_taxable_income_people_by_band__row_74", + "ird_taxable_income_people_by_band__row_75", + "ird_taxable_income_people_by_band__row_76", + "ird_taxable_income_people_by_band__row_77", + "ird_taxable_income_people_by_band__row_78", + "ird_taxable_income_people_by_band__row_79", + "ird_taxable_income_people_by_band__row_80", + "ird_taxable_income_people_by_band__row_81", + "ird_taxable_income_people_by_band__row_82", + "ird_taxable_income_people_by_band__row_83", + "ird_taxable_income_people_by_band__row_84", + "ird_taxable_income_people_by_band__row_85", + "ird_taxable_income_people_by_band__row_86", + "ird_taxable_income_people_by_band__row_87", + "ird_taxable_income_people_by_band__row_88", + "ird_taxable_income_people_by_band__row_89", + "ird_taxable_income_people_by_band__row_90", + "ird_taxable_income_people_by_band__row_91", + "ird_taxable_income_people_by_band__row_92", + "ird_taxable_income_people_by_band__row_93", + "ird_taxable_income_people_by_band__row_94", + "ird_taxable_income_people_by_band__row_95", + "ird_taxable_income_people_by_band__row_96", + "ird_taxable_income_people_by_band__row_97", + "ird_taxable_income_people_by_band__row_98", + "ird_taxable_income_people_by_band__row_99", + "ird_taxable_income_people_by_band__row_100", + "ird_taxable_income_people_by_band__row_101", + "ird_taxable_income_people_by_band__row_102", + "ird_taxable_income_people_by_band__row_103", + "ird_taxable_income_people_by_band__row_104", + "ird_taxable_income_people_by_band__row_105", + "ird_taxable_income_people_by_band__row_106", + "ird_taxable_income_people_by_band__row_107", + "ird_taxable_income_people_by_band__row_108", + "ird_taxable_income_people_by_band__row_109", + "ird_taxable_income_people_by_band__row_110", + "ird_taxable_income_people_by_band__row_111", + "ird_taxable_income_people_by_band__row_112", + "ird_taxable_income_people_by_band__row_113", + "ird_taxable_income_people_by_band__row_114", + "ird_taxable_income_people_by_band__row_115", + "ird_taxable_income_people_by_band__row_116", + "ird_taxable_income_people_by_band__row_117", + "ird_taxable_income_people_by_band__row_118", + "ird_taxable_income_people_by_band__row_119", + "ird_taxable_income_people_by_band__row_120", + "ird_taxable_income_people_by_band__row_121", + "ird_taxable_income_people_by_band__row_122", + "ird_taxable_income_people_by_band__row_123", + "ird_taxable_income_people_by_band__row_124", + "ird_taxable_income_people_by_band__row_125", + "ird_taxable_income_people_by_band__row_126", + "ird_taxable_income_people_by_band__row_127", + "ird_taxable_income_people_by_band__row_128", + "ird_taxable_income_people_by_band__row_129", + "ird_taxable_income_people_by_band__row_130", + "ird_taxable_income_people_by_band__row_131", + "ird_taxable_income_people_by_band__row_132", + "ird_taxable_income_people_by_band__row_133", + "ird_taxable_income_people_by_band__row_134", + "ird_taxable_income_people_by_band__row_135", + "ird_taxable_income_people_by_band__row_136", + "ird_taxable_income_people_by_band__row_137", + "ird_taxable_income_people_by_band__row_138", + "ird_taxable_income_people_by_band__row_139", + "ird_taxable_income_people_by_band__row_140", + "ird_taxable_income_people_by_band__row_141", + "ird_taxable_income_people_by_band__row_142", + "ird_taxable_income_people_by_band__row_143", + "ird_taxable_income_people_by_band__row_144", + "ird_taxable_income_people_by_band__row_145", + "ird_taxable_income_people_by_band__row_146", + "ird_taxable_income_people_by_band__row_147", + "ird_taxable_income_people_by_band__row_148", + "ird_taxable_income_people_by_band__row_149", + "ird_taxable_income_people_by_band__row_150", + "ird_taxable_income_people_by_band__row_151", + "ird_taxable_income_people_by_band__row_152", + "ird_taxable_income_people_by_band__row_153", + "ird_taxable_income_people_by_band__row_154", + "ird_taxable_income_people_by_band__row_155", + "ird_taxable_income_people_by_band__row_156", + "ird_taxable_income_people_by_band__row_157", + "ird_taxable_income_people_by_band__row_158", + "ird_taxable_income_people_by_band__row_159", + "ird_taxable_income_people_by_band__row_160", + "ird_taxable_income_people_by_band__row_161", + "ird_taxable_income_people_by_band__row_162", + "ird_taxable_income_people_by_band__row_163", + "ird_taxable_income_people_by_band__row_164", + "ird_taxable_income_people_by_band__row_165", + "ird_taxable_income_people_by_band__row_166", + "ird_taxable_income_people_by_band__row_167", + "ird_taxable_income_people_by_band__row_168", + "ird_taxable_income_people_by_band__row_169", + "ird_taxable_income_people_by_band__row_170", + "ird_taxable_income_people_by_band__row_171", + "ird_taxable_income_people_by_band__row_172", + "ird_taxable_income_people_by_band__row_173", + "ird_taxable_income_people_by_band__row_174", + "ird_taxable_income_people_by_band__row_175", + "ird_taxable_income_people_by_band__row_176", + "ird_taxable_income_people_by_band__row_177", + "ird_taxable_income_people_by_band__row_178", + "ird_taxable_income_people_by_band__row_179", + "ird_taxable_income_people_by_band__row_180", + "ird_taxable_income_people_by_band__row_181", + "ird_taxable_income_people_by_band__row_182", + "ird_taxable_income_people_by_band__row_183", + "ird_taxable_income_people_by_band__row_184", + "ird_taxable_income_people_by_band__row_185", + "ird_taxable_income_people_by_band__row_186", + "ird_taxable_income_people_by_band__row_187", + "ird_taxable_income_people_by_band__row_188", + "ird_taxable_income_people_by_band__row_189", + "ird_taxable_income_people_by_band__row_190", + "ird_taxable_income_people_by_band__row_191", + "ird_taxable_income_people_by_band__row_192", + "ird_taxable_income_people_by_band__row_193", + "ird_taxable_income_people_by_band__row_194", + "ird_taxable_income_people_by_band__row_195", + "ird_taxable_income_people_by_band__row_196", + "ird_taxable_income_people_by_band__row_197", + "ird_taxable_income_people_by_band__row_198", + "ird_taxable_income_people_by_band__row_199", + "ird_taxable_income_people_by_band__row_200", + "ird_taxable_income_people_by_band__row_201", + "ird_taxable_income_people_by_band__row_202", + "ird_taxable_income_people_by_band__row_203", + "ird_taxable_income_people_by_band__row_204", + "ird_taxable_income_people_by_band__row_205", + "ird_taxable_income_people_by_band__row_206", + "ird_taxable_income_people_by_band__row_207", + "ird_taxable_income_people_by_band__row_208", + "ird_taxable_income_people_by_band__row_209", + "ird_taxable_income_people_by_band__row_210", + "ird_taxable_income_people_by_band__row_211", + "ird_taxable_income_people_by_band__row_212", + "ird_taxable_income_people_by_band__row_213", + "ird_taxable_income_people_by_band__row_214", + "ird_taxable_income_people_by_band__row_215", + "ird_taxable_income_people_by_band__row_216", + "ird_taxable_income_people_by_band__row_217", + "ird_taxable_income_people_by_band__row_218", + "ird_taxable_income_people_by_band__row_219", + "ird_taxable_income_people_by_band__row_220", + "ird_taxable_income_people_by_band__row_221", + "ird_taxable_income_people_by_band__row_222", + "ird_taxable_income_people_by_band__row_223", + "ird_taxable_income_people_by_band__row_224", + "ird_taxable_income_people_by_band__row_225", + "ird_taxable_income_people_by_band__row_226", + "ird_taxable_income_people_by_band__row_227", + "ird_taxable_income_people_by_band__row_228", + "ird_taxable_income_people_by_band__row_229", + "ird_taxable_income_people_by_band__row_230", + "ird_taxable_income_people_by_band__row_231", + "ird_taxable_income_people_by_band__row_232", + "ird_taxable_income_people_by_band__row_233", + "ird_taxable_income_people_by_band__row_234", + "ird_taxable_income_people_by_band__row_235", + "ird_taxable_income_people_by_band__row_236", + "ird_taxable_income_people_by_band__row_237", + "ird_taxable_income_people_by_band__row_238", + "ird_taxable_income_people_by_band__row_239", + "ird_taxable_income_people_by_band__row_240", + "ird_taxable_income_people_by_band__row_241", + "ird_taxable_income_people_by_band__row_242", + "ird_taxable_income_people_by_band__row_243", + "ird_taxable_income_people_by_band__row_244" + ], + "interval_table_ids": [ + "ird_taxable_income_national_ty2024" + ], + "required_measurement_binding": "ird_taxable_income_ty2027_v1" + } + ] }, "high": { - "available": false, - "transformations": [] + "transformations": [ + { + "operation": "scale_money_and_intervals", + "factor_id": "ird_ty2024_to_ty2027_high", + "reference_names": [ + "ird_taxable_income_total_by_band__row_8", + "ird_taxable_income_total_by_band__row_9", + "ird_taxable_income_total_by_band__row_10", + "ird_taxable_income_total_by_band__row_11", + "ird_taxable_income_total_by_band__row_12", + "ird_taxable_income_total_by_band__row_13", + "ird_taxable_income_total_by_band__row_14", + "ird_taxable_income_total_by_band__row_15", + "ird_taxable_income_total_by_band__row_16", + "ird_taxable_income_total_by_band__row_17", + "ird_taxable_income_total_by_band__row_18", + "ird_taxable_income_total_by_band__row_19", + "ird_taxable_income_total_by_band__row_20", + "ird_taxable_income_total_by_band__row_21", + "ird_taxable_income_total_by_band__row_22", + "ird_taxable_income_total_by_band__row_23", + "ird_taxable_income_total_by_band__row_24", + "ird_taxable_income_total_by_band__row_25", + "ird_taxable_income_total_by_band__row_26", + "ird_taxable_income_total_by_band__row_27", + "ird_taxable_income_total_by_band__row_28", + "ird_taxable_income_total_by_band__row_29", + "ird_taxable_income_total_by_band__row_30", + "ird_taxable_income_total_by_band__row_31", + "ird_taxable_income_total_by_band__row_32", + "ird_taxable_income_total_by_band__row_33", + "ird_taxable_income_total_by_band__row_34", + "ird_taxable_income_total_by_band__row_35", + "ird_taxable_income_total_by_band__row_36", + "ird_taxable_income_total_by_band__row_37", + "ird_taxable_income_total_by_band__row_38", + "ird_taxable_income_total_by_band__row_39", + "ird_taxable_income_total_by_band__row_40", + "ird_taxable_income_total_by_band__row_41", + "ird_taxable_income_total_by_band__row_42", + "ird_taxable_income_total_by_band__row_43", + "ird_taxable_income_total_by_band__row_44", + "ird_taxable_income_total_by_band__row_45", + "ird_taxable_income_total_by_band__row_46", + "ird_taxable_income_total_by_band__row_47", + "ird_taxable_income_total_by_band__row_48", + "ird_taxable_income_total_by_band__row_49", + "ird_taxable_income_total_by_band__row_50", + "ird_taxable_income_total_by_band__row_51", + "ird_taxable_income_total_by_band__row_52", + "ird_taxable_income_total_by_band__row_53", + "ird_taxable_income_total_by_band__row_54", + "ird_taxable_income_total_by_band__row_55", + "ird_taxable_income_total_by_band__row_56", + "ird_taxable_income_total_by_band__row_57", + "ird_taxable_income_total_by_band__row_58", + "ird_taxable_income_total_by_band__row_59", + "ird_taxable_income_total_by_band__row_60", + "ird_taxable_income_total_by_band__row_61", + "ird_taxable_income_total_by_band__row_62", + "ird_taxable_income_total_by_band__row_63", + "ird_taxable_income_total_by_band__row_64", + "ird_taxable_income_total_by_band__row_65", + "ird_taxable_income_total_by_band__row_66", + "ird_taxable_income_total_by_band__row_67", + "ird_taxable_income_total_by_band__row_68", + "ird_taxable_income_total_by_band__row_69", + "ird_taxable_income_total_by_band__row_70", + "ird_taxable_income_total_by_band__row_71", + "ird_taxable_income_total_by_band__row_72", + "ird_taxable_income_total_by_band__row_73", + "ird_taxable_income_total_by_band__row_74", + "ird_taxable_income_total_by_band__row_75", + "ird_taxable_income_total_by_band__row_76", + "ird_taxable_income_total_by_band__row_77", + "ird_taxable_income_total_by_band__row_78", + "ird_taxable_income_total_by_band__row_79", + "ird_taxable_income_total_by_band__row_80", + "ird_taxable_income_total_by_band__row_81", + "ird_taxable_income_total_by_band__row_82", + "ird_taxable_income_total_by_band__row_83", + "ird_taxable_income_total_by_band__row_84", + "ird_taxable_income_total_by_band__row_85", + "ird_taxable_income_total_by_band__row_86", + "ird_taxable_income_total_by_band__row_87", + "ird_taxable_income_total_by_band__row_88", + "ird_taxable_income_total_by_band__row_89", + "ird_taxable_income_total_by_band__row_90", + "ird_taxable_income_total_by_band__row_91", + "ird_taxable_income_total_by_band__row_92", + "ird_taxable_income_total_by_band__row_93", + "ird_taxable_income_total_by_band__row_94", + "ird_taxable_income_total_by_band__row_95", + "ird_taxable_income_total_by_band__row_96", + "ird_taxable_income_total_by_band__row_97", + "ird_taxable_income_total_by_band__row_98", + "ird_taxable_income_total_by_band__row_99", + "ird_taxable_income_total_by_band__row_100", + "ird_taxable_income_total_by_band__row_101", + "ird_taxable_income_total_by_band__row_102", + "ird_taxable_income_total_by_band__row_103", + "ird_taxable_income_total_by_band__row_104", + "ird_taxable_income_total_by_band__row_105", + "ird_taxable_income_total_by_band__row_106", + "ird_taxable_income_total_by_band__row_107", + "ird_taxable_income_total_by_band__row_108", + "ird_taxable_income_total_by_band__row_109", + "ird_taxable_income_total_by_band__row_110", + "ird_taxable_income_total_by_band__row_111", + "ird_taxable_income_total_by_band__row_112", + "ird_taxable_income_total_by_band__row_113", + "ird_taxable_income_total_by_band__row_114", + "ird_taxable_income_total_by_band__row_115", + "ird_taxable_income_total_by_band__row_116", + "ird_taxable_income_total_by_band__row_117", + "ird_taxable_income_total_by_band__row_118", + "ird_taxable_income_total_by_band__row_119", + "ird_taxable_income_total_by_band__row_120", + "ird_taxable_income_total_by_band__row_121", + "ird_taxable_income_total_by_band__row_122", + "ird_taxable_income_total_by_band__row_123", + "ird_taxable_income_total_by_band__row_124", + "ird_taxable_income_total_by_band__row_125", + "ird_taxable_income_total_by_band__row_126", + "ird_taxable_income_total_by_band__row_127", + "ird_taxable_income_total_by_band__row_128", + "ird_taxable_income_total_by_band__row_129", + "ird_taxable_income_total_by_band__row_130", + "ird_taxable_income_total_by_band__row_131", + "ird_taxable_income_total_by_band__row_132", + "ird_taxable_income_total_by_band__row_133", + "ird_taxable_income_total_by_band__row_134", + "ird_taxable_income_total_by_band__row_135", + "ird_taxable_income_total_by_band__row_136", + "ird_taxable_income_total_by_band__row_137", + "ird_taxable_income_total_by_band__row_138", + "ird_taxable_income_total_by_band__row_139", + "ird_taxable_income_total_by_band__row_140", + "ird_taxable_income_total_by_band__row_141", + "ird_taxable_income_total_by_band__row_142", + "ird_taxable_income_total_by_band__row_143", + "ird_taxable_income_total_by_band__row_144", + "ird_taxable_income_total_by_band__row_145", + "ird_taxable_income_total_by_band__row_146", + "ird_taxable_income_total_by_band__row_147", + "ird_taxable_income_total_by_band__row_148", + "ird_taxable_income_total_by_band__row_149", + "ird_taxable_income_total_by_band__row_150", + "ird_taxable_income_total_by_band__row_151", + "ird_taxable_income_total_by_band__row_152", + "ird_taxable_income_total_by_band__row_153", + "ird_taxable_income_total_by_band__row_154", + "ird_taxable_income_total_by_band__row_155", + "ird_taxable_income_total_by_band__row_156", + "ird_taxable_income_total_by_band__row_157", + "ird_taxable_income_total_by_band__row_158", + "ird_taxable_income_total_by_band__row_159", + "ird_taxable_income_total_by_band__row_160", + "ird_taxable_income_total_by_band__row_161", + "ird_taxable_income_total_by_band__row_162", + "ird_taxable_income_total_by_band__row_163", + "ird_taxable_income_total_by_band__row_164", + "ird_taxable_income_total_by_band__row_165", + "ird_taxable_income_total_by_band__row_166", + "ird_taxable_income_total_by_band__row_167", + "ird_taxable_income_total_by_band__row_168", + "ird_taxable_income_total_by_band__row_169", + "ird_taxable_income_total_by_band__row_170", + "ird_taxable_income_total_by_band__row_171", + "ird_taxable_income_total_by_band__row_172", + "ird_taxable_income_total_by_band__row_173", + "ird_taxable_income_total_by_band__row_174", + "ird_taxable_income_total_by_band__row_175", + "ird_taxable_income_total_by_band__row_176", + "ird_taxable_income_total_by_band__row_177", + "ird_taxable_income_total_by_band__row_178", + "ird_taxable_income_total_by_band__row_179", + "ird_taxable_income_total_by_band__row_180", + "ird_taxable_income_total_by_band__row_181", + "ird_taxable_income_total_by_band__row_182", + "ird_taxable_income_total_by_band__row_183", + "ird_taxable_income_total_by_band__row_184", + "ird_taxable_income_total_by_band__row_185", + "ird_taxable_income_total_by_band__row_186", + "ird_taxable_income_total_by_band__row_187", + "ird_taxable_income_total_by_band__row_188", + "ird_taxable_income_total_by_band__row_189", + "ird_taxable_income_total_by_band__row_190", + "ird_taxable_income_total_by_band__row_191", + "ird_taxable_income_total_by_band__row_192", + "ird_taxable_income_total_by_band__row_193", + "ird_taxable_income_total_by_band__row_194", + "ird_taxable_income_total_by_band__row_195", + "ird_taxable_income_total_by_band__row_196", + "ird_taxable_income_total_by_band__row_197", + "ird_taxable_income_total_by_band__row_198", + "ird_taxable_income_total_by_band__row_199", + "ird_taxable_income_total_by_band__row_200", + "ird_taxable_income_total_by_band__row_201", + "ird_taxable_income_total_by_band__row_202", + "ird_taxable_income_total_by_band__row_203", + "ird_taxable_income_total_by_band__row_204", + "ird_taxable_income_total_by_band__row_205", + "ird_taxable_income_total_by_band__row_206", + "ird_taxable_income_total_by_band__row_207", + "ird_taxable_income_total_by_band__row_208", + "ird_taxable_income_total_by_band__row_209", + "ird_taxable_income_total_by_band__row_210", + "ird_taxable_income_total_by_band__row_211", + "ird_taxable_income_total_by_band__row_212", + "ird_taxable_income_total_by_band__row_213", + "ird_taxable_income_total_by_band__row_214", + "ird_taxable_income_total_by_band__row_215", + "ird_taxable_income_total_by_band__row_216", + "ird_taxable_income_total_by_band__row_217", + "ird_taxable_income_total_by_band__row_218", + "ird_taxable_income_total_by_band__row_219", + "ird_taxable_income_total_by_band__row_220", + "ird_taxable_income_total_by_band__row_221", + "ird_taxable_income_total_by_band__row_222", + "ird_taxable_income_total_by_band__row_223", + "ird_taxable_income_total_by_band__row_224", + "ird_taxable_income_total_by_band__row_225", + "ird_taxable_income_total_by_band__row_226", + "ird_taxable_income_total_by_band__row_227", + "ird_taxable_income_total_by_band__row_228", + "ird_taxable_income_total_by_band__row_229", + "ird_taxable_income_total_by_band__row_230", + "ird_taxable_income_total_by_band__row_231", + "ird_taxable_income_total_by_band__row_232", + "ird_taxable_income_total_by_band__row_233", + "ird_taxable_income_total_by_band__row_234", + "ird_taxable_income_total_by_band__row_235", + "ird_taxable_income_total_by_band__row_236", + "ird_taxable_income_total_by_band__row_237", + "ird_taxable_income_total_by_band__row_238", + "ird_taxable_income_total_by_band__row_239", + "ird_taxable_income_total_by_band__row_240", + "ird_taxable_income_total_by_band__row_241", + "ird_taxable_income_total_by_band__row_242", + "ird_taxable_income_total_by_band__row_243", + "ird_taxable_income_total_by_band__row_244" + ], + "count_reference_names": [ + "ird_taxable_income_people_by_band__row_8", + "ird_taxable_income_people_by_band__row_9", + "ird_taxable_income_people_by_band__row_10", + "ird_taxable_income_people_by_band__row_11", + "ird_taxable_income_people_by_band__row_12", + "ird_taxable_income_people_by_band__row_13", + "ird_taxable_income_people_by_band__row_14", + "ird_taxable_income_people_by_band__row_15", + "ird_taxable_income_people_by_band__row_16", + "ird_taxable_income_people_by_band__row_17", + "ird_taxable_income_people_by_band__row_18", + "ird_taxable_income_people_by_band__row_19", + "ird_taxable_income_people_by_band__row_20", + "ird_taxable_income_people_by_band__row_21", + "ird_taxable_income_people_by_band__row_22", + "ird_taxable_income_people_by_band__row_23", + "ird_taxable_income_people_by_band__row_24", + "ird_taxable_income_people_by_band__row_25", + "ird_taxable_income_people_by_band__row_26", + "ird_taxable_income_people_by_band__row_27", + "ird_taxable_income_people_by_band__row_28", + "ird_taxable_income_people_by_band__row_29", + "ird_taxable_income_people_by_band__row_30", + "ird_taxable_income_people_by_band__row_31", + "ird_taxable_income_people_by_band__row_32", + "ird_taxable_income_people_by_band__row_33", + "ird_taxable_income_people_by_band__row_34", + "ird_taxable_income_people_by_band__row_35", + "ird_taxable_income_people_by_band__row_36", + "ird_taxable_income_people_by_band__row_37", + "ird_taxable_income_people_by_band__row_38", + "ird_taxable_income_people_by_band__row_39", + "ird_taxable_income_people_by_band__row_40", + "ird_taxable_income_people_by_band__row_41", + "ird_taxable_income_people_by_band__row_42", + "ird_taxable_income_people_by_band__row_43", + "ird_taxable_income_people_by_band__row_44", + "ird_taxable_income_people_by_band__row_45", + "ird_taxable_income_people_by_band__row_46", + "ird_taxable_income_people_by_band__row_47", + "ird_taxable_income_people_by_band__row_48", + "ird_taxable_income_people_by_band__row_49", + "ird_taxable_income_people_by_band__row_50", + "ird_taxable_income_people_by_band__row_51", + "ird_taxable_income_people_by_band__row_52", + "ird_taxable_income_people_by_band__row_53", + "ird_taxable_income_people_by_band__row_54", + "ird_taxable_income_people_by_band__row_55", + "ird_taxable_income_people_by_band__row_56", + "ird_taxable_income_people_by_band__row_57", + "ird_taxable_income_people_by_band__row_58", + "ird_taxable_income_people_by_band__row_59", + "ird_taxable_income_people_by_band__row_60", + "ird_taxable_income_people_by_band__row_61", + "ird_taxable_income_people_by_band__row_62", + "ird_taxable_income_people_by_band__row_63", + "ird_taxable_income_people_by_band__row_64", + "ird_taxable_income_people_by_band__row_65", + "ird_taxable_income_people_by_band__row_66", + "ird_taxable_income_people_by_band__row_67", + "ird_taxable_income_people_by_band__row_68", + "ird_taxable_income_people_by_band__row_69", + "ird_taxable_income_people_by_band__row_70", + "ird_taxable_income_people_by_band__row_71", + "ird_taxable_income_people_by_band__row_72", + "ird_taxable_income_people_by_band__row_73", + "ird_taxable_income_people_by_band__row_74", + "ird_taxable_income_people_by_band__row_75", + "ird_taxable_income_people_by_band__row_76", + "ird_taxable_income_people_by_band__row_77", + "ird_taxable_income_people_by_band__row_78", + "ird_taxable_income_people_by_band__row_79", + "ird_taxable_income_people_by_band__row_80", + "ird_taxable_income_people_by_band__row_81", + "ird_taxable_income_people_by_band__row_82", + "ird_taxable_income_people_by_band__row_83", + "ird_taxable_income_people_by_band__row_84", + "ird_taxable_income_people_by_band__row_85", + "ird_taxable_income_people_by_band__row_86", + "ird_taxable_income_people_by_band__row_87", + "ird_taxable_income_people_by_band__row_88", + "ird_taxable_income_people_by_band__row_89", + "ird_taxable_income_people_by_band__row_90", + "ird_taxable_income_people_by_band__row_91", + "ird_taxable_income_people_by_band__row_92", + "ird_taxable_income_people_by_band__row_93", + "ird_taxable_income_people_by_band__row_94", + "ird_taxable_income_people_by_band__row_95", + "ird_taxable_income_people_by_band__row_96", + "ird_taxable_income_people_by_band__row_97", + "ird_taxable_income_people_by_band__row_98", + "ird_taxable_income_people_by_band__row_99", + "ird_taxable_income_people_by_band__row_100", + "ird_taxable_income_people_by_band__row_101", + "ird_taxable_income_people_by_band__row_102", + "ird_taxable_income_people_by_band__row_103", + "ird_taxable_income_people_by_band__row_104", + "ird_taxable_income_people_by_band__row_105", + "ird_taxable_income_people_by_band__row_106", + "ird_taxable_income_people_by_band__row_107", + "ird_taxable_income_people_by_band__row_108", + "ird_taxable_income_people_by_band__row_109", + "ird_taxable_income_people_by_band__row_110", + "ird_taxable_income_people_by_band__row_111", + "ird_taxable_income_people_by_band__row_112", + "ird_taxable_income_people_by_band__row_113", + "ird_taxable_income_people_by_band__row_114", + "ird_taxable_income_people_by_band__row_115", + "ird_taxable_income_people_by_band__row_116", + "ird_taxable_income_people_by_band__row_117", + "ird_taxable_income_people_by_band__row_118", + "ird_taxable_income_people_by_band__row_119", + "ird_taxable_income_people_by_band__row_120", + "ird_taxable_income_people_by_band__row_121", + "ird_taxable_income_people_by_band__row_122", + "ird_taxable_income_people_by_band__row_123", + "ird_taxable_income_people_by_band__row_124", + "ird_taxable_income_people_by_band__row_125", + "ird_taxable_income_people_by_band__row_126", + "ird_taxable_income_people_by_band__row_127", + "ird_taxable_income_people_by_band__row_128", + "ird_taxable_income_people_by_band__row_129", + "ird_taxable_income_people_by_band__row_130", + "ird_taxable_income_people_by_band__row_131", + "ird_taxable_income_people_by_band__row_132", + "ird_taxable_income_people_by_band__row_133", + "ird_taxable_income_people_by_band__row_134", + "ird_taxable_income_people_by_band__row_135", + "ird_taxable_income_people_by_band__row_136", + "ird_taxable_income_people_by_band__row_137", + "ird_taxable_income_people_by_band__row_138", + "ird_taxable_income_people_by_band__row_139", + "ird_taxable_income_people_by_band__row_140", + "ird_taxable_income_people_by_band__row_141", + "ird_taxable_income_people_by_band__row_142", + "ird_taxable_income_people_by_band__row_143", + "ird_taxable_income_people_by_band__row_144", + "ird_taxable_income_people_by_band__row_145", + "ird_taxable_income_people_by_band__row_146", + "ird_taxable_income_people_by_band__row_147", + "ird_taxable_income_people_by_band__row_148", + "ird_taxable_income_people_by_band__row_149", + "ird_taxable_income_people_by_band__row_150", + "ird_taxable_income_people_by_band__row_151", + "ird_taxable_income_people_by_band__row_152", + "ird_taxable_income_people_by_band__row_153", + "ird_taxable_income_people_by_band__row_154", + "ird_taxable_income_people_by_band__row_155", + "ird_taxable_income_people_by_band__row_156", + "ird_taxable_income_people_by_band__row_157", + "ird_taxable_income_people_by_band__row_158", + "ird_taxable_income_people_by_band__row_159", + "ird_taxable_income_people_by_band__row_160", + "ird_taxable_income_people_by_band__row_161", + "ird_taxable_income_people_by_band__row_162", + "ird_taxable_income_people_by_band__row_163", + "ird_taxable_income_people_by_band__row_164", + "ird_taxable_income_people_by_band__row_165", + "ird_taxable_income_people_by_band__row_166", + "ird_taxable_income_people_by_band__row_167", + "ird_taxable_income_people_by_band__row_168", + "ird_taxable_income_people_by_band__row_169", + "ird_taxable_income_people_by_band__row_170", + "ird_taxable_income_people_by_band__row_171", + "ird_taxable_income_people_by_band__row_172", + "ird_taxable_income_people_by_band__row_173", + "ird_taxable_income_people_by_band__row_174", + "ird_taxable_income_people_by_band__row_175", + "ird_taxable_income_people_by_band__row_176", + "ird_taxable_income_people_by_band__row_177", + "ird_taxable_income_people_by_band__row_178", + "ird_taxable_income_people_by_band__row_179", + "ird_taxable_income_people_by_band__row_180", + "ird_taxable_income_people_by_band__row_181", + "ird_taxable_income_people_by_band__row_182", + "ird_taxable_income_people_by_band__row_183", + "ird_taxable_income_people_by_band__row_184", + "ird_taxable_income_people_by_band__row_185", + "ird_taxable_income_people_by_band__row_186", + "ird_taxable_income_people_by_band__row_187", + "ird_taxable_income_people_by_band__row_188", + "ird_taxable_income_people_by_band__row_189", + "ird_taxable_income_people_by_band__row_190", + "ird_taxable_income_people_by_band__row_191", + "ird_taxable_income_people_by_band__row_192", + "ird_taxable_income_people_by_band__row_193", + "ird_taxable_income_people_by_band__row_194", + "ird_taxable_income_people_by_band__row_195", + "ird_taxable_income_people_by_band__row_196", + "ird_taxable_income_people_by_band__row_197", + "ird_taxable_income_people_by_band__row_198", + "ird_taxable_income_people_by_band__row_199", + "ird_taxable_income_people_by_band__row_200", + "ird_taxable_income_people_by_band__row_201", + "ird_taxable_income_people_by_band__row_202", + "ird_taxable_income_people_by_band__row_203", + "ird_taxable_income_people_by_band__row_204", + "ird_taxable_income_people_by_band__row_205", + "ird_taxable_income_people_by_band__row_206", + "ird_taxable_income_people_by_band__row_207", + "ird_taxable_income_people_by_band__row_208", + "ird_taxable_income_people_by_band__row_209", + "ird_taxable_income_people_by_band__row_210", + "ird_taxable_income_people_by_band__row_211", + "ird_taxable_income_people_by_band__row_212", + "ird_taxable_income_people_by_band__row_213", + "ird_taxable_income_people_by_band__row_214", + "ird_taxable_income_people_by_band__row_215", + "ird_taxable_income_people_by_band__row_216", + "ird_taxable_income_people_by_band__row_217", + "ird_taxable_income_people_by_band__row_218", + "ird_taxable_income_people_by_band__row_219", + "ird_taxable_income_people_by_band__row_220", + "ird_taxable_income_people_by_band__row_221", + "ird_taxable_income_people_by_band__row_222", + "ird_taxable_income_people_by_band__row_223", + "ird_taxable_income_people_by_band__row_224", + "ird_taxable_income_people_by_band__row_225", + "ird_taxable_income_people_by_band__row_226", + "ird_taxable_income_people_by_band__row_227", + "ird_taxable_income_people_by_band__row_228", + "ird_taxable_income_people_by_band__row_229", + "ird_taxable_income_people_by_band__row_230", + "ird_taxable_income_people_by_band__row_231", + "ird_taxable_income_people_by_band__row_232", + "ird_taxable_income_people_by_band__row_233", + "ird_taxable_income_people_by_band__row_234", + "ird_taxable_income_people_by_band__row_235", + "ird_taxable_income_people_by_band__row_236", + "ird_taxable_income_people_by_band__row_237", + "ird_taxable_income_people_by_band__row_238", + "ird_taxable_income_people_by_band__row_239", + "ird_taxable_income_people_by_band__row_240", + "ird_taxable_income_people_by_band__row_241", + "ird_taxable_income_people_by_band__row_242", + "ird_taxable_income_people_by_band__row_243", + "ird_taxable_income_people_by_band__row_244" + ], + "interval_table_ids": [ + "ird_taxable_income_national_ty2024" + ], + "required_measurement_binding": "ird_taxable_income_ty2027_v1" + } + ] } } } diff --git a/packages/microcosm-build/src/microcosm/build/nz/target_references.json b/packages/microcosm-build/src/microcosm/build/nz/target_references.json index bbe44aee98..8137a17bc3 100644 --- a/packages/microcosm-build/src/microcosm/build/nz/target_references.json +++ b/packages/microcosm-build/src/microcosm/build/nz/target_references.json @@ -5,7 +5,8 @@ "allowed_value_operations": [ "identity", "rolled_forward_by_ratio", - "allocated_residual_by_ratio" + "allocated_residual_by_ratio", + "linear_combination" ], "target_references": [ { @@ -25597,14 +25598,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25619,7 +25624,7 @@ "target_label": "TY2026-27 ird taxable income people by band nil (parked)" }, "filter": "taxable_income_band_row_8_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_9", @@ -25649,14 +25654,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25671,7 +25680,7 @@ "target_label": "TY2026-27 ird taxable income people by band $0.01 - $100 (parked)" }, "filter": "taxable_income_band_row_9_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_10", @@ -25701,14 +25710,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25723,7 +25736,7 @@ "target_label": "TY2026-27 ird taxable income people by band $101 - $1,000 (parked)" }, "filter": "taxable_income_band_row_10_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_11", @@ -25753,14 +25766,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25775,7 +25792,7 @@ "target_label": "TY2026-27 ird taxable income people by band $1,001 - $2,000 (parked)" }, "filter": "taxable_income_band_row_11_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_12", @@ -25805,14 +25822,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25827,7 +25848,7 @@ "target_label": "TY2026-27 ird taxable income people by band $2,001 - $3,000 (parked)" }, "filter": "taxable_income_band_row_12_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_13", @@ -25857,14 +25878,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25879,7 +25904,7 @@ "target_label": "TY2026-27 ird taxable income people by band $3,001 - $4,000 (parked)" }, "filter": "taxable_income_band_row_13_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_14", @@ -25909,14 +25934,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25931,7 +25960,7 @@ "target_label": "TY2026-27 ird taxable income people by band $4,001 - $5,000 (parked)" }, "filter": "taxable_income_band_row_14_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_15", @@ -25961,14 +25990,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -25983,7 +26016,7 @@ "target_label": "TY2026-27 ird taxable income people by band $5,001 - $6,000 (parked)" }, "filter": "taxable_income_band_row_15_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_16", @@ -26013,14 +26046,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26035,7 +26072,7 @@ "target_label": "TY2026-27 ird taxable income people by band $6,001 - $7,000 (parked)" }, "filter": "taxable_income_band_row_16_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_17", @@ -26065,14 +26102,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26087,7 +26128,7 @@ "target_label": "TY2026-27 ird taxable income people by band $7,001 - $8,000 (parked)" }, "filter": "taxable_income_band_row_17_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_18", @@ -26117,14 +26158,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26139,7 +26184,7 @@ "target_label": "TY2026-27 ird taxable income people by band $8,001 - $9,000 (parked)" }, "filter": "taxable_income_band_row_18_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_19", @@ -26169,14 +26214,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26191,7 +26240,7 @@ "target_label": "TY2026-27 ird taxable income people by band $9,001 - $10,000 (parked)" }, "filter": "taxable_income_band_row_19_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_20", @@ -26221,14 +26270,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26243,7 +26296,7 @@ "target_label": "TY2026-27 ird taxable income people by band $10,001 - $11,000 (parked)" }, "filter": "taxable_income_band_row_20_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_21", @@ -26273,14 +26326,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26295,7 +26352,7 @@ "target_label": "TY2026-27 ird taxable income people by band $11,001 - $12,000 (parked)" }, "filter": "taxable_income_band_row_21_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_22", @@ -26325,14 +26382,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26347,7 +26408,7 @@ "target_label": "TY2026-27 ird taxable income people by band $12,001 - $13,000 (parked)" }, "filter": "taxable_income_band_row_22_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_23", @@ -26377,14 +26438,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26399,7 +26464,7 @@ "target_label": "TY2026-27 ird taxable income people by band $13,001 - $14,000 (parked)" }, "filter": "taxable_income_band_row_23_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_24", @@ -26429,14 +26494,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26451,7 +26520,7 @@ "target_label": "TY2026-27 ird taxable income people by band $14,001 - $15,000 (parked)" }, "filter": "taxable_income_band_row_24_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_25", @@ -26481,14 +26550,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26503,7 +26576,7 @@ "target_label": "TY2026-27 ird taxable income people by band $15,001 - $16,000 (parked)" }, "filter": "taxable_income_band_row_25_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_26", @@ -26533,14 +26606,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26555,7 +26632,7 @@ "target_label": "TY2026-27 ird taxable income people by band $16,001 - $17,000 (parked)" }, "filter": "taxable_income_band_row_26_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_27", @@ -26585,14 +26662,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26607,7 +26688,7 @@ "target_label": "TY2026-27 ird taxable income people by band $17,001 - $18,000 (parked)" }, "filter": "taxable_income_band_row_27_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_28", @@ -26637,14 +26718,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26659,7 +26744,7 @@ "target_label": "TY2026-27 ird taxable income people by band $18,001 - $19,000 (parked)" }, "filter": "taxable_income_band_row_28_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_29", @@ -26689,14 +26774,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26711,7 +26800,7 @@ "target_label": "TY2026-27 ird taxable income people by band $19,001 - $20,000 (parked)" }, "filter": "taxable_income_band_row_29_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_30", @@ -26741,14 +26830,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26763,7 +26856,7 @@ "target_label": "TY2026-27 ird taxable income people by band $20,000 - $21,000 (parked)" }, "filter": "taxable_income_band_row_30_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_31", @@ -26793,14 +26886,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26815,7 +26912,7 @@ "target_label": "TY2026-27 ird taxable income people by band $21,001 - $22,000 (parked)" }, "filter": "taxable_income_band_row_31_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_32", @@ -26845,14 +26942,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26867,7 +26968,7 @@ "target_label": "TY2026-27 ird taxable income people by band $22,001 - $23,000 (parked)" }, "filter": "taxable_income_band_row_32_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_33", @@ -26897,14 +26998,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26919,7 +27024,7 @@ "target_label": "TY2026-27 ird taxable income people by band $23,001 - $24,000 (parked)" }, "filter": "taxable_income_band_row_33_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_34", @@ -26949,14 +27054,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -26971,7 +27080,7 @@ "target_label": "TY2026-27 ird taxable income people by band $24,001 - $25,000 (parked)" }, "filter": "taxable_income_band_row_34_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_35", @@ -27001,14 +27110,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27023,7 +27136,7 @@ "target_label": "TY2026-27 ird taxable income people by band $25,001 - $26,000 (parked)" }, "filter": "taxable_income_band_row_35_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_36", @@ -27053,14 +27166,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27075,7 +27192,7 @@ "target_label": "TY2026-27 ird taxable income people by band $26,001 - $27,000 (parked)" }, "filter": "taxable_income_band_row_36_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_37", @@ -27105,14 +27222,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27127,7 +27248,7 @@ "target_label": "TY2026-27 ird taxable income people by band $27,001 - $28,000 (parked)" }, "filter": "taxable_income_band_row_37_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_38", @@ -27157,14 +27278,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27179,7 +27304,7 @@ "target_label": "TY2026-27 ird taxable income people by band $28,001 - $29,000 (parked)" }, "filter": "taxable_income_band_row_38_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_39", @@ -27209,14 +27334,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27231,7 +27360,7 @@ "target_label": "TY2026-27 ird taxable income people by band $29,001 - $30,000 (parked)" }, "filter": "taxable_income_band_row_39_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_40", @@ -27261,14 +27390,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27283,7 +27416,7 @@ "target_label": "TY2026-27 ird taxable income people by band $30,001 - $31,000 (parked)" }, "filter": "taxable_income_band_row_40_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_41", @@ -27313,14 +27446,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27335,7 +27472,7 @@ "target_label": "TY2026-27 ird taxable income people by band $31,001 - $32,000 (parked)" }, "filter": "taxable_income_band_row_41_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_42", @@ -27365,14 +27502,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27387,7 +27528,7 @@ "target_label": "TY2026-27 ird taxable income people by band $32,001 - $33,000 (parked)" }, "filter": "taxable_income_band_row_42_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_43", @@ -27417,14 +27558,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27439,7 +27584,7 @@ "target_label": "TY2026-27 ird taxable income people by band $33,001 - $34,000 (parked)" }, "filter": "taxable_income_band_row_43_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_44", @@ -27469,14 +27614,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27491,7 +27640,7 @@ "target_label": "TY2026-27 ird taxable income people by band $34,001 - $35,000 (parked)" }, "filter": "taxable_income_band_row_44_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_45", @@ -27521,14 +27670,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27543,7 +27696,7 @@ "target_label": "TY2026-27 ird taxable income people by band $35,001 - $36,000 (parked)" }, "filter": "taxable_income_band_row_45_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_46", @@ -27573,14 +27726,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27595,7 +27752,7 @@ "target_label": "TY2026-27 ird taxable income people by band $36,001 - $37,000 (parked)" }, "filter": "taxable_income_band_row_46_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_47", @@ -27625,14 +27782,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27647,7 +27808,7 @@ "target_label": "TY2026-27 ird taxable income people by band $37,001 - $38,000 (parked)" }, "filter": "taxable_income_band_row_47_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_48", @@ -27677,14 +27838,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27699,7 +27864,7 @@ "target_label": "TY2026-27 ird taxable income people by band $38,001 - $39,000 (parked)" }, "filter": "taxable_income_band_row_48_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_49", @@ -27729,14 +27894,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27751,7 +27920,7 @@ "target_label": "TY2026-27 ird taxable income people by band $39,001 - $40,000 (parked)" }, "filter": "taxable_income_band_row_49_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_50", @@ -27781,14 +27950,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27803,7 +27976,7 @@ "target_label": "TY2026-27 ird taxable income people by band $40,001 - $41,000 (parked)" }, "filter": "taxable_income_band_row_50_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_51", @@ -27833,14 +28006,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27855,7 +28032,7 @@ "target_label": "TY2026-27 ird taxable income people by band $41,001 - $42,000 (parked)" }, "filter": "taxable_income_band_row_51_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_52", @@ -27885,14 +28062,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27907,7 +28088,7 @@ "target_label": "TY2026-27 ird taxable income people by band $42,001 - $43,000 (parked)" }, "filter": "taxable_income_band_row_52_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_53", @@ -27937,14 +28118,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -27959,7 +28144,7 @@ "target_label": "TY2026-27 ird taxable income people by band $43,001 - $44,000 (parked)" }, "filter": "taxable_income_band_row_53_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_54", @@ -27989,14 +28174,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28011,7 +28200,7 @@ "target_label": "TY2026-27 ird taxable income people by band $44,001 - $45,000 (parked)" }, "filter": "taxable_income_band_row_54_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_55", @@ -28041,14 +28230,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28063,7 +28256,7 @@ "target_label": "TY2026-27 ird taxable income people by band $45,001 - $46,000 (parked)" }, "filter": "taxable_income_band_row_55_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_56", @@ -28093,14 +28286,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28115,7 +28312,7 @@ "target_label": "TY2026-27 ird taxable income people by band $46,001 - $47,000 (parked)" }, "filter": "taxable_income_band_row_56_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_57", @@ -28145,14 +28342,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28167,7 +28368,7 @@ "target_label": "TY2026-27 ird taxable income people by band $47,001 - $48,000 (parked)" }, "filter": "taxable_income_band_row_57_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_58", @@ -28197,14 +28398,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28219,7 +28424,7 @@ "target_label": "TY2026-27 ird taxable income people by band $48,001 - $49,000 (parked)" }, "filter": "taxable_income_band_row_58_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_59", @@ -28249,14 +28454,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28271,7 +28480,7 @@ "target_label": "TY2026-27 ird taxable income people by band $49,001 - $50,000 (parked)" }, "filter": "taxable_income_band_row_59_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_60", @@ -28301,14 +28510,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28323,7 +28536,7 @@ "target_label": "TY2026-27 ird taxable income people by band $50,000 - $51,000 (parked)" }, "filter": "taxable_income_band_row_60_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_61", @@ -28353,14 +28566,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28375,7 +28592,7 @@ "target_label": "TY2026-27 ird taxable income people by band $51,001 - $52,000 (parked)" }, "filter": "taxable_income_band_row_61_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_62", @@ -28405,14 +28622,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28427,7 +28648,7 @@ "target_label": "TY2026-27 ird taxable income people by band $52,001 - $53,000 (parked)" }, "filter": "taxable_income_band_row_62_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_63", @@ -28457,14 +28678,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28479,7 +28704,7 @@ "target_label": "TY2026-27 ird taxable income people by band $53,001 - $54,000 (parked)" }, "filter": "taxable_income_band_row_63_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_64", @@ -28509,14 +28734,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28531,7 +28760,7 @@ "target_label": "TY2026-27 ird taxable income people by band $54,001 - $55,000 (parked)" }, "filter": "taxable_income_band_row_64_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_65", @@ -28561,14 +28790,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28583,7 +28816,7 @@ "target_label": "TY2026-27 ird taxable income people by band $55,001 - $56,000 (parked)" }, "filter": "taxable_income_band_row_65_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_66", @@ -28613,14 +28846,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28635,7 +28872,7 @@ "target_label": "TY2026-27 ird taxable income people by band $56,001 - $57,000 (parked)" }, "filter": "taxable_income_band_row_66_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_67", @@ -28665,14 +28902,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28687,7 +28928,7 @@ "target_label": "TY2026-27 ird taxable income people by band $57,001 - $58,000 (parked)" }, "filter": "taxable_income_band_row_67_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_68", @@ -28717,14 +28958,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28739,7 +28984,7 @@ "target_label": "TY2026-27 ird taxable income people by band $58,001 - $59,000 (parked)" }, "filter": "taxable_income_band_row_68_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_69", @@ -28769,14 +29014,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28791,7 +29040,7 @@ "target_label": "TY2026-27 ird taxable income people by band $59,001 - $60,000 (parked)" }, "filter": "taxable_income_band_row_69_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_70", @@ -28821,14 +29070,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28843,7 +29096,7 @@ "target_label": "TY2026-27 ird taxable income people by band $60,001 - $61,000 (parked)" }, "filter": "taxable_income_band_row_70_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_71", @@ -28873,14 +29126,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28895,7 +29152,7 @@ "target_label": "TY2026-27 ird taxable income people by band $61,001 - $62,000 (parked)" }, "filter": "taxable_income_band_row_71_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_72", @@ -28925,14 +29182,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28947,7 +29208,7 @@ "target_label": "TY2026-27 ird taxable income people by band $62,001 - $63,000 (parked)" }, "filter": "taxable_income_band_row_72_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_73", @@ -28977,14 +29238,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -28999,7 +29264,7 @@ "target_label": "TY2026-27 ird taxable income people by band $63,001 - $64,000 (parked)" }, "filter": "taxable_income_band_row_73_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_74", @@ -29029,14 +29294,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29051,7 +29320,7 @@ "target_label": "TY2026-27 ird taxable income people by band $64,001 - $65,000 (parked)" }, "filter": "taxable_income_band_row_74_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_75", @@ -29081,14 +29350,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29103,7 +29376,7 @@ "target_label": "TY2026-27 ird taxable income people by band $65,001 - $66,000 (parked)" }, "filter": "taxable_income_band_row_75_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_76", @@ -29133,14 +29406,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29155,7 +29432,7 @@ "target_label": "TY2026-27 ird taxable income people by band $66,001 - $67,000 (parked)" }, "filter": "taxable_income_band_row_76_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_77", @@ -29185,14 +29462,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29207,7 +29488,7 @@ "target_label": "TY2026-27 ird taxable income people by band $67,001 - $68,000 (parked)" }, "filter": "taxable_income_band_row_77_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_78", @@ -29237,14 +29518,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29259,7 +29544,7 @@ "target_label": "TY2026-27 ird taxable income people by band $68,001 - $69,000 (parked)" }, "filter": "taxable_income_band_row_78_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_79", @@ -29289,14 +29574,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29311,7 +29600,7 @@ "target_label": "TY2026-27 ird taxable income people by band $69,001 - $70,000 (parked)" }, "filter": "taxable_income_band_row_79_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_80", @@ -29341,14 +29630,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29363,7 +29656,7 @@ "target_label": "TY2026-27 ird taxable income people by band $70,001 - $71,000 (parked)" }, "filter": "taxable_income_band_row_80_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_81", @@ -29393,14 +29686,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29415,7 +29712,7 @@ "target_label": "TY2026-27 ird taxable income people by band $71,001 - $72,000 (parked)" }, "filter": "taxable_income_band_row_81_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_82", @@ -29445,14 +29742,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29467,7 +29768,7 @@ "target_label": "TY2026-27 ird taxable income people by band $72,001 - $73,000 (parked)" }, "filter": "taxable_income_band_row_82_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_83", @@ -29497,14 +29798,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29519,7 +29824,7 @@ "target_label": "TY2026-27 ird taxable income people by band $73,001 - $74,000 (parked)" }, "filter": "taxable_income_band_row_83_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_84", @@ -29549,14 +29854,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29571,7 +29880,7 @@ "target_label": "TY2026-27 ird taxable income people by band $74,001 - $75,000 (parked)" }, "filter": "taxable_income_band_row_84_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_85", @@ -29601,14 +29910,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29623,7 +29936,7 @@ "target_label": "TY2026-27 ird taxable income people by band $75,001 - $76,000 (parked)" }, "filter": "taxable_income_band_row_85_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_86", @@ -29653,14 +29966,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29675,7 +29992,7 @@ "target_label": "TY2026-27 ird taxable income people by band $76,001 - $77,000 (parked)" }, "filter": "taxable_income_band_row_86_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_87", @@ -29705,14 +30022,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29727,7 +30048,7 @@ "target_label": "TY2026-27 ird taxable income people by band $77,001 - $78,000 (parked)" }, "filter": "taxable_income_band_row_87_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_88", @@ -29757,14 +30078,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29779,7 +30104,7 @@ "target_label": "TY2026-27 ird taxable income people by band $78,001 - $79,000 (parked)" }, "filter": "taxable_income_band_row_88_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_89", @@ -29809,14 +30134,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29831,7 +30160,7 @@ "target_label": "TY2026-27 ird taxable income people by band $79,001 - $80,000 (parked)" }, "filter": "taxable_income_band_row_89_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_90", @@ -29861,14 +30190,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29883,7 +30216,7 @@ "target_label": "TY2026-27 ird taxable income people by band $80,000 - $81,000 (parked)" }, "filter": "taxable_income_band_row_90_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_91", @@ -29913,14 +30246,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29935,7 +30272,7 @@ "target_label": "TY2026-27 ird taxable income people by band $81,001 - $82,000 (parked)" }, "filter": "taxable_income_band_row_91_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_92", @@ -29965,14 +30302,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -29987,7 +30328,7 @@ "target_label": "TY2026-27 ird taxable income people by band $82,001 - $83,000 (parked)" }, "filter": "taxable_income_band_row_92_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_93", @@ -30017,14 +30358,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30039,7 +30384,7 @@ "target_label": "TY2026-27 ird taxable income people by band $83,001 - $84,000 (parked)" }, "filter": "taxable_income_band_row_93_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_94", @@ -30069,14 +30414,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30091,7 +30440,7 @@ "target_label": "TY2026-27 ird taxable income people by band $84,001 - $85,000 (parked)" }, "filter": "taxable_income_band_row_94_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_95", @@ -30121,14 +30470,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30143,7 +30496,7 @@ "target_label": "TY2026-27 ird taxable income people by band $85,001 - $86,000 (parked)" }, "filter": "taxable_income_band_row_95_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_96", @@ -30173,14 +30526,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30195,7 +30552,7 @@ "target_label": "TY2026-27 ird taxable income people by band $86,001 - $87,000 (parked)" }, "filter": "taxable_income_band_row_96_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_97", @@ -30225,14 +30582,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30247,7 +30608,7 @@ "target_label": "TY2026-27 ird taxable income people by band $87,001 - $88,000 (parked)" }, "filter": "taxable_income_band_row_97_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_98", @@ -30277,14 +30638,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30299,7 +30664,7 @@ "target_label": "TY2026-27 ird taxable income people by band $88,001 - $89,000 (parked)" }, "filter": "taxable_income_band_row_98_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_99", @@ -30329,14 +30694,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30351,7 +30720,7 @@ "target_label": "TY2026-27 ird taxable income people by band $89,001 - $90,000 (parked)" }, "filter": "taxable_income_band_row_99_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_100", @@ -30381,14 +30750,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30403,7 +30776,7 @@ "target_label": "TY2026-27 ird taxable income people by band $90,001 - $91,000 (parked)" }, "filter": "taxable_income_band_row_100_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_101", @@ -30433,14 +30806,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30455,7 +30832,7 @@ "target_label": "TY2026-27 ird taxable income people by band $91,001 - $92,000 (parked)" }, "filter": "taxable_income_band_row_101_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_102", @@ -30485,14 +30862,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30507,7 +30888,7 @@ "target_label": "TY2026-27 ird taxable income people by band $92,001 - $93,000 (parked)" }, "filter": "taxable_income_band_row_102_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_103", @@ -30537,14 +30918,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30559,7 +30944,7 @@ "target_label": "TY2026-27 ird taxable income people by band $93,001 - $94,000 (parked)" }, "filter": "taxable_income_band_row_103_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_104", @@ -30589,14 +30974,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30611,7 +31000,7 @@ "target_label": "TY2026-27 ird taxable income people by band $94,001 - $95,000 (parked)" }, "filter": "taxable_income_band_row_104_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_105", @@ -30641,14 +31030,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30663,7 +31056,7 @@ "target_label": "TY2026-27 ird taxable income people by band $95,001 - $96,000 (parked)" }, "filter": "taxable_income_band_row_105_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_106", @@ -30693,14 +31086,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30715,7 +31112,7 @@ "target_label": "TY2026-27 ird taxable income people by band $96,001 - $97,000 (parked)" }, "filter": "taxable_income_band_row_106_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_107", @@ -30745,14 +31142,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30767,7 +31168,7 @@ "target_label": "TY2026-27 ird taxable income people by band $97,001 - $98,000 (parked)" }, "filter": "taxable_income_band_row_107_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_108", @@ -30797,14 +31198,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30819,7 +31224,7 @@ "target_label": "TY2026-27 ird taxable income people by band $98,001 - $99,000 (parked)" }, "filter": "taxable_income_band_row_108_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_109", @@ -30849,14 +31254,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30871,7 +31280,7 @@ "target_label": "TY2026-27 ird taxable income people by band $99,001 - $100,000 (parked)" }, "filter": "taxable_income_band_row_109_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_110", @@ -30901,14 +31310,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30923,7 +31336,7 @@ "target_label": "TY2026-27 ird taxable income people by band $100,001 - $101,000 (parked)" }, "filter": "taxable_income_band_row_110_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_111", @@ -30953,14 +31366,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -30975,7 +31392,7 @@ "target_label": "TY2026-27 ird taxable income people by band $101,001 - $102,000 (parked)" }, "filter": "taxable_income_band_row_111_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_112", @@ -31005,14 +31422,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31027,7 +31448,7 @@ "target_label": "TY2026-27 ird taxable income people by band $102,001 - $103,000 (parked)" }, "filter": "taxable_income_band_row_112_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_113", @@ -31057,14 +31478,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31079,7 +31504,7 @@ "target_label": "TY2026-27 ird taxable income people by band $103,001 - $104,000 (parked)" }, "filter": "taxable_income_band_row_113_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_114", @@ -31109,14 +31534,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31131,7 +31560,7 @@ "target_label": "TY2026-27 ird taxable income people by band $104,001 - $105,000 (parked)" }, "filter": "taxable_income_band_row_114_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_115", @@ -31161,14 +31590,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31183,7 +31616,7 @@ "target_label": "TY2026-27 ird taxable income people by band $105,001 - $106,000 (parked)" }, "filter": "taxable_income_band_row_115_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_116", @@ -31213,14 +31646,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31235,7 +31672,7 @@ "target_label": "TY2026-27 ird taxable income people by band $106,001 - $107,000 (parked)" }, "filter": "taxable_income_band_row_116_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_117", @@ -31265,14 +31702,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31287,7 +31728,7 @@ "target_label": "TY2026-27 ird taxable income people by band $107,001 - $108,000 (parked)" }, "filter": "taxable_income_band_row_117_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_118", @@ -31317,14 +31758,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31339,7 +31784,7 @@ "target_label": "TY2026-27 ird taxable income people by band $108,001 - $109,000 (parked)" }, "filter": "taxable_income_band_row_118_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_119", @@ -31369,14 +31814,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31391,7 +31840,7 @@ "target_label": "TY2026-27 ird taxable income people by band $109,001 - $110,000 (parked)" }, "filter": "taxable_income_band_row_119_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_120", @@ -31421,14 +31870,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31443,7 +31896,7 @@ "target_label": "TY2026-27 ird taxable income people by band $110,001 - $111,000 (parked)" }, "filter": "taxable_income_band_row_120_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_121", @@ -31473,14 +31926,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31495,7 +31952,7 @@ "target_label": "TY2026-27 ird taxable income people by band $111,001 - $112,000 (parked)" }, "filter": "taxable_income_band_row_121_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_122", @@ -31525,14 +31982,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31547,7 +32008,7 @@ "target_label": "TY2026-27 ird taxable income people by band $112,001 - $113,000 (parked)" }, "filter": "taxable_income_band_row_122_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_123", @@ -31577,14 +32038,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31599,7 +32064,7 @@ "target_label": "TY2026-27 ird taxable income people by band $113,001 - $114,000 (parked)" }, "filter": "taxable_income_band_row_123_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_124", @@ -31629,14 +32094,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31651,7 +32120,7 @@ "target_label": "TY2026-27 ird taxable income people by band $114,001 - $115,000 (parked)" }, "filter": "taxable_income_band_row_124_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_125", @@ -31681,14 +32150,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31703,7 +32176,7 @@ "target_label": "TY2026-27 ird taxable income people by band $115,001 - $116,000 (parked)" }, "filter": "taxable_income_band_row_125_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_126", @@ -31733,14 +32206,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31755,7 +32232,7 @@ "target_label": "TY2026-27 ird taxable income people by band $116,001 - $117,000 (parked)" }, "filter": "taxable_income_band_row_126_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_127", @@ -31785,14 +32262,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31807,7 +32288,7 @@ "target_label": "TY2026-27 ird taxable income people by band $117,001 - $118,000 (parked)" }, "filter": "taxable_income_band_row_127_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_128", @@ -31837,14 +32318,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31859,7 +32344,7 @@ "target_label": "TY2026-27 ird taxable income people by band $118,001 - $119,000 (parked)" }, "filter": "taxable_income_band_row_128_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_129", @@ -31889,14 +32374,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31911,7 +32400,7 @@ "target_label": "TY2026-27 ird taxable income people by band $119,001 - $120,000 (parked)" }, "filter": "taxable_income_band_row_129_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_130", @@ -31941,14 +32430,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -31963,7 +32456,7 @@ "target_label": "TY2026-27 ird taxable income people by band $120,001 - $121,000 (parked)" }, "filter": "taxable_income_band_row_130_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_131", @@ -31993,14 +32486,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32015,7 +32512,7 @@ "target_label": "TY2026-27 ird taxable income people by band $121,001 - $122,000 (parked)" }, "filter": "taxable_income_band_row_131_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_132", @@ -32045,14 +32542,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32067,7 +32568,7 @@ "target_label": "TY2026-27 ird taxable income people by band $122,001 - $123,000 (parked)" }, "filter": "taxable_income_band_row_132_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_133", @@ -32097,14 +32598,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32119,7 +32624,7 @@ "target_label": "TY2026-27 ird taxable income people by band $123,001 - $124,000 (parked)" }, "filter": "taxable_income_band_row_133_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_134", @@ -32149,14 +32654,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32171,7 +32680,7 @@ "target_label": "TY2026-27 ird taxable income people by band $124,001 - $125,000 (parked)" }, "filter": "taxable_income_band_row_134_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_135", @@ -32201,14 +32710,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32223,7 +32736,7 @@ "target_label": "TY2026-27 ird taxable income people by band $125,001 - $126,000 (parked)" }, "filter": "taxable_income_band_row_135_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_136", @@ -32253,14 +32766,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32275,7 +32792,7 @@ "target_label": "TY2026-27 ird taxable income people by band $126,001 - $127,000 (parked)" }, "filter": "taxable_income_band_row_136_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_137", @@ -32305,14 +32822,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32327,7 +32848,7 @@ "target_label": "TY2026-27 ird taxable income people by band $127,001 - $128,000 (parked)" }, "filter": "taxable_income_band_row_137_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_138", @@ -32357,14 +32878,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32379,7 +32904,7 @@ "target_label": "TY2026-27 ird taxable income people by band $128,001 - $129,000 (parked)" }, "filter": "taxable_income_band_row_138_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_139", @@ -32409,14 +32934,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32431,7 +32960,7 @@ "target_label": "TY2026-27 ird taxable income people by band $129,001 - $130,000 (parked)" }, "filter": "taxable_income_band_row_139_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_140", @@ -32461,14 +32990,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32483,7 +33016,7 @@ "target_label": "TY2026-27 ird taxable income people by band $130,001 - $131,000 (parked)" }, "filter": "taxable_income_band_row_140_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_141", @@ -32513,14 +33046,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32535,7 +33072,7 @@ "target_label": "TY2026-27 ird taxable income people by band $131,001 - $132,000 (parked)" }, "filter": "taxable_income_band_row_141_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_142", @@ -32565,14 +33102,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32587,7 +33128,7 @@ "target_label": "TY2026-27 ird taxable income people by band $132,001 - $133,000 (parked)" }, "filter": "taxable_income_band_row_142_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_143", @@ -32617,14 +33158,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32639,7 +33184,7 @@ "target_label": "TY2026-27 ird taxable income people by band $133,001 - $134,000 (parked)" }, "filter": "taxable_income_band_row_143_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_144", @@ -32669,14 +33214,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32691,7 +33240,7 @@ "target_label": "TY2026-27 ird taxable income people by band $134,001 - $135,000 (parked)" }, "filter": "taxable_income_band_row_144_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_145", @@ -32721,14 +33270,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32743,7 +33296,7 @@ "target_label": "TY2026-27 ird taxable income people by band $135,001 - $136,000 (parked)" }, "filter": "taxable_income_band_row_145_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_146", @@ -32773,14 +33326,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32795,7 +33352,7 @@ "target_label": "TY2026-27 ird taxable income people by band $136,001 - $137,000 (parked)" }, "filter": "taxable_income_band_row_146_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_147", @@ -32825,14 +33382,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32847,7 +33408,7 @@ "target_label": "TY2026-27 ird taxable income people by band $137,001 - $138,000 (parked)" }, "filter": "taxable_income_band_row_147_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_148", @@ -32877,14 +33438,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32899,7 +33464,7 @@ "target_label": "TY2026-27 ird taxable income people by band $138,001 - $139,000 (parked)" }, "filter": "taxable_income_band_row_148_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_149", @@ -32929,14 +33494,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -32951,7 +33520,7 @@ "target_label": "TY2026-27 ird taxable income people by band $139,001 - $140,000 (parked)" }, "filter": "taxable_income_band_row_149_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_150", @@ -32981,14 +33550,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33003,7 +33576,7 @@ "target_label": "TY2026-27 ird taxable income people by band $140,001 - $141,000 (parked)" }, "filter": "taxable_income_band_row_150_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_151", @@ -33033,14 +33606,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33055,7 +33632,7 @@ "target_label": "TY2026-27 ird taxable income people by band $141,001 - $142,000 (parked)" }, "filter": "taxable_income_band_row_151_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_152", @@ -33085,14 +33662,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33107,7 +33688,7 @@ "target_label": "TY2026-27 ird taxable income people by band $142,001 - $143,000 (parked)" }, "filter": "taxable_income_band_row_152_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_153", @@ -33137,14 +33718,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33159,7 +33744,7 @@ "target_label": "TY2026-27 ird taxable income people by band $143,001 - $144,000 (parked)" }, "filter": "taxable_income_band_row_153_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_154", @@ -33189,14 +33774,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33211,7 +33800,7 @@ "target_label": "TY2026-27 ird taxable income people by band $144,001 - $145,000 (parked)" }, "filter": "taxable_income_band_row_154_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_155", @@ -33241,14 +33830,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33263,7 +33856,7 @@ "target_label": "TY2026-27 ird taxable income people by band $145,001 - $146,000 (parked)" }, "filter": "taxable_income_band_row_155_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_156", @@ -33293,14 +33886,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33315,7 +33912,7 @@ "target_label": "TY2026-27 ird taxable income people by band $146,001 - $147,000 (parked)" }, "filter": "taxable_income_band_row_156_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_157", @@ -33345,14 +33942,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33367,7 +33968,7 @@ "target_label": "TY2026-27 ird taxable income people by band $147,001 - $148,000 (parked)" }, "filter": "taxable_income_band_row_157_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_158", @@ -33397,14 +33998,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33419,7 +34024,7 @@ "target_label": "TY2026-27 ird taxable income people by band $148,001 - $149,000 (parked)" }, "filter": "taxable_income_band_row_158_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_159", @@ -33449,14 +34054,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33471,7 +34080,7 @@ "target_label": "TY2026-27 ird taxable income people by band $149,001 - $150,000 (parked)" }, "filter": "taxable_income_band_row_159_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_160", @@ -33501,14 +34110,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33523,7 +34136,7 @@ "target_label": "TY2026-27 ird taxable income people by band $150,001 - $151,000 (parked)" }, "filter": "taxable_income_band_row_160_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_161", @@ -33553,14 +34166,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33575,7 +34192,7 @@ "target_label": "TY2026-27 ird taxable income people by band $151,001 - $152,000 (parked)" }, "filter": "taxable_income_band_row_161_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_162", @@ -33605,14 +34222,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33627,7 +34248,7 @@ "target_label": "TY2026-27 ird taxable income people by band $152,001 - $153,000 (parked)" }, "filter": "taxable_income_band_row_162_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_163", @@ -33657,14 +34278,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33679,7 +34304,7 @@ "target_label": "TY2026-27 ird taxable income people by band $153,001 - $154,000 (parked)" }, "filter": "taxable_income_band_row_163_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_164", @@ -33709,14 +34334,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33731,7 +34360,7 @@ "target_label": "TY2026-27 ird taxable income people by band $154,001 - $155,000 (parked)" }, "filter": "taxable_income_band_row_164_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_165", @@ -33761,14 +34390,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33783,7 +34416,7 @@ "target_label": "TY2026-27 ird taxable income people by band $155,001 - $156,000 (parked)" }, "filter": "taxable_income_band_row_165_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_166", @@ -33813,14 +34446,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33835,7 +34472,7 @@ "target_label": "TY2026-27 ird taxable income people by band $156,001 - $157,000 (parked)" }, "filter": "taxable_income_band_row_166_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_167", @@ -33865,14 +34502,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33887,7 +34528,7 @@ "target_label": "TY2026-27 ird taxable income people by band $157,001 - $158,000 (parked)" }, "filter": "taxable_income_band_row_167_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_168", @@ -33917,14 +34558,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33939,7 +34584,7 @@ "target_label": "TY2026-27 ird taxable income people by band $158,001 - $159,000 (parked)" }, "filter": "taxable_income_band_row_168_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_169", @@ -33969,14 +34614,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -33991,7 +34640,7 @@ "target_label": "TY2026-27 ird taxable income people by band $159,001 - $160,000 (parked)" }, "filter": "taxable_income_band_row_169_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_170", @@ -34021,14 +34670,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34043,7 +34696,7 @@ "target_label": "TY2026-27 ird taxable income people by band $160,001 - $161,000 (parked)" }, "filter": "taxable_income_band_row_170_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_171", @@ -34073,14 +34726,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34095,7 +34752,7 @@ "target_label": "TY2026-27 ird taxable income people by band $161,001 - $162,000 (parked)" }, "filter": "taxable_income_band_row_171_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_172", @@ -34125,14 +34782,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34147,7 +34808,7 @@ "target_label": "TY2026-27 ird taxable income people by band $162,001 - $163,000 (parked)" }, "filter": "taxable_income_band_row_172_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_173", @@ -34177,14 +34838,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34199,7 +34864,7 @@ "target_label": "TY2026-27 ird taxable income people by band $163,001 - $164,000 (parked)" }, "filter": "taxable_income_band_row_173_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_174", @@ -34229,14 +34894,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34251,7 +34920,7 @@ "target_label": "TY2026-27 ird taxable income people by band $164,001 - $165,000 (parked)" }, "filter": "taxable_income_band_row_174_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_175", @@ -34281,14 +34950,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34303,7 +34976,7 @@ "target_label": "TY2026-27 ird taxable income people by band $165,001 - $166,000 (parked)" }, "filter": "taxable_income_band_row_175_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_176", @@ -34333,14 +35006,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34355,7 +35032,7 @@ "target_label": "TY2026-27 ird taxable income people by band $166,001 - $167,000 (parked)" }, "filter": "taxable_income_band_row_176_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_177", @@ -34385,14 +35062,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34407,7 +35088,7 @@ "target_label": "TY2026-27 ird taxable income people by band $167,001 - $168,000 (parked)" }, "filter": "taxable_income_band_row_177_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_178", @@ -34437,14 +35118,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34459,7 +35144,7 @@ "target_label": "TY2026-27 ird taxable income people by band $168,001 - $169,000 (parked)" }, "filter": "taxable_income_band_row_178_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_179", @@ -34489,14 +35174,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34511,7 +35200,7 @@ "target_label": "TY2026-27 ird taxable income people by band $169,001 - $170,000 (parked)" }, "filter": "taxable_income_band_row_179_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_180", @@ -34541,14 +35230,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34563,7 +35256,7 @@ "target_label": "TY2026-27 ird taxable income people by band $170,001 - $171,000 (parked)" }, "filter": "taxable_income_band_row_180_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_181", @@ -34593,14 +35286,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34615,7 +35312,7 @@ "target_label": "TY2026-27 ird taxable income people by band $171,001 - $172,000 (parked)" }, "filter": "taxable_income_band_row_181_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_182", @@ -34645,14 +35342,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34667,7 +35368,7 @@ "target_label": "TY2026-27 ird taxable income people by band $172,001 - $173,000 (parked)" }, "filter": "taxable_income_band_row_182_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_183", @@ -34697,14 +35398,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34719,7 +35424,7 @@ "target_label": "TY2026-27 ird taxable income people by band $173,001 - $174,000 (parked)" }, "filter": "taxable_income_band_row_183_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_184", @@ -34749,14 +35454,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34771,7 +35480,7 @@ "target_label": "TY2026-27 ird taxable income people by band $174,001 - $175,000 (parked)" }, "filter": "taxable_income_band_row_184_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_185", @@ -34801,14 +35510,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34823,7 +35536,7 @@ "target_label": "TY2026-27 ird taxable income people by band $175,001 - $176,000 (parked)" }, "filter": "taxable_income_band_row_185_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_186", @@ -34853,14 +35566,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34875,7 +35592,7 @@ "target_label": "TY2026-27 ird taxable income people by band $176,001 - $177,000 (parked)" }, "filter": "taxable_income_band_row_186_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_187", @@ -34905,14 +35622,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34927,7 +35648,7 @@ "target_label": "TY2026-27 ird taxable income people by band $177,001 - $178,000 (parked)" }, "filter": "taxable_income_band_row_187_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_188", @@ -34957,14 +35678,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -34979,7 +35704,7 @@ "target_label": "TY2026-27 ird taxable income people by band $178,001 - $179,000 (parked)" }, "filter": "taxable_income_band_row_188_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_189", @@ -35009,14 +35734,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35031,7 +35760,7 @@ "target_label": "TY2026-27 ird taxable income people by band $179,001 - $180,000 (parked)" }, "filter": "taxable_income_band_row_189_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_190", @@ -35061,14 +35790,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35083,7 +35816,7 @@ "target_label": "TY2026-27 ird taxable income people by band $180,001 - $181,000 (parked)" }, "filter": "taxable_income_band_row_190_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_191", @@ -35113,14 +35846,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35135,7 +35872,7 @@ "target_label": "TY2026-27 ird taxable income people by band $181,001 - $182,000 (parked)" }, "filter": "taxable_income_band_row_191_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_192", @@ -35165,14 +35902,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35187,7 +35928,7 @@ "target_label": "TY2026-27 ird taxable income people by band $182,001 - $183,000 (parked)" }, "filter": "taxable_income_band_row_192_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_193", @@ -35217,14 +35958,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35239,7 +35984,7 @@ "target_label": "TY2026-27 ird taxable income people by band $183,001 - $184,000 (parked)" }, "filter": "taxable_income_band_row_193_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_194", @@ -35269,14 +36014,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35291,7 +36040,7 @@ "target_label": "TY2026-27 ird taxable income people by band $184,001 - $185,000 (parked)" }, "filter": "taxable_income_band_row_194_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_195", @@ -35321,14 +36070,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35343,7 +36096,7 @@ "target_label": "TY2026-27 ird taxable income people by band $185,001 - $186,000 (parked)" }, "filter": "taxable_income_band_row_195_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_196", @@ -35373,14 +36126,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35395,7 +36152,7 @@ "target_label": "TY2026-27 ird taxable income people by band $186,001 - $187,000 (parked)" }, "filter": "taxable_income_band_row_196_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_197", @@ -35425,14 +36182,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35447,7 +36208,7 @@ "target_label": "TY2026-27 ird taxable income people by band $187,001 - $188,000 (parked)" }, "filter": "taxable_income_band_row_197_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_198", @@ -35477,14 +36238,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35499,7 +36264,7 @@ "target_label": "TY2026-27 ird taxable income people by band $188,001 - $189,000 (parked)" }, "filter": "taxable_income_band_row_198_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_199", @@ -35529,14 +36294,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35551,7 +36320,7 @@ "target_label": "TY2026-27 ird taxable income people by band $189,001 - $190,000 (parked)" }, "filter": "taxable_income_band_row_199_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_200", @@ -35581,14 +36350,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35603,7 +36376,7 @@ "target_label": "TY2026-27 ird taxable income people by band $190,001 - $191,000 (parked)" }, "filter": "taxable_income_band_row_200_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_201", @@ -35633,14 +36406,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35655,7 +36432,7 @@ "target_label": "TY2026-27 ird taxable income people by band $191,001 - $192,000 (parked)" }, "filter": "taxable_income_band_row_201_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_202", @@ -35685,14 +36462,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35707,7 +36488,7 @@ "target_label": "TY2026-27 ird taxable income people by band $192,001 - $193,000 (parked)" }, "filter": "taxable_income_band_row_202_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_203", @@ -35737,14 +36518,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35759,7 +36544,7 @@ "target_label": "TY2026-27 ird taxable income people by band $193,001 - $194,000 (parked)" }, "filter": "taxable_income_band_row_203_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_204", @@ -35789,14 +36574,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35811,7 +36600,7 @@ "target_label": "TY2026-27 ird taxable income people by band $194,001 - $195,000 (parked)" }, "filter": "taxable_income_band_row_204_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_205", @@ -35841,14 +36630,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35863,7 +36656,7 @@ "target_label": "TY2026-27 ird taxable income people by band $195,001 - $196,000 (parked)" }, "filter": "taxable_income_band_row_205_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_206", @@ -35893,14 +36686,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35915,7 +36712,7 @@ "target_label": "TY2026-27 ird taxable income people by band $196,001 - $197,000 (parked)" }, "filter": "taxable_income_band_row_206_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_207", @@ -35945,14 +36742,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -35967,7 +36768,7 @@ "target_label": "TY2026-27 ird taxable income people by band $197,001 - $198,000 (parked)" }, "filter": "taxable_income_band_row_207_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_208", @@ -35997,14 +36798,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36019,7 +36824,7 @@ "target_label": "TY2026-27 ird taxable income people by band $198,001 - $199,000 (parked)" }, "filter": "taxable_income_band_row_208_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_209", @@ -36049,14 +36854,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36071,7 +36880,7 @@ "target_label": "TY2026-27 ird taxable income people by band $199,001 - $200,000 (parked)" }, "filter": "taxable_income_band_row_209_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_210", @@ -36101,14 +36910,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36123,7 +36936,7 @@ "target_label": "TY2026-27 ird taxable income people by band $200,001 - $205,000 (parked)" }, "filter": "taxable_income_band_row_210_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_211", @@ -36153,14 +36966,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36175,7 +36992,7 @@ "target_label": "TY2026-27 ird taxable income people by band $205,001 - $210,000 (parked)" }, "filter": "taxable_income_band_row_211_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_212", @@ -36205,14 +37022,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36227,7 +37048,7 @@ "target_label": "TY2026-27 ird taxable income people by band $210,001 - $215,000 (parked)" }, "filter": "taxable_income_band_row_212_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_213", @@ -36257,14 +37078,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36279,7 +37104,7 @@ "target_label": "TY2026-27 ird taxable income people by band $215,001 - $220,000 (parked)" }, "filter": "taxable_income_band_row_213_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_214", @@ -36309,14 +37134,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36331,7 +37160,7 @@ "target_label": "TY2026-27 ird taxable income people by band $220,001 - $225,000 (parked)" }, "filter": "taxable_income_band_row_214_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_215", @@ -36361,14 +37190,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36383,7 +37216,7 @@ "target_label": "TY2026-27 ird taxable income people by band $225,001 - $230,000 (parked)" }, "filter": "taxable_income_band_row_215_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_216", @@ -36413,14 +37246,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36435,7 +37272,7 @@ "target_label": "TY2026-27 ird taxable income people by band $230,001 - $235,000 (parked)" }, "filter": "taxable_income_band_row_216_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_217", @@ -36465,14 +37302,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36487,7 +37328,7 @@ "target_label": "TY2026-27 ird taxable income people by band $235,001 - $240,000 (parked)" }, "filter": "taxable_income_band_row_217_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_218", @@ -36517,14 +37358,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36539,7 +37384,7 @@ "target_label": "TY2026-27 ird taxable income people by band $240,001 - $245,000 (parked)" }, "filter": "taxable_income_band_row_218_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_219", @@ -36569,14 +37414,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36591,7 +37440,7 @@ "target_label": "TY2026-27 ird taxable income people by band $245,001 - $250,000 (parked)" }, "filter": "taxable_income_band_row_219_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_220", @@ -36621,14 +37470,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36643,7 +37496,7 @@ "target_label": "TY2026-27 ird taxable income people by band $250,001 - $255,000 (parked)" }, "filter": "taxable_income_band_row_220_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_221", @@ -36673,14 +37526,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36695,7 +37552,7 @@ "target_label": "TY2026-27 ird taxable income people by band $255,001 - $260,000 (parked)" }, "filter": "taxable_income_band_row_221_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_222", @@ -36725,14 +37582,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36747,7 +37608,7 @@ "target_label": "TY2026-27 ird taxable income people by band $260,001 - $265,000 (parked)" }, "filter": "taxable_income_band_row_222_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_223", @@ -36777,14 +37638,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36799,7 +37664,7 @@ "target_label": "TY2026-27 ird taxable income people by band $265,001 - $270,000 (parked)" }, "filter": "taxable_income_band_row_223_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_224", @@ -36829,14 +37694,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36851,7 +37720,7 @@ "target_label": "TY2026-27 ird taxable income people by band $270,001 - $275,000 (parked)" }, "filter": "taxable_income_band_row_224_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_225", @@ -36881,14 +37750,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36903,7 +37776,7 @@ "target_label": "TY2026-27 ird taxable income people by band $275,001 - $280,000 (parked)" }, "filter": "taxable_income_band_row_225_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_226", @@ -36933,14 +37806,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -36955,7 +37832,7 @@ "target_label": "TY2026-27 ird taxable income people by band $280,001 - $285,000 (parked)" }, "filter": "taxable_income_band_row_226_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_227", @@ -36985,14 +37862,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37007,7 +37888,7 @@ "target_label": "TY2026-27 ird taxable income people by band $285,001 - $290,000 (parked)" }, "filter": "taxable_income_band_row_227_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_228", @@ -37037,14 +37918,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37059,7 +37944,7 @@ "target_label": "TY2026-27 ird taxable income people by band $290,001 - $295,000 (parked)" }, "filter": "taxable_income_band_row_228_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_229", @@ -37089,14 +37974,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37111,7 +38000,7 @@ "target_label": "TY2026-27 ird taxable income people by band $295,001 - $300,000 (parked)" }, "filter": "taxable_income_band_row_229_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_230", @@ -37141,14 +38030,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37163,7 +38056,7 @@ "target_label": "TY2026-27 ird taxable income people by band $300,000 - $350,000 (parked)" }, "filter": "taxable_income_band_row_230_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_231", @@ -37193,14 +38086,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37215,7 +38112,7 @@ "target_label": "TY2026-27 ird taxable income people by band $350,001 - $400,000 (parked)" }, "filter": "taxable_income_band_row_231_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_232", @@ -37245,14 +38142,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37267,7 +38168,7 @@ "target_label": "TY2026-27 ird taxable income people by band $400,001 - $450,000 (parked)" }, "filter": "taxable_income_band_row_232_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_233", @@ -37297,14 +38198,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37319,7 +38224,7 @@ "target_label": "TY2026-27 ird taxable income people by band $450,001 - $500,000 (parked)" }, "filter": "taxable_income_band_row_233_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_234", @@ -37349,14 +38254,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37371,7 +38280,7 @@ "target_label": "TY2026-27 ird taxable income people by band $500,001 - $550,000 (parked)" }, "filter": "taxable_income_band_row_234_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_235", @@ -37401,14 +38310,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37423,7 +38336,7 @@ "target_label": "TY2026-27 ird taxable income people by band $550,001 - $600,000 (parked)" }, "filter": "taxable_income_band_row_235_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_236", @@ -37453,14 +38366,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37475,7 +38392,7 @@ "target_label": "TY2026-27 ird taxable income people by band $600,001 - $650,000 (parked)" }, "filter": "taxable_income_band_row_236_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_237", @@ -37505,14 +38422,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37527,7 +38448,7 @@ "target_label": "TY2026-27 ird taxable income people by band $650,001 - $700,000 (parked)" }, "filter": "taxable_income_band_row_237_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_238", @@ -37557,14 +38478,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37579,7 +38504,7 @@ "target_label": "TY2026-27 ird taxable income people by band $700,001 - $750,000 (parked)" }, "filter": "taxable_income_band_row_238_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_239", @@ -37609,14 +38534,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37631,7 +38560,7 @@ "target_label": "TY2026-27 ird taxable income people by band $750,001 - $800,000 (parked)" }, "filter": "taxable_income_band_row_239_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_240", @@ -37661,14 +38590,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37683,7 +38616,7 @@ "target_label": "TY2026-27 ird taxable income people by band $800,001 - $850,000 (parked)" }, "filter": "taxable_income_band_row_240_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_241", @@ -37713,14 +38646,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37735,7 +38672,7 @@ "target_label": "TY2026-27 ird taxable income people by band $850,001 - $900,000 (parked)" }, "filter": "taxable_income_band_row_241_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_242", @@ -37765,14 +38702,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37787,7 +38728,7 @@ "target_label": "TY2026-27 ird taxable income people by band $900,001 - $950,000 (parked)" }, "filter": "taxable_income_band_row_242_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_243", @@ -37817,14 +38758,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37839,7 +38784,7 @@ "target_label": "TY2026-27 ird taxable income people by band $950,001 - $1,000,000 (parked)" }, "filter": "taxable_income_band_row_243_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_people_by_band__row_244", @@ -37869,14 +38814,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_people_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "count" }, "hierarchy": { "provider": { @@ -37891,7 +38840,7 @@ "target_label": "TY2026-27 ird taxable income people by band Over $1,000,000 (parked)" }, "filter": "taxable_income_band_row_244_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_8", @@ -37921,14 +38870,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -37943,7 +38896,7 @@ "target_label": "TY2026-27 ird taxable income total by band nil (parked)" }, "filter": "taxable_income_band_row_8_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_9", @@ -37973,14 +38926,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -37995,7 +38952,7 @@ "target_label": "TY2026-27 ird taxable income total by band $0.01 - $100 (parked)" }, "filter": "taxable_income_band_row_9_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_10", @@ -38025,14 +38982,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38047,7 +39008,7 @@ "target_label": "TY2026-27 ird taxable income total by band $101 - $1,000 (parked)" }, "filter": "taxable_income_band_row_10_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_11", @@ -38077,14 +39038,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38099,7 +39064,7 @@ "target_label": "TY2026-27 ird taxable income total by band $1,001 - $2,000 (parked)" }, "filter": "taxable_income_band_row_11_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_12", @@ -38129,14 +39094,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38151,7 +39120,7 @@ "target_label": "TY2026-27 ird taxable income total by band $2,001 - $3,000 (parked)" }, "filter": "taxable_income_band_row_12_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_13", @@ -38181,14 +39150,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38203,7 +39176,7 @@ "target_label": "TY2026-27 ird taxable income total by band $3,001 - $4,000 (parked)" }, "filter": "taxable_income_band_row_13_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_14", @@ -38233,14 +39206,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38255,7 +39232,7 @@ "target_label": "TY2026-27 ird taxable income total by band $4,001 - $5,000 (parked)" }, "filter": "taxable_income_band_row_14_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_15", @@ -38285,14 +39262,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38307,7 +39288,7 @@ "target_label": "TY2026-27 ird taxable income total by band $5,001 - $6,000 (parked)" }, "filter": "taxable_income_band_row_15_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_16", @@ -38337,14 +39318,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38359,7 +39344,7 @@ "target_label": "TY2026-27 ird taxable income total by band $6,001 - $7,000 (parked)" }, "filter": "taxable_income_band_row_16_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_17", @@ -38389,14 +39374,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38411,7 +39400,7 @@ "target_label": "TY2026-27 ird taxable income total by band $7,001 - $8,000 (parked)" }, "filter": "taxable_income_band_row_17_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_18", @@ -38441,14 +39430,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38463,7 +39456,7 @@ "target_label": "TY2026-27 ird taxable income total by band $8,001 - $9,000 (parked)" }, "filter": "taxable_income_band_row_18_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_19", @@ -38493,14 +39486,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38515,7 +39512,7 @@ "target_label": "TY2026-27 ird taxable income total by band $9,001 - $10,000 (parked)" }, "filter": "taxable_income_band_row_19_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_20", @@ -38545,14 +39542,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38567,7 +39568,7 @@ "target_label": "TY2026-27 ird taxable income total by band $10,001 - $11,000 (parked)" }, "filter": "taxable_income_band_row_20_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_21", @@ -38597,14 +39598,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38619,7 +39624,7 @@ "target_label": "TY2026-27 ird taxable income total by band $11,001 - $12,000 (parked)" }, "filter": "taxable_income_band_row_21_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_22", @@ -38649,14 +39654,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38671,7 +39680,7 @@ "target_label": "TY2026-27 ird taxable income total by band $12,001 - $13,000 (parked)" }, "filter": "taxable_income_band_row_22_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_23", @@ -38701,14 +39710,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38723,7 +39736,7 @@ "target_label": "TY2026-27 ird taxable income total by band $13,001 - $14,000 (parked)" }, "filter": "taxable_income_band_row_23_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_24", @@ -38753,14 +39766,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38775,7 +39792,7 @@ "target_label": "TY2026-27 ird taxable income total by band $14,001 - $15,000 (parked)" }, "filter": "taxable_income_band_row_24_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_25", @@ -38805,14 +39822,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38827,7 +39848,7 @@ "target_label": "TY2026-27 ird taxable income total by band $15,001 - $16,000 (parked)" }, "filter": "taxable_income_band_row_25_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_26", @@ -38857,14 +39878,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38879,7 +39904,7 @@ "target_label": "TY2026-27 ird taxable income total by band $16,001 - $17,000 (parked)" }, "filter": "taxable_income_band_row_26_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_27", @@ -38909,14 +39934,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38931,7 +39960,7 @@ "target_label": "TY2026-27 ird taxable income total by band $17,001 - $18,000 (parked)" }, "filter": "taxable_income_band_row_27_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_28", @@ -38961,14 +39990,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -38983,7 +40016,7 @@ "target_label": "TY2026-27 ird taxable income total by band $18,001 - $19,000 (parked)" }, "filter": "taxable_income_band_row_28_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_29", @@ -39013,14 +40046,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39035,7 +40072,7 @@ "target_label": "TY2026-27 ird taxable income total by band $19,001 - $20,000 (parked)" }, "filter": "taxable_income_band_row_29_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_30", @@ -39065,14 +40102,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39087,7 +40128,7 @@ "target_label": "TY2026-27 ird taxable income total by band $20,000 - $21,000 (parked)" }, "filter": "taxable_income_band_row_30_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_31", @@ -39117,14 +40158,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39139,7 +40184,7 @@ "target_label": "TY2026-27 ird taxable income total by band $21,001 - $22,000 (parked)" }, "filter": "taxable_income_band_row_31_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_32", @@ -39169,14 +40214,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39191,7 +40240,7 @@ "target_label": "TY2026-27 ird taxable income total by band $22,001 - $23,000 (parked)" }, "filter": "taxable_income_band_row_32_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_33", @@ -39221,14 +40270,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39243,7 +40296,7 @@ "target_label": "TY2026-27 ird taxable income total by band $23,001 - $24,000 (parked)" }, "filter": "taxable_income_band_row_33_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_34", @@ -39273,14 +40326,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39295,7 +40352,7 @@ "target_label": "TY2026-27 ird taxable income total by band $24,001 - $25,000 (parked)" }, "filter": "taxable_income_band_row_34_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_35", @@ -39325,14 +40382,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39347,7 +40408,7 @@ "target_label": "TY2026-27 ird taxable income total by band $25,001 - $26,000 (parked)" }, "filter": "taxable_income_band_row_35_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_36", @@ -39377,14 +40438,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39399,7 +40464,7 @@ "target_label": "TY2026-27 ird taxable income total by band $26,001 - $27,000 (parked)" }, "filter": "taxable_income_band_row_36_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_37", @@ -39429,14 +40494,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39451,7 +40520,7 @@ "target_label": "TY2026-27 ird taxable income total by band $27,001 - $28,000 (parked)" }, "filter": "taxable_income_band_row_37_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_38", @@ -39481,14 +40550,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39503,7 +40576,7 @@ "target_label": "TY2026-27 ird taxable income total by band $28,001 - $29,000 (parked)" }, "filter": "taxable_income_band_row_38_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_39", @@ -39533,14 +40606,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39555,7 +40632,7 @@ "target_label": "TY2026-27 ird taxable income total by band $29,001 - $30,000 (parked)" }, "filter": "taxable_income_band_row_39_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_40", @@ -39585,14 +40662,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39607,7 +40688,7 @@ "target_label": "TY2026-27 ird taxable income total by band $30,001 - $31,000 (parked)" }, "filter": "taxable_income_band_row_40_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_41", @@ -39637,14 +40718,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39659,7 +40744,7 @@ "target_label": "TY2026-27 ird taxable income total by band $31,001 - $32,000 (parked)" }, "filter": "taxable_income_band_row_41_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_42", @@ -39689,14 +40774,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39711,7 +40800,7 @@ "target_label": "TY2026-27 ird taxable income total by band $32,001 - $33,000 (parked)" }, "filter": "taxable_income_band_row_42_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_43", @@ -39741,14 +40830,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39763,7 +40856,7 @@ "target_label": "TY2026-27 ird taxable income total by band $33,001 - $34,000 (parked)" }, "filter": "taxable_income_band_row_43_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_44", @@ -39793,14 +40886,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39815,7 +40912,7 @@ "target_label": "TY2026-27 ird taxable income total by band $34,001 - $35,000 (parked)" }, "filter": "taxable_income_band_row_44_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_45", @@ -39845,14 +40942,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39867,7 +40968,7 @@ "target_label": "TY2026-27 ird taxable income total by band $35,001 - $36,000 (parked)" }, "filter": "taxable_income_band_row_45_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_46", @@ -39897,14 +40998,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39919,7 +41024,7 @@ "target_label": "TY2026-27 ird taxable income total by band $36,001 - $37,000 (parked)" }, "filter": "taxable_income_band_row_46_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_47", @@ -39949,14 +41054,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -39971,7 +41080,7 @@ "target_label": "TY2026-27 ird taxable income total by band $37,001 - $38,000 (parked)" }, "filter": "taxable_income_band_row_47_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_48", @@ -40001,14 +41110,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40023,7 +41136,7 @@ "target_label": "TY2026-27 ird taxable income total by band $38,001 - $39,000 (parked)" }, "filter": "taxable_income_band_row_48_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_49", @@ -40053,14 +41166,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40075,7 +41192,7 @@ "target_label": "TY2026-27 ird taxable income total by band $39,001 - $40,000 (parked)" }, "filter": "taxable_income_band_row_49_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_50", @@ -40105,14 +41222,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40127,7 +41248,7 @@ "target_label": "TY2026-27 ird taxable income total by band $40,001 - $41,000 (parked)" }, "filter": "taxable_income_band_row_50_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_51", @@ -40157,14 +41278,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40179,7 +41304,7 @@ "target_label": "TY2026-27 ird taxable income total by band $41,001 - $42,000 (parked)" }, "filter": "taxable_income_band_row_51_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_52", @@ -40209,14 +41334,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40231,7 +41360,7 @@ "target_label": "TY2026-27 ird taxable income total by band $42,001 - $43,000 (parked)" }, "filter": "taxable_income_band_row_52_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_53", @@ -40261,14 +41390,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40283,7 +41416,7 @@ "target_label": "TY2026-27 ird taxable income total by band $43,001 - $44,000 (parked)" }, "filter": "taxable_income_band_row_53_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_54", @@ -40313,14 +41446,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40335,7 +41472,7 @@ "target_label": "TY2026-27 ird taxable income total by band $44,001 - $45,000 (parked)" }, "filter": "taxable_income_band_row_54_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_55", @@ -40365,14 +41502,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40387,7 +41528,7 @@ "target_label": "TY2026-27 ird taxable income total by band $45,001 - $46,000 (parked)" }, "filter": "taxable_income_band_row_55_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_56", @@ -40417,14 +41558,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40439,7 +41584,7 @@ "target_label": "TY2026-27 ird taxable income total by band $46,001 - $47,000 (parked)" }, "filter": "taxable_income_band_row_56_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_57", @@ -40469,14 +41614,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40491,7 +41640,7 @@ "target_label": "TY2026-27 ird taxable income total by band $47,001 - $48,000 (parked)" }, "filter": "taxable_income_band_row_57_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_58", @@ -40521,14 +41670,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40543,7 +41696,7 @@ "target_label": "TY2026-27 ird taxable income total by band $48,001 - $49,000 (parked)" }, "filter": "taxable_income_band_row_58_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_59", @@ -40573,14 +41726,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40595,7 +41752,7 @@ "target_label": "TY2026-27 ird taxable income total by band $49,001 - $50,000 (parked)" }, "filter": "taxable_income_band_row_59_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_60", @@ -40625,14 +41782,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40647,7 +41808,7 @@ "target_label": "TY2026-27 ird taxable income total by band $50,000 - $51,000 (parked)" }, "filter": "taxable_income_band_row_60_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_61", @@ -40677,14 +41838,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40699,7 +41864,7 @@ "target_label": "TY2026-27 ird taxable income total by band $51,001 - $52,000 (parked)" }, "filter": "taxable_income_band_row_61_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_62", @@ -40729,14 +41894,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40751,7 +41920,7 @@ "target_label": "TY2026-27 ird taxable income total by band $52,001 - $53,000 (parked)" }, "filter": "taxable_income_band_row_62_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_63", @@ -40781,14 +41950,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40803,7 +41976,7 @@ "target_label": "TY2026-27 ird taxable income total by band $53,001 - $54,000 (parked)" }, "filter": "taxable_income_band_row_63_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_64", @@ -40833,14 +42006,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40855,7 +42032,7 @@ "target_label": "TY2026-27 ird taxable income total by band $54,001 - $55,000 (parked)" }, "filter": "taxable_income_band_row_64_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_65", @@ -40885,14 +42062,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40907,7 +42088,7 @@ "target_label": "TY2026-27 ird taxable income total by band $55,001 - $56,000 (parked)" }, "filter": "taxable_income_band_row_65_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_66", @@ -40937,14 +42118,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -40959,7 +42144,7 @@ "target_label": "TY2026-27 ird taxable income total by band $56,001 - $57,000 (parked)" }, "filter": "taxable_income_band_row_66_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_67", @@ -40989,14 +42174,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41011,7 +42200,7 @@ "target_label": "TY2026-27 ird taxable income total by band $57,001 - $58,000 (parked)" }, "filter": "taxable_income_band_row_67_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_68", @@ -41041,14 +42230,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41063,7 +42256,7 @@ "target_label": "TY2026-27 ird taxable income total by band $58,001 - $59,000 (parked)" }, "filter": "taxable_income_band_row_68_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_69", @@ -41093,14 +42286,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41115,7 +42312,7 @@ "target_label": "TY2026-27 ird taxable income total by band $59,001 - $60,000 (parked)" }, "filter": "taxable_income_band_row_69_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_70", @@ -41145,14 +42342,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41167,7 +42368,7 @@ "target_label": "TY2026-27 ird taxable income total by band $60,001 - $61,000 (parked)" }, "filter": "taxable_income_band_row_70_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_71", @@ -41197,14 +42398,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41219,7 +42424,7 @@ "target_label": "TY2026-27 ird taxable income total by band $61,001 - $62,000 (parked)" }, "filter": "taxable_income_band_row_71_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_72", @@ -41249,14 +42454,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41271,7 +42480,7 @@ "target_label": "TY2026-27 ird taxable income total by band $62,001 - $63,000 (parked)" }, "filter": "taxable_income_band_row_72_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_73", @@ -41301,14 +42510,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41323,7 +42536,7 @@ "target_label": "TY2026-27 ird taxable income total by band $63,001 - $64,000 (parked)" }, "filter": "taxable_income_band_row_73_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_74", @@ -41353,14 +42566,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41375,7 +42592,7 @@ "target_label": "TY2026-27 ird taxable income total by band $64,001 - $65,000 (parked)" }, "filter": "taxable_income_band_row_74_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_75", @@ -41405,14 +42622,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41427,7 +42648,7 @@ "target_label": "TY2026-27 ird taxable income total by band $65,001 - $66,000 (parked)" }, "filter": "taxable_income_band_row_75_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_76", @@ -41457,14 +42678,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41479,7 +42704,7 @@ "target_label": "TY2026-27 ird taxable income total by band $66,001 - $67,000 (parked)" }, "filter": "taxable_income_band_row_76_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_77", @@ -41509,14 +42734,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41531,7 +42760,7 @@ "target_label": "TY2026-27 ird taxable income total by band $67,001 - $68,000 (parked)" }, "filter": "taxable_income_band_row_77_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_78", @@ -41561,14 +42790,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41583,7 +42816,7 @@ "target_label": "TY2026-27 ird taxable income total by band $68,001 - $69,000 (parked)" }, "filter": "taxable_income_band_row_78_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_79", @@ -41613,14 +42846,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41635,7 +42872,7 @@ "target_label": "TY2026-27 ird taxable income total by band $69,001 - $70,000 (parked)" }, "filter": "taxable_income_band_row_79_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_80", @@ -41665,14 +42902,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41687,7 +42928,7 @@ "target_label": "TY2026-27 ird taxable income total by band $70,001 - $71,000 (parked)" }, "filter": "taxable_income_band_row_80_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_81", @@ -41717,14 +42958,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41739,7 +42984,7 @@ "target_label": "TY2026-27 ird taxable income total by band $71,001 - $72,000 (parked)" }, "filter": "taxable_income_band_row_81_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_82", @@ -41769,14 +43014,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41791,7 +43040,7 @@ "target_label": "TY2026-27 ird taxable income total by band $72,001 - $73,000 (parked)" }, "filter": "taxable_income_band_row_82_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_83", @@ -41821,14 +43070,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41843,7 +43096,7 @@ "target_label": "TY2026-27 ird taxable income total by band $73,001 - $74,000 (parked)" }, "filter": "taxable_income_band_row_83_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_84", @@ -41873,14 +43126,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41895,7 +43152,7 @@ "target_label": "TY2026-27 ird taxable income total by band $74,001 - $75,000 (parked)" }, "filter": "taxable_income_band_row_84_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_85", @@ -41925,14 +43182,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41947,7 +43208,7 @@ "target_label": "TY2026-27 ird taxable income total by band $75,001 - $76,000 (parked)" }, "filter": "taxable_income_band_row_85_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_86", @@ -41977,14 +43238,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -41999,7 +43264,7 @@ "target_label": "TY2026-27 ird taxable income total by band $76,001 - $77,000 (parked)" }, "filter": "taxable_income_band_row_86_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_87", @@ -42029,14 +43294,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42051,7 +43320,7 @@ "target_label": "TY2026-27 ird taxable income total by band $77,001 - $78,000 (parked)" }, "filter": "taxable_income_band_row_87_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_88", @@ -42081,14 +43350,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42103,7 +43376,7 @@ "target_label": "TY2026-27 ird taxable income total by band $78,001 - $79,000 (parked)" }, "filter": "taxable_income_band_row_88_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_89", @@ -42133,14 +43406,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42155,7 +43432,7 @@ "target_label": "TY2026-27 ird taxable income total by band $79,001 - $80,000 (parked)" }, "filter": "taxable_income_band_row_89_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_90", @@ -42185,14 +43462,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42207,7 +43488,7 @@ "target_label": "TY2026-27 ird taxable income total by band $80,000 - $81,000 (parked)" }, "filter": "taxable_income_band_row_90_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_91", @@ -42237,14 +43518,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42259,7 +43544,7 @@ "target_label": "TY2026-27 ird taxable income total by band $81,001 - $82,000 (parked)" }, "filter": "taxable_income_band_row_91_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_92", @@ -42289,14 +43574,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42311,7 +43600,7 @@ "target_label": "TY2026-27 ird taxable income total by band $82,001 - $83,000 (parked)" }, "filter": "taxable_income_band_row_92_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_93", @@ -42341,14 +43630,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42363,7 +43656,7 @@ "target_label": "TY2026-27 ird taxable income total by band $83,001 - $84,000 (parked)" }, "filter": "taxable_income_band_row_93_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_94", @@ -42393,14 +43686,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42415,7 +43712,7 @@ "target_label": "TY2026-27 ird taxable income total by band $84,001 - $85,000 (parked)" }, "filter": "taxable_income_band_row_94_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_95", @@ -42445,14 +43742,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42467,7 +43768,7 @@ "target_label": "TY2026-27 ird taxable income total by band $85,001 - $86,000 (parked)" }, "filter": "taxable_income_band_row_95_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_96", @@ -42497,14 +43798,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42519,7 +43824,7 @@ "target_label": "TY2026-27 ird taxable income total by band $86,001 - $87,000 (parked)" }, "filter": "taxable_income_band_row_96_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_97", @@ -42549,14 +43854,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42571,7 +43880,7 @@ "target_label": "TY2026-27 ird taxable income total by band $87,001 - $88,000 (parked)" }, "filter": "taxable_income_band_row_97_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_98", @@ -42601,14 +43910,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42623,7 +43936,7 @@ "target_label": "TY2026-27 ird taxable income total by band $88,001 - $89,000 (parked)" }, "filter": "taxable_income_band_row_98_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_99", @@ -42653,14 +43966,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42675,7 +43992,7 @@ "target_label": "TY2026-27 ird taxable income total by band $89,001 - $90,000 (parked)" }, "filter": "taxable_income_band_row_99_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_100", @@ -42705,14 +44022,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42727,7 +44048,7 @@ "target_label": "TY2026-27 ird taxable income total by band $90,001 - $91,000 (parked)" }, "filter": "taxable_income_band_row_100_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_101", @@ -42757,14 +44078,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42779,7 +44104,7 @@ "target_label": "TY2026-27 ird taxable income total by band $91,001 - $92,000 (parked)" }, "filter": "taxable_income_band_row_101_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_102", @@ -42809,14 +44134,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42831,7 +44160,7 @@ "target_label": "TY2026-27 ird taxable income total by band $92,001 - $93,000 (parked)" }, "filter": "taxable_income_band_row_102_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_103", @@ -42861,14 +44190,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42883,7 +44216,7 @@ "target_label": "TY2026-27 ird taxable income total by band $93,001 - $94,000 (parked)" }, "filter": "taxable_income_band_row_103_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_104", @@ -42913,14 +44246,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42935,7 +44272,7 @@ "target_label": "TY2026-27 ird taxable income total by band $94,001 - $95,000 (parked)" }, "filter": "taxable_income_band_row_104_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_105", @@ -42965,14 +44302,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -42987,7 +44328,7 @@ "target_label": "TY2026-27 ird taxable income total by band $95,001 - $96,000 (parked)" }, "filter": "taxable_income_band_row_105_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_106", @@ -43017,14 +44358,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43039,7 +44384,7 @@ "target_label": "TY2026-27 ird taxable income total by band $96,001 - $97,000 (parked)" }, "filter": "taxable_income_band_row_106_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_107", @@ -43069,14 +44414,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43091,7 +44440,7 @@ "target_label": "TY2026-27 ird taxable income total by band $97,001 - $98,000 (parked)" }, "filter": "taxable_income_band_row_107_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_108", @@ -43121,14 +44470,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43143,7 +44496,7 @@ "target_label": "TY2026-27 ird taxable income total by band $98,001 - $99,000 (parked)" }, "filter": "taxable_income_band_row_108_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_109", @@ -43173,14 +44526,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43195,7 +44552,7 @@ "target_label": "TY2026-27 ird taxable income total by band $99,001 - $100,000 (parked)" }, "filter": "taxable_income_band_row_109_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_110", @@ -43225,14 +44582,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43247,7 +44608,7 @@ "target_label": "TY2026-27 ird taxable income total by band $100,001 - $101,000 (parked)" }, "filter": "taxable_income_band_row_110_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_111", @@ -43277,14 +44638,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43299,7 +44664,7 @@ "target_label": "TY2026-27 ird taxable income total by band $101,001 - $102,000 (parked)" }, "filter": "taxable_income_band_row_111_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_112", @@ -43329,14 +44694,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43351,7 +44720,7 @@ "target_label": "TY2026-27 ird taxable income total by band $102,001 - $103,000 (parked)" }, "filter": "taxable_income_band_row_112_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_113", @@ -43381,14 +44750,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43403,7 +44776,7 @@ "target_label": "TY2026-27 ird taxable income total by band $103,001 - $104,000 (parked)" }, "filter": "taxable_income_band_row_113_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_114", @@ -43433,14 +44806,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43455,7 +44832,7 @@ "target_label": "TY2026-27 ird taxable income total by band $104,001 - $105,000 (parked)" }, "filter": "taxable_income_band_row_114_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_115", @@ -43485,14 +44862,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43507,7 +44888,7 @@ "target_label": "TY2026-27 ird taxable income total by band $105,001 - $106,000 (parked)" }, "filter": "taxable_income_band_row_115_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_116", @@ -43537,14 +44918,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43559,7 +44944,7 @@ "target_label": "TY2026-27 ird taxable income total by band $106,001 - $107,000 (parked)" }, "filter": "taxable_income_band_row_116_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_117", @@ -43589,14 +44974,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43611,7 +45000,7 @@ "target_label": "TY2026-27 ird taxable income total by band $107,001 - $108,000 (parked)" }, "filter": "taxable_income_band_row_117_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_118", @@ -43641,14 +45030,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43663,7 +45056,7 @@ "target_label": "TY2026-27 ird taxable income total by band $108,001 - $109,000 (parked)" }, "filter": "taxable_income_band_row_118_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_119", @@ -43693,14 +45086,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43715,7 +45112,7 @@ "target_label": "TY2026-27 ird taxable income total by band $109,001 - $110,000 (parked)" }, "filter": "taxable_income_band_row_119_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_120", @@ -43745,14 +45142,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43767,7 +45168,7 @@ "target_label": "TY2026-27 ird taxable income total by band $110,001 - $111,000 (parked)" }, "filter": "taxable_income_band_row_120_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_121", @@ -43797,14 +45198,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43819,7 +45224,7 @@ "target_label": "TY2026-27 ird taxable income total by band $111,001 - $112,000 (parked)" }, "filter": "taxable_income_band_row_121_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_122", @@ -43849,14 +45254,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43871,7 +45280,7 @@ "target_label": "TY2026-27 ird taxable income total by band $112,001 - $113,000 (parked)" }, "filter": "taxable_income_band_row_122_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_123", @@ -43901,14 +45310,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43923,7 +45336,7 @@ "target_label": "TY2026-27 ird taxable income total by band $113,001 - $114,000 (parked)" }, "filter": "taxable_income_band_row_123_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_124", @@ -43953,14 +45366,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -43975,7 +45392,7 @@ "target_label": "TY2026-27 ird taxable income total by band $114,001 - $115,000 (parked)" }, "filter": "taxable_income_band_row_124_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_125", @@ -44005,14 +45422,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44027,7 +45448,7 @@ "target_label": "TY2026-27 ird taxable income total by band $115,001 - $116,000 (parked)" }, "filter": "taxable_income_band_row_125_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_126", @@ -44057,14 +45478,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44079,7 +45504,7 @@ "target_label": "TY2026-27 ird taxable income total by band $116,001 - $117,000 (parked)" }, "filter": "taxable_income_band_row_126_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_127", @@ -44109,14 +45534,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44131,7 +45560,7 @@ "target_label": "TY2026-27 ird taxable income total by band $117,001 - $118,000 (parked)" }, "filter": "taxable_income_band_row_127_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_128", @@ -44161,14 +45590,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44183,7 +45616,7 @@ "target_label": "TY2026-27 ird taxable income total by band $118,001 - $119,000 (parked)" }, "filter": "taxable_income_band_row_128_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_129", @@ -44213,14 +45646,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44235,7 +45672,7 @@ "target_label": "TY2026-27 ird taxable income total by band $119,001 - $120,000 (parked)" }, "filter": "taxable_income_band_row_129_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_130", @@ -44265,14 +45702,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44287,7 +45728,7 @@ "target_label": "TY2026-27 ird taxable income total by band $120,001 - $121,000 (parked)" }, "filter": "taxable_income_band_row_130_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_131", @@ -44317,14 +45758,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44339,7 +45784,7 @@ "target_label": "TY2026-27 ird taxable income total by band $121,001 - $122,000 (parked)" }, "filter": "taxable_income_band_row_131_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_132", @@ -44369,14 +45814,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44391,7 +45840,7 @@ "target_label": "TY2026-27 ird taxable income total by band $122,001 - $123,000 (parked)" }, "filter": "taxable_income_band_row_132_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_133", @@ -44421,14 +45870,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44443,7 +45896,7 @@ "target_label": "TY2026-27 ird taxable income total by band $123,001 - $124,000 (parked)" }, "filter": "taxable_income_band_row_133_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_134", @@ -44473,14 +45926,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44495,7 +45952,7 @@ "target_label": "TY2026-27 ird taxable income total by band $124,001 - $125,000 (parked)" }, "filter": "taxable_income_band_row_134_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_135", @@ -44525,14 +45982,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44547,7 +46008,7 @@ "target_label": "TY2026-27 ird taxable income total by band $125,001 - $126,000 (parked)" }, "filter": "taxable_income_band_row_135_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_136", @@ -44577,14 +46038,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44599,7 +46064,7 @@ "target_label": "TY2026-27 ird taxable income total by band $126,001 - $127,000 (parked)" }, "filter": "taxable_income_band_row_136_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_137", @@ -44629,14 +46094,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44651,7 +46120,7 @@ "target_label": "TY2026-27 ird taxable income total by band $127,001 - $128,000 (parked)" }, "filter": "taxable_income_band_row_137_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_138", @@ -44681,14 +46150,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44703,7 +46176,7 @@ "target_label": "TY2026-27 ird taxable income total by band $128,001 - $129,000 (parked)" }, "filter": "taxable_income_band_row_138_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_139", @@ -44733,14 +46206,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44755,7 +46232,7 @@ "target_label": "TY2026-27 ird taxable income total by band $129,001 - $130,000 (parked)" }, "filter": "taxable_income_band_row_139_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_140", @@ -44785,14 +46262,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44807,7 +46288,7 @@ "target_label": "TY2026-27 ird taxable income total by band $130,001 - $131,000 (parked)" }, "filter": "taxable_income_band_row_140_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_141", @@ -44837,14 +46318,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44859,7 +46344,7 @@ "target_label": "TY2026-27 ird taxable income total by band $131,001 - $132,000 (parked)" }, "filter": "taxable_income_band_row_141_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_142", @@ -44889,14 +46374,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44911,7 +46400,7 @@ "target_label": "TY2026-27 ird taxable income total by band $132,001 - $133,000 (parked)" }, "filter": "taxable_income_band_row_142_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_143", @@ -44941,14 +46430,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -44963,7 +46456,7 @@ "target_label": "TY2026-27 ird taxable income total by band $133,001 - $134,000 (parked)" }, "filter": "taxable_income_band_row_143_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_144", @@ -44993,14 +46486,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45015,7 +46512,7 @@ "target_label": "TY2026-27 ird taxable income total by band $134,001 - $135,000 (parked)" }, "filter": "taxable_income_band_row_144_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_145", @@ -45045,14 +46542,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45067,7 +46568,7 @@ "target_label": "TY2026-27 ird taxable income total by band $135,001 - $136,000 (parked)" }, "filter": "taxable_income_band_row_145_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_146", @@ -45097,14 +46598,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45119,7 +46624,7 @@ "target_label": "TY2026-27 ird taxable income total by band $136,001 - $137,000 (parked)" }, "filter": "taxable_income_band_row_146_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_147", @@ -45149,14 +46654,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45171,7 +46680,7 @@ "target_label": "TY2026-27 ird taxable income total by band $137,001 - $138,000 (parked)" }, "filter": "taxable_income_band_row_147_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_148", @@ -45201,14 +46710,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45223,7 +46736,7 @@ "target_label": "TY2026-27 ird taxable income total by band $138,001 - $139,000 (parked)" }, "filter": "taxable_income_band_row_148_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_149", @@ -45253,14 +46766,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45275,7 +46792,7 @@ "target_label": "TY2026-27 ird taxable income total by band $139,001 - $140,000 (parked)" }, "filter": "taxable_income_band_row_149_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_150", @@ -45305,14 +46822,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45327,7 +46848,7 @@ "target_label": "TY2026-27 ird taxable income total by band $140,001 - $141,000 (parked)" }, "filter": "taxable_income_band_row_150_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_151", @@ -45357,14 +46878,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45379,7 +46904,7 @@ "target_label": "TY2026-27 ird taxable income total by band $141,001 - $142,000 (parked)" }, "filter": "taxable_income_band_row_151_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_152", @@ -45409,14 +46934,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45431,7 +46960,7 @@ "target_label": "TY2026-27 ird taxable income total by band $142,001 - $143,000 (parked)" }, "filter": "taxable_income_band_row_152_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_153", @@ -45461,14 +46990,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45483,7 +47016,7 @@ "target_label": "TY2026-27 ird taxable income total by band $143,001 - $144,000 (parked)" }, "filter": "taxable_income_band_row_153_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_154", @@ -45513,14 +47046,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45535,7 +47072,7 @@ "target_label": "TY2026-27 ird taxable income total by band $144,001 - $145,000 (parked)" }, "filter": "taxable_income_band_row_154_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_155", @@ -45565,14 +47102,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45587,7 +47128,7 @@ "target_label": "TY2026-27 ird taxable income total by band $145,001 - $146,000 (parked)" }, "filter": "taxable_income_band_row_155_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_156", @@ -45617,14 +47158,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45639,7 +47184,7 @@ "target_label": "TY2026-27 ird taxable income total by band $146,001 - $147,000 (parked)" }, "filter": "taxable_income_band_row_156_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_157", @@ -45669,14 +47214,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45691,7 +47240,7 @@ "target_label": "TY2026-27 ird taxable income total by band $147,001 - $148,000 (parked)" }, "filter": "taxable_income_band_row_157_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_158", @@ -45721,14 +47270,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45743,7 +47296,7 @@ "target_label": "TY2026-27 ird taxable income total by band $148,001 - $149,000 (parked)" }, "filter": "taxable_income_band_row_158_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_159", @@ -45773,14 +47326,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45795,7 +47352,7 @@ "target_label": "TY2026-27 ird taxable income total by band $149,001 - $150,000 (parked)" }, "filter": "taxable_income_band_row_159_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_160", @@ -45825,14 +47382,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45847,7 +47408,7 @@ "target_label": "TY2026-27 ird taxable income total by band $150,001 - $151,000 (parked)" }, "filter": "taxable_income_band_row_160_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_161", @@ -45877,14 +47438,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45899,7 +47464,7 @@ "target_label": "TY2026-27 ird taxable income total by band $151,001 - $152,000 (parked)" }, "filter": "taxable_income_band_row_161_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_162", @@ -45929,14 +47494,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -45951,7 +47520,7 @@ "target_label": "TY2026-27 ird taxable income total by band $152,001 - $153,000 (parked)" }, "filter": "taxable_income_band_row_162_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_163", @@ -45981,14 +47550,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46003,7 +47576,7 @@ "target_label": "TY2026-27 ird taxable income total by band $153,001 - $154,000 (parked)" }, "filter": "taxable_income_band_row_163_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_164", @@ -46033,14 +47606,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46055,7 +47632,7 @@ "target_label": "TY2026-27 ird taxable income total by band $154,001 - $155,000 (parked)" }, "filter": "taxable_income_band_row_164_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_165", @@ -46085,14 +47662,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46107,7 +47688,7 @@ "target_label": "TY2026-27 ird taxable income total by band $155,001 - $156,000 (parked)" }, "filter": "taxable_income_band_row_165_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_166", @@ -46137,14 +47718,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46159,7 +47744,7 @@ "target_label": "TY2026-27 ird taxable income total by band $156,001 - $157,000 (parked)" }, "filter": "taxable_income_band_row_166_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_167", @@ -46189,14 +47774,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46211,7 +47800,7 @@ "target_label": "TY2026-27 ird taxable income total by band $157,001 - $158,000 (parked)" }, "filter": "taxable_income_band_row_167_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_168", @@ -46241,14 +47830,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46263,7 +47856,7 @@ "target_label": "TY2026-27 ird taxable income total by band $158,001 - $159,000 (parked)" }, "filter": "taxable_income_band_row_168_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_169", @@ -46293,14 +47886,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46315,7 +47912,7 @@ "target_label": "TY2026-27 ird taxable income total by band $159,001 - $160,000 (parked)" }, "filter": "taxable_income_band_row_169_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_170", @@ -46345,14 +47942,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46367,7 +47968,7 @@ "target_label": "TY2026-27 ird taxable income total by band $160,001 - $161,000 (parked)" }, "filter": "taxable_income_band_row_170_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_171", @@ -46397,14 +47998,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46419,7 +48024,7 @@ "target_label": "TY2026-27 ird taxable income total by band $161,001 - $162,000 (parked)" }, "filter": "taxable_income_band_row_171_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_172", @@ -46449,14 +48054,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46471,7 +48080,7 @@ "target_label": "TY2026-27 ird taxable income total by band $162,001 - $163,000 (parked)" }, "filter": "taxable_income_band_row_172_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_173", @@ -46501,14 +48110,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46523,7 +48136,7 @@ "target_label": "TY2026-27 ird taxable income total by band $163,001 - $164,000 (parked)" }, "filter": "taxable_income_band_row_173_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_174", @@ -46553,14 +48166,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46575,7 +48192,7 @@ "target_label": "TY2026-27 ird taxable income total by band $164,001 - $165,000 (parked)" }, "filter": "taxable_income_band_row_174_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_175", @@ -46605,14 +48222,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46627,7 +48248,7 @@ "target_label": "TY2026-27 ird taxable income total by band $165,001 - $166,000 (parked)" }, "filter": "taxable_income_band_row_175_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_176", @@ -46657,14 +48278,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46679,7 +48304,7 @@ "target_label": "TY2026-27 ird taxable income total by band $166,001 - $167,000 (parked)" }, "filter": "taxable_income_band_row_176_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_177", @@ -46709,14 +48334,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46731,7 +48360,7 @@ "target_label": "TY2026-27 ird taxable income total by band $167,001 - $168,000 (parked)" }, "filter": "taxable_income_band_row_177_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_178", @@ -46761,14 +48390,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46783,7 +48416,7 @@ "target_label": "TY2026-27 ird taxable income total by band $168,001 - $169,000 (parked)" }, "filter": "taxable_income_band_row_178_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_179", @@ -46813,14 +48446,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46835,7 +48472,7 @@ "target_label": "TY2026-27 ird taxable income total by band $169,001 - $170,000 (parked)" }, "filter": "taxable_income_band_row_179_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_180", @@ -46865,14 +48502,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46887,7 +48528,7 @@ "target_label": "TY2026-27 ird taxable income total by band $170,001 - $171,000 (parked)" }, "filter": "taxable_income_band_row_180_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_181", @@ -46917,14 +48558,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46939,7 +48584,7 @@ "target_label": "TY2026-27 ird taxable income total by band $171,001 - $172,000 (parked)" }, "filter": "taxable_income_band_row_181_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_182", @@ -46969,14 +48614,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -46991,7 +48640,7 @@ "target_label": "TY2026-27 ird taxable income total by band $172,001 - $173,000 (parked)" }, "filter": "taxable_income_band_row_182_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_183", @@ -47021,14 +48670,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47043,7 +48696,7 @@ "target_label": "TY2026-27 ird taxable income total by band $173,001 - $174,000 (parked)" }, "filter": "taxable_income_band_row_183_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_184", @@ -47073,14 +48726,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47095,7 +48752,7 @@ "target_label": "TY2026-27 ird taxable income total by band $174,001 - $175,000 (parked)" }, "filter": "taxable_income_band_row_184_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_185", @@ -47125,14 +48782,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47147,7 +48808,7 @@ "target_label": "TY2026-27 ird taxable income total by band $175,001 - $176,000 (parked)" }, "filter": "taxable_income_band_row_185_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_186", @@ -47177,14 +48838,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47199,7 +48864,7 @@ "target_label": "TY2026-27 ird taxable income total by band $176,001 - $177,000 (parked)" }, "filter": "taxable_income_band_row_186_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_187", @@ -47229,14 +48894,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47251,7 +48920,7 @@ "target_label": "TY2026-27 ird taxable income total by band $177,001 - $178,000 (parked)" }, "filter": "taxable_income_band_row_187_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_188", @@ -47281,14 +48950,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47303,7 +48976,7 @@ "target_label": "TY2026-27 ird taxable income total by band $178,001 - $179,000 (parked)" }, "filter": "taxable_income_band_row_188_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_189", @@ -47333,14 +49006,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47355,7 +49032,7 @@ "target_label": "TY2026-27 ird taxable income total by band $179,001 - $180,000 (parked)" }, "filter": "taxable_income_band_row_189_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_190", @@ -47385,14 +49062,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47407,7 +49088,7 @@ "target_label": "TY2026-27 ird taxable income total by band $180,001 - $181,000 (parked)" }, "filter": "taxable_income_band_row_190_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_191", @@ -47437,14 +49118,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47459,7 +49144,7 @@ "target_label": "TY2026-27 ird taxable income total by band $181,001 - $182,000 (parked)" }, "filter": "taxable_income_band_row_191_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_192", @@ -47489,14 +49174,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47511,7 +49200,7 @@ "target_label": "TY2026-27 ird taxable income total by band $182,001 - $183,000 (parked)" }, "filter": "taxable_income_band_row_192_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_193", @@ -47541,14 +49230,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47563,7 +49256,7 @@ "target_label": "TY2026-27 ird taxable income total by band $183,001 - $184,000 (parked)" }, "filter": "taxable_income_band_row_193_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_194", @@ -47593,14 +49286,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47615,7 +49312,7 @@ "target_label": "TY2026-27 ird taxable income total by band $184,001 - $185,000 (parked)" }, "filter": "taxable_income_band_row_194_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_195", @@ -47645,14 +49342,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47667,7 +49368,7 @@ "target_label": "TY2026-27 ird taxable income total by band $185,001 - $186,000 (parked)" }, "filter": "taxable_income_band_row_195_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_196", @@ -47697,14 +49398,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47719,7 +49424,7 @@ "target_label": "TY2026-27 ird taxable income total by band $186,001 - $187,000 (parked)" }, "filter": "taxable_income_band_row_196_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_197", @@ -47749,14 +49454,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47771,7 +49480,7 @@ "target_label": "TY2026-27 ird taxable income total by band $187,001 - $188,000 (parked)" }, "filter": "taxable_income_band_row_197_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_198", @@ -47801,14 +49510,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47823,7 +49536,7 @@ "target_label": "TY2026-27 ird taxable income total by band $188,001 - $189,000 (parked)" }, "filter": "taxable_income_band_row_198_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_199", @@ -47853,14 +49566,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47875,7 +49592,7 @@ "target_label": "TY2026-27 ird taxable income total by band $189,001 - $190,000 (parked)" }, "filter": "taxable_income_band_row_199_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_200", @@ -47905,14 +49622,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47927,7 +49648,7 @@ "target_label": "TY2026-27 ird taxable income total by band $190,001 - $191,000 (parked)" }, "filter": "taxable_income_band_row_200_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_201", @@ -47957,14 +49678,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -47979,7 +49704,7 @@ "target_label": "TY2026-27 ird taxable income total by band $191,001 - $192,000 (parked)" }, "filter": "taxable_income_band_row_201_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_202", @@ -48009,14 +49734,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48031,7 +49760,7 @@ "target_label": "TY2026-27 ird taxable income total by band $192,001 - $193,000 (parked)" }, "filter": "taxable_income_band_row_202_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_203", @@ -48061,14 +49790,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48083,7 +49816,7 @@ "target_label": "TY2026-27 ird taxable income total by band $193,001 - $194,000 (parked)" }, "filter": "taxable_income_band_row_203_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_204", @@ -48113,14 +49846,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48135,7 +49872,7 @@ "target_label": "TY2026-27 ird taxable income total by band $194,001 - $195,000 (parked)" }, "filter": "taxable_income_band_row_204_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_205", @@ -48165,14 +49902,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48187,7 +49928,7 @@ "target_label": "TY2026-27 ird taxable income total by band $195,001 - $196,000 (parked)" }, "filter": "taxable_income_band_row_205_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_206", @@ -48217,14 +49958,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48239,7 +49984,7 @@ "target_label": "TY2026-27 ird taxable income total by band $196,001 - $197,000 (parked)" }, "filter": "taxable_income_band_row_206_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_207", @@ -48269,14 +50014,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48291,7 +50040,7 @@ "target_label": "TY2026-27 ird taxable income total by band $197,001 - $198,000 (parked)" }, "filter": "taxable_income_band_row_207_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_208", @@ -48321,14 +50070,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48343,7 +50096,7 @@ "target_label": "TY2026-27 ird taxable income total by band $198,001 - $199,000 (parked)" }, "filter": "taxable_income_band_row_208_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_209", @@ -48373,14 +50126,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48395,7 +50152,7 @@ "target_label": "TY2026-27 ird taxable income total by band $199,001 - $200,000 (parked)" }, "filter": "taxable_income_band_row_209_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_210", @@ -48425,14 +50182,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48447,7 +50208,7 @@ "target_label": "TY2026-27 ird taxable income total by band $200,001 - $205,000 (parked)" }, "filter": "taxable_income_band_row_210_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_211", @@ -48477,14 +50238,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48499,7 +50264,7 @@ "target_label": "TY2026-27 ird taxable income total by band $205,001 - $210,000 (parked)" }, "filter": "taxable_income_band_row_211_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_212", @@ -48529,14 +50294,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48551,7 +50320,7 @@ "target_label": "TY2026-27 ird taxable income total by band $210,001 - $215,000 (parked)" }, "filter": "taxable_income_band_row_212_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_213", @@ -48581,14 +50350,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48603,7 +50376,7 @@ "target_label": "TY2026-27 ird taxable income total by band $215,001 - $220,000 (parked)" }, "filter": "taxable_income_band_row_213_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_214", @@ -48633,14 +50406,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48655,7 +50432,7 @@ "target_label": "TY2026-27 ird taxable income total by band $220,001 - $225,000 (parked)" }, "filter": "taxable_income_band_row_214_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_215", @@ -48685,14 +50462,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48707,7 +50488,7 @@ "target_label": "TY2026-27 ird taxable income total by band $225,001 - $230,000 (parked)" }, "filter": "taxable_income_band_row_215_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_216", @@ -48737,14 +50518,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48759,7 +50544,7 @@ "target_label": "TY2026-27 ird taxable income total by band $230,001 - $235,000 (parked)" }, "filter": "taxable_income_band_row_216_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_217", @@ -48789,14 +50574,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48811,7 +50600,7 @@ "target_label": "TY2026-27 ird taxable income total by band $235,001 - $240,000 (parked)" }, "filter": "taxable_income_band_row_217_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_218", @@ -48841,14 +50630,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48863,7 +50656,7 @@ "target_label": "TY2026-27 ird taxable income total by band $240,001 - $245,000 (parked)" }, "filter": "taxable_income_band_row_218_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_219", @@ -48893,14 +50686,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48915,7 +50712,7 @@ "target_label": "TY2026-27 ird taxable income total by band $245,001 - $250,000 (parked)" }, "filter": "taxable_income_band_row_219_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_220", @@ -48945,14 +50742,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -48967,7 +50768,7 @@ "target_label": "TY2026-27 ird taxable income total by band $250,001 - $255,000 (parked)" }, "filter": "taxable_income_band_row_220_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_221", @@ -48997,14 +50798,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49019,7 +50824,7 @@ "target_label": "TY2026-27 ird taxable income total by band $255,001 - $260,000 (parked)" }, "filter": "taxable_income_band_row_221_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_222", @@ -49049,14 +50854,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49071,7 +50880,7 @@ "target_label": "TY2026-27 ird taxable income total by band $260,001 - $265,000 (parked)" }, "filter": "taxable_income_band_row_222_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_223", @@ -49101,14 +50910,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49123,7 +50936,7 @@ "target_label": "TY2026-27 ird taxable income total by band $265,001 - $270,000 (parked)" }, "filter": "taxable_income_band_row_223_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_224", @@ -49153,14 +50966,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49175,7 +50992,7 @@ "target_label": "TY2026-27 ird taxable income total by band $270,001 - $275,000 (parked)" }, "filter": "taxable_income_band_row_224_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_225", @@ -49205,14 +51022,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49227,7 +51048,7 @@ "target_label": "TY2026-27 ird taxable income total by band $275,001 - $280,000 (parked)" }, "filter": "taxable_income_band_row_225_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_226", @@ -49257,14 +51078,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49279,7 +51104,7 @@ "target_label": "TY2026-27 ird taxable income total by band $280,001 - $285,000 (parked)" }, "filter": "taxable_income_band_row_226_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_227", @@ -49309,14 +51134,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49331,7 +51160,7 @@ "target_label": "TY2026-27 ird taxable income total by band $285,001 - $290,000 (parked)" }, "filter": "taxable_income_band_row_227_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_228", @@ -49361,14 +51190,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49383,7 +51216,7 @@ "target_label": "TY2026-27 ird taxable income total by band $290,001 - $295,000 (parked)" }, "filter": "taxable_income_band_row_228_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_229", @@ -49413,14 +51246,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49435,7 +51272,7 @@ "target_label": "TY2026-27 ird taxable income total by band $295,001 - $300,000 (parked)" }, "filter": "taxable_income_band_row_229_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_230", @@ -49465,14 +51302,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49487,7 +51328,7 @@ "target_label": "TY2026-27 ird taxable income total by band $300,000 - $350,000 (parked)" }, "filter": "taxable_income_band_row_230_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_231", @@ -49517,14 +51358,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49539,7 +51384,7 @@ "target_label": "TY2026-27 ird taxable income total by band $350,001 - $400,000 (parked)" }, "filter": "taxable_income_band_row_231_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_232", @@ -49569,14 +51414,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49591,7 +51440,7 @@ "target_label": "TY2026-27 ird taxable income total by band $400,001 - $450,000 (parked)" }, "filter": "taxable_income_band_row_232_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_233", @@ -49621,14 +51470,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49643,7 +51496,7 @@ "target_label": "TY2026-27 ird taxable income total by band $450,001 - $500,000 (parked)" }, "filter": "taxable_income_band_row_233_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_234", @@ -49673,14 +51526,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49695,7 +51552,7 @@ "target_label": "TY2026-27 ird taxable income total by band $500,001 - $550,000 (parked)" }, "filter": "taxable_income_band_row_234_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_235", @@ -49725,14 +51582,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49747,7 +51608,7 @@ "target_label": "TY2026-27 ird taxable income total by band $550,001 - $600,000 (parked)" }, "filter": "taxable_income_band_row_235_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_236", @@ -49777,14 +51638,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49799,7 +51664,7 @@ "target_label": "TY2026-27 ird taxable income total by band $600,001 - $650,000 (parked)" }, "filter": "taxable_income_band_row_236_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_237", @@ -49829,14 +51694,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49851,7 +51720,7 @@ "target_label": "TY2026-27 ird taxable income total by band $650,001 - $700,000 (parked)" }, "filter": "taxable_income_band_row_237_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_238", @@ -49881,14 +51750,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49903,7 +51776,7 @@ "target_label": "TY2026-27 ird taxable income total by band $700,001 - $750,000 (parked)" }, "filter": "taxable_income_band_row_238_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_239", @@ -49933,14 +51806,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -49955,7 +51832,7 @@ "target_label": "TY2026-27 ird taxable income total by band $750,001 - $800,000 (parked)" }, "filter": "taxable_income_band_row_239_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_240", @@ -49985,14 +51862,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -50007,7 +51888,7 @@ "target_label": "TY2026-27 ird taxable income total by band $800,001 - $850,000 (parked)" }, "filter": "taxable_income_band_row_240_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_241", @@ -50037,14 +51918,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -50059,7 +51944,7 @@ "target_label": "TY2026-27 ird taxable income total by band $850,001 - $900,000 (parked)" }, "filter": "taxable_income_band_row_241_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_242", @@ -50089,14 +51974,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -50111,7 +52000,7 @@ "target_label": "TY2026-27 ird taxable income total by band $900,001 - $950,000 (parked)" }, "filter": "taxable_income_band_row_242_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_243", @@ -50141,14 +52030,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -50163,7 +52056,7 @@ "target_label": "TY2026-27 ird taxable income total by band $950,001 - $1,000,000 (parked)" }, "filter": "taxable_income_band_row_243_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "ird_taxable_income_total_by_band__row_244", @@ -50193,14 +52086,18 @@ "assertion_policy": "observed_only", "metadata": { "contract_target_id": "ird_taxable_income_total_by_band", - "activation_status": "requires_g8r2_taxable_income_contract_intervals_and_ageing", + "activation_status": "requires_prerequisites", "criticality": "release_blocking", "measure_kind": "prepared_column", "publisher": "Inland Revenue", "target_role": "calibration", "reference_set": "calibration", "method_card_row": "MC5", - "parking_reason": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "parking_reason": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement.", + "reference_prerequisites": "[\"reference_profile:aging_index\",\"measurement:ird_taxable_income_ty2027_v1\"]", + "required_reference_profile": "aging_index", + "required_measurement_binding": "ird_taxable_income_ty2027_v1", + "reference_value_kind": "money" }, "hierarchy": { "provider": { @@ -50215,7 +52112,7 @@ "target_label": "TY2026-27 ird taxable income total by band Over $1,000,000 (parked)" }, "filter": "taxable_income_band_row_244_indicator", - "notes": "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." + "notes": "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." }, { "name": "msd_jobseeker_support_recipients__age_18_24", diff --git a/packages/microcosm-build/src/microcosm/build/reference_factors.py b/packages/microcosm-build/src/microcosm/build/reference_factors.py new file mode 100644 index 0000000000..d84e56b9e2 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/reference_factors.py @@ -0,0 +1,781 @@ +"""Closed, declaration-driven factor calculations with exact feed operands. + +The operational rational is rounded once. Irrational roots remain certified +intervals in the evidence; they are never represented as exact rationals. +Composition checks declared evidence; compilation must replay it against the +facts returned by the authenticated artifact loader. +""" + +from __future__ import annotations + +import hashlib +from collections.abc import Iterable, Mapping +from datetime import date, timedelta +from decimal import Decimal, InvalidOperation +from fractions import Fraction + +from microcosm.build.ledger_artifact import LexicalFloat, chronicle_feed_identity +from microcosm.build.ledger_targets import ( + ALLOWED_ASSERTION_POLICIES, + fact_matches_selector, + selector_field_is_supported, +) +from microcosm.graph.canonical import canonical_json + +OPERATION = "projected_period_average_ratio" +OPERATION_VERSION = 1 +#: Closed and additive: a reference profile accepts only ``profile_input``, +#: and money normalization accepts only ``money_normalization_input``. +FACTOR_PURPOSES = frozenset( + {"profile_input", "diagnostic", "money_normalization_input"} +) +# Exact powers in projected receipts grow with both schedule size and root +# precision. Keep ordinary schedules within the stock integer serialization +# budget, including when rounding needs a finer root enclosure. +_MAX_SUBPERIODS_PER_YEAR = 12 +_MAX_ROUNDING_DIGITS = 18 +_MAX_RECEIPT_ROOT_PLACES = 256 +_DECLARATION_FIELDS = frozenset( + { + "schema_version", + "id", + "operation", + "operation_version", + "purpose", + "feed_identity", + "source_period", + "target_period", + "source_schedule", + "target_schedule", + "growth", + "subperiods_per_year", + "projection_policy", + "rounding", + "expected_operational_factor", + "validity", + "evidence", + "decision_references", + } +) +_VALIDATION_FIELDS = frozenset( + { + "unit", + "source_sha256", + "source_vintage", + "entity_role", + "provenance_class", + "assertion", + "period_coverage", + "source_column_id", + } +) + + +def factor_document_sha256(document: object) -> str: + """Hash canonical declaration/evidence bytes without a registry identity.""" + return hashlib.sha256(canonical_json(document)).hexdigest() + + +def _mapping(value: object, label: str) -> dict: + if not isinstance(value, Mapping): + raise ValueError(f"{label} must be an object.") + return dict(value) + + +def _closed(value: object, fields: set | frozenset, label: str) -> dict: + result = _mapping(value, label) + unknown = result.keys() - fields + if unknown: + raise ValueError(f"Unknown {label} fields: {sorted(unknown)}.") + return result + + +def _closed_feed_identity(value: object, label: str) -> dict: + identity = _mapping(value, label) + normalized = chronicle_feed_identity(identity) + unknown = identity.keys() - normalized.keys() + if unknown: + raise ValueError(f"Unknown {label} fields: {sorted(unknown)}.") + return normalized + + +def _list(value: object, label: str) -> list: + if not isinstance(value, (list, tuple)) or not value: + raise ValueError(f"{label} must be a nonempty list.") + return list(value) + + +def _decimal(value: object, label: str) -> Fraction: + if isinstance(value, bool) or not isinstance(value, (str, int, Decimal)): + raise ValueError(f"{label} requires a finite lexical decimal, never a float.") + try: + result = Decimal(value) + except (InvalidOperation, ValueError) as exc: + raise ValueError(f"{label} requires a finite decimal.") from exc + if not result.is_finite(): + raise ValueError(f"{label} requires a finite decimal.") + return Fraction(result) + + +def _rational(value: Fraction) -> dict: + return {"numerator": value.numerator, "denominator": value.denominator} + + +def _read_rational(value: object, label: str) -> Fraction: + row = _closed(value, {"numerator", "denominator"}, label) + numerator, denominator = row.get("numerator"), row.get("denominator") + if type(numerator) is not int or type(denominator) is not int or denominator <= 0: + raise ValueError( + f"{label} requires integer numerator and positive denominator." + ) + return Fraction(numerator, denominator) + + +def _integer_root(value: int, degree: int) -> int: + lower, upper = 0, 1 << ((value.bit_length() + degree - 1) // degree) + while lower < upper: + middle = (lower + upper + 1) // 2 + if middle**degree <= value: + lower = middle + else: + upper = middle - 1 + return lower + + +def certified_nth_root_bounds( + value: Fraction, degree: int, decimal_places: int +) -> tuple[Fraction, Fraction, bool]: + """Enclose a positive root on an integer grid; mark only exact roots exact.""" + if value <= 0 or type(degree) is not int or degree <= 0: + raise ValueError("Root radicand and degree must be positive.") + if type(decimal_places) is not int or not 0 <= decimal_places <= 4096: + raise ValueError("Root precision must be between 0 and 4096.") + numerator_root = _integer_root(value.numerator, degree) + denominator_root = _integer_root(value.denominator, degree) + if ( + numerator_root**degree == value.numerator + and denominator_root**degree == value.denominator + ): + exact = Fraction(numerator_root, denominator_root) + return exact, exact, True + scale = 10**decimal_places + floor = _integer_root(value.numerator * scale**degree // value.denominator, degree) + return Fraction(floor, scale), Fraction(floor + 1, scale), False + + +def _round_nearest(value: Fraction, digits: int) -> int: + scaled = value * 10**digits + quotient, remainder = divmod(scaled.numerator, scaled.denominator) + twice = 2 * remainder + return quotient + ( + twice > scaled.denominator + or (twice == scaled.denominator and quotient % 2 == 1) + ) + + +def _declaration(declaration: object) -> dict: + row = _closed(declaration, _DECLARATION_FIELDS, "factor declaration") + missing = _DECLARATION_FIELDS - row.keys() + if missing: + raise ValueError(f"Factor declaration missing fields: {sorted(missing)}.") + if type(row["schema_version"]) is not int or row["schema_version"] != 1: + raise ValueError("Factor declaration requires schema_version 1.") + if ( + row["operation"] != OPERATION + or type(row["operation_version"]) is not int + or row["operation_version"] != OPERATION_VERSION + ): + raise ValueError("Unknown factor operation or operation version.") + if not isinstance(row["id"], str) or not row["id"]: + raise ValueError("Factor id must be nonempty text.") + if row["purpose"] not in FACTOR_PURPOSES: + raise ValueError("Unknown factor purpose.") + for field in ("source_period", "target_period"): + label = row[field] + if type(label) not in {str, int} or isinstance(label, str) and not label: + raise ValueError("Factor period labels require nonempty text or integers.") + if row["projection_policy"] != "observed_preferred_latest_contiguous": + raise ValueError("Unknown factor projection policy.") + frequency = row["subperiods_per_year"] + if type(frequency) is not int or not 1 <= frequency <= _MAX_SUBPERIODS_PER_YEAR: + raise ValueError( + "subperiods_per_year must be an integer from 1 to " + f"{_MAX_SUBPERIODS_PER_YEAR} for serializable exact receipts." + ) + rounding = _closed(row["rounding"], {"digits", "mode"}, "factor rounding") + if ( + type(rounding.get("digits")) is not int + or not 0 <= rounding["digits"] <= _MAX_ROUNDING_DIGITS + ): + raise ValueError( + "Factor rounding digits must be an integer from 0 to " + f"{_MAX_ROUNDING_DIGITS} for serializable exact receipts." + ) + if rounding.get("mode") != "nearest_half_even": + raise ValueError("Factor rounding must be nearest_half_even.") + expected = _read_rational(row["expected_operational_factor"], "expected factor") + if ( + expected <= 0 + or row["expected_operational_factor"]["denominator"] != 10 ** rounding["digits"] + ): + raise ValueError("Expected operational factor must be positive p / 10**digits.") + validity = _closed( + row["validity"], + {"declared_on", "valid_from", "valid_until", "as_of"}, + "factor validity", + ) + try: + dates = { + key: date.fromisoformat(validity[key]) + for key in ("declared_on", "valid_from", "valid_until", "as_of") + } + except (KeyError, TypeError, ValueError) as exc: + raise ValueError("Factor validity requires explicit ISO dates.") from exc + if ( + not dates["declared_on"] <= dates["as_of"] + or not dates["valid_from"] <= dates["as_of"] <= dates["valid_until"] + ): + raise ValueError( + "Factor declaration is expired or not yet valid at its declared as_of." + ) + for key in ("evidence", "decision_references"): + if any(not isinstance(item, str) or not item for item in _list(row[key], key)): + raise ValueError(f"{key} must contain nonempty text.") + row["feed_identity"] = _closed_feed_identity( + row["feed_identity"], "factor declaration feed identity" + ) + return row + + +def _schedule(value: object, frequency: int, *, target: bool) -> list[dict]: + rows = _list(value, "factor schedule") + if len(rows) != frequency: + raise ValueError("Factor schedule must contain every declared subperiod once.") + result, previous_end, periods = [], None, set() + for index, raw in enumerate(rows): + fields = {"selector", "validation", "weight", "sequence"} + if target: + fields.add("projection_offset") + row = _closed(raw, fields, "factor schedule slot") + if type(row.get("sequence")) is not int or row["sequence"] != index: + raise ValueError("Factor schedule is misordered or noncontiguous.") + if target and ( + type(row.get("projection_offset")) is not int + or row["projection_offset"] != index + ): + raise ValueError("Invalid projection offsets.") + selector = _mapping(row.get("selector"), "factor selector") + if not selector or any( + not selector_field_is_supported(key) for key in selector + ): + raise ValueError( + "Unknown or empty factor selector; use the closed selector vocabulary." + ) + if "period_value" not in selector or isinstance( + selector["period_value"], (list, tuple) + ): + raise ValueError("Each schedule slot must select one explicit period.") + period = str(selector["period_value"]) + if period in periods: + raise ValueError("Duplicate factor schedule period.") + periods.add(period) + validation = _validation(row.get("validation")) + coverage = validation["period_coverage"] + try: + start, end = ( + date.fromisoformat(coverage["start_date"]), + date.fromisoformat(coverage["end_date"]), + ) + except (KeyError, TypeError, ValueError) as exc: + raise ValueError("Factor schedules require ISO period coverage.") from exc + if start > end or ( + previous_end is not None and start != previous_end + timedelta(days=1) + ): + raise ValueError("Factor schedule coverage is misordered or noncontiguous.") + previous_end = end + if _decimal(row.get("weight"), "schedule weight") <= 0: + raise ValueError("Averaging weights must be positive.") + result.append(row) + return result + + +def _validation(value: object) -> dict: + result = _closed(value, _VALIDATION_FIELDS, "factor operand validation") + required = _VALIDATION_FIELDS - {"source_column_id"} + if required - result.keys(): + raise ValueError( + "Factor operands require unit, source hash/vintage, role, assertion, provenance and coverage." + ) + if result["assertion"] not in {"observation", "source_projection"}: + raise ValueError("Unknown factor assertion.") + coverage = _closed( + result["period_coverage"], + {"start_date", "end_date", "basis", "source_period_label", "notes"}, + "factor operand coverage", + ) + if not {"start_date", "end_date"} <= coverage.keys(): + raise ValueError("Factor operand coverage requires both endpoints.") + for key in required - {"period_coverage"}: + if not isinstance(result[key], str) or not result[key]: + raise ValueError("Factor operand validations require nonempty text.") + source_hash = result["source_sha256"] + if len(source_hash) != 64 or any( + character not in "0123456789abcdef" for character in source_hash + ): + raise ValueError("Factor operand source hash must be a lowercase SHA-256.") + return result + + +def _operand( + slot: dict, + facts: tuple, + *, + optional: bool = False, + assertion_policy: str = "observed_only", +) -> tuple[Fraction, dict] | None: + selector = slot["selector"] + # Exact keys do not make duplicate same-period source vintages unambiguous. + broad = { + key: value + for key, value in selector.items() + if key + not in { + "aggregate_fact_key", + "semantic_fact_key", + "legacy_fact_key", + "source_record_id", + "record_set_id", + "layout_record_set_id", + "record_set_spec_id", + "layout_record_set_spec_id", + } + } + if ( + "period_value" in broad + and sum(fact_matches_selector(fact, broad) for fact in facts) > 1 + ): + raise ValueError("Duplicate factor period across source vintages or facts.") + matches = [fact for fact in facts if fact_matches_selector(fact, selector)] + if not matches and optional: + return None + if len(matches) != 1: + raise ValueError( + f"Factor selector must resolve exactly once; found {len(matches)}." + ) + fact = matches[0] + validation = _validation(slot["validation"]) + actual = { + "unit": fact.get("observed_measure", {}).get("unit"), + "source_sha256": fact.get("source", {}).get("source_sha256"), + "source_vintage": fact.get("source", {}).get("vintage"), + "entity_role": fact.get("entity", {}).get("role"), + "provenance_class": fact.get("provenance_class"), + "assertion": fact.get("assertion", "observation"), + "period_coverage": fact.get("period_coverage", {}), + "source_column_id": fact.get("layout", {}).get("source_column_id"), + } + for key, expected in validation.items(): + if key == "period_coverage": + if any(actual[key].get(field) != item for field, item in expected.items()): + raise ValueError("Factor operand period coverage mismatch.") + elif actual[key] != expected: + raise ValueError(f"Factor operand {key} mismatch.") + # Reuse the existing target assertion-policy vocabulary after all match paths. + if ( + actual["assertion"] == "source_projection" + and assertion_policy != "allow_source_projection" + ): + raise ValueError("Source projections require allow_source_projection.") + value = fact.get("value") + if isinstance(value, bool): + raise ValueError("Boolean factor operand is not numeric.") + if isinstance(value, LexicalFloat): + token = value.lexical_token + recovered = str(float(value)) + if _decimal(token, "feed numeric token") != _decimal( + recovered, "float decimal recovery" + ): + raise ValueError( + "Float conversion does not reproduce the feed lexical numeric token." + ) + proof = { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": token, + "recovered_decimal": recovered, + "decimal_values_equal": True, + } + elif type(value) is int: + token = str(value) + proof = { + "numeric_representation": "exact_integer", + "lexical_token": token, + "recovered_decimal": token, + "decimal_values_equal": True, + } + elif isinstance(value, Decimal): + token = str(value) + proof = { + "numeric_representation": "exact_decimal", + "lexical_token": token, + "recovered_decimal": token, + "decimal_values_equal": True, + } + else: + raise ValueError( + "Factor operand lacks an authenticated lexical numeric token; " + "load the artifact with lexical_floats=True." + ) + numeric = _decimal(token, "feed numeric token") + return numeric, { + "selector": selector, + "aggregate_fact_key": fact.get("aggregate_fact_key"), + "decimal_operand": token, + "numeric_token_proof": proof, + "validation": validation, + "period": fact.get("period"), + "source": fact.get("source"), + "unit": actual["unit"], + "assertion": actual["assertion"], + "provenance_class": actual["provenance_class"], + "entity_role": actual["entity_role"], + "period_coverage": actual["period_coverage"], + } + + +def resolve_feed_operand( + slot: Mapping, facts: Iterable, *, assertion_policy: str = "observed_only" +) -> tuple[Fraction, dict]: + """Resolve one declared operand exactly once, as factor operands resolve. + + The slot is ``{"selector", "validation"}`` in the factor vocabulary. The + exact value comes from the authenticated lexical token, never a float. + """ + row = _closed(slot, {"selector", "validation"}, "feed operand") + selector = _mapping(row.get("selector"), "feed operand selector") + if not selector or any(not selector_field_is_supported(key) for key in selector): + raise ValueError( + "Unknown or empty feed operand selector; use the closed selector vocabulary." + ) + if assertion_policy not in ALLOWED_ASSERTION_POLICIES: + raise ValueError("Unknown feed operand assertion policy.") + _validation(row.get("validation")) + return _operand( + {"selector": selector, "validation": row["validation"]}, + tuple(facts), + assertion_policy=assertion_policy, + ) + + +def resolve_reference_factor( + declaration: Mapping, facts: Iterable, feed_identity: Mapping +) -> tuple[dict, dict]: + """Resolve one closed operation, returning its operational rational and proof.""" + row = _declaration(declaration) + feed_identity = chronicle_feed_identity( + _mapping(feed_identity, "factor feed identity") + ) + if canonical_json(row["feed_identity"]) != canonical_json(feed_identity): + raise ValueError("Factor feed identity mismatch; re-declaration is required.") + fact_rows = tuple(facts) + frequency = row["subperiods_per_year"] + source = _schedule(row["source_schedule"], frequency, target=False) + target = _schedule(row["target_schedule"], frequency, target=True) + source_values, source_evidence = [], [] + for slot in source: + if slot["validation"]["assertion"] != "observation": + raise ValueError("Source schedule requires observations.") + value, evidence = _operand(slot, fact_rows) + if value <= 0: + raise ValueError("Factor denominator operands must be positive.") + source_values.append((value, _decimal(slot["weight"], "source weight"))) + evidence.update({"weight": slot["weight"], "sequence": slot["sequence"]}) + source_evidence.append(evidence) + denominator = sum(value * weight for value, weight in source_values) / sum( + weight for _, weight in source_values + ) + growth = row["growth"] + growth_evidence, effective_growth = None, Fraction(0) + if growth is not None: + growth = _closed( + growth, + { + "selector", + "validation", + "unit_multiplier", + "scenario_delta", + "sensitivity_provenance", + "assertion_policy", + }, + "factor growth", + ) + _validation(growth.get("validation")) + selector = _mapping(growth.get("selector"), "growth selector") + if not selector or any( + not selector_field_is_supported(key) for key in selector + ): + raise ValueError("Unknown growth selector field.") + policy = growth.get("assertion_policy") + if policy not in ALLOWED_ASSERTION_POLICIES: + raise ValueError("Unknown growth assertion policy.") + growth_value, growth_evidence = _operand( + growth, fact_rows, assertion_policy=policy + ) + multiplier = _decimal(growth.get("unit_multiplier"), "growth unit multiplier") + delta = _decimal(growth.get("scenario_delta"), "scenario delta") + if multiplier <= 0: + raise ValueError("Growth unit multiplier must be positive.") + if any( + not isinstance(item, str) or not item + for item in _list( + growth.get("sensitivity_provenance"), "sensitivity provenance" + ) + ): + raise ValueError("Missing sensitivity provenance.") + effective_growth = (growth_value + delta) * multiplier + growth_evidence.update( + { + "unit_multiplier": growth["unit_multiplier"], + "scenario_delta": growth["scenario_delta"], + "sensitivity_provenance": growth["sensitivity_provenance"], + "effective_growth": _rational(effective_growth), + } + ) + if 1 + effective_growth <= 0: + raise ValueError("Effective annual growth must have positive 1 + g.") + observations, target_evidence, gap = [], [], False + for slot in target: + if slot["validation"]["assertion"] != "observation": + raise ValueError("Target schedules prefer observations only.") + resolved = _operand(slot, fact_rows, optional=slot["sequence"] != 0) + if resolved is None: + gap = True + observations.append(None) + target_evidence.append( + { + "selector": slot["selector"], + "validation": slot["validation"], + "status": "projected", + } + ) + else: + if gap: + raise ValueError( + "Observed target subperiods must be contiguous from the anchor." + ) + value, evidence = resolved + if value <= 0: + raise ValueError("Observed target anchors must be positive.") + observations.append(value) + target_evidence.append({**evidence, "status": "observed"}) + if gap and growth is None: + raise ValueError("Projected target periods require a declared growth operand.") + last_observed = max( + index for index, value in enumerate(observations) if value is not None + ) + anchor = observations[last_observed] + target_weights = [_decimal(slot["weight"], "target weight") for slot in target] + + def numerator_at(root: Fraction) -> Fraction: + return sum( + (value if value is not None else anchor * root ** (index - last_observed)) + * target_weights[index] + for index, value in enumerate(observations) + ) / sum(target_weights) + + digits = row["rounding"]["digits"] + precision = digits + 8 + while precision <= _MAX_RECEIPT_ROOT_PLACES: + lower, upper, exact = certified_nth_root_bounds( + 1 + effective_growth, frequency, precision + ) + numerator_lower, numerator_upper = numerator_at(lower), numerator_at(upper) + factor_lower, factor_upper = ( + numerator_lower / denominator, + numerator_upper / denominator, + ) + rounded_lower, rounded_upper = ( + _round_nearest(factor_lower, digits), + _round_nearest(factor_upper, digits), + ) + if rounded_lower == rounded_upper: + break + precision *= 2 + else: + raise ValueError( + "Factor rounding cannot be certified within the supported exact receipt " + "precision." + ) + factor = {"numerator": rounded_lower, "denominator": 10**digits} + if rounded_lower <= 0: + raise ValueError("Operational factor must be positive.") + if factor != row["expected_operational_factor"]: + raise ValueError( + f"Declared expected factor mismatch: computed {factor}; re-declaration is required." + ) + for index, evidence in enumerate(target_evidence): + evidence.update( + { + "sequence": index, + "weight": target[index]["weight"], + "projection_offset": 0 + if observations[index] is not None + else index - last_observed, + } + ) + if observations[index] is None: + evidence["projection_anchor"] = { + "aggregate_fact_key": target_evidence[last_observed][ + "aggregate_fact_key" + ] + } + receipt = { + "schema_version": 1, + "operation": OPERATION, + "operation_version": OPERATION_VERSION, + "factor_id": row["id"], + "declaration_sha256": factor_document_sha256(row), + "feed_identity": dict(feed_identity), + "source_period": row["source_period"], + "target_period": row["target_period"], + "source_schedule": source_evidence, + "target_schedule": target_evidence, + "subperiods_per_year": frequency, + "projection_policy": row["projection_policy"], + "denominator_mean": _rational(denominator), + "growth": growth_evidence, + "root_bounds": { + "lower": _rational(lower), + "upper": _rational(upper), + "exact": exact, + "decimal_places": precision, + "degree": frequency, + "radicand": _rational(1 + effective_growth), + }, + "numerator_mean_bounds": { + "lower": _rational(numerator_lower), + "upper": _rational(numerator_upper), + }, + "unrounded_factor_bounds": { + "lower": _rational(factor_lower), + "upper": _rational(factor_upper), + }, + "rounding": row["rounding"], + "operational_factor": factor, + "validity": row["validity"], + "evidence": row["evidence"], + "decision_references": row["decision_references"], + } + receipt["receipt_sha256"] = factor_document_sha256(receipt) + return factor, receipt + + +def validate_factor_context( + context: Mapping, selected_ids: Iterable[str] | None = None +) -> dict[str, dict]: + """Check selected declared evidence without claiming source authentication. + + Only selected declarations/evidence are validated or returned. Independent + profile edits therefore cannot change a branch's identity or availability. + """ + document = _closed( + context, + {"schema_version", "factors", "country", "feed_identity"}, + "factor context", + ) + if ( + type(document.get("schema_version")) is not int + or document["schema_version"] != 1 + ): + raise ValueError("Factor context requires schema_version 1.") + if "feed_identity" in document: + document["feed_identity"] = _closed_feed_identity( + document["feed_identity"], "factor context feed identity" + ) + entries = _list(document.get("factors"), "factor context entries") + by_id = {} + for raw in entries: + entry = _mapping(raw, "factor context entry") + declaration = _mapping(entry.get("declaration"), "factor declaration") + factor_id = declaration.get("id") + if not isinstance(factor_id, str) or not factor_id or factor_id in by_id: + raise ValueError("Factor ids must be unique nonempty text.") + by_id[factor_id] = entry + selected = list(by_id) if selected_ids is None else list(selected_ids) + result = {} + for factor_id in selected: + if factor_id not in by_id: + raise ValueError(f"Missing selected factor {factor_id!r}.") + entry = _closed( + by_id[factor_id], + {"declaration", "operational_factor", "receipt"}, + "factor context entry", + ) + declaration = _declaration(entry["declaration"]) + if ( + "feed_identity" in document + and document["feed_identity"] != declaration["feed_identity"] + ): + raise ValueError( + "Factor context feed identity is incompatible with the declaration." + ) + factor = entry.get("operational_factor") + _read_rational(factor, "declared operational factor") + if factor != declaration["expected_operational_factor"]: + raise ValueError( + "Declared operational factor differs from its expectation." + ) + receipt = _mapping(entry.get("receipt"), "factor receipt") + body = {key: value for key, value in receipt.items() if key != "receipt_sha256"} + if receipt.get("receipt_sha256") != factor_document_sha256(body): + raise ValueError("Factor receipt hash mismatch.") + if ( + type(receipt.get("schema_version")) is not int + or receipt.get("schema_version") != 1 + or receipt.get("operation") != declaration["operation"] + or type(receipt.get("operation_version")) is not int + or receipt.get("operation_version") != declaration["operation_version"] + or receipt.get("factor_id") != factor_id + or receipt.get("declaration_sha256") != factor_document_sha256(declaration) + or receipt.get("operational_factor") != factor + or receipt.get("feed_identity") != declaration["feed_identity"] + ): + raise ValueError("Factor declaration and receipt are incompatible.") + result[factor_id] = entry + return result + + +def require_current_factors(selected: Mapping[str, dict], *, today: date) -> None: + """Require current declarations from ``validate_factor_context``'s result. + + The currency check guards new runs only. Validation and replay remain pure + functions of their recorded inputs; run entry supplies the explicit date. + """ + outside_validity = [] + for factor_id, entry in selected.items(): + validity = entry["declaration"]["validity"] + if not ( + date.fromisoformat(validity["valid_from"]) + <= today + <= date.fromisoformat(validity["valid_until"]) + ): + outside_validity.append(factor_id) + if outside_validity: + raise ValueError( + f"Selected factors {sorted(outside_validity)} are expired or not yet " + f"valid on {today.isoformat()}; a reviewed re-declaration is needed." + ) + + +def replay_reference_factor( + declaration: Mapping, + facts: Iterable, + feed_identity: Mapping, + operational_factor: Mapping, + receipt: Mapping, +) -> None: + """Authenticate evidence by recomputing it from the loaded feed facts.""" + factor, recomputed = resolve_reference_factor(declaration, facts, feed_identity) + if factor != operational_factor or canonical_json(recomputed) != canonical_json( + receipt + ): + raise ValueError("Factor replay differs from declared result or evidence.") diff --git a/packages/microcosm-build/src/microcosm/build/reference_intervals.py b/packages/microcosm-build/src/microcosm/build/reference_intervals.py new file mode 100644 index 0000000000..250be3fd64 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/reference_intervals.py @@ -0,0 +1,976 @@ +"""Exact income-band intervals with declared semantics and rational cuts. + +Country data declares every interval table: its entity, currency, input unit, +income and universe columns, closure, nil band, negative-income policy, first +lower cut, source locator and diagnostics window. This module supplies no +default for any of them. It validates the declarations, scales cuts exactly, +classifies signed integer-cent amounts and reports fixed-weight boundary +diagnostics. + +Each cut is stored once: a positive band's lower cut is its predecessor's +upper cut, and the first positive band starts at the declared first lower cut. +Source cuts are integer cents. Scaling by a positive rational factor produces +reduced rational cents through integer cross-products and never rounds a +scaled cut. A scaled table keeps its source cuts and the digest of its source +table, and cannot be scaled again. + +Classification validates every record before computing any band. A universe +status is ``included``, ``excluded`` or ``unresolved``; an unresolved record, +an invalid status or an invalid amount on an included record refuses with a +summary of counts by reason. Excluded records never enter a band. Amounts are +signed integers; booleans, floats, strings, missing and nonfinite values are +invalid, and nothing converts a float to cents. + +Diagnostics weigh records with ``frame.resolve_weights(entity)``. They report +weighted mass near every finite cut and each positive band's weighted count +and amount under the declared closure and the other closure, computed +exactly. They never change a reference value or target. A published histogram +does not identify the publisher's mass exactly on a cut, so publisher +reclassification is unavailable: the report gives only the fixed-population +model measurement change. Records below the first cut remain visible, and +floored negative records retain their signed pre-floor amounts and their +destination under each closure. +""" + +from __future__ import annotations + +import copy +import hashlib +import json +import math +from collections import Counter +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from fractions import Fraction +from numbers import Integral + +import numpy as np + +from microcosm.graph.canonical import canonical_json + +__all__ = [ + "BELOW_FIRST_CUT", + "EXCLUDED", + "INTERVAL_DOCUMENT_SCHEMA_VERSION", + "IntervalPreparationError", + "IntervalTable", + "alternative_closure", + "boundary_diagnostics", + "classify_records", + "frame_boundary_diagnostics", + "interval_document_sha256", + "interval_indicators", + "interval_input_fingerprint", + "interval_table", + "scale_interval_document", + "scale_interval_table", + "source_interval_table", + "validate_interval_document", +] + +INTERVAL_DOCUMENT_SCHEMA_VERSION = 1 +SCALING_OPERATION = "exact_cut_multiplication" +BELOW_FIRST_CUT = -1 +EXCLUDED = -2 +CLOSURES = ("left", "right") +NIL_BAND_STATES = ("present", "absent") +NEGATIVE_INCOME_POLICIES = ("floor_zero", "refuse") +INPUT_UNITS = ("integer_cent",) +UNIVERSE_STATES = ("included", "excluded", "unresolved") +PUBLISHER_RECLASSIFICATION = "unavailable" +LIMITATION = ( + "A published histogram does not identify the publisher's mass exactly on a " + "cut, so the publisher's reclassification under the alternative closure " + "cannot be recovered. These are fixed-weight model measurement changes; " + "reference values and calibration targets are unchanged." +) + +_DOCUMENT_FIELDS = frozenset( + {"schema_version", "country", "description", "disclosure", "tables"} +) +_TABLE_FIELDS = frozenset( + { + "id", + "entity", + "currency", + "input_unit", + "income_column", + "universe_column", + "source_period", + "target_period", + "positive_band_closure", + "nil_band", + "negative_income_policy", + "first_lower_cut_cents", + "source_locator", + "diagnostics", + "bands", + } +) +_SCALED_TABLE_FIELDS = frozenset({"scaling", "source_first_lower_cut_cents"}) +_OPTIONAL_TABLE_FIELDS = frozenset({"description"}) +_BAND_FIELDS = frozenset({"id", "kind", "indicator"}) +_OPTIONAL_BAND_FIELDS = frozenset( + {"count_reference", "amount_reference", "source_label"} +) +_SCALING_FIELDS = frozenset( + { + "operation", + "factor_id", + "operational_factor", + "factor_declaration_sha256", + "factor_receipt_sha256", + "source_table_sha256", + "source_cuts_sha256", + } +) +_DIAGNOSTIC_FIELDS = frozenset( + {"boundary_window_cents", "alternative_positive_band_closure"} +) + + +class IntervalPreparationError(ValueError): + """Records cannot be classified; ``summary`` counts every reason.""" + + def __init__(self, summary: Mapping[str, object]) -> None: + self.summary = dict(summary) + super().__init__( + "Interval preparation refused before emitting indicators: " + + json.dumps(self.summary, sort_keys=True) + ) + + +@dataclass(frozen=True) +class IntervalTable: + """One validated table. ``cuts`` holds the first lower cut then each finite upper cut.""" + + id: str + entity: str + income_column: str + universe_column: str + closure: str + nil_present: bool + negative_income_policy: str + window_cents: int + band_ids: tuple[str, ...] + indicators: tuple[str, ...] + nil_index: int | None + positive_indices: tuple[int, ...] + cuts: tuple[Fraction, ...] + scaled: bool + declaration: Mapping[str, object] + + +def interval_document_sha256(document: object) -> str: + """Hash canonical document bytes.""" + + return hashlib.sha256(canonical_json(document)).hexdigest() + + +def alternative_closure(closure: str) -> str: + """The other positive-band closure, whichever is declared.""" + + if closure not in CLOSURES: + raise ValueError("Positive band closure must be 'left' or 'right'.") + return "left" if closure == "right" else "right" + + +def _mapping(value: object, label: str) -> dict: + if not isinstance(value, Mapping): + raise ValueError(f"{label} must be an object.") + return dict(value) + + +def _closed(value: object, required: frozenset, optional: frozenset, label: str): + row = _mapping(value, label) + unknown = row.keys() - required - optional + missing = required - row.keys() + if unknown: + raise ValueError(f"Unknown {label} fields: {sorted(unknown)}.") + if missing: + raise ValueError(f"{label} is missing declared fields: {sorted(missing)}.") + return row + + +def _text(value: object, label: str) -> str: + if not isinstance(value, str) or not value: + raise ValueError(f"{label} must be nonempty text.") + return value + + +def _choice(value: object, choices: tuple[str, ...], label: str) -> str: + if not isinstance(value, str) or value not in choices: + raise ValueError(f"{label} must be declared as one of {list(choices)}.") + return value + + +def _period(value: object, label: str): + if isinstance(value, bool) or type(value) not in {int, str}: + raise ValueError(f"{label} must be an integer or nonempty text.") + if isinstance(value, str) and not value: + raise ValueError(f"{label} must be an integer or nonempty text.") + return value + + +def _integer_cents(value: object, label: str) -> int: + # ``type`` rather than ``isinstance`` keeps booleans out; JSON floats, + # infinity and NaN are floats and refuse here too. + if type(value) is not int: + raise ValueError(f"{label} must be integer cents, never a float or boolean.") + return value + + +def _rational_cents(value: object, label: str) -> Fraction: + row = _closed(value, frozenset({"numerator", "denominator"}), frozenset(), label) + numerator, denominator = row["numerator"], row["denominator"] + if type(numerator) is not int or type(denominator) is not int or denominator <= 0: + raise ValueError( + f"{label} requires integer numerator and positive integer denominator." + ) + if math.gcd(numerator, denominator) != 1: + raise ValueError(f"{label} must be a reduced rational.") + return Fraction(numerator, denominator) + + +def _rational(value: Fraction) -> dict[str, int]: + return {"numerator": value.numerator, "denominator": value.denominator} + + +def _positive_factor(value: object) -> Fraction: + row = _closed( + value, frozenset({"numerator", "denominator"}), frozenset(), "scaling factor" + ) + numerator, denominator = row["numerator"], row["denominator"] + if ( + type(numerator) is not int + or type(denominator) is not int + or numerator <= 0 + or denominator <= 0 + ): + raise ValueError("A scaling factor must be a positive integer rational.") + return Fraction(numerator, denominator) + + +def _source_table(table: Mapping) -> dict: + """Reconstruct the source declaration a scaled table was derived from.""" + + source = { + key: copy.deepcopy(value) + for key, value in table.items() + if key not in _SCALED_TABLE_FIELDS + } + source["first_lower_cut_cents"] = table["source_first_lower_cut_cents"] + bands = [] + for band in table["bands"]: + row = {key: copy.deepcopy(value) for key, value in band.items()} + if "source_upper_cent" in row: + row["upper_cent"] = row.pop("source_upper_cent") + bands.append(row) + source["bands"] = bands + return source + + +def _source_cuts(source: Mapping) -> dict: + return { + "first_lower_cut_cents": source["first_lower_cut_cents"], + "upper_cents": [ + band["upper_cent"] for band in source["bands"] if band["kind"] == "positive" + ], + } + + +def _cut_payload(table: IntervalTable) -> dict: + return { + "first_lower_cut_cents": _rational(table.cuts[0]), + "upper_cents": [_rational(cut) for cut in table.cuts[1:]] + [None], + } + + +def interval_table(value: object) -> IntervalTable: + """Validate one source or scaled table declaration.""" + + raw = _mapping(value, "interval table") + scaled = "scaling" in raw + required = _TABLE_FIELDS | (_SCALED_TABLE_FIELDS if scaled else frozenset()) + row = _closed(raw, required, _OPTIONAL_TABLE_FIELDS, "interval table") + table_id = _text(row["id"], "Interval table id") + entity = _text(row["entity"], "Interval table entity") + _text(row["currency"], "Interval table currency") + _choice(row["input_unit"], INPUT_UNITS, "Interval table input_unit") + income = _text(row["income_column"], "Interval income column") + universe = _text(row["universe_column"], "Interval universe column") + if income == universe: + raise ValueError("Interval income and universe columns must differ.") + _period(row["source_period"], "Interval source period") + _period(row["target_period"], "Interval target period") + closure = _choice( + row["positive_band_closure"], CLOSURES, "Interval positive_band_closure" + ) + nil_present = ( + _choice(row["nil_band"], NIL_BAND_STATES, "Interval nil_band") == "present" + ) + policy = _choice( + row["negative_income_policy"], + NEGATIVE_INCOME_POLICIES, + "Interval negative_income_policy", + ) + _text(row["source_locator"], "Interval source locator") + if "description" in row: + _text(row["description"], "Interval table description") + diagnostics = _closed( + row["diagnostics"], _DIAGNOSTIC_FIELDS, frozenset(), "interval diagnostics" + ) + window = diagnostics["boundary_window_cents"] + if type(window) is not int or window < 0: + raise ValueError("boundary_window_cents must be a nonnegative integer.") + if diagnostics["alternative_positive_band_closure"] != alternative_closure(closure): + raise ValueError( + "The diagnostic alternative must be the other positive band closure." + ) + bands = row["bands"] + if not isinstance(bands, list) or not bands: + raise ValueError("An interval table needs a nonempty band list.") + if scaled: + first = _rational_cents(row["first_lower_cut_cents"], "Scaled first lower cut") + else: + first = Fraction( + _integer_cents(row["first_lower_cut_cents"], "First lower cut") + ) + if first < 0: + raise ValueError("The first lower cut must be nonnegative.") + band_ids, indicators, cuts, positive = [], [], [first], [] + nil_index, open_tail = None, False + companions = set() + for index, raw_band in enumerate(bands): + optional = _OPTIONAL_BAND_FIELDS + if scaled: + optional = optional | {"source_upper_cent"} + band = _mapping(raw_band, "interval band") + kind = band.get("kind") + required_band = _BAND_FIELDS | ( + {"upper_cent"} if kind == "positive" else frozenset() + ) + band = _closed(band, frozenset(required_band), optional, "interval band") + band_ids.append(_text(band["id"], "Interval band id")) + indicators.append(_text(band["indicator"], "Interval band indicator")) + if "source_label" in band: + _text(band["source_label"], "Interval band source label") + has_companions = tuple( + key in band for key in ("count_reference", "amount_reference") + ) + if has_companions not in {(True, True), (False, False)}: + raise ValueError("A band names both companion references or neither.") + companions.add(has_companions[0]) + if has_companions[0]: + _text(band["count_reference"], "Band count reference") + _text(band["amount_reference"], "Band amount reference") + if band["count_reference"] == band["amount_reference"]: + raise ValueError("A band's count and amount references must differ.") + if kind == "nil": + if not nil_present: + raise ValueError("A table declaring nil_band absent has a nil band.") + if index != 0: + raise ValueError("A nil band, when present, comes first.") + if "source_upper_cent" in band: + raise ValueError("A nil band has no cut.") + nil_index = index + continue + if kind != "positive": + raise ValueError("Interval band kind must be 'nil' or 'positive'.") + if open_tail: + raise ValueError("Exactly one final open tail ends the band list.") + positive.append(index) + upper = band["upper_cent"] + if upper is None: + open_tail = True + if scaled and band.get("source_upper_cent", 0) is not None: + raise ValueError("A scaled open tail keeps a null source cut.") + continue + if scaled: + value = _rational_cents(upper, "Scaled upper cut") + if "source_upper_cent" not in band: + raise ValueError("A scaled band keeps its source cut.") + else: + value = Fraction(_integer_cents(upper, "Upper cut")) + if value <= 0 or value <= cuts[-1]: + raise ValueError("Upper cuts must be positive and strictly increasing.") + cuts.append(value) + if nil_present and nil_index is None: + raise ValueError("A table declaring nil_band present needs a first nil band.") + if not open_tail: + raise ValueError( + "Exactly one final open tail with upper_cent null is required." + ) + if not positive: + raise ValueError("An interval table needs positive bands.") + if len(companions) != 1: + raise ValueError( + "A table's bands either all name companion references or none do." + ) + if len(set(band_ids)) != len(band_ids): + raise ValueError("Interval band ids must be unique within a table.") + if len(set(indicators)) != len(indicators) or {income, universe} & set(indicators): + raise ValueError("Interval band indicators must be unique output columns.") + table = IntervalTable( + id=table_id, + entity=entity, + income_column=income, + universe_column=universe, + closure=closure, + nil_present=nil_present, + negative_income_policy=policy, + window_cents=window, + band_ids=tuple(band_ids), + indicators=tuple(indicators), + nil_index=nil_index, + positive_indices=tuple(positive), + cuts=tuple(cuts), + scaled=scaled, + declaration=copy.deepcopy(row), + ) + if scaled: + _validate_scaling(table, row) + return table + + +def _validate_scaling(table: IntervalTable, row: Mapping) -> None: + scaling = _closed(row["scaling"], _SCALING_FIELDS, frozenset(), "interval scaling") + if scaling["operation"] != SCALING_OPERATION: + raise ValueError("Unknown interval scaling operation.") + _text(scaling["factor_id"], "Scaling factor id") + factor = _positive_factor(scaling["operational_factor"]) + for key in ( + "factor_declaration_sha256", + "factor_receipt_sha256", + "source_table_sha256", + "source_cuts_sha256", + ): + digest = scaling[key] + if ( + not isinstance(digest, str) + or len(digest) != 64 + or set(digest) - set("0123456789abcdef") + ): + raise ValueError(f"Scaling {key} must be a lowercase SHA-256.") + source = _source_table(row) + parsed = interval_table(source) + if parsed.scaled: + raise ValueError("A scaled table's source cannot itself be scaled.") + if ( + interval_document_sha256(source) != scaling["source_table_sha256"] + or interval_document_sha256(_source_cuts(source)) + != scaling["source_cuts_sha256"] + ): + raise ValueError("Scaled interval source table identity mismatch.") + expected = tuple(cut * factor for cut in parsed.cuts) + if expected != table.cuts: + raise ValueError( + "Scaled interval cuts must equal their source cuts times the factor " + "exactly once." + ) + + +def validate_interval_document(document: object) -> dict[str, IntervalTable]: + """Validate a whole document; return its tables by id in declared order.""" + + row = _closed( + document, + frozenset({"schema_version", "tables"}), + _DOCUMENT_FIELDS - {"schema_version", "tables"}, + "interval document", + ) + if ( + type(row["schema_version"]) is not int + or row["schema_version"] != INTERVAL_DOCUMENT_SCHEMA_VERSION + ): + raise ValueError("Interval documents require schema_version 1.") + for key in ("country", "description", "disclosure"): + if key in row: + _text(row[key], f"Interval document {key}") + tables = row["tables"] + if not isinstance(tables, list) or not tables: + raise ValueError("An interval document needs a nonempty table list.") + result: dict[str, IntervalTable] = {} + outputs, references = set(), [] + for raw in tables: + table = interval_table(raw) + if table.id in result: + raise ValueError("Interval table ids must be unique.") + result[table.id] = table + if outputs & set(table.indicators): + raise ValueError("Interval indicator ids must be unique.") + outputs.update(table.indicators) + for band in table.declaration["bands"]: + references.extend( + band[key] + for key in ("count_reference", "amount_reference") + if key in band + ) + if len(set(references)) != len(references): + raise ValueError("A companion reference belongs to at most one band.") + return result + + +def scale_interval_table( + table: Mapping, + factor: Mapping, + *, + factor_id: str, + factor_declaration_sha256: str, + factor_receipt_sha256: str, +) -> dict: + """Multiply every cut once by a positive rational; never round a cut.""" + + source = interval_table(table) + if source.scaled: + raise ValueError(f"Interval table {source.id!r} is already scaled.") + rational = _positive_factor(factor) + raw = copy.deepcopy(source.declaration) + scaled = { + key: value for key, value in raw.items() if key != "first_lower_cut_cents" + } + scaled["source_first_lower_cut_cents"] = raw["first_lower_cut_cents"] + first = Fraction(raw["first_lower_cut_cents"]) * rational + scaled["first_lower_cut_cents"] = _rational(first) + bands = [] + for band in raw["bands"]: + band = dict(band) + if band["kind"] == "positive": + cut = band["upper_cent"] + band["source_upper_cent"] = cut + band["upper_cent"] = None if cut is None else _rational(cut * rational) + bands.append(band) + scaled["bands"] = bands + scaled["scaling"] = { + "operation": SCALING_OPERATION, + "factor_id": factor_id, + # Recorded as declared (for example p / 10**digits), not reduced. + "operational_factor": { + "numerator": factor["numerator"], + "denominator": factor["denominator"], + }, + "factor_declaration_sha256": factor_declaration_sha256, + "factor_receipt_sha256": factor_receipt_sha256, + "source_table_sha256": interval_document_sha256(raw), + "source_cuts_sha256": interval_document_sha256(_source_cuts(raw)), + } + interval_table(scaled) + return scaled + + +def scale_interval_document( + document: Mapping, scalings: Mapping[str, Mapping[str, object]] +) -> dict: + """Return a document with only the named source tables scaled.""" + + tables = validate_interval_document(document) + if any(table.scaled for table in tables.values()): + raise ValueError("A base interval document cannot contain scaled tables.") + unknown = set(scalings) - set(tables) + if unknown: + raise ValueError(f"Unknown interval tables {sorted(unknown)}.") + effective = copy.deepcopy(dict(document)) + effective["tables"] = [ + scale_interval_table(raw, **scalings[raw["id"]]) + if raw["id"] in scalings + else raw + for raw in effective["tables"] + ] + validate_interval_document(effective) + return effective + + +def source_interval_table(table: Mapping) -> dict: + """The source declaration of a scaled table, or the table itself.""" + + parsed = interval_table(table) + return _source_table(parsed.declaration) if parsed.scaled else dict(table) + + +# --------------------------------------------------------------------------- +# Classification +# --------------------------------------------------------------------------- + + +def _amount_reason(value: object) -> str | None: + if isinstance(value, (bool, np.bool_)): + return "boolean_amount" + if isinstance(value, Integral): + return None + if value is None: + return "missing_amount" + if isinstance(value, float): + if math.isnan(value): + return "missing_amount" + return "non_integer_amount" if math.isfinite(value) else "nonfinite_amount" + if isinstance(value, str): + return "text_amount" + try: + if value != value: # pandas NA, numpy NaN scalars + return "missing_amount" + except TypeError: + return "missing_amount" + return "non_integer_amount" + + +def _validated( + table: IntervalTable, amounts: Sequence[object], universe: Sequence[object] +) -> list[int | None]: + """Validate every record, then return floored cents or None when excluded.""" + + amounts, universe = list(amounts), list(universe) + if len(amounts) != len(universe): + raise ValueError("Interval amounts and universe statuses must align.") + reasons, statuses, floored = Counter(), Counter(), [] + for amount, status in zip(amounts, universe, strict=True): + if not isinstance(status, str) or status not in UNIVERSE_STATES: + reasons["invalid_universe_status"] += 1 + statuses["invalid"] += 1 + floored.append(None) + continue + statuses[status] += 1 + if status == "excluded": + floored.append(None) + continue + if status == "unresolved": + reasons["unresolved_universe_status"] += 1 + floored.append(None) + continue + reason = _amount_reason(amount) + if reason is not None: + reasons[reason] += 1 + floored.append(None) + continue + value = int(amount) + if value < 0 and table.negative_income_policy == "refuse": + reasons["negative_amount_refused"] += 1 + floored.append(None) + continue + floored.append(max(0, value)) + if reasons: + raise IntervalPreparationError( + { + "table": table.id, + "entity": table.entity, + "records": len(amounts), + "refused_records": sum(reasons.values()), + "reasons": dict(sorted(reasons.items())), + "universe_statuses": dict(sorted(statuses.items())), + } + ) + return floored + + +def _cuts_at_or_below(z: int, cuts: Sequence[Fraction], inclusive: bool) -> int: + """Count cuts ``c`` with ``c < z`` (or ``c <= z``) by cross-multiplication.""" + + low, high = 0, len(cuts) + while low < high: + middle = (low + high) // 2 + cut = cuts[middle] + scaled = z * cut.denominator + if cut.numerator < scaled or (inclusive and cut.numerator == scaled): + low = middle + 1 + else: + high = middle + return low + + +def _position(table: IntervalTable, z: int, closure: str) -> int: + if table.nil_present and z == 0: + return table.nil_index + # Right-closed bands are ``lower < z <= upper`` and the tail ``z > last``; + # left-closed bands are ``lower <= z < upper`` and the tail ``z >= last``. + count = _cuts_at_or_below(z, table.cuts, inclusive=closure == "left") + return BELOW_FIRST_CUT if count == 0 else table.positive_indices[count - 1] + + +def classify_records( + table: IntervalTable, + amounts: Sequence[object], + universe: Sequence[object], + *, + closure: str | None = None, +) -> tuple[int, ...]: + """Band index per record, ``BELOW_FIRST_CUT`` or ``EXCLUDED``. + + Every record is validated first; any refusal raises before a band is + computed. ``closure`` defaults to the table's own declaration. + """ + + closure = ( + table.closure if closure is None else _choice(closure, CLOSURES, "closure") + ) + floored = _validated(table, amounts, universe) + return tuple( + EXCLUDED if z is None else _position(table, z, closure) for z in floored + ) + + +def interval_indicators( + table: IntervalTable, amounts: Sequence[object], universe: Sequence[object] +) -> dict[str, np.ndarray]: + """One Boolean indicator per band, produced only after validation passes.""" + + positions = np.asarray(classify_records(table, amounts, universe), dtype=np.int64) + return { + indicator: positions == index + for index, indicator in enumerate(table.indicators) + } + + +def _fingerprint_value(value: object) -> object: + if isinstance(value, (bool, np.bool_)): + return ["bool", bool(value)] + if isinstance(value, Integral): + return int(value) + if isinstance(value, str): + return value + return [type(value).__name__, repr(value)] + + +def interval_input_fingerprint(values: Sequence[object]) -> str: + """Digest of an input column that keeps value types, including invalid ones.""" + + return interval_document_sha256([_fingerprint_value(value) for value in values]) + + +# --------------------------------------------------------------------------- +# Boundary diagnostics +# --------------------------------------------------------------------------- + + +def _record_order(record_ids: Sequence[object]) -> list: + result = [] + for value in record_ids: + if isinstance(value, (bool, np.bool_)) or not isinstance( + value, (str, Integral) + ): + raise ValueError("Diagnostic record identities are strings or integers.") + result.append(value if isinstance(value, str) else int(value)) + if len(set(result)) != len(result): + raise ValueError("Diagnostic record identities must be unique.") + return result + + +def boundary_diagnostics( + table: IntervalTable, + record_ids: Sequence[object], + amounts: Sequence[object], + universe: Sequence[object], + weights: Sequence[float], + *, + weight_record_ids: Sequence[object], + weight_kind: str, +) -> dict[str, object]: + """Fixed-weight boundary sensitivity, computed with exact weight arithmetic.""" + + order = _record_order(record_ids) + if _record_order(weight_record_ids) != order: + raise ValueError("Diagnostic weights are not aligned with the record order.") + values = np.asarray(weights, dtype=" 0 else "none" + ] + else: + destination = destinations["positive_bands"][ + table.band_ids[position] + ] + destination[0] += exact + destination[1] += exact * raw_negative + destination[2] += 1 + destination[3] += raw_negative + first = _cuts_at_or_below(z - window, table.cuts, inclusive=False) + last = _cuts_at_or_below(z + window, table.cuts, inclusive=True) + for index in range(first, last): + near[index][0] += exact + near[index][1] += 1 + cut_names = ["first_lower_cut"] + [ + table.band_ids[index] for index in table.positive_indices[:-1] + ] + source = _source_table(table.declaration) if table.scaled else table.declaration + source_cuts = _source_cuts(source) + source_values = [source_cuts["first_lower_cut_cents"], *source_cuts["upper_cents"]] + cuts = [ + { + "cut": name, + "cut_cents": _rational(cut), + "source_cut_cents": source_values[index], + "near_cut_weighted_count": _rational(near[index][0]), + "near_cut_records": near[index][1], + } + for index, (name, cut) in enumerate(zip(cut_names, table.cuts, strict=True)) + ] + + def measures(sums): + count, amount, records = sums + return { + "weighted_count": _rational(count), + "weighted_amount_cents": _rational(amount), + "records": records, + } + + def delta(slot): + return { + "weighted_count": _rational(other[slot][0] - declared[slot][0]), + "weighted_amount_cents": _rational(other[slot][1] - declared[slot][1]), + "records": other[slot][2] - declared[slot][2], + } + + def floored_measures(sums): + count, weighted_amount, records, amount = sums + return { + "records": records, + "weighted_count": _rational(count), + "pre_floor_amount_cents": amount, + "weighted_pre_floor_amount_cents": _rational(weighted_amount), + } + + def floored_by_destination(closure): + destinations = floored_destinations[closure] + return { + "nil": floored_measures(destinations["nil"]), + "below_first_cut": floored_measures(destinations["below_first_cut"]), + "none": floored_measures(destinations["none"]), + "positive_bands": { + band_id: floored_measures(sums) + for band_id, sums in destinations["positive_bands"].items() + }, + } + + if table.nil_index is not None and ( + declared[table.nil_index] != other[table.nil_index] + ): + raise ValueError("The nil band must be identical under both closures.") + bands = [] + for index, band_id in enumerate(table.band_ids): + row = { + "band": band_id, + "kind": "nil" if index == table.nil_index else "positive", + "declared": measures(declared[index]), + } + if index != table.nil_index: + row["alternative"] = measures(other[index]) + row["delta"] = delta(index) + bands.append(row) + scaling = table.declaration.get("scaling") + return { + "schema_version": 1, + "kind": "interval_boundary_diagnostics", + "table": table.id, + "entity": table.entity, + "currency": table.declaration["currency"], + "input_unit": table.declaration["input_unit"], + "positive_band_closure": table.closure, + "alternative_positive_band_closure": alternative, + "nil_band": "present" if table.nil_present else "absent", + "negative_income_policy": table.negative_income_policy, + "boundary_window_cents": window, + "weight_kind": weight_kind, + "records": len(order), + "included_records": included, + "digests": { + "table_sha256": interval_document_sha256(table.declaration), + "source_cuts_sha256": interval_document_sha256(source_cuts), + "effective_cuts_sha256": interval_document_sha256(_cut_payload(table)), + "factor": None + if scaling is None + else { + key: scaling[key] + for key in ( + "factor_id", + "operational_factor", + "factor_declaration_sha256", + "factor_receipt_sha256", + ) + }, + "weights_sha256": hashlib.sha256(values.tobytes()).hexdigest(), + "record_order_sha256": interval_document_sha256(order), + }, + "cuts": cuts, + "bands": bands, + "below_first_cut": { + "declared": measures(declared[BELOW_FIRST_CUT]), + "alternative": measures(other[BELOW_FIRST_CUT]), + "delta": delta(BELOW_FIRST_CUT), + }, + "floored_negative_income": { + **floored_measures(floored_negative), + "by_destination": { + "declared": floored_by_destination(table.closure), + "alternative": floored_by_destination(alternative), + }, + }, + "publisher_reclassification": PUBLISHER_RECLASSIFICATION, + "limitation": LIMITATION, + } + + +def frame_boundary_diagnostics(table: IntervalTable, frame) -> dict[str, object]: + """Diagnostics on a frame, weighted through ``resolve_weights(entity)``.""" + + data = frame.table(table.entity) + for column in (table.income_column, table.universe_column): + if column not in data: + raise ValueError( + f"Interval diagnostics need {table.entity}.{column} on the frame." + ) + weights = frame.resolve_weights(table.entity) + ids = data[frame.schema.entity_id_column(table.entity)].tolist() + return boundary_diagnostics( + table, + ids, + data[table.income_column].tolist(), + data[table.universe_column].tolist(), + weights.values, + weight_record_ids=ids, + weight_kind=weights.kind.value, + ) diff --git a/packages/microcosm-build/src/microcosm/build/reference_profiles.py b/packages/microcosm-build/src/microcosm/build/reference_profiles.py index 9a11ac22af..5354a795bc 100644 --- a/packages/microcosm-build/src/microcosm/build/reference_profiles.py +++ b/packages/microcosm-build/src/microcosm/build/reference_profiles.py @@ -19,7 +19,13 @@ from microcosm.graph.canonical import canonical_json REFERENCE_PROFILE_RESOLVER_VERSION = "reference-profiles-v1" +FACTOR_REFERENCE_PROFILE_RESOLVER_VERSION = "reference-profiles-v2" +ALLOWED_REFERENCE_PROFILE_RESOLVER_VERSIONS = frozenset( + {REFERENCE_PROFILE_RESOLVER_VERSION, FACTOR_REFERENCE_PROFILE_RESOLVER_VERSION} +) +SCALE_MONEY_AND_INTERVALS = "scale_money_and_intervals" REQUIRES_REFERENCE_PROFILE_RESOLUTION = "requires_reference_profile_resolution" +REQUIRES_PREREQUISITES = "requires_prerequisites" REFERENCE_DOCUMENT_KEYS = frozenset( {"country", "schema_version", "hierarchy", "target_references"} ) @@ -47,7 +53,7 @@ def _sequence(value: object, context: str) -> list: def _selection_identity(receipt: Mapping) -> dict: - return { + identity = { key: receipt[key] for key in ( "resolver_version", @@ -57,6 +63,394 @@ def _selection_identity(receipt: Mapping) -> dict: "selection", ) } + if receipt.get("resolver_version") == FACTOR_REFERENCE_PROFILE_RESOLVER_VERSION: + identity["selected_factor_identities"] = receipt["selected_factor_identities"] + identity["factor_companions_sha256"] = receipt["factor_companions_sha256"] + identity["transformed_names"] = receipt["transformed_names"] + if "interval_tables" in receipt: + identity["interval_tables_sha256"] = receipt["interval_tables_sha256"] + return identity + + +def validate_reference_factor_evidence(receipt: Mapping) -> dict: + """Bind selected declarations and receipts without claiming feed authenticity.""" + from microcosm.build.reference_factors import validate_factor_context + + if receipt.get("resolver_version") == REFERENCE_PROFILE_RESOLVER_VERSION: + if any( + key in receipt + for key in ( + "selected_factors", + "selected_factor_identities", + "factor_companions", + "factor_companions_sha256", + ) + ): + raise ValueError("Legacy resolution cannot carry factor evidence.") + return {} + factors = _mapping(receipt.get("selected_factors"), "selected factors") + if not factors: + raise ValueError("Factor resolution needs selected factor evidence.") + validated = validate_factor_context( + {"schema_version": 1, "factors": list(factors.values())}, list(factors) + ) + if validated != factors: + raise ValueError("Selected factor identities do not match their declarations.") + identities = { + factor_id: { + "declaration_sha256": reference_document_sha256(entry["declaration"]), + "receipt_sha256": entry["receipt"]["receipt_sha256"], + } + for factor_id, entry in sorted(factors.items()) + } + if identities != receipt.get("selected_factor_identities"): + raise ValueError("Selected factor evidence identity mismatch.") + return validated + + +def _profile_metadata(transform: Mapping) -> dict: + metadata = _mapping(transform.get("metadata", {}), "profile metadata") + if any( + not isinstance(key, str) or not isinstance(value, str) or not value + for key, value in metadata.items() + ): + raise ValueError("Profile metadata requires nonempty strings.") + if set(metadata) & { + "activation_status", + "activation_reason", + "parking_reason", + "required_reference_profile", + "required_measurement_binding", + "reference_value_kind", + "reference_prerequisites", + "reference_prerequisites_remaining", + "reference_selection_sha256", + "effective_intervals_sha256", + "reference_profiles_resolved", + }: + raise ValueError("A profile cannot override resolution identities.") + return metadata + + +def _explicit_names(transform: Mapping, field: str) -> list[str]: + names = _sequence(transform.get(field), field) + if ( + not names + or any(not isinstance(name, str) or not name for name in names) + or len(set(names)) != len(names) + ): + raise ValueError("A transformation needs unique explicit reference names.") + return names + + +def _companion_key(row: Mapping, *, scope: bool = False) -> bytes: + selector = _mapping(row.get("ledger_selector"), "companion selector") + excluded = {"aggregate_fact_key", "source_record_id"} + if not scope: + excluded.update({"source_measure_id", "source_concept"}) + else: + excluded.add("dimension_values") + payload = { + "selector": { + key: value for key, value in selector.items() if key not in excluded + }, + "entity": row.get("entity"), + "period": row.get("period"), + "reference_set": row.get("metadata", {}).get("reference_set"), + } + if not scope: + payload["filter"] = row.get("filter") + return canonical_json(payload) + + +def _validate_companions(transform: Mapping, by_name: Mapping) -> tuple[list, list]: + money = _explicit_names(transform, "reference_names") + counts = _explicit_names(transform, "count_reference_names") + if set(money) & set(counts) or len(money) != len(counts): + raise ValueError("Money and count rosters need disjoint complete companions.") + for kind, names in (("money", money), ("count", counts)): + if set(names) - set(by_name): + raise ValueError("A profile names missing companion references.") + if any( + by_name[name].get("metadata", {}).get("reference_value_kind") != kind + for name in names + ): + raise ValueError( + "Money and count rosters require matching declared value kinds." + ) + scopes = {_companion_key(by_name[name], scope=True) for name in names} + complete = { + name + for name, row in by_name.items() + if isinstance(row.get("ledger_selector"), Mapping) + and _companion_key(row, scope=True) in scopes + } + if complete != set(names): + raise ValueError("Money and count rosters are incomplete companions.") + money_keys = [_companion_key(by_name[name]) for name in money] + count_keys = [_companion_key(by_name[name]) for name in counts] + if len(set(money_keys)) != len(money) or set(money_keys) != set(count_keys): + raise ValueError("Money and count rosters must match one-to-one companions.") + if {_companion_key(by_name[name], scope=True) for name in money} & { + _companion_key(by_name[name], scope=True) for name in counts + }: + raise ValueError("Money and count companions must select distinct measures.") + return money, counts + + +def _factor_companions(chosen: Mapping, by_name: Mapping) -> dict: + """Capture roster roles and source value semantics before transforming rows.""" + companions = {} + for dimension, choice in chosen.items(): + for transform in choice["declaration"].get("transformations", []): + if transform.get("operation") != SCALE_MONEY_AND_INTERVALS: + continue + money, counts = _validate_companions(transform, by_name) + for role, names in (("money", money), ("count", counts)): + for name in names: + if name in companions: + raise ValueError("Duplicate factor companion roster entry.") + row = by_name[name] + companions[name] = { + "role": role, + "factor_id": transform["factor_id"], + "dimension": dimension, + "profile": choice["name"], + "source": _copy( + { + "ledger_selector": row["ledger_selector"], + "period": row.get("period"), + "value_operation": row.get( + "value_operation", "identity" + ), + "value_operands": row.get("value_operands", []), + "metadata": { + "reference_value_kind": row["metadata"][ + "reference_value_kind" + ] + }, + } + ), + } + return companions + + +def _interval_table_ids(transform: Mapping) -> list[str]: + tables = _sequence(transform.get("interval_table_ids"), "interval table ids") + if any(not isinstance(table, str) or not table for table in tables) or len( + set(tables) + ) != len(tables): + raise ValueError("Interval table ids must be unique nonempty text.") + return tables + + +def _validate_interval_companions( + transform: Mapping, tables: Mapping, by_name: Mapping, declaration: Mapping +) -> None: + """Every scaled band names exactly one count/money companion pair.""" + money, counts = _validate_companions(transform, by_name) + band_money, band_counts = [], [] + for table_id in _interval_table_ids(transform): + table = tables[table_id] + if ( + table.declaration["source_period"] != declaration["source_period"] + or table.declaration["target_period"] != declaration["target_period"] + ): + raise ValueError( + "Factor and interval table source/target periods are incompatible." + ) + for band in table.declaration["bands"]: + if "count_reference" not in band: + raise ValueError( + "An interval table without companion references cannot be scaled." + ) + pair = (band["count_reference"], band["amount_reference"]) + for name in pair: + row = by_name.get(name) + if ( + row is None + or row.get("entity") != table.entity + or row.get("filter") != band["indicator"] + ): + raise ValueError( + "Interval companions must filter on their band indicator." + ) + if _companion_key(by_name[pair[0]]) != _companion_key(by_name[pair[1]]): + raise ValueError( + "A band's count and money references are not companions." + ) + band_counts.append(pair[0]) + band_money.append(pair[1]) + if ( + len(set(band_money)) != len(band_money) + or len(set(band_counts)) != len(band_counts) + or set(band_money) != set(money) + or set(band_counts) != set(counts) + ): + raise ValueError( + "Interval band companions must be exactly the money and count rosters." + ) + + +def _scaled_intervals( + chosen: Mapping, + intervals: Mapping | None, + by_name: Mapping, + selected_factors: Mapping, +) -> tuple[dict | None, dict]: + """Scale each named table once with its transformation's selected factor.""" + requests = {} + for dimension, choice in chosen.items(): + for transform in choice["declaration"].get("transformations", []): + if transform.get("operation") != SCALE_MONEY_AND_INTERVALS: + continue + for table_id in _interval_table_ids(transform): + if table_id in requests: + raise ValueError( + f"Interval table {table_id!r} is scaled by more than one " + "transformation." + ) + requests[table_id] = (dimension, choice["name"], transform) + if not requests: + return intervals, {} + if intervals is None: + raise ValueError("Interval scaling needs the declared interval document.") + from microcosm.build.reference_intervals import ( + scale_interval_document, + validate_interval_document, + ) + + tables = validate_interval_document(intervals) + if set(requests) - set(tables): + raise ValueError("A profile names an undeclared interval table.") + scalings, records, checked = {}, {}, set() + for table_id, (dimension, profile, transform) in sorted(requests.items()): + factor_id = transform["factor_id"] + entry = selected_factors[factor_id] + if id(transform) not in checked: + _validate_interval_companions( + transform, tables, by_name, entry["declaration"] + ) + checked.add(id(transform)) + scalings[table_id] = { + "factor": entry["operational_factor"], + "factor_id": factor_id, + "factor_declaration_sha256": reference_document_sha256( + entry["declaration"] + ), + "factor_receipt_sha256": entry["receipt"]["receipt_sha256"], + } + records[table_id] = { + "dimension": dimension, + "profile": profile, + "factor_id": factor_id, + "operational_factor": _copy(entry["operational_factor"]), + "source_table_sha256": reference_document_sha256( + tables[table_id].declaration + ), + "bands": len(tables[table_id].band_ids), + } + effective = scale_interval_document(intervals, scalings) + for raw in effective["tables"]: + if raw["id"] in records: + records[raw["id"]]["effective_table_sha256"] = reference_document_sha256( + raw + ) + return effective, records + + +def _validate_interval_resolution( + resolution: Mapping, factors: Mapping, interval_document: Mapping | None +) -> None: + """Bind recorded interval scaling to selected factors and the document.""" + records = resolution.get("interval_tables") + if records is None: + if "interval_tables_sha256" in resolution or resolution.get( + "effective_intervals_sha256" + ) != resolution.get("base_intervals_sha256"): + raise ValueError( + "Unscaled effective intervals must equal their base document." + ) + return + records = _mapping(records, "interval scaling records") + if ( + resolution.get("resolver_version") != FACTOR_REFERENCE_PROFILE_RESOLVER_VERSION + or not records + or reference_document_sha256(records) + != resolution.get("interval_tables_sha256") + or resolution.get("effective_intervals_sha256") + == resolution.get("base_intervals_sha256") + ): + raise ValueError("Interval scaling record identity mismatch.") + identities = resolution.get("selected_factor_identities", {}) + for record in records.values(): + record = _mapping(record, "interval scaling record") + factor_id = record.get("factor_id") + if ( + factor_id not in factors + or record.get("operational_factor") + != factors[factor_id]["operational_factor"] + or record.get("profile") + != resolution["selection"].get(record.get("dimension")) + ): + raise ValueError("Interval scaling disagrees with its selected factor.") + if interval_document is None: + return + from microcosm.build.reference_intervals import validate_interval_document + + tables = validate_interval_document(interval_document) + scaled = {table_id for table_id, table in tables.items() if table.scaled} + if scaled != set(records): + raise ValueError("Effective interval tables differ from their scaling records.") + for table_id in scaled: + record, declaration = records[table_id], tables[table_id].declaration + scaling = declaration["scaling"] + identity = identities.get(record["factor_id"], {}) + if ( + reference_document_sha256(declaration) + != record.get("effective_table_sha256") + or scaling["source_table_sha256"] != record.get("source_table_sha256") + or scaling["factor_id"] != record["factor_id"] + or scaling["operational_factor"] != record["operational_factor"] + or scaling["factor_receipt_sha256"] != identity.get("receipt_sha256") + or scaling["factor_declaration_sha256"] + != identity.get("declaration_sha256") + ): + raise ValueError("Effective interval table scaling identity mismatch.") + + +def _satisfy_factor_prerequisite(row: dict, dimension: str, binding: str) -> list: + metadata = row.setdefault("metadata", {}) + if metadata.get("activation_status") != REQUIRES_PREREQUISITES: + raise ValueError( + "Money scaling requires the explicit prerequisite parking status." + ) + if ( + metadata.get("required_reference_profile") != dimension + or metadata.get("required_measurement_binding") != binding + ): + raise ValueError("Money scaling has incompatible reference prerequisites.") + try: + prerequisites = json.loads(metadata.get("reference_prerequisites", "")) + except (TypeError, ValueError) as error: + raise ValueError( + "Reference prerequisites must be an explicit JSON list." + ) from error + required = {f"reference_profile:{dimension}", f"measurement:{binding}"} + if ( + not isinstance(prerequisites, list) + or any(not isinstance(item, str) or not item for item in prerequisites) + or len(set(prerequisites)) != len(prerequisites) + or set(prerequisites) != required + ): + raise ValueError( + "Reference prerequisites must name the profile and measurement." + ) + remaining = [ + item for item in prerequisites if item != f"reference_profile:{dimension}" + ] + metadata["reference_prerequisites_remaining"] = canonical_json(remaining).decode() + return remaining def resolve_reference_profiles( @@ -64,13 +458,17 @@ def resolve_reference_profiles( profile_declarations: Mapping, selection: Mapping, interval_document: Mapping | None = None, + *, + factor_context: Mapping | None = None, ) -> tuple[dict, dict | None, dict]: """Return effective references, effective intervals and a bound receipt. Only selected declarations enter selection identity. An unavailable profile refuses; an explicitly deferred dimension is reported without claiming its - transformations ran. Interval transformations are intentionally unsupported - until a consumer declares their complete transformation contract. + transformations ran. Money scaling consumes declared factor evidence; + compilation authenticates it against the feed. Named interval tables are + scaled by the same selected factor as their money companions; without + named tables the interval document passes through unchanged. Resource headers are bound into the base identity; effective references use the compiler's closed document shape. """ @@ -120,21 +518,74 @@ def resolve_reference_profiles( deferred.append(dimension) chosen[dimension] = {"name": name, "declaration": profile} resolved_selection[dimension] = name + factor_ids = set() + for choice in chosen.values(): + for raw_transform in choice["declaration"].get("transformations", []): + transform = _mapping(raw_transform, "reference transformation") + if transform.get("operation") == SCALE_MONEY_AND_INTERVALS: + _interval_table_ids(transform) + factor_id = transform.get("factor_id") + if not isinstance(factor_id, str) or not factor_id: + raise ValueError("Money scaling needs a declared factor id.") + factor_ids.add(factor_id) + selected_factors = {} + if factor_ids: + from microcosm.build.reference_factors import validate_factor_context + + selected_factors = validate_factor_context(factor_context, sorted(factor_ids)) + base_intervals_hash = ( + None if intervals is None else reference_document_sha256(intervals) + ) + intervals, interval_records = _scaled_intervals( + chosen, intervals, by_name, selected_factors + ) receipt = { "resolver_version": REFERENCE_PROFILE_RESOLVER_VERSION, "base_references_sha256": reference_document_sha256(base), - "base_intervals_sha256": ( - None if intervals is None else reference_document_sha256(intervals) - ), + "base_intervals_sha256": base_intervals_hash, "selected_declarations_sha256": reference_document_sha256(chosen), "selection": resolved_selection, } + if selected_factors: + companions = _factor_companions(chosen, by_name) + receipt.update( + resolver_version=FACTOR_REFERENCE_PROFILE_RESOLVER_VERSION, + factor_companions=companions, + factor_companions_sha256=reference_document_sha256(companions), + transformed_names=sorted( + name + for choice in chosen.values() + for transform in choice["declaration"].get("transformations", []) + for field in ( + ("reference_names", "count_reference_names") + if transform.get("operation") == SCALE_MONEY_AND_INTERVALS + else ("reference_names",) + ) + for name in _explicit_names(transform, field) + ), + selected_factors=_copy(selected_factors), + selected_factor_identities={ + factor_id: { + "declaration_sha256": reference_document_sha256( + entry["declaration"] + ), + "receipt_sha256": entry["receipt"]["receipt_sha256"], + } + for factor_id, entry in sorted(selected_factors.items()) + }, + ) + if interval_records: + receipt.update( + interval_tables=interval_records, + interval_tables_sha256=reference_document_sha256(interval_records), + ) selection_hash = reference_document_sha256(_selection_identity(receipt)) interval_hash = None if intervals is None else reference_document_sha256(intervals) transformed, policies = {}, [] for dimension, choice in chosen.items(): for raw_transform in choice["declaration"].get("transformations", []): transform = _mapping(raw_transform, "reference transformation") + operation = transform.get("operation") allowed = { "reference_names", "operation", @@ -143,30 +594,108 @@ def resolve_reference_profiles( "notes", "target_label", } + if operation == SCALE_MONEY_AND_INTERVALS: + allowed = { + "reference_names", + "count_reference_names", + "operation", + "factor_id", + "interval_table_ids", + "required_measurement_binding", + "metadata", + } if set(transform) - allowed: raise ValueError("Unknown reference transformation fields.") - if transform.get("operation") != ALLOCATED_RESIDUAL_BY_RATIO: + if operation not in { + ALLOCATED_RESIDUAL_BY_RATIO, + SCALE_MONEY_AND_INTERVALS, + }: raise ValueError("Unsupported reference profile transformation.") + metadata = _profile_metadata(transform) + if _carried_value_keys(transform): + raise ValueError("Reference transformations carry observed values.") + if operation == SCALE_MONEY_AND_INTERVALS: + binding = transform.get("required_measurement_binding") + if not isinstance(binding, str) or not binding: + raise ValueError( + "Money scaling needs a required measurement binding." + ) + money, counts = _validate_companions(transform, by_name) + factor_id = transform["factor_id"] + factor = selected_factors[factor_id] + declaration = factor["declaration"] + if declaration["purpose"] != "profile_input": + raise ValueError("A diagnostic factor cannot be a profile input.") + rational = factor["operational_factor"] + weight = rational["numerator"] / rational["denominator"] + for name in [*money, *counts]: + if name in transformed: + raise ValueError( + f"Duplicate or incompatible transformation: {name!r}." + ) + row = by_name[name] + if ( + row.get("ledger_selector", {}).get("period_value") + != declaration["source_period"] + or row.get("period") != declaration["target_period"] + ): + raise ValueError( + "Factor and reference source/target periods are incompatible." + ) + remaining = _satisfy_factor_prerequisite(row, dimension, binding) + if name in money: + if row.get( + "value_operation", "identity" + ) != "identity" or row.get("value_operands", []): + raise ValueError( + "Money scaling requires an untransformed source reference." + ) + selector = _mapping( + row.get("ledger_selector"), "money selector" + ) + key = selector.get("aggregate_fact_key") + if not isinstance(key, str) or not key: + raise ValueError( + "Money scaling needs a singleton source fact key." + ) + row["value_operation"] = "linear_combination" + row["value_operands"] = [ + { + "aggregate_fact_key": key, + "expected_member_count": 1, + "weight": weight, + } + ] + row_metadata = row.setdefault("metadata", {}) + row_metadata.update(metadata) + row_metadata["reference_selection_sha256"] = selection_hash + row_metadata["reference_profiles_resolved"] = "1" + if interval_hash is not None: + row_metadata["effective_intervals_sha256"] = interval_hash + transformed[name] = { + "dimension": dimension, + "profile": choice["name"], + "operation": "linear_combination" + if name in money + else "identity", + "factor_id": factor_id, + "operational_factor": _copy(rational), + "metadata": metadata, + "operand_selectors": _copy(row.get("value_operands", [])), + "source_period": row.get("ledger_selector", {}).get( + "period_value" + ), + "target_period": row.get("period"), + "unresolved_prerequisites": remaining, + } + policies.append( + {"dimension": dimension, "profile": choice["name"], **metadata} + ) + continue names = _sequence(transform.get("reference_names"), "transformed names") if not names or any(not isinstance(name, str) for name in names): raise ValueError("A transformation needs explicit reference names.") operands = _sequence(transform.get("value_operands"), "value operands") - metadata = _mapping(transform.get("metadata", {}), "profile metadata") - if any( - not isinstance(key, str) or not isinstance(value, str) or not value - for key, value in metadata.items() - ): - raise ValueError("Profile metadata requires nonempty strings.") - if set(metadata) & { - "activation_status", - "required_reference_profile", - "reference_selection_sha256", - "effective_intervals_sha256", - "reference_profiles_resolved", - }: - raise ValueError("A profile cannot override resolution identities.") - if _carried_value_keys(transform): - raise ValueError("Reference transformations carry observed values.") for name in names: if name not in by_name: raise ValueError(f"Profile names missing reference {name!r}.") @@ -233,6 +762,32 @@ def resolve_reference_profiles( return effective, intervals, receipt +def _factor_reference_bindings(resolution: Mapping) -> dict: + """Validate the compact roster bound into the v2 selection identity.""" + if resolution["resolver_version"] == REFERENCE_PROFILE_RESOLVER_VERSION: + return {} + bindings = _mapping(resolution.get("factor_companions"), "factor companions") + if reference_document_sha256(bindings) != resolution.get( + "factor_companions_sha256" + ): + raise ValueError("Factor companion source record identity mismatch.") + for name, raw_binding in bindings.items(): + binding = _mapping(raw_binding, "factor companion") + source = _mapping(binding.get("source"), "factor companion source") + if ( + not isinstance(name, str) + or not name + or binding.get("role") not in {"money", "count"} + or binding.get("profile") + != resolution["selection"].get(binding.get("dimension")) + or source.get("metadata", {}).get("reference_value_kind") != binding["role"] + ): + raise ValueError( + "Factor companion roster role or profile identity mismatch." + ) + return bindings + + def validate_reference_resolution( document: Mapping, receipt: Mapping, @@ -240,14 +795,25 @@ def validate_reference_resolution( references_sha256: str | None = None, interval_document: Mapping | None = None, ) -> dict: - """Refuse stale effective documents, receipts or interval companions.""" + """Refuse stale effective documents, receipts or interval companions. + + The checks bind the effective rows to the receipt and the receipt's + factor companions to its selection identity. They assume that identity is + trusted, as when composition resolves a fresh receipt: a forger who also + replaces the selection identity is outside this layer. Selected factors + are anchored independently by replay against the authenticated feed. + """ resolution = _copy(_mapping(receipt, "reference resolution receipt")) - if resolution.get("resolver_version") != REFERENCE_PROFILE_RESOLVER_VERSION: + if ( + resolution.get("resolver_version") + not in ALLOWED_REFERENCE_PROFILE_RESOLVER_VERSIONS + ): raise ValueError("Unknown reference resolver version.") claimed_receipt_hash = resolution.pop("receipt_sha256", None) if claimed_receipt_hash != reference_document_sha256(resolution): raise ValueError("Reference resolution receipt hash mismatch.") resolution["receipt_sha256"] = claimed_receipt_hash + factors = validate_reference_factor_evidence(resolution) actual = reference_document_sha256(document) if actual != resolution.get("effective_references_sha256") or ( references_sha256 is not None and actual != references_sha256 @@ -263,16 +829,115 @@ def validate_reference_resolution( interval_document ) != resolution.get("effective_intervals_sha256"): raise ValueError("Effective interval document hash mismatch.") - names = [] + _validate_interval_resolution(resolution, factors, interval_document) + factor_bindings = _factor_reference_bindings(resolution) + transformed_names = resolution.get("transformed_names") + if not isinstance(transformed_names, list) or not all( + isinstance(name, str) for name in transformed_names + ): + raise ValueError("Resolution receipt transformed names are malformed.") + transformations = _mapping( + resolution.get("transformations"), "resolution transformations" + ) + if any(not isinstance(action, Mapping) for action in transformations.values()): + raise ValueError("Resolution transformations must map names to objects.") + other_names = set(transformed_names) - set(factor_bindings) + names, used_factors, seen_names = [], set(), set() for row in document.get("target_references", ()): + name = row.get("name") metadata = row.get("metadata", {}) - if metadata.get("reference_profiles_resolved"): - names.append(row["name"]) + resolved = metadata.get("reference_profiles_resolved") + if (factors or resolved) and (not isinstance(name, str) or not name): + raise ValueError( + "Resolved and factor reference names must be nonempty strings." + ) + if factors: + if name in seen_names: + raise ValueError("Duplicate factor companion reference roster name.") + seen_names.add(name) + if resolved: + names.append(name) if metadata.get("reference_selection_sha256") != selection_hash or ( metadata.get("effective_intervals_sha256") != resolution.get("effective_intervals_sha256") ): raise ValueError("Effective reference profile identity mismatch.") + action = transformations.get(name, {}) + binding = factor_bindings.get(name) + if factors and binding is None and name not in other_names: + raise ValueError( + "Factor action names a row outside its companion roster." + ) + if binding is not None or "factor_id" in action: + if binding is None: + raise ValueError( + "Factor action names a row outside its companion roster." + ) + role = binding["role"] + operation = "linear_combination" if role == "money" else "identity" + if action.get("operation") != operation or any( + action.get(key) != binding[key] + for key in ("dimension", "profile", "factor_id") + ): + raise ValueError( + "Factor action disagrees with its companion roster role." + ) + source = binding["source"] + if ( + metadata.get("reference_value_kind") != role + or row.get("ledger_selector") != source.get("ledger_selector") + or row.get("period") != source.get("period") + ): + raise ValueError( + "Effective factor companion source semantics changed." + ) + factor_id = binding["factor_id"] + if factor_id not in factors: + raise ValueError( + "A transformed reference has missing factor evidence." + ) + used_factors.add(factor_id) + entry = factors[factor_id] + declaration = entry["declaration"] + rational = entry["operational_factor"] + if ( + action.get("operational_factor") != rational + or action.get("source_period") != declaration["source_period"] + or action.get("target_period") != declaration["target_period"] + ): + raise ValueError("Effective reference factor identity mismatch.") + if role == "money": + expected = [ + { + "aggregate_fact_key": source.get("ledger_selector", {}).get( + "aggregate_fact_key" + ), + "expected_member_count": 1, + "weight": rational["numerator"] / rational["denominator"], + } + ] + if ( + row.get("value_operation") != "linear_combination" + or row.get("value_operands") != expected + or action.get("operand_selectors") != expected + ): + raise ValueError( + "Effective money factor must be applied exactly once." + ) + elif ( + row.get("value_operation", "identity") + != source.get("value_operation", "identity") + or row.get("value_operands", []) != source.get("value_operands", []) + or action.get("operand_selectors") + != source.get("value_operands", []) + ): + raise ValueError( + "Effective count value semantics must remain unchanged." + ) if sorted(names) != resolution.get("transformed_names"): raise ValueError("Resolution receipt transformed names mismatch.") + if used_factors != set(factors): + raise ValueError("Resolution receipt has unused selected factor evidence.") + if set(factor_bindings) - set(names): + raise ValueError("Resolution receipt is missing factor companion rows.") return resolution diff --git a/packages/microcosm-build/src/microcosm/build/transport/cli.py b/packages/microcosm-build/src/microcosm/build/transport/cli.py index 3d26dd15ff..6f42b3b0f8 100644 --- a/packages/microcosm-build/src/microcosm/build/transport/cli.py +++ b/packages/microcosm-build/src/microcosm/build/transport/cli.py @@ -14,6 +14,10 @@ explorer page and ``build-status.json``. The status file lists the outputs the spec declares as still owed by a later package. There is no upload, staging or publication path; ``--local-only`` is the only mode. + +The currency check guards new runs only: ``auto`` and ``forbid`` require +selected factors current on today's UTC date before CREATE; ``require`` only +verifies stored work. """ from __future__ import annotations @@ -24,6 +28,7 @@ import sys from collections.abc import Mapping, Sequence from dataclasses import dataclass, replace +from datetime import UTC, date, datetime from pathlib import Path from tempfile import TemporaryDirectory from types import MappingProxyType @@ -32,6 +37,7 @@ import pandas as pd from microcosm.build.artifact_files import file_artifact, materialize_bytes +from microcosm.build.reference_factors import require_current_factors from microcosm.frame import Frame from microcosm.frame.adapters.axiom import AxiomEntityTableDataset from microcosm.frame.materialize import put_frame_table @@ -60,6 +66,7 @@ TransportGraphExtension, compose_transport_graph, load_transport_spec, + reference_profile_views, skeleton_create_declaration, transport_endpoints, transport_pending_outputs, @@ -223,7 +230,12 @@ def prepare_transport_build( inventory_store: ContentStore | str | Path | None = None, resume: ResumePolicy = "auto", ) -> PreparedTransportBuild: - """Check activation, build the registry and CREATE inventory, then compose. + """Check factor currency and activation, then compose the prepared graph. + + A new run (``auto`` or ``forbid``) first requires every factor the default + and scenario views select to be current on today's UTC date; ``require`` + only verifies stored work. This is the run-entry check for every caller, + the CLI included. Composition and execution stay clock-free. Before registry preparation, CREATE or graph source reads, activation checks declaration resource paths, non-null selected roots and encoding @@ -241,9 +253,10 @@ def prepare_transport_build( CREATE sources and reuses the stored inventory without running the probe. """ - validate_transport_activation(spec) if resume not in {"auto", "require", "forbid"}: raise ValueError("resume must be 'auto', 'require' or 'forbid'.") + _require_current_run_factors(spec, resume=resume) + validate_transport_activation(spec) if resume == "require": root = ( inventory_store.root @@ -466,6 +479,9 @@ def execute_transport_graph( later checkpoints reuse this invocation's work. The skeleton H5 is rewritten only when the export descriptor changed; an unchanged H5 must pass readback, and under ``require`` it must already exist. + + Execution is clock-free. It runs a graph that :func:`prepare_transport_build` + checked; a caller that composes a graph itself owns the currency check. """ if resume not in {"auto", "require", "forbid"}: @@ -634,8 +650,34 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: return args +def _today() -> date: + """Today's UTC date, so a local run and CI agree on a validity boundary.""" + return datetime.now(UTC).date() + + +def _require_current_run_factors( + spec: Mapping[str, object], *, resume: ResumePolicy +) -> None: + """The currency check guards new runs only, including scenario selections. + + ``require`` only replays stored work and may verify historical factors. + Check every factor the default and scenario views select, through the + walk activation uses, against today's UTC date, before CREATE. + """ + if resume == "require": + return + selected = {} + for view in reference_profile_views(spec): + selected.update(view[2].get("selected_factors", {})) + require_current_factors(selected, today=_today()) + + def main(argv: Sequence[str] | None = None) -> int: - """Run the CLI; return 0 on success and 1 on a refused build.""" + """Run the CLI; return 0 on success and 1 on a refused build. + + Preparation's currency check guards new runs only (``auto`` and + ``forbid``), before CREATE. ``require`` verifies existing stored work. + """ try: args = parse_args(argv) diff --git a/packages/microcosm-build/src/microcosm/build/transport/compose.py b/packages/microcosm-build/src/microcosm/build/transport/compose.py index 211cf380a9..ea7129b29d 100644 --- a/packages/microcosm-build/src/microcosm/build/transport/compose.py +++ b/packages/microcosm-build/src/microcosm/build/transport/compose.py @@ -49,7 +49,10 @@ shapes, the receipt kernel's data-independent contract constraints, reference activation rows, required age/pin presence and null scenario knobs. Refusals name every enumerated gap (:func:`validate_transport_activation`). Optional -null JSON fields remain allowed; no proposed value is chosen. +null JSON fields remain allowed; no proposed value is chosen. A spec that +declares the opt-in ``money_normalization`` resource also gets its consumed +money columns checked for exactly one declared producer, and composition +refuses a second money producer in any population lineage. """ from __future__ import annotations @@ -68,13 +71,22 @@ ATOMIC_GEOGRAPHY_VALIDATION_TYPE, ATOMIC_SUPPORT_TYPE, ) -from microcosm.build.ledger_artifact import CHRONICLE_FEED_RESOURCE +from microcosm.build.ledger_artifact import ( + CHRONICLE_FEED_RESOURCE, + chronicle_feed_identity, +) +from microcosm.build.money_normalization import ( + money_column_rosters, + money_concept_columns, + validate_money_normalization, +) from microcosm.build.reference_profiles import resolve_reference_profiles from microcosm.calibrate.ordered_kernels import OUTPUT_TYPES from microcosm.graph import ( ArtifactInput, ArtifactOutput, ArtifactType, + CompiledGraph, Graph, Node, Owned, @@ -89,10 +101,12 @@ from .artifact_types import KERNEL_OUTPUTS from .column_kernels import validate_receipt_contract +from .money_kernels import MONEY_NORMALIZE_REF from .target_kernels import TARGET_COMPILATION_KERNELS __all__ = [ "GRAPH_RESOURCE", + "MONEY_NORMALIZATION_RESOURCE", "RULES_ROUTER", "SKELETON_KERNELS", "TransportGraphConfig", @@ -123,6 +137,7 @@ "transport.boundary@1", "transport.currency@1", "transport.quantile_map@1", + MONEY_NORMALIZE_REF, "transport.unit_attributes@1", "concepts.encode@1", "concepts.encode_groups@1", @@ -143,6 +158,16 @@ "transport.package@1", } ) +#: The opt-in spec resource that declares exact donor money normalization. +MONEY_NORMALIZATION_RESOURCE = "money_normalization" +#: The only kernels that may produce donor money after CREATE once a spec +#: declares money normalization. +_MONEY_PRODUCERS = frozenset({MONEY_NORMALIZE_REF, "transport.quantile_map@1"}) +#: Conversion changes the basis of an existing donor column; derived money +#: outputs and cross-population evidence artifacts do not change that basis. +_MONEY_CONVERSIONS = frozenset({MONEY_NORMALIZE_REF, "transport.currency@1"}) +#: Kernels outside target compilation that replay factors against the feed. +_FEED_REPLAY_KERNELS = frozenset({MONEY_NORMALIZE_REF}) _DOCUMENT_FIELDS = frozenset( { "schema_version", @@ -338,7 +363,7 @@ def _target_feed_params( """Bind the declared feed to every target compiler, including preparation.""" result = dict(params) - if kernel in TARGET_COMPILATION_KERNELS and _has_resource( + if kernel in TARGET_COMPILATION_KERNELS | _FEED_REPLAY_KERNELS and _has_resource( spec, CHRONICLE_FEED_RESOURCE ): pin = canonical_json(transport_resource(spec, CHRONICLE_FEED_RESOURCE)).decode( @@ -386,14 +411,52 @@ def reference_profile_view( else {name: value for name, value in selection.items() if name in dimensions} ) intervals = declarations.get("interval_resource") - return resolve_reference_profiles( + factor_context_resource = declarations.get("factor_context_resource") + view = resolve_reference_profiles( _raw_transport_resource(spec, resource), declarations, selected, interval_document=None if intervals is None else _raw_transport_resource(spec, intervals), + factor_context=None + if factor_context_resource is None + else _raw_transport_resource(spec, factor_context_resource), ) + if _has_resource(spec, CHRONICLE_FEED_RESOURCE): + feed_pin = _raw_transport_resource(spec, CHRONICLE_FEED_RESOURCE) + for entry in view[2].get("selected_factors", {}).values(): + if entry["declaration"]["feed_identity"] != chronicle_feed_identity( + feed_pin + ): + raise ValueError( + "Selected factor context is incompatible with the declared feed." + ) + return view + + +def reference_profile_views(spec: Mapping[str, object]) -> tuple: + """Resolve the default profile view and each distinct scenario-knob view. + + Activation and run entry share this walk. Scenario views resolve only when + both ``reference_profiles`` and ``scenarios`` are declared; knob sets that + repeat, or equal the default selection, resolve once. + """ + view = reference_profile_view(spec) + if view is None: + return () + views = [view] + if _has_resource(spec, "scenarios"): + checked_selections = {canonical_json({})} + for scenario in _sequence( + _raw_transport_resource(spec, "scenarios").get("scenarios"), "scenarios" + ): + knobs = _mapping(_mapping(scenario, "scenario").get("knobs", {}), "knobs") + identity = canonical_json(knobs) + if identity not in checked_selections: + views.append(reference_profile_view(spec, knobs)) + checked_selections.add(identity) + return tuple(views) def transport_resource( @@ -520,6 +583,269 @@ def _reference_activation_gaps(document: Mapping, resource: str) -> Iterator[str yield f"placeholder reference {resource}.{reference.get('name', '?')}" +def _declared_rows(spec: Mapping[str, object]) -> list[Mapping]: + """Skeleton rows plus the entitlement rows the factory instantiates.""" + rows = [] + if _has_resource(spec, GRAPH_RESOURCE): + nodes = transport_resource(spec, GRAPH_RESOURCE).get("nodes", ()) + rows.extend( + row + for row in _sequence(nodes, "transport nodes") + if isinstance(row, Mapping) + ) + if _has_resource(spec, "entitlement_graph") and _has_resource(spec, "scenarios"): + from .entitlement import _rows + + rows.extend(row for row, _ in _rows(spec)[0]) + return rows + + +def _owned_columns(row: Mapping, *, rewrite: bool | None = None) -> set: + return { + (output.get("entity"), output.get("column")) + for output in row.get("outputs", ()) + if isinstance(output, Mapping) + and (rewrite is None or bool(output.get("rewrite")) is rewrite) + } + + +def _derived_money_owner(key, donor_columns, kernel, *, rewrite: bool) -> bool: + """Distinguish newly derived money from the declaration's donor columns. + + A new engine output may be a money concept (for example a destination + pension), but it neither rewrites donor money nor supplies its basis. + A declared donor route always retains its stricter producer contract. + """ + return ( + key not in donor_columns + and kernel not in _MONEY_PRODUCERS | _MONEY_CONVERSIONS + and not rewrite + ) + + +def _money_producer_gaps(spec: Mapping[str, object]) -> list[str]: + """Name every consumed money column without exactly one declared producer. + + Opt-in: a spec without the money-normalization resource returns no gap + and reads nothing. Otherwise every donor money concept a node reads + after CREATE needs exactly one producer in its population lineage (its + own version and each base version): ``money.normalize@1`` for a + normalized column, ``transport.quantile_map@1`` for a quantile-mapped + one. The concept's default transport rule is not consulted. A consumed + pending or quantile-mapped column without its producer names what it + awaits. New destination money outputs are classified separately. + """ + if not _has_resource(spec, MONEY_NORMALIZATION_RESOURCE): + return [] + try: + declaration = validate_money_normalization( + _raw_transport_resource(spec, MONEY_NORMALIZATION_RESOURCE) + ) + except (ValueError, TypeError) as error: + return [f"{MONEY_NORMALIZATION_RESOURCE}.json: {error}"] + rows = _declared_rows(spec) + structural = { + row["id"]: row + for row in rows + if row.get("structural", "none") != "none" and isinstance(row.get("id"), str) + } + default = next(iter(structural)) if len(structural) == 1 else None + + def lineage(version: object) -> list[str]: + chain: list[str] = [] + while isinstance(version, str) and version in structural: + if version in chain: + break + chain.append(version) + version = structural[version].get("base") + return chain + + owners: dict[tuple, list[Mapping]] = {} + for row in rows: + if row.get("structural", "none") == "none": + version = row.get("population") or default + for entity, column in _owned_columns(row): + owners.setdefault((version, entity, column), []).append(row) + money = money_concept_columns() + rosters = money_column_rosters(declaration) + found: dict[str, set[str]] = {} + for row in rows: + reads = ( + row.get("base") + if row.get("structural", "none") != "none" + else row.get("population") or default + ) + donor_columns = set(rosters) + donor_columns.update( + key + for version in lineage(reads) + if structural[version].get("structural") == "create" + for key in _owned_columns(structural[version]) + ) + rewritten = _owned_columns(row, rewrite=True) + for item in row.get("inputs", ()): + if not isinstance(item, Mapping): + continue + for column in item.get("columns", ()): + key = (item.get("entity"), column) + if key not in money or key in rewritten: + continue + producers = [ + producer + for version in lineage(reads) + for producer in owners.get((version, *key), ()) + ] + route, awaits = rosters.get(key, (None, ())) + name = f"money column {key[0]}.{key[1]}" + if not producers: + if route == "pending": + message = f"{name} awaits {', '.join(awaits)}" + elif route == "quantile_mapped": + message = f"{name} awaits quantile map <- {', '.join(awaits)}" + elif route == "normalized": + message = f"{name} has no {MONEY_NORMALIZE_REF} producer" + else: + message = ( + f"{name} is not declared in " + f"{MONEY_NORMALIZATION_RESOURCE}.json" + ) + elif len(producers) > 1: + message = ( + f"{name} has {len(producers)} producers in one lineage " + f"{sorted(str(item.get('id')) for item in producers)}" + ) + else: + (producer,) = producers + kernel = producer.get("kernel") + if _derived_money_owner( + key, + donor_columns, + kernel, + rewrite=key in _owned_columns(producer, rewrite=True), + ): + continue + expected = { + "normalized": MONEY_NORMALIZE_REF, + "quantile_mapped": "transport.quantile_map@1", + }.get(route) + if kernel not in _MONEY_PRODUCERS: + message = ( + f"{name} is produced by {kernel} node " + f"{producer.get('id')!r}; only {MONEY_NORMALIZE_REF} or " + "transport.quantile_map@1 may produce it after CREATE" + ) + elif kernel != expected: + message = ( + f"{name} is declared {route} but produced by {kernel} " + f"node {producer.get('id')!r}" + ) + else: + continue + found.setdefault(message, set()).add(str(row.get("id"))) + return [ + f"{message} (read by {sorted(readers)})" + for message, readers in sorted(found.items()) + ] + + +def _check_single_money_normalization( + spec: Mapping[str, object], graph: Graph, compiled: CompiledGraph +) -> None: + """Track donor money ownership and currency basis through base versions. + + Opt-in like :func:`_money_producer_gaps`. A FILTER carries every column + of its base version and its members. Artifact predecessors cross versions + without changing ownership, so they never enter this structural chain. + Newly derived money is classified separately from declared donor money. + """ + if not _has_resource(spec, MONEY_NORMALIZATION_RESOURCE): + return + declaration = validate_money_normalization( + _raw_transport_resource(spec, MONEY_NORMALIZATION_RESOURCE) + ) + rosters = money_column_rosters(declaration) + money = money_concept_columns() + expected = { + MONEY_NORMALIZE_REF: "normalized", + "transport.quantile_map@1": "quantile_mapped", + } + by_id = {node.id: node for node in graph.nodes} + for (_, entity, column), owner in compiled.owners.items(): + node = by_id[owner] + if node.kernel in expected and (entity, column) in money: + route = rosters.get((entity, column), (None, ()))[0] + if route != expected[node.kernel]: + raise ValueError( + f"Money column {entity}.{column} is declared {route} but " + f"{node.kernel} node {owner!r} produces it." + ) + for version in sorted(set(compiled.versions.values())): + chain, current = [], version + while current is not None and current not in chain: + chain.append(current) + current = by_id[current].base + donor_columns = set(rosters) + donor_columns.update( + (entity, column) + for (item, entity, column), owner in compiled.owners.items() + if item in chain and by_id[owner].structural is StructuralDelta.CREATE + ) + normalizers = sorted( + node.id + for node in graph.nodes + if node.kernel == MONEY_NORMALIZE_REF + and compiled.versions[node.id] in chain + ) + if len(normalizers) > 1: + raise ValueError( + f"Money is normalized more than once in the lineage of {version!r}: " + f"{normalizers}." + ) + for entity, column in money: + producers = sorted( + owner + for item in chain + if (owner := compiled.owners.get((item, entity, column))) is not None + and by_id[owner].structural is StructuralDelta.NONE + ) + conversions = [ + owner + for owner in producers + if by_id[owner].kernel in _MONEY_CONVERSIONS + ] + if len(conversions) > 1: + raise ValueError( + f"Money is normalized more than once in the lineage of {version!r} " + f"for {entity}.{column}: {conversions}." + ) + if producers and all( + _derived_money_owner( + (entity, column), + donor_columns, + by_id[owner].kernel, + rewrite=any( + item.entity == entity and item.column == column and item.rewrite + for item in by_id[owner].outputs + ), + ) + for owner in producers + ): + continue + for owner in producers: + kernel = by_id[owner].kernel + route = rosters.get((entity, column), (None, ()))[0] + if kernel not in expected or route != expected[kernel]: + raise ValueError( + f"Money column {entity}.{column} is declared {route} but " + f"{kernel} node {owner!r} produces it." + ) + if len(producers) > 1: + raise ValueError( + f"Money column {entity}.{column} has {len(producers)} producers " + f"in the lineage of {version!r}: {producers}." + ) + + def validate_transport_activation(spec: Mapping[str, object]) -> None: """Refuse enumerated activation gaps before registry/CREATE/source reads. @@ -535,7 +861,7 @@ def validate_transport_activation(spec: Mapping[str, object]) -> None: missing = [] # Resolve first so activation never inspects an executable profile's raw rows. - reference_profile_view(spec) + reference_profile_views(spec) selections = ( tuple(_declared_resource_selections(transport_resource(spec, GRAPH_RESOURCE))) if _has_resource(spec, GRAPH_RESOURCE) @@ -645,6 +971,7 @@ def validate_transport_activation(spec: Mapping[str, object]) -> None: missing.append(f"scenario {scenario.get('id', '?')} {path}") if not _has_resource(spec, GRAPH_RESOURCE): missing.append(f"{GRAPH_RESOURCE}.json (the skeleton declaration)") + missing.extend(_money_producer_gaps(spec)) if missing: raise ValueError("Transport spec is not activated: " + "; ".join(missing) + ".") @@ -1199,9 +1526,17 @@ def _node( ): view = reference_profile_view(spec, profile_selection) if view is not None: - references, _, receipt = view + references, intervals, receipt = view if canonical_json(references).decode("utf-8") == params["references"]: params["reference_resolution"] = canonical_json(receipt).decode("utf-8") + # Preparation receives the exact effective interval companion. + if kernel == "calibration.prepare@1" and intervals is not None: + companion = canonical_json(intervals).decode("utf-8") + if params.setdefault("intervals", companion) != companion: + raise ValueError( + f"Node {row.get('id')!r} supplies an interval document " + "that differs from its effective interval companion." + ) if "rules_binding" in row: params = _rules_params(spec, row, params, config) elif kernel == RULES_ROUTER: @@ -1350,6 +1685,8 @@ def compose_transport_graph( ) compiled = compile_graph(skeleton) validate_transport_calibration_ancestry(spec, graph=skeleton, config=config) + # Money ownership is checked after each graph pass of the ancestry guard. + _check_single_money_normalization(spec, skeleton, compiled) endpoints = transport_endpoints(spec) named = {endpoints["geography"], endpoints["calibration"], *endpoints["terminal"]} if not named <= set(compiled.order): @@ -1438,6 +1775,8 @@ def compose_transport_graph( extended = compile_graph(graph) _check_extension_checkpoints(spec, config, graph, extended) validate_transport_calibration_ancestry(spec, graph=graph, config=config) + # Money ownership is checked after each graph pass of the ancestry guard. + _check_single_money_normalization(spec, graph, extended) for node in skeleton.nodes: expected = ( set(compiled.predecessors[node.id]) diff --git a/packages/microcosm-build/src/microcosm/build/transport/money_kernels.py b/packages/microcosm-build/src/microcosm/build/transport/money_kernels.py new file mode 100644 index 0000000000..c42e0093b1 --- /dev/null +++ b/packages/microcosm-build/src/microcosm/build/transport/money_kernels.py @@ -0,0 +1,295 @@ +"""Exact donor money normalization as a graph kernel. + +``money.normalize@1`` rewrites every column a spec's money-normalization +declaration lists, once, and adds each column's integer minor-unit companion. +Its parameters are the declaration (canonical JSON) and the factor context +(the country's declared factor document), each with its resource digest, plus +the scenario's profile selection. The node reads exactly one source, the +authenticated Chronicle feed: every selected factor is replayed against it +with :func:`~microcosm.build.reference_factors.replay_reference_factor`, as +target compilation does, so a self-consistent receipt alone never +authenticates a factor. A feed-selected exchange rate is replayed from the +same facts. + +CREATE records the donor's model period in the frame metadata +(``donor_time_period``); the kernel refuses a frame whose recorded period is +absent, null or different from the declaration's. This checks consistency +with the declared money basis; it does not authenticate that basis. +""" + +from __future__ import annotations + +import pandas as pd + +import microcosm.build.chronicle_epoch as chronicle_epoch_module +import microcosm.build.ledger_artifact as ledger_artifact_module +import microcosm.build.ledger_targets as ledger_targets_module +import microcosm.build.money_normalization as money_normalization_module +import microcosm.build.reference_factors as reference_factors_module +import microcosm.frame.concepts as concepts_module +from microcosm.build.ledger_artifact import ( + chronicle_feed_identity, + load_ledger_consumer_artifact, + validate_chronicle_feed_pin, +) +from microcosm.build.money_normalization import ( + money_factor_inputs, + normalize_money_tables, + normalized_column_families, + resolve_money_rate, + selected_money_factors, + validate_money_normalization, +) +from microcosm.build.reference_factors import ( + replay_reference_factor, + validate_factor_context, +) +from microcosm.graph import ( + ROWS_ALL, + Capabilities, + Determinism, + KernelBase, + KernelContext, + KernelRegistry, + KernelResult, + Numeric, + SeedSource, + source_hash, +) + +from . import graph_inputs +from .graph_inputs import ( + canonical_document_param, + require_params, + sha256_param, + single_source, +) + +__all__ = [ + "MONEY_NORMALIZE", + "MONEY_NORMALIZE_REF", + "MoneyNormalizeKernel", + "register_money_kernels", +] + +MONEY_NORMALIZE_REF = "money.normalize@1" +#: The frame-metadata key CREATE writes from the donor's ``_time_period``. +DONOR_TIME_PERIOD = "donor_time_period" + + +def _recorded_period(context: KernelContext, declaration) -> int: + if DONOR_TIME_PERIOD not in context.frame_metadata: + raise ValueError( + f"{MONEY_NORMALIZE_REF} needs the frame's recorded {DONOR_TIME_PERIOD}; " + "CREATE records it from the donor." + ) + recorded = context.frame_metadata[DONOR_TIME_PERIOD] + declared = declaration["donor_money_period"] + if recorded is None: + raise ValueError( + f"The donor records no money period, but the declaration names " + f"{declared['type']} {declared['value']}." + ) + if type(recorded) is not int or recorded != declared["value"]: + raise ValueError( + f"The donor's recorded money period {recorded!r} differs from the " + f"declared {declared['type']} {declared['value']}." + ) + return recorded + + +def _authenticated_facts(context: KernelContext, entries) -> tuple[tuple, dict]: + """Load the one facts source and authenticate it as target compilation does.""" + source = single_source(context, MONEY_NORMALIZE_REF) + feed_pin = ( + canonical_document_param(context, MONEY_NORMALIZE_REF, "chronicle_feed_pin") + if "chronicle_feed_pin" in context.params + else None + ) + artifact = load_ledger_consumer_artifact( + context.sources[source], feed_pin=feed_pin, lexical_floats=True + ) + identities = [] + for entry in entries: + identity = chronicle_feed_identity( + feed_pin if feed_pin is not None else entry["declaration"]["feed_identity"] + ) + if feed_pin is None: + validate_chronicle_feed_pin(artifact, identity) + if (artifact.manifest or {}).get("source_commit") != identity[ + "source_commit" + ]: + raise ValueError( + "Factor source commit cannot be authenticated without a feed pin." + ) + replay_reference_factor( + entry["declaration"], + artifact.facts, + identity, + entry["operational_factor"], + entry["receipt"], + ) + identities.append(identity) + if feed_pin is not None: + identity = chronicle_feed_identity(feed_pin) + elif identities: + identity = identities[0] + else: + identity = None + return artifact.facts, { + "identity": identity, + "facts_sha256": artifact.facts_sha256, + "manifest_sha256": artifact.manifest_sha256, + } + + +class MoneyNormalizeKernel(KernelBase): + """``money.normalize@1``: declared exact currency and period normalization.""" + + ref = MONEY_NORMALIZE_REF + capabilities = Capabilities( + Determinism.DETERMINISTIC, + numeric=Numeric.BITWISE, + seed_source=SeedSource.NONE, + dependencies=("numpy", "pandas"), + ) + _required = frozenset( + { + "declaration", + "declaration_sha256", + "factor_context", + "factor_context_sha256", + "profile_selection", + } + ) + + def implementation_hash(self) -> str: + return source_hash( + type(self), + graph_inputs, + money_normalization_module, + reference_factors_module, + ledger_artifact_module, + ledger_targets_module, + chronicle_epoch_module, + concepts_module, + dependencies=self.capabilities.dependencies, + ) + + def run(self, context: KernelContext) -> KernelResult: + require_params( + context, + self.ref, + required=self._required, + optional=frozenset({"chronicle_feed_pin"}), + ) + if context.artifacts or context.node.artifact_outputs: + raise ValueError(f"{self.ref} reads and writes no artifacts.") + if any(item.rows != ROWS_ALL for item in context.node.inputs) or any( + item.rows != ROWS_ALL for item in context.node.outputs + ): + raise ValueError(f"{self.ref} requires whole-row slices and outputs.") + for name in ("declaration_sha256", "factor_context_sha256"): + sha256_param(context, self.ref, name) + declaration = validate_money_normalization( + canonical_document_param(context, self.ref, "declaration") + ) + period = _recorded_period(context, declaration) + profile, chosen = selected_money_factors( + declaration, + canonical_document_param(context, self.ref, "profile_selection"), + ) + families = normalized_column_families(declaration) + suffix = declaration["cents_suffix"] + expected_outputs = { + (entity, column, "float64", True) for entity, column in families + } | { + (entity, f"{column}{suffix}", "int64", False) for entity, column in families + } + declared_outputs = { + (item.entity, item.column, item.dtype, item.rewrite) + for item in context.node.outputs + } + if declared_outputs != expected_outputs: + raise ValueError( + f"{self.ref} must rewrite exactly the declared money columns as " + "float64 and add each int64 companion, and own nothing else." + ) + sliced = { + (item.entity, column) + for item in context.node.inputs + for column in item.columns + } + if sliced != set(families): + raise ValueError( + f"{self.ref} slices exactly the declared money columns it rewrites." + ) + factor_ids = sorted({value for value in chosen.values() if value is not None}) + entries = validate_factor_context( + canonical_document_param(context, self.ref, "factor_context"), factor_ids + ) + inputs = { + factor_id: money_factor_inputs(entries[factor_id], declaration) + for factor_id in factor_ids + } + facts, feed = _authenticated_facts( + context, [entries[factor_id] for factor_id in factor_ids] + ) + rate, rate_evidence = resolve_money_rate(declaration, facts, feed["identity"]) + factors = { + family: ( + { + "identity": True, + "reason": declaration["index_family_factors"][family]["reason"], + } + if factor_id is None + else inputs[factor_id] + ) + for family, factor_id in chosen.items() + } + arrays, columns = normalize_money_tables( + context.tables, + declaration, + rate=rate, + rate_text=rate_evidence["text"], + factors=factors, + ) + result = {} + for (entity, column), values in arrays.items(): + ids = context.tables[entity][f"{entity}_id"] + result[entity, column] = pd.Series( + values, + index=pd.Index(ids.to_numpy(copy=True), name=f"{entity}_id"), + name=column, + dtype=values.dtype, + ) + return KernelResult( + columns=result, + receipt={ + "declaration_sha256": context.params["declaration_sha256"], + "factor_context_sha256": context.params["factor_context_sha256"], + "from_currency": declaration["from_currency"], + "to_currency": declaration["to_currency"], + "quote": declaration["quote"], + "rounding": dict(declaration["rounding"]), + "rate": rate_evidence, + "donor_money_period": dict(declaration["donor_money_period"]), + "recorded_donor_time_period": period, + "profile": profile, + "factors": {family: factors[family] for family in sorted(factors)}, + "feed": { + "facts_sha256": feed["facts_sha256"], + "manifest_sha256": feed["manifest_sha256"], + }, + "columns": columns, + }, + ) + + +MONEY_NORMALIZE = MoneyNormalizeKernel() + + +def register_money_kernels(registry: KernelRegistry) -> KernelRegistry: + """Register the money kernel explicitly and idempotently.""" + registry.register(MONEY_NORMALIZE) + return registry diff --git a/packages/microcosm-build/src/microcosm/build/transport/population_kernels.py b/packages/microcosm-build/src/microcosm/build/transport/population_kernels.py index ccbc6a609b..ed6ba2368f 100644 --- a/packages/microcosm-build/src/microcosm/build/transport/population_kernels.py +++ b/packages/microcosm-build/src/microcosm/build/transport/population_kernels.py @@ -273,6 +273,7 @@ def run(self, context: KernelContext) -> KernelResult: "donor_country": donor.donor_country, "country": country, "donor_currency": donor.currency, + "donor_time_period": donor.time_period, }, ) return KernelResult( @@ -281,6 +282,7 @@ def run(self, context: KernelContext) -> KernelResult: "content_basis": donor.content_basis.value, "donor_country": donor.donor_country, "country": country, + "donor_time_period": donor.time_period, "dropped_columns": { entity: list(names) for entity, names in dropped.items() }, diff --git a/packages/microcosm-build/src/microcosm/build/transport/registry.py b/packages/microcosm-build/src/microcosm/build/transport/registry.py index 57a9d74497..269dcb3a29 100644 --- a/packages/microcosm-build/src/microcosm/build/transport/registry.py +++ b/packages/microcosm-build/src/microcosm/build/transport/registry.py @@ -46,6 +46,7 @@ from .gate_bindings import TRANSPORT_GATE_REGISTRY from .gate_kernels import register_gate_kernel from .geography_kernels import register_geography_kernels +from .money_kernels import register_money_kernels from .population_kernels import register_population_kernels from .reporting_kernels import register_reporting_kernels from .takeup_kernels import register_takeup_kernels @@ -388,7 +389,7 @@ def build_transport_registry( def register_transport_population_kernels(registry: KernelRegistry) -> KernelRegistry: - """Install the ten population kernels without import-time side effects. + """Install the eleven population kernels without import-time side effects. Source codecs are separately installed with ``register_transport_codecs``; target, solver and terminal kernels keep their own registration functions. @@ -397,4 +398,5 @@ def register_transport_population_kernels(registry: KernelRegistry) -> KernelReg register_population_kernels(registry) register_column_kernels(registry) register_geography_kernels(registry) + register_money_kernels(registry) return registry diff --git a/packages/microcosm-build/src/microcosm/build/transport/target_kernels.py b/packages/microcosm-build/src/microcosm/build/transport/target_kernels.py index 2752a924a0..46f25de2fa 100644 --- a/packages/microcosm-build/src/microcosm/build/transport/target_kernels.py +++ b/packages/microcosm-build/src/microcosm/build/transport/target_kernels.py @@ -53,6 +53,8 @@ import microcosm.build.country_spec as country_spec_module import microcosm.build.ledger_artifact as ledger_artifact_module import microcosm.build.ledger_targets as ledger_targets_module +import microcosm.build.reference_factors as reference_factors_module +import microcosm.build.reference_intervals as reference_intervals_module import microcosm.build.reference_profiles as reference_profiles_module import microcosm.calibrate.artifacts as calibrate_artifacts_module import microcosm.calibrate.hierarchy as hierarchy_module @@ -71,7 +73,9 @@ compile_ledger_target_references, ) from microcosm.build.reference_profiles import ( + ALLOWED_REFERENCE_PROFILE_RESOLVER_VERSIONS, REFERENCE_DOCUMENT_KEYS, + validate_reference_factor_evidence, validate_reference_resolution, ) from microcosm.calibrate import TargetRegistry, TargetSpec @@ -274,8 +278,7 @@ def _surface_resolution(document: Mapping, registry: TargetRegistry) -> Mapping body = dict(resolution) claimed = body.pop("receipt_sha256", None) if ( - body.get("resolver_version") - != reference_profiles_module.REFERENCE_PROFILE_RESOLVER_VERSION + body.get("resolver_version") not in ALLOWED_REFERENCE_PROFILE_RESOLVER_VERSIONS or claimed != reference_profiles_module.reference_document_sha256(body) ): raise ValueError("Surface reference resolution receipt hash mismatch.") @@ -291,6 +294,7 @@ def _surface_resolution(document: Mapping, registry: TargetRegistry) -> Mapping ) from error if selection != resolution.get("reference_selection_sha256"): raise ValueError("Surface reference selection identity mismatch.") + validate_reference_factor_evidence(resolution) for spec in resolved: if spec.metadata.get( "reference_selection_sha256" @@ -396,8 +400,37 @@ def compile_target_surface( document, reference_resolution, references_sha256=references_sha256 ) artifact = load_ledger_consumer_artifact( - facts_path, expected_facts_sha256=expected_facts_sha256, feed_pin=feed_pin + facts_path, + expected_facts_sha256=expected_facts_sha256, + feed_pin=feed_pin, + lexical_floats=bool( + reference_resolution and reference_resolution.get("selected_factors") + ), ) + if reference_resolution is not None: + for entry in reference_resolution.get("selected_factors", {}).values(): + factor_feed_identity = ledger_artifact_module.chronicle_feed_identity( + feed_pin + if feed_pin is not None + else entry["declaration"]["feed_identity"] + ) + if feed_pin is None: + ledger_artifact_module.validate_chronicle_feed_pin( + artifact, factor_feed_identity + ) + if (artifact.manifest or {}).get( + "source_commit" + ) != factor_feed_identity["source_commit"]: + raise ValueError( + "Factor source commit cannot be authenticated without a feed pin." + ) + reference_factors_module.replay_reference_factor( + entry["declaration"], + artifact.facts, + factor_feed_identity, + entry["operational_factor"], + entry["receipt"], + ) registry = compile_ledger_target_references( artifact.facts, references, country=country ) @@ -489,6 +522,7 @@ def _optional_float(value: float) -> float | None: ledger_targets_module, ledger_artifact_module, chronicle_epoch_module, + reference_factors_module, reference_profiles_module, registry_module, target_module, @@ -524,6 +558,8 @@ def implementation_hash(self) -> str: graph_inputs, artifact_types, calibration_columns_module, + reference_intervals_module, + reference_factors_module, reference_profiles_module, dependencies=self.capabilities.dependencies, ) @@ -552,6 +588,12 @@ def run(self, context: KernelContext) -> KernelResult: bindings = canonical_document_param(context, self.ref, "bindings") document = canonical_document_param(context, self.ref, "references") digest = sha256_param(context, self.ref, "references_sha256") + # Parsed once: the receipt check and column preparation read one document. + intervals = ( + canonical_document_param(context, self.ref, "intervals") + if "intervals" in context.params + else None + ) resolution = None if "reference_resolution" in context.params: candidate = canonical_document_param( @@ -559,7 +601,7 @@ def run(self, context: KernelContext) -> KernelResult: ) if ( candidate.get("effective_intervals_sha256") is not None - and "intervals" not in context.params + and intervals is None ): raise ValueError( "Calibration preparation requires its effective interval companion." @@ -568,11 +610,7 @@ def run(self, context: KernelContext) -> KernelResult: document, candidate, references_sha256=digest, - interval_document=( - canonical_document_param(context, self.ref, "intervals") - if "intervals" in context.params - else None - ), + interval_document=intervals, ) elif any( row.get("metadata", {}).get("reference_profiles_resolved") @@ -581,7 +619,13 @@ def run(self, context: KernelContext) -> KernelResult: raise ValueError( "Profile-resolved preparation requires a reference resolution receipt." ) - tables = prepare_calibration_columns(context.tables, bindings) + if intervals is not None and resolution is None: + raise ValueError( + "An interval companion requires its reference resolution receipt." + ) + tables = prepare_calibration_columns( + context.tables, bindings, intervals=intervals + ) inventory = { (row["entity"], row["name"]) for row in (*bindings["columns"], *bindings.get("roster_counts", ())) @@ -619,6 +663,10 @@ def run(self, context: KernelContext) -> KernelResult: for entity, name in sorted(inventory) ], } + interval_inputs = _interval_input_evidence(context, bindings, intervals) + if interval_inputs: + evidence["effective_intervals"] = intervals + evidence["interval_inputs"] = interval_inputs payload = canonical_json(evidence) return KernelResult( columns=columns, @@ -627,13 +675,77 @@ def run(self, context: KernelContext) -> KernelResult: ) +def _interval_tables(bindings: Mapping, intervals: Mapping | None) -> list: + """The validated tables named by a binding's interval outputs, in order.""" + named = [] + for row in bindings.get("columns", ()): + if isinstance(row, Mapping) and "interval" in row: + if not isinstance(row["interval"], Mapping): + raise ValueError("An interval output names its table and band.") + table = row["interval"].get("table") + if table not in named: + named.append(table) + if not named: + return [] + if intervals is None: + raise ValueError( + "Interval outputs require their effective interval evidence; it is missing." + ) + tables = reference_intervals_module.validate_interval_document(intervals) + if set(named) - set(tables): + raise ValueError("Interval outputs name an undeclared interval table.") + return [tables[name] for name in named] + + +def _interval_input_rows(context: KernelContext, tables: Sequence) -> list[dict]: + rows = [] + for table in tables: + data = context.tables.get(table.entity) + if data is None or any( + column not in data + for column in ( + f"{table.entity}_id", + table.income_column, + table.universe_column, + ) + ): + raise ValueError( + f"Interval evidence for {table.id!r} is missing its " + f"{table.entity} income, universe or identity column." + ) + fingerprint = reference_intervals_module.interval_input_fingerprint + rows.append( + { + "table": table.id, + "entity": table.entity, + "income_column": table.income_column, + "universe_column": table.universe_column, + "rows": len(data), + "entity_ids_sha256": sha256_text( + canonical_json(data[f"{table.entity}_id"].tolist()) + ), + "income_values_sha256": fingerprint(data[table.income_column].tolist()), + "universe_values_sha256": fingerprint( + data[table.universe_column].tolist() + ), + } + ) + return rows + + +def _interval_input_evidence( + context: KernelContext, bindings: Mapping, intervals: Mapping | None +) -> list[dict]: + return _interval_input_rows(context, _interval_tables(bindings, intervals)) + + def _preparation_binding( context: KernelContext, surface: TargetSurface -) -> dict[str, object]: +) -> tuple[dict[str, object], list]: resolution = surface.reference_resolution artifact = context.artifacts.get("preparation") if resolution is None and artifact is None: - return {} + return {}, [] if artifact is None or artifact.type != CALIBRATION_PREPARATION_TYPE: raise ValueError( "Resolved targets require a typed calibration preparation artifact." @@ -687,11 +799,51 @@ def _preparation_binding( raise ValueError( "Calibration preparation inventory differs from the problem population." ) + intervals = evidence.get("effective_intervals") + if intervals is not None and sha256_text(canonical_json(intervals)) != evidence.get( + "effective_intervals_sha256" + ): + raise ValueError("Calibration preparation interval evidence hash mismatch.") + tables = _interval_tables(bindings, intervals) + if (intervals is None) != (not tables) or evidence.get( + "interval_inputs", [] + ) != _interval_input_rows(context, tables): + raise ValueError( + "Calibration preparation interval inputs differ from the problem population." + ) return { "reference_resolution": resolution, "preparation_sha256": sha256_text(artifact.payload), "preparation_artifact_key": artifact.key, **expected, + }, tables + + +def _interval_diagnostics( + frame: Frame, tables: Sequence, entities: Sequence[str], intervals_sha256 +) -> dict[str, object]: + """Fixed-weight boundary diagnostics; no reference value or target changes.""" + if not tables: + return {} + reports = [] + for table in tables: + if table.entity not in entities: + raise ValueError( + f"Interval diagnostics for {table.id!r} are missing evidence: the " + f"problem frame has no {table.entity!r} entity." + ) + reports.append( + reference_intervals_module.frame_boundary_diagnostics(table, frame) + ) + report = { + "schema_version": 1, + "kind": "interval_boundary_diagnostics_report", + "effective_intervals_sha256": intervals_sha256, + "tables": reports, + } + return { + "interval_diagnostics": report, + "interval_diagnostics_sha256": sha256_text(canonical_json(report)), } @@ -762,6 +914,7 @@ def implementation_hash(self) -> str: registry_module, hierarchy_module, calibrate_artifacts_module, + reference_intervals_module, *_MATRIX_MODULES, dependencies=self.capabilities.dependencies, ) @@ -778,10 +931,16 @@ def run(self, context: KernelContext) -> KernelResult: "under the alias 'surface'." ) surface = decode_target_surface(value.payload) - preparation = _preparation_binding(context, surface) + preparation, interval_tables = _preparation_binding(context, surface) frame = context_frame( context, self.ref, entities=entities, weight_entity=weight_entity ) + diagnostics = _interval_diagnostics( + frame, + interval_tables, + entities, + preparation.get("effective_intervals_sha256"), + ) problem = _compiled_problem(frame, surface.registry, weight_entity, self.ref) target_metadata = [ { @@ -808,6 +967,7 @@ def run(self, context: KernelContext) -> KernelResult: "entities": list(entities), "weight_entity": weight_entity, **preparation, + **diagnostics, }, ) return KernelResult( @@ -820,6 +980,7 @@ def run(self, context: KernelContext) -> KernelResult: "n_records": int(problem.n_weights), "weight_kind": problem.initial_weights.kind.value, **preparation, + **diagnostics, }, ) diff --git a/packages/microcosm-build/src/microcosm/build/uk_runtime/target_compile_modules.py b/packages/microcosm-build/src/microcosm/build/uk_runtime/target_compile_modules.py index 58ba918311..642a1e912a 100644 --- a/packages/microcosm-build/src/microcosm/build/uk_runtime/target_compile_modules.py +++ b/packages/microcosm-build/src/microcosm/build/uk_runtime/target_compile_modules.py @@ -23,6 +23,7 @@ UK_TARGET_COMPILE_MODULE_NAMES: tuple[str, ...] = ( "microcosm.build.cross_grain", + "microcosm.build.ledger_artifact", "microcosm.build.ledger_targets", "microcosm.build.target_materialization", "microcosm.build.target_reference_authoring", @@ -66,8 +67,10 @@ "refuses an undeclared condition and never moves a value" ), "microcosm.build.chronicle_epoch": ( - "the feed-pin epoch arithmetic chronicle_feed checks; the pinned " - "facts themselves are node inputs hashed by content" + "the feed-pin epoch arithmetic chronicle_feed checks, and the " + "consumer schema gate and epoch labels ledger_artifact applies; " + "none moves a fact value, and the pinned facts themselves are node " + "inputs hashed by content" ), "microcosm.build.gates": ( "imported for the GateResult and TargetCoverageRequirement types only" diff --git a/packages/microcosm-build/tests/engine_free/shared/test_calibration_columns.py b/packages/microcosm-build/tests/engine_free/shared/test_calibration_columns.py index c3eda2039d..2c7c73cd21 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_calibration_columns.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_calibration_columns.py @@ -18,6 +18,10 @@ from microcosm.calibrate.matrix import build_constraint_matrix from microcosm.frame import EntitySchema, Frame, WeightKind, Weights from microcosm.frame.concepts import TENURE_DOMAIN +from test_support.microcosm_build.reference_intervals import ( + interval_bindings, + interval_table_fixture, +) from test_support.paths import paths_for @@ -182,10 +186,15 @@ def test_receipt_age_indicators_retain_the_published_strata_boundaries(): def test_all_active_calibration_bindings_enter_the_small_frame_matrix(): frame = _frame(prepare_calibration_columns(_tables(), _bindings())) root = paths_for("microcosm-build").package / "src/microcosm/build/nz" + profiles = json.loads((root / "reference_profiles.json").read_text()) effective, _, _ = resolve_reference_profiles( json.loads((root / "target_references.json").read_text()), - json.loads((root / "reference_profiles.json").read_text()), + profiles, {}, + json.loads((root / profiles["interval_resource"]).read_text()), + factor_context=json.loads( + (root / profiles["factor_context_resource"]).read_text() + ), ) references = [ ref @@ -336,3 +345,192 @@ def test_malformed_preparation_contracts_refuse(defect): bindings["columns"][0]["constant"] = float("nan") with pytest.raises(ValueError): prepare_calibration_columns(_tables(), bindings) + + +# --------------------------------------------------------------------------- +# Interval outputs: their own binding kind beside the predicate outputs +# --------------------------------------------------------------------------- + + +def _interval_contract(entity="person"): + table = interval_table_fixture(entity=entity, source_period=2002) + bindings = interval_bindings(table) + bindings["columns"].append({"entity": entity, "name": "counted", "constant": 1}) + return {"schema_version": 1, "tables": [table]}, bindings + + +def _interval_tables(amounts, statuses, entity="person"): + return { + entity: pd.DataFrame( + { + f"{entity}_id": range(len(amounts)), + "toy_income_cents": amounts, + "toy_income_universe": statuses, + } + ) + } + + +def test_interval_outputs_follow_the_universe_and_recompute_stale_values(): + intervals, bindings = _interval_contract() + tables = _interval_tables( + [-50, 0, 0, 100_000, 100_001, 5_000_000], + ["included", "included", "excluded", "included", "included", "excluded"], + ) + tables["person"]["toy_income_band_nil"] = [True] * 6 + prepared = prepare_calibration_columns(tables, bindings, intervals=intervals) + person = prepared["person"] + # A nonfiler with no income is excluded: never an all-person nil indicator. + assert person["toy_income_band_nil"].tolist() == [ + True, + True, + False, + False, + False, + False, + ] + assert person["toy_income_band_b0"].tolist() == [ + False, + False, + False, + True, + False, + False, + ] + assert person["toy_income_band_b1"].tolist() == [False] * 4 + [True, False] + assert person["toy_income_band_b2"].tolist() == [False] * 6 + assert person["toy_income_band_b0"].dtype == bool + assert person["counted"].tolist() == [1] * 6 + assert tables["person"]["toy_income_band_nil"].tolist() == [True] * 6 + tables["person"].loc[3, "toy_income_cents"] = 100_001 + fresh = prepare_calibration_columns(tables, bindings, intervals=intervals) + assert fresh["person"].loc[3, "toy_income_band_b1"] + assert not fresh["person"].loc[3, "toy_income_band_b0"] + + +@pytest.mark.parametrize( + ("amount", "status"), + [ + (10, "unresolved"), + (10, "unknown"), + (True, "included"), + (10.0, "included"), + ("10", "included"), + (pd.NA, "included"), + ], +) +def test_unresolved_universe_or_invalid_amount_refuses_before_any_output( + amount, status +): + intervals, bindings = _interval_contract() + tables = _interval_tables(pd.array([5, amount], dtype=object), ["included", status]) + before = copy.deepcopy(tables) + with pytest.raises(ValueError, match="refused before emitting indicators"): + prepare_calibration_columns(tables, bindings, intervals=intervals) + pd.testing.assert_frame_equal(tables["person"], before["person"]) + + +def test_household_entity_interval_outputs_are_entity_generic(): + intervals, bindings = _interval_contract(entity="household") + prepared = prepare_calibration_columns( + _interval_tables([0, 100_000], ["excluded", "included"], entity="household"), + bindings, + intervals=intervals, + ) + assert prepared["household"]["toy_income_band_b0"].tolist() == [False, True] + assert prepared["household"]["toy_income_band_nil"].tolist() == [False, False] + + +@pytest.mark.parametrize( + "defect", + [ + "no_document", + "unknown_table", + "unknown_band", + "wrong_entity", + "not_the_band_indicator", + "mixed_with_predicates", + "mixed_with_constant", + "extra_interval_field", + "unaged_source_cuts", + "missing_universe_column", + "missing_income_column", + "invalid_document", + ], +) +def test_malformed_interval_outputs_refuse(defect): + intervals, bindings = _interval_contract() + entry = bindings["columns"][1] + tables = _interval_tables([1, 2], ["included", "included"]) + if defect == "no_document": + intervals = None + elif defect == "unknown_table": + entry["interval"]["table"] = "other" + elif defect == "unknown_band": + entry["interval"]["band"] = "b9" + elif defect == "wrong_entity": + bindings["required_inputs"]["household"] = ["household_id"] + bindings["input_domains"]["household"] = {"household_id": {"kind": "identity"}} + tables["household"] = pd.DataFrame({"household_id": [0]}) + entry["entity"] = "household" + elif defect == "not_the_band_indicator": + entry["name"] = "renamed_indicator" + elif defect == "mixed_with_predicates": + entry["predicates"] = [{"column": "person_id", "operator": "ge", "value": 0}] + elif defect == "mixed_with_constant": + entry["constant"] = 1 + elif defect == "extra_interval_field": + entry["interval"]["closure"] = "left" + elif defect == "unaged_source_cuts": + intervals["tables"][0]["source_period"] = 2001 + elif defect == "missing_universe_column": + tables["person"] = tables["person"].drop(columns="toy_income_universe") + elif defect == "missing_income_column": + tables["person"] = tables["person"].drop(columns="toy_income_cents") + else: + intervals["tables"][0].pop("positive_band_closure") + with pytest.raises(ValueError): + prepare_calibration_columns(tables, bindings, intervals=intervals) + + +def test_interval_outputs_enter_a_constraint_row_through_their_filter(): + intervals, bindings = _interval_contract() + tables = _interval_tables([0, 50_000, 150_000], ["included"] * 3) + tables["household"] = pd.DataFrame({"household_id": [0, 1, 2]}) + tables["person"]["person_household_id"] = [0, 1, 2] + prepared = prepare_calibration_columns(tables, bindings, intervals=intervals) + frame = _frame(prepared) + target = Target( + name="first band", + entity="person", + measure="toy_income_cents", + filter="toy_income_band_b0", + ) + assert target.constraint_row(frame).tolist() == [0, 50_000, 0] + + +def test_signed_integer_domain_admits_losses_and_refuses_booleans_and_floats(): + bindings = { + "schema_version": 1, + "required_inputs": {"person": ["person_id", "net_cents"]}, + "input_domains": { + "person": { + "person_id": {"kind": "identity"}, + "net_cents": {"kind": "signed_integer"}, + } + }, + "columns": [ + { + "entity": "person", + "name": "loss_indicator", + "predicates": [{"column": "net_cents", "operator": "lt", "value": 0}], + } + ], + } + tables = {"person": pd.DataFrame({"person_id": [0, 1], "net_cents": [-5, 7]})} + prepared = prepare_calibration_columns(tables, bindings) + assert prepared["person"]["loss_indicator"].tolist() == [True, False] + for values in ([True, False], [-5.0, 7.0], pd.array([-5, None], dtype="Int64")): + tables["person"]["net_cents"] = values + with pytest.raises(ValueError): + prepare_calibration_columns(tables, bindings) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_ledger_artifact.py b/packages/microcosm-build/tests/engine_free/shared/test_ledger_artifact.py index 1307e09d06..2c59d73e20 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_ledger_artifact.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_ledger_artifact.py @@ -2,11 +2,14 @@ from __future__ import annotations +import ast import hashlib import json import os +import pickle import runpy from argparse import ArgumentParser, Namespace +from copy import deepcopy from dataclasses import replace from pathlib import Path @@ -18,11 +21,13 @@ CONSUMER_ARTIFACT_SCHEMA_VERSION, ChronicleFeedPinMismatchError, LedgerConsumerArtifact, + LexicalFloat, add_ledger_artifact_args, load_ledger_consumer_artifact, resolve_ledger_artifact, validate_chronicle_feed_pin, ) +from microcosm.graph.canonical import canonical_json from test_support.microcosm_build.chronicle_feed import write_pinned_artifact from test_support.paths import paths_for @@ -87,6 +92,110 @@ def test_loads_bare_consumer_facts_file(tmp_path): assert provenance["schema_version"] is None +def test_authenticated_float_retains_lexical_token_without_changing_json(tmp_path): + facts_path = tmp_path / "consumer_facts.jsonl" + original = '{"aggregate_fact_key":"decimal","value":1.2500e2}\n' + facts_path.write_text(original) + facts_sha = hashlib.sha256(original.encode()).hexdigest() + artifact = load_ledger_consumer_artifact( + facts_path, expected_facts_sha256=facts_sha, lexical_floats=True + ) + value = artifact.facts[0]["value"] + assert isinstance(value, LexicalFloat) + assert not hasattr(value, "__dict__") + assert value.lexical_token == "1.2500e2" + assert value == 125.0 + assert hash(value) == hash(125.0) + assert json.dumps(value).encode() == json.dumps(125.0).encode() + assert json.dumps(artifact.facts[0], sort_keys=True) == json.dumps( + json.loads(original), sort_keys=True + ) + assert artifact.facts_sha256 == facts_sha + + +@pytest.mark.parametrize("pinned", [False, True]) +def test_default_float_loading_returns_exact_builtin_float(tmp_path, pinned): + path, pin = write_pinned_artifact(tmp_path / "artifact", [_fact_row(value=0.1)]) + artifact = load_ledger_consumer_artifact( + path, **({"feed_pin": pin} if pinned else {}) + ) + + assert type(artifact.facts[0]["value"]) is float + + +def test_lexical_float_opt_in_is_limited_to_factor_consumers(): + repository = paths_for("microcosm-build").repository + sources = list((repository / "packages").glob("*/src/**/*.py")) + sources.extend((repository / "tools").rglob("*.py")) + opt_ins = set() + for path in sources: + for node in ast.walk(ast.parse(path.read_text())): + if not isinstance(node, ast.Call): + continue + for keyword in node.keywords: + if keyword.arg != "lexical_floats": + continue + # A loader forwarding its caller's choice is not an opt-in. + if ( + isinstance(keyword.value, ast.Name) + and keyword.value.id == "lexical_floats" + ): + continue + if ( + isinstance(keyword.value, ast.Constant) + and keyword.value.value is False + ): + continue + opt_ins.add(path.relative_to(repository).as_posix()) + assert opt_ins == { + "packages/microcosm-build/src/microcosm/build/transport/money_kernels.py", + "packages/microcosm-build/src/microcosm/build/transport/target_kernels.py", + "tools/generate_nz_reference_aging.py", + } + + +@pytest.mark.parametrize("protocol", range(pickle.HIGHEST_PROTOCOL + 1)) +def test_authenticated_float_pickle_preserves_lexical_token(tmp_path, protocol): + facts_path = tmp_path / "consumer_facts.jsonl" + facts_path.write_text('{"aggregate_fact_key":"decimal","value":1.2500e2}\n') + value = load_ledger_consumer_artifact(facts_path, lexical_floats=True).facts[0][ + "value" + ] + + restored = pickle.loads(pickle.dumps(value, protocol=protocol)) + + assert isinstance(restored, LexicalFloat) + assert restored.lexical_token == "1.2500e2" + assert restored == value + assert not hasattr(restored, "__dict__") + + +def test_authenticated_float_deepcopy_preserves_lexical_token(tmp_path): + facts_path = tmp_path / "consumer_facts.jsonl" + facts_path.write_text('{"aggregate_fact_key":"decimal","value":1.2500e2}\n') + value = load_ledger_consumer_artifact(facts_path, lexical_floats=True).facts[0][ + "value" + ] + + restored = deepcopy(value) + + assert isinstance(restored, LexicalFloat) + assert restored.lexical_token == "1.2500e2" + assert restored == value + assert not hasattr(restored, "__dict__") + + +@pytest.mark.parametrize("token", ["125", "9007199254740993", str(10**100 + 1)]) +def test_authenticated_integer_tokens_stay_exact_integers(tmp_path, token): + facts_path = tmp_path / "consumer_facts.jsonl" + facts_path.write_text('{"aggregate_fact_key":"integer","value":' + token + "}\n") + + value = load_ledger_consumer_artifact(facts_path).facts[0]["value"] + + assert type(value) is int + assert value == int(token) + + def test_loads_artifact_directory_and_records_provenance(tmp_path): rows = [_fact_row(), _fact_row(assertion="source_projection")] artifact_dir = _write_artifact_dir(tmp_path, rows) @@ -520,3 +629,38 @@ def test_real_nz_artifact_matches_committed_feed_pin(): assert artifact.fact_row_count == pin["fact_row_count"] assert artifact.schema_version == pin["artifact_schema_version"] assert list(artifact.fact_schema_versions) == pin["consumer_fact_schema_versions"] + + +def test_real_nz_lexical_opt_in_preserves_canonical_bytes(): + artifact_path = os.environ.get("NZ_CHRONICLE_ARTIFACT") + if not artifact_path: + pytest.skip("NZ_CHRONICLE_ARTIFACT is unset") + pin_path = ( + paths_for("microcosm-build").package + / "src/microcosm/build/nz/chronicle_feed.json" + ) + pin = json.loads(pin_path.read_text()) + plain = load_ledger_consumer_artifact(artifact_path, feed_pin=pin) + lexical = load_ledger_consumer_artifact( + artifact_path, feed_pin=pin, lexical_floats=True + ) + + def floats(value): + if isinstance(value, float): + yield value + elif isinstance(value, dict): + for child in value.values(): + yield from floats(child) + elif isinstance(value, (list, tuple)): + for child in value: + yield from floats(child) + + plain_values, lexical_values = ( + list(floats(plain.facts)), + list(floats(lexical.facts)), + ) + assert plain_values + assert all(type(value) is float for value in plain_values) + assert all(type(value) is LexicalFloat for value in lexical_values) + assert [float(value.lexical_token) for value in lexical_values] == plain_values + assert canonical_json(plain.facts) == canonical_json(lexical.facts) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_ledger_targets.py b/packages/microcosm-build/tests/engine_free/shared/test_ledger_targets.py index 59fe6e2246..2a9413fb65 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_ledger_targets.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_ledger_targets.py @@ -7,9 +7,14 @@ TargetCoverageRequirement, target_profile_coverage_gate, ) +from microcosm.build.ledger_artifact import ( + LexicalFloat, + load_ledger_consumer_artifact, +) from microcosm.build.ledger_targets import ( LedgerTargetMapping, LedgerTargetReference, + _strict_scalar_equal, apply_ledger_target_profile, compile_ledger_target_references, constraint_bound_shadowing_dimensions, @@ -1729,6 +1734,58 @@ def test__given_selector_matches_multiple_eligible_months__then_latest_month_is_ class TestLedgerSelectorExtensions: + @pytest.mark.parametrize( + ("actual", "expected", "equal"), + [ + (LexicalFloat("0.1"), 0.1, True), + (0.1, LexicalFloat("0.1"), True), + (LexicalFloat("0.1"), LexicalFloat("0.10"), True), + (1, 1.0, False), + (1.0, 1, False), + (True, 1, False), + (1, True, False), + (True, 1.0, False), + (1.0, True, False), + (LexicalFloat("1.0"), 1, False), + (1, LexicalFloat("1.0"), False), + (LexicalFloat("1.0"), True, False), + (True, LexicalFloat("1.0"), False), + ], + ) + def test_strict_scalar_equal_treats_lexical_floats_as_floats( + self, actual, expected, equal + ) -> None: + assert _strict_scalar_equal(actual, expected) is equal + + @pytest.mark.parametrize("lexical_floats", [False, True]) + def test_float_dimension_pin_compiles_loaded_fact( + self, tmp_path, lexical_floats + ) -> None: + facts_path = tmp_path / "consumer_facts.jsonl" + facts_path.write_text( + json.dumps(_consumer_fact_row(dimensions={"band": 0.1})) + "\n" + ) + artifact = load_ledger_consumer_artifact( + facts_path, lexical_floats=lexical_floats + ) + dimension = artifact.facts[0]["dimensions"]["band"] + assert type(dimension) is (LexicalFloat if lexical_floats else float) + reference = LedgerTargetReference( + name="float dimension selector", + ledger_selector={"dimension_values": {"band": 0.1}}, + entity="tax_unit", + measure="adjusted_gross_income", + period=2023, + family="irs_soi", + ) + + (target,) = compile_ledger_target_references( + artifact.facts, [reference], country="us" + ).specs + + assert target.name == reference.name + assert target.value == artifact.facts[0]["value"] + def test_dimension_values_use_strict_typed_equality(self) -> None: reference = LedgerTargetReference( name="typed dimension selector", diff --git a/packages/microcosm-build/tests/engine_free/shared/test_money_normalization.py b/packages/microcosm-build/tests/engine_free/shared/test_money_normalization.py new file mode 100644 index 0000000000..6595197f29 --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_money_normalization.py @@ -0,0 +1,837 @@ +"""Exact donor money normalization: declaration, arithmetic, receipts, factors. + +The property tests compare against an independent oracle built only from +``fractions.Fraction`` and the built-in ``round`` (ties to even); it never +calls a production helper. +""" + +from __future__ import annotations + +import copy +import math +from decimal import localcontext +from fractions import Fraction + +import numpy as np +import pandas as pd +import pytest +from hypothesis import HealthCheck, example, given, settings +from hypothesis import strategies as st + +from microcosm.build.money_normalization import ( + MONEY_FACTOR_PURPOSE, + check_money_factor, + money_column_rosters, + money_factor_inputs, + normalize_money_tables, + normalized_column_families, + resolve_money_rate, + selected_money_factors, + validate_money_normalization, +) +from microcosm.build.reference_factors import FACTOR_PURPOSES +from test_support.microcosm_build.money_normalization import ( + DIMENSION, + NORMALIZED, + factor_id, + money_factor_context, + oracle_factor, + rate_feed_form, + toy_declaration, + write_money_feed, +) + +PROPERTY = settings( + max_examples=200, + deadline=None, + derandomize=True, + database=None, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +_FIELDS = ( + "schema_version", + "from_currency", + "to_currency", + "rate", + "quote", + "rounding", + "donor_money_period", + "target_period", + "columns", + "index_family_factors", + "factor_profile", + "cents_suffix", + "quantile_mapped", + "pending", +) + + +def _oracle(value: float, rate: Fraction, quote: str, factor: Fraction, digits=2): + """Two roundings from first principles: currency, then factor.""" + x = Fraction(repr(float(value))) + scale = 10**digits + converted = x * rate * scale if quote == "to_per_from" else x * scale / rate + first = round(converted) + return first, round(first * factor), round(converted * factor) + + +def _identity_factor(): + return {"identity": True, "reason": "test"} + + +def _factor(numerator: int, digits: int = 6, name: str = "f") -> dict: + return { + "factor_id": name, + "numerator": numerator, + "digits": digits, + "declaration_sha256": "d" * 64, + "receipt_sha256": "e" * 64, + } + + +def _one_column_declaration(column="rental_income", entity="person", **options): + """The toy declaration with one normalized column and the rest pending.""" + declaration = toy_declaration(**options) + validated = validate_money_normalization(copy.deepcopy(declaration)) + family = normalized_column_families(validated)[entity, column] + others = { + key: value + for key, value in money_column_rosters(validated).items() + if value[0] == "normalized" and key != (entity, column) + } + declaration["columns"] = {entity: [column]} + declaration["index_family_factors"] = { + family: declaration["index_family_factors"][family] + } + pending = {} + for other_entity, other_column in others: + pending.setdefault(other_entity, {})[other_column] = { + "awaits": ["test"], + "reason": "test", + } + declaration["pending"] = pending + return validate_money_normalization(declaration), family + + +def _normalize(values, *, rate="1.25", quote="to_per_from", factor=None, column=None): + column = column or "rental_income" + declaration, family = _one_column_declaration(column, rate=rate, quote=quote) + tables = { + "person": pd.DataFrame( + {"person_id": np.arange(len(values)), column: np.asarray(values, float)} + ) + } + arrays, receipts = normalize_money_tables( + tables, + declaration, + rate=Fraction(rate), + rate_text=rate, + factors={family: factor or _identity_factor()}, + ) + return arrays, receipts, declaration, tables + + +# --------------------------------------------------------------------------- +# Declaration validation +# --------------------------------------------------------------------------- + + +def test_toy_declaration_validates_and_rosters_partition_every_money_concept(): + declaration = validate_money_normalization(toy_declaration()) + rosters = money_column_rosters(declaration) + assert {key for key, (route, _) in rosters.items() if route == "normalized"} == { + (entity, column) for entity, names in NORMALIZED.items() for column in names + } + assert {key for key, (route, _) in rosters.items() if route != "normalized"} == { + ("person", "employment_income"), + ("person", "liquid_financial_assets"), + ("household", "rent"), + } + assert ("person", "public_pension_income") not in rosters + assert set(normalized_column_families(declaration).values()) == { + "mixed_income", + "capital_income", + "pensions", + "owner_housing_costs", + } + + +def test_a_dropped_concept_cannot_be_normalized(): + declaration = toy_declaration() + declaration["columns"]["person"].append("public_pension_income") + with pytest.raises(ValueError, match="public_pension_income is dropped in"): + validate_money_normalization(declaration) + # A pending row may still name a dropped concept. + declaration = toy_declaration() + declaration["pending"] = { + "person": {"public_pension_income": {"awaits": ["test"], "reason": "test"}} + } + rosters = money_column_rosters(validate_money_normalization(declaration)) + assert rosters["person", "public_pension_income"] == ("pending", ("test",)) + + +@pytest.mark.parametrize("value", ["2027", 2027.0, True, None, 0, 10000]) +def test_the_target_period_is_an_integer_period_label(value): + declaration = toy_declaration() + declaration["target_period"] = value + with pytest.raises(ValueError, match="target_period must be the integer period"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize("field", _FIELDS) +def test_every_field_is_required(field): + declaration = toy_declaration() + declaration.pop(field) + with pytest.raises(ValueError, match=f"missing fields \\['{field}'\\]"): + validate_money_normalization(declaration) + + +def test_unknown_fields_refuse_and_descriptive_text_rides_along(): + declaration = toy_declaration() + declaration["fallback_rate"] = "1" + with pytest.raises(ValueError, match="unknown fields"): + validate_money_normalization(declaration) + declaration.pop("fallback_rate") + declaration["notes"] = "Reviewed prose." + assert validate_money_normalization(declaration)["notes"] == "Reviewed prose." + + +@pytest.mark.parametrize("value", [1.25, 1, None, True]) +def test_a_rate_given_as_a_json_number_refuses(value): + declaration = toy_declaration() + declaration["rate"]["text"] = value + with pytest.raises(ValueError, match="decimal text, never a JSON number"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize("text", ["1.2e3", "-1.25", "0", "0.000", " 1.25", "1,25", ""]) +def test_rate_text_must_be_plain_positive_decimal(text): + declaration = toy_declaration() + declaration["rate"]["text"] = text + with pytest.raises(ValueError, match="rate text"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize( + "edit", + [ + lambda rate: rate.pop("citation"), + lambda rate: rate["citation"].pop("url"), + lambda rate: rate.update(citation="IRS"), + lambda rate: rate.update(form="literal"), + lambda rate: rate.update(extra="x"), + ], +) +def test_decimal_rate_needs_its_citation_and_closed_form(edit): + declaration = toy_declaration() + edit(declaration["rate"]) + with pytest.raises(ValueError, match="rate"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize( + ("path", "value", "message"), + [ + (("quote",), "multiply", "quote"), + (("quote",), None, "quote"), + (("rounding", "mode"), "half_up", "half_even"), + (("rounding", "digits"), 7, "digits"), + (("rounding", "digits"), 2.0, "digits"), + (("donor_money_period", "type"), "fiscal_year", "period type"), + (("donor_money_period", "value"), "2024", "year"), + (("from_currency",), "usd", "ISO 4217"), + (("to_currency",), "USD", "distinct currencies"), + (("cents_suffix",), "cents", "cents_suffix"), + (("schema_version",), 2, "schema_version"), + ], +) +def test_closed_values_refuse(path, value, message): + declaration = toy_declaration() + target = declaration + for key in path[:-1]: + target = target[key] + target[path[-1]] = value + with pytest.raises(ValueError, match=message): + validate_money_normalization(declaration) + + +def test_rounding_and_period_objects_are_closed(): + declaration = toy_declaration() + declaration["rounding"]["places"] = 2 + with pytest.raises(ValueError, match="rounding has unknown"): + validate_money_normalization(declaration) + declaration = toy_declaration() + declaration["donor_money_period"].pop("value") + with pytest.raises(ValueError, match="donor_money_period is missing"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize("field", ["type", "value", "basis", "evidence"]) +def test_every_donor_period_field_is_required(field): + declaration = toy_declaration() + declaration["donor_money_period"].pop(field) + with pytest.raises(ValueError, match=f"missing fields \\['{field}'\\]"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize("basis", ["declared_convention", "authenticated"]) +def test_donor_period_basis_is_explicit_with_evidence(basis): + declaration = toy_declaration() + declaration["donor_money_period"]["basis"] = basis + validated = validate_money_normalization(declaration) + assert validated["donor_money_period"] == declaration["donor_money_period"] + + +@pytest.mark.parametrize( + ("field", "value", "message"), + [ + ("basis", "model_period", "period basis"), + ("basis", None, "period basis"), + ("evidence", [], "evidence"), + ("evidence", "not a citation list", "evidence"), + ("evidence", [""], "evidence"), + ("evidence", [None], "evidence"), + ], +) +def test_donor_period_refuses_an_unknown_basis_or_missing_evidence( + field, value, message +): + declaration = toy_declaration() + declaration["donor_money_period"][field] = value + with pytest.raises(ValueError, match=message): + validate_money_normalization(declaration) + + +def test_each_column_appears_in_exactly_one_roster(): + declaration = toy_declaration() + declaration["columns"]["person"].append("employment_income") + with pytest.raises(ValueError, match="both normalized and quantile_mapped"): + validate_money_normalization(declaration) + declaration = toy_declaration() + declaration["pending"] = { + "household": {"rent": {"awaits": ["x"], "reason": "x"}}, + } + with pytest.raises(ValueError, match="both quantile_mapped and pending"): + validate_money_normalization(declaration) + + +def test_every_transported_money_concept_must_be_declared(): + declaration = toy_declaration() + declaration["columns"]["household"].remove("property_tax") + with pytest.raises(ValueError, match=r"unclassified: \['household.property_tax'\]"): + validate_money_normalization(declaration) + + +def test_rosters_name_money_concepts_and_their_awaited_references(): + declaration = toy_declaration() + declaration["columns"]["person"].append("age") + with pytest.raises(ValueError, match="not a money concept"): + validate_money_normalization(declaration) + for awaits in ([], [""], "toy bands", ["x", "x"]): + declaration = toy_declaration() + declaration["quantile_mapped"]["household"]["rent"]["awaits"] = awaits + with pytest.raises(ValueError, match="awaits"): + validate_money_normalization(declaration) + + +def test_a_quantile_mapped_drop_default_column_is_admitted(): + # Belgium condition d: the declaration does not consult transport rules. + declaration = toy_declaration() + declaration["quantile_mapped"]["person"]["public_pension_income"] = { + "awaits": ["toy pension bands"], + "reason": "A country may quantile-map a column whose default is drop.", + } + rosters = money_column_rosters(validate_money_normalization(declaration)) + assert rosters["person", "public_pension_income"][0] == "quantile_mapped" + + +def test_an_unmapped_family_refuses_rather_than_defaulting_to_one(): + declaration = toy_declaration() + declaration["index_family_factors"].pop("pensions") + with pytest.raises(ValueError, match=r"\['pensions'\] have no declared factor"): + validate_money_normalization(declaration) + + +def test_unused_and_unknown_families_refuse(): + declaration = toy_declaration() + declaration["index_family_factors"]["rents"] = { + "identity": True, + "reason": "unused", + } + with pytest.raises(ValueError, match="that no normalized column uses"): + validate_money_normalization(declaration) + declaration = toy_declaration() + declaration["index_family_factors"]["wages"] = {"identity": True, "reason": "x"} + with pytest.raises(ValueError, match="unknown index families"): + validate_money_normalization(declaration) + + +@pytest.mark.parametrize( + "entry", + [ + {"identity": True}, + {"identity": True, "reason": ""}, + {"identity": "yes", "reason": "x"}, + {"identity": False, "reason": "x"}, + {"identity": True, "reason": "x", "factor_id": "f"}, + ], +) +def test_an_identity_family_is_explicit_with_a_reason(entry): + declaration = toy_declaration() + declaration["index_family_factors"]["pensions"] = entry + with pytest.raises(ValueError, match="identity"): + validate_money_normalization(declaration) + + +def test_explicit_identity_is_admitted_and_selected_as_none(): + declaration = validate_money_normalization( + toy_declaration(identity_families=("pensions",)) + ) + _, factors = selected_money_factors(declaration, {}) + assert factors["pensions"] is None + assert factors["capital_income"] == factor_id("central") + + +def test_profiled_families_name_one_factor_per_profile_and_a_disclosure(): + declaration = toy_declaration() + declaration["index_family_factors"]["pensions"]["factors"].pop("high") + with pytest.raises(ValueError, match="every declared profile"): + validate_money_normalization(declaration) + declaration = toy_declaration() + declaration["index_family_factors"]["pensions"].pop("disclosure") + with pytest.raises(ValueError, match="missing fields \\['disclosure'\\]"): + validate_money_normalization(declaration) + + +def test_unprofiled_declarations_use_one_factor_id_per_family(): + declaration = toy_declaration() + declaration["factor_profile"] = None + for entry in declaration["index_family_factors"].values(): + entry.pop("factors") + entry["factor_id"] = factor_id("central") + validated = validate_money_normalization(declaration) + assert selected_money_factors(validated, {DIMENSION: "low"}) == ( + None, + dict.fromkeys(validated["index_family_factors"], factor_id("central")), + ) + declaration["factor_profile"] = { + "dimension": DIMENSION, + "profiles": ["central"], + "central_profile": "central", + } + with pytest.raises(ValueError, match="unknown fields \\['factor_id'\\]"): + validate_money_normalization(declaration) + + +def test_profile_selection_follows_the_knob_and_refuses_unknown_profiles(): + declaration = validate_money_normalization(toy_declaration()) + for selection, expected in ( + ({}, "central"), + ({DIMENSION: "low"}, "low"), + ({DIMENSION: "high", "other_knob": "x"}, "high"), + ): + profile, factors = selected_money_factors(declaration, selection) + assert profile == expected + assert set(factors.values()) == {factor_id(expected)} + with pytest.raises(ValueError, match="Unknown money normalization profile"): + selected_money_factors(declaration, {DIMENSION: "bogus"}) + + +def test_feed_rate_form_is_closed_and_needs_its_expected_decimal(): + identity = { + "source_commit": "c" * 40, + "facts_sha256": "a" * 64, + "manifest_sha256": "b" * 64, + "artifact_schema_version": "policyengine_ledger.consumer_artifact.v2", + "consumer_fact_schema_versions": ["chronicle.consumer_fact.v4"], + "consumer_fact_schema_sha256": "d" * 64, + "fact_row_count": 5, + } + declaration = toy_declaration() + declaration["rate"] = rate_feed_form(identity, "1.0824") + validated = validate_money_normalization(declaration) + assert validated["rate"]["expected_decimal"] == "1.0824" + for edit in ( + lambda rate: rate.pop("expected_decimal"), + lambda rate: rate.update(expected_decimal=1.0824), + lambda rate: rate.update(assertion_policy="anything"), + lambda rate: rate["selector"].update(not_a_selector_field=1), + lambda rate: rate.update(citation={"publisher": "x"}), + ): + broken = copy.deepcopy(declaration) + edit(broken["rate"]) + with pytest.raises(ValueError): + validate_money_normalization(broken) + + +# --------------------------------------------------------------------------- +# Exact arithmetic +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + ("value", "rate", "quote", "factor", "expected"), + [ + # 3 * 1.25 = 3.75 -> 375 cents; 375 * 1.1 = 412.5 ties to even 412. + (3.0, "1.25", "to_per_from", 1100000, 412), + # 5 * 1.25 * 1.1 = 6.875 -> 687.5 ties to even 688. + (5.0, "1.25", "to_per_from", 1100000, 688), + # Negative signed amount: -2.675 * 125 = -334.375 -> -334; * 1.1 = -367.4. + (-2.675, "1.25", "to_per_from", 1100000, -367), + # Binary 2.675 is below 2.675; the decimal tie still rounds to even 268. + (2.675, "1", "to_per_from", 1000000, 268), + # A negative half-cent tie rounds to even: -0.125 -> -12.5 -> -12. + (-0.125, "1", "to_per_from", 1000000, -12), + (-0.135, "1", "to_per_from", 1000000, -14), + # Float multiplication gives 57.49999999999999 rather than the exact tie. + (0.575, "1", "to_per_from", 1000000, 58), + # from_per_to divides by the exact rate: 3 / 0.6 = 5.00 -> 500 cents. + (3.0, "0.6", "from_per_to", 1000000, 500), + # A large amount stays exact: 1e15 * 1.654 = 1.654e15 exactly. + (1e15, "1.654", "to_per_from", 1000000, 165400000000000000), + # And 1e9 / 0.6 = 1666666666.666... -> 166666666667 cents. + (1e9, "0.6", "from_per_to", 1000000, 166666666667), + ], +) +def test_hand_cases_round_half_even_at_each_step(value, rate, quote, factor, expected): + arrays, _, declaration, _ = _normalize( + [value], + rate=rate, + quote=quote, + factor=None if factor == 1000000 else _factor(factor), + ) + cents = arrays["person", "rental_income_cents"] + assert cents.dtype == np.int64 and int(cents[0]) == expected + assert arrays["person", "rental_income"][0] == expected / 100 + + +def test_two_roundings_are_recorded_separately_from_one_rounding(): + # 0.004 * 100 = 0.4 -> 0 cents before ageing; one rounding of 0.4 * 1.5 = 0.6 -> 1. + first, two, one = _oracle(0.004, Fraction(1), "to_per_from", Fraction(3, 2)) + assert (first, two, one) == (0, 0, 1) + arrays, *_ = _normalize([0.004], rate="1", factor=_factor(1500000)) + assert int(arrays["person", "rental_income_cents"][0]) == 0 + + +def test_four_cents_rounds_to_seven_fx_then_eight_indexed_cents(): + rate, factor = Fraction("1.654"), Fraction("1.079073") + assert _oracle(0.04, rate, "to_per_from", factor) == (7, 8, 7) + arrays, receipts, *_ = _normalize([0.04], rate="1.654", factor=_factor(1079073)) + assert int(arrays["person", "rental_income_cents"][0]) == 8 + assert receipts[0]["sum_after_fx"] == "0.07" + assert receipts[0]["sum_after"] == "0.08" + + +def test_decimal_context_does_not_round_products_quotients_or_receipt_sums(): + values = [1e15, 0.04, -1e15, 0.125, -0.135] + expected, expected_receipts, *_ = _normalize( + values, rate="1.654", factor=_factor(1079073) + ) + with localcontext() as context: + context.prec = 2 + actual, receipts, *_ = _normalize(values, rate="1.654", factor=_factor(1079073)) + assert {key: values.tolist() for key, values in actual.items()} == { + key: values.tolist() for key, values in expected.items() + } + assert receipts == expected_receipts + assert receipts[0]["sum_before"] == "0.03" + + +@pytest.mark.parametrize("bad", [math.nan, math.inf, -math.inf]) +def test_nan_and_infinite_amounts_refuse(bad): + with pytest.raises(ValueError, match="NaN and infinite"): + _normalize([1.0, bad]) + + +def test_an_unsigned_column_refuses_a_negative_amount_and_signed_keeps_sign(): + with pytest.raises( + ValueError, match="Unsigned money column person.interest_income" + ): + _normalize([-1.0], column="interest_income") + arrays, *_ = _normalize([-1.0, 1.0]) + assert arrays["person", "rental_income"].tolist() == [-1.25, 1.25] + + +@pytest.mark.parametrize("value", [1e17, -1e17]) +def test_int64_minor_units_bound_refuses_overflow(value): + with pytest.raises(ValueError, match="exceeds int64"): + _normalize([value], rate="1") + + +def test_int64_bound_is_checked_at_both_rounding_steps(): + # The FX cents overflow even though the factor would shrink them into range. + with pytest.raises(ValueError, match="exceeds int64"): + _normalize([1e17], rate="1", factor=_factor(1, digits=1)) + # The FX cents fit, but the factor would overflow the companion column. + with pytest.raises(ValueError, match="exceeds int64"): + _normalize([1e16], rate="1", factor=_factor(10, digits=0)) + + +def test_int64_lower_endpoint_is_a_valid_signed_cent_amount(): + arrays, *_ = _normalize([-1.0], rate="1", factor=_factor(2**63, digits=2)) + assert int(arrays["person", "rental_income_cents"][0]) == -(2**63) + + +@pytest.mark.parametrize( + ("entity", "column"), + [(entity, column) for entity, names in NORMALIZED.items() for column in names], +) +def test_every_declared_cents_column_checks_int64_overflow(entity, column): + declaration, family = _one_column_declaration(column, entity) + with pytest.raises(ValueError, match=f"{entity}.{column} exceeds int64"): + normalize_money_tables( + {entity: pd.DataFrame({column: [1e17]})}, + declaration, + rate=Fraction(1), + rate_text="1", + factors={family: _identity_factor()}, + ) + + +def test_normalizing_twice_refuses(): + arrays, _, declaration, tables = _normalize([1.0, 2.0]) + table = tables["person"].copy() + for (_, column), values in arrays.items(): + table[column] = values + with pytest.raises(ValueError, match="normalized exactly once"): + normalize_money_tables( + {"person": table}, + declaration, + rate=Fraction(5, 4), + rate_text="1.25", + factors={"capital_income": _identity_factor()}, + ) + + +def test_receipt_records_exact_sums_residuals_and_identities(): + _, receipts, *_ = _normalize( + [3.0, 5.0, -2.675, 0.1], factor=_factor(1100000, name="toy") + ) + (receipt,) = receipts + assert receipt["n"] == 4 + assert receipt["sum_before"] == "5.425" + # Currency cents: 375, 625, -334 (from -334.375), 12 (from 12.5). + assert receipt["sum_after_fx"] == "6.78" + # Aged cents: 412 (412.5), 688 (687.5), -367 (-367.4), 13 (13.2). + assert receipt["sum_after"] == "7.46" + assert ( + Fraction(**receipt["fx_rounding_residual_minor_units"]) + == Fraction(678) - Fraction("5.425") * 125 + ) + assert Fraction(**receipt["factor_rounding_residual_minor_units"]) == Fraction( + 746 + ) - Fraction(678) * Fraction(11, 10) + assert receipt["factor"] == { + "factor_id": "toy", + "declaration_sha256": "d" * 64, + "receipt_sha256": "e" * 64, + "operational_factor": {"numerator": 1100000, "denominator": 1000000}, + } + assert receipt["rate_text"] == "1.25" + assert receipt["quote"] == "to_per_from" + assert receipt["donor_money_period"] == toy_declaration()["donor_money_period"] + assert receipt["cents_column"] == "rental_income_cents" + assert receipt["index_family"] == "capital_income" + + +def test_identity_receipt_names_its_reason_and_has_no_factor_residual(): + _, receipts, *_ = _normalize([0.5]) + assert receipts[0]["factor"] == {"identity": True, "reason": "test"} + assert receipts[0]["factor_rounding_residual_minor_units"] == { + "numerator": 0, + "denominator": 1, + } + + +# --------------------------------------------------------------------------- +# Rates and factors +# --------------------------------------------------------------------------- + + +def test_decimal_rate_is_exact_and_never_passes_through_float(): + rate, evidence = resolve_money_rate( + validate_money_normalization(toy_declaration(rate="1.654")) + ) + assert rate == Fraction(827, 500) != Fraction(float("1.654")) + assert evidence["text"] == "1.654" + + +def test_feed_rate_replays_against_the_authenticated_feed(tmp_path): + _, pin, identity, facts = write_money_feed(tmp_path / "feed", rate="1.0824") + declaration = toy_declaration() + declaration["rate"] = rate_feed_form(identity, "1.0824") + validated = validate_money_normalization(declaration) + rate, evidence = resolve_money_rate(validated, facts, pin) + assert rate == Fraction("1.0824") + assert evidence["operand"]["decimal_operand"] == "1.0824" + with pytest.raises(ValueError, match="authenticated feed facts"): + resolve_money_rate(validated) + declaration["rate"]["expected_decimal"] = "1.0825" + with pytest.raises(ValueError, match="replay differs"): + resolve_money_rate(validate_money_normalization(declaration), facts, pin) + declaration["rate"] = rate_feed_form({**identity, "fact_row_count": 1}, "1.0824") + with pytest.raises(ValueError, match="feed identity mismatch"): + resolve_money_rate(validate_money_normalization(declaration), facts, pin) + + +def test_money_factors_carry_their_purpose_and_cover_the_donor_period(tmp_path): + assert MONEY_FACTOR_PURPOSE in FACTOR_PURPOSES + _, _, identity, facts = write_money_feed(tmp_path / "feed") + declaration = validate_money_normalization(toy_declaration()) + context = money_factor_context(identity, facts) + for entry, profile in zip( + context["factors"], ("low", "central", "high"), strict=True + ): + numerator, digits = check_money_factor(entry, declaration) + assert Fraction(numerator, 10**digits) == oracle_factor(profile) + assert money_factor_inputs(entry, declaration)["factor_id"] == factor_id( + profile + ) + profile_context = money_factor_context(identity, facts, purpose="profile_input") + with pytest.raises(ValueError, match="applies only money_normalization_input"): + check_money_factor(profile_context["factors"][1], declaration) + other = copy.deepcopy(declaration) + other["donor_money_period"]["value"] = 2023 + with pytest.raises(ValueError, match="not the declared donor money period"): + check_money_factor(context["factors"][1], other) + + +@pytest.mark.parametrize("target", [2026, 2028]) +def test_every_factor_ages_money_to_the_declared_target_period(tmp_path, target): + # Each family's factor is checked against the one declared destination, + # so no two families can age to different periods. + _, _, identity, facts = write_money_feed(tmp_path / "feed") + declaration = validate_money_normalization(toy_declaration()) + context = money_factor_context(identity, facts) + other = copy.deepcopy(declaration) + other["target_period"] = target + for entry in context["factors"]: + assert entry["declaration"]["target_period"] == declaration["target_period"] + check_money_factor(entry, declaration) + with pytest.raises( + ValueError, match=f"not the declared target_period {target}" + ): + check_money_factor(entry, other) + stray = copy.deepcopy(entry) + stray["declaration"]["target_period"] = target + with pytest.raises(ValueError, match=f"ages money to {target}, not"): + check_money_factor(stray, declaration) + + +# --------------------------------------------------------------------------- +# Properties against the independent oracle +# --------------------------------------------------------------------------- + +_AMOUNTS = st.one_of( + st.floats(min_value=-1e9, max_value=1e9, allow_nan=False, allow_infinity=False), + st.integers(min_value=-(10**9), max_value=10**9).map(lambda value: value / 1000), + st.integers(min_value=-(10**6), max_value=10**6).map(lambda value: value / 8), +) +_RATES = st.decimals(min_value="0.001", max_value="99.999", places=3).map(str) +_FACTORS = st.integers(min_value=500000, max_value=2500000) + + +@PROPERTY +@example(values=[2.675, -0.125, 3.0], rate="1.000", factor=1100000, quote="to_per_from") +@example(values=[1e9], rate="0.600", factor=1000000, quote="from_per_to") +@given( + values=st.lists(_AMOUNTS, min_size=1, max_size=12), + rate=_RATES, + factor=_FACTORS, + quote=st.sampled_from(["to_per_from", "from_per_to"]), +) +def test_normalization_equals_the_fraction_oracle(values, rate, factor, quote): + arrays, receipts, *_ = _normalize( + values, rate=rate, quote=quote, factor=_factor(factor) + ) + exact_rate = Fraction(rate) + oracle = [ + _oracle(value, exact_rate, quote, Fraction(factor, 10**6))[1] + for value in values + ] + cents = arrays["person", "rental_income_cents"].tolist() + assert cents == oracle + # The float column is exactly the cents over 100, and cents times one + # hundredth reproduces the oracle's decimal amount. + assert arrays["person", "rental_income"].tolist() == [ + float(Fraction(units, 100)) for units in oracle + ] + assert Fraction(receipts[0]["sum_after"]) == Fraction(sum(oracle), 100) + assert Fraction(receipts[0]["sum_before"]) == sum( + Fraction(repr(float(value))) for value in values + ) + + +@PROPERTY +@given(values=st.lists(_AMOUNTS, min_size=1, max_size=12), rate=_RATES) +def test_normalizing_twice_is_refused(values, rate): + arrays, _, declaration, tables = _normalize(values, rate=rate) + table = tables["person"].copy() + for (_, column), result in arrays.items(): + table[column] = result + with pytest.raises(ValueError, match="normalized exactly once"): + normalize_money_tables( + {"person": table}, + declaration, + rate=Fraction(rate), + rate_text=rate, + factors={"capital_income": _identity_factor()}, + ) + + +@PROPERTY +@given( + values=st.lists(_AMOUNTS, min_size=2, max_size=12), + seed=st.integers(min_value=0, max_value=2**32 - 1), + factor=_FACTORS, +) +def test_permuting_rows_commutes_with_normalization(values, seed, factor): + order = np.random.default_rng(seed).permutation(len(values)) + arrays, receipts, *_ = _normalize(values, factor=_factor(factor)) + shuffled, shuffled_receipts, *_ = _normalize( + [values[index] for index in order], factor=_factor(factor) + ) + for key in arrays: + assert shuffled[key].tolist() == arrays[key][order].tolist() + assert shuffled_receipts == receipts + + +@PROPERTY +@example(value=0.004, rate="1.000", factor=1500000, quote="to_per_from") +@given( + value=_AMOUNTS, + rate=_RATES, + factor=_FACTORS, + quote=st.sampled_from(["to_per_from", "from_per_to"]), +) +def test_two_step_rounding_stays_within_the_factor_bound_of_one_step( + value, rate, factor, quote +): + # At most floor(1 + f/2) minor units, so one cent for every factor below 2. + exact_factor = Fraction(factor, 10**6) + _, two, one = _oracle(value, Fraction(rate), quote, exact_factor) + assert abs(two - one) <= math.floor(1 + exact_factor / 2) + if exact_factor < 2: + assert abs(two - one) <= 1 + arrays, *_ = _normalize([value], rate=rate, quote=quote, factor=_factor(factor)) + assert int(arrays["person", "rental_income_cents"][0]) == two + + +@PROPERTY +@example(value=0.04, factor=1079073) +@given( + value=_AMOUNTS, + factor=st.sampled_from([1077090, 1079073, 1081052]), +) +def test_the_three_declared_factors_bound_two_step_difference_to_one_cent( + value, factor +): + # For y = x * rate * 100, half-even r satisfies |r(y) - y| <= 1/2. + # Thus |r(r(y)*f) - r(y*f)| <= f/2 + 1 < 2 for each declared f < 2. + # The difference is an integer, so its magnitude is at most one cent. + exact_factor = Fraction(factor, 10**6) + assert exact_factor / 2 + 1 < 2 + _, two, one = _oracle(value, Fraction("1.654"), "to_per_from", exact_factor) + assert abs(two - one) <= 1 + arrays, *_ = _normalize([value], rate="1.654", factor=_factor(factor)) + assert int(arrays["person", "rental_income_cents"][0]) == two diff --git a/packages/microcosm-build/tests/engine_free/shared/test_nz_money_normalization.py b/packages/microcosm-build/tests/engine_free/shared/test_nz_money_normalization.py new file mode 100644 index 0000000000..e1fe039bed --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_nz_money_normalization.py @@ -0,0 +1,324 @@ +"""New Zealand's declared donor money normalization (G8-w w2a). + +The declaration is spec data; nothing activates it until a New Zealand +transport graph reads it (w3). These checks pin its routes against the concept +schema, its single-source exchange rate, and the generated donor factors. +""" + +from __future__ import annotations + +import ast +import json +from fractions import Fraction + +import pytest + +from microcosm.build.money_normalization import ( + check_money_factor, + money_column_rosters, + money_concept_columns, + normalized_column_families, + resolve_money_rate, + selected_money_factors, + validate_money_normalization, +) +from microcosm.build.reference_factors import validate_factor_context +from microcosm.build.transport import compose as compose_module +from microcosm.frame.concepts import TransportRule +from test_support.paths import paths_for + +_SPEC = paths_for("microcosm-build").package / "src/microcosm/build/nz" +_DONOR = { + "donor_cy2024_to_ty2027_low": 1077090, + "donor_cy2024_to_ty2027_central": 1079073, + "donor_cy2024_to_ty2027_high": 1081052, +} + + +def _load(name): + return json.loads((_SPEC / name).read_text()) + + +def _declaration(): + return validate_money_normalization(_load("money_normalization.json")) + + +def _factor(factor_id): + return next( + entry + for entry in _load("reference_aging.json")["factors"] + if entry["declaration"]["id"] == factor_id + ) + + +def test_normalized_routes_follow_the_design_table_and_the_concept_schema(): + declaration = _declaration() + assert (declaration["from_currency"], declaration["to_currency"]) == ("USD", "NZD") + assert declaration["quote"] == "to_per_from" + assert declaration["rounding"] == {"digits": 2, "mode": "half_even"} + period = declaration["donor_money_period"] + assert (period["type"], period["value"], period["basis"]) == ( + "calendar_year", + 2024, + "declared_convention", + ) + assert declaration["cents_suffix"] == "_cents" + assert normalized_column_families(declaration) == { + ("person", "nonfarm_self_employment_income"): "mixed_income", + ("person", "farm_self_employment_income"): "mixed_income", + ("person", "interest_income"): "capital_income", + ("person", "dividend_income"): "capital_income", + ("person", "rental_income"): "capital_income", + ("person", "realized_capital_gains"): "capital_income", + ("person", "private_pension_income"): "pensions", + ("household", "mortgage_interest"): "owner_housing_costs", + ("household", "mortgage_principal"): "owner_housing_costs", + ("household", "property_tax"): "owner_housing_costs", + } + rosters = money_column_rosters(declaration) + assert {key for key, (route, _) in rosters.items() if route != "normalized"} == { + ("person", "employment_income"), + ("household", "rent"), + ("person", "liquid_financial_assets"), + } + assert {route for route, _ in rosters.values()} == {"normalized", "quantile_mapped"} + assert declaration["pending"] == {} + assert rosters["person", "employment_income"][1][0].startswith("P1:") + assert rosters["household", "rent"][1][0].startswith("C1:") + assert [item[:3] for item in rosters["person", "liquid_financial_assets"][1]] == [ + "P3:", + "C2:", + ] + + +def test_every_money_concept_surviving_create_is_routed_and_drop_is_not(): + rosters = money_column_rosters(_declaration()) + surviving = { + key + for key, item in money_concept_columns().items() + if item.transport is not TransportRule.DROP + } + assert set(rosters) == surviving + assert len(surviving) == 13 + assert ("person", "public_pension_income") not in rosters + signed = { + key for key, item in money_concept_columns().items() if item.monetary.signed + } + assert signed == { + ("person", "nonfarm_self_employment_income"), + ("person", "farm_self_employment_income"), + ("person", "rental_income"), + ("person", "realized_capital_gains"), + } + + +def test_every_family_uses_the_donor_factor_for_its_profile_with_a_disclosure(): + declaration = _declaration() + assert declaration["factor_profile"] == { + "dimension": "aging_index", + "profiles": ["low", "central", "high"], + "central_profile": "central", + } + for family, entry in declaration["index_family_factors"].items(): + assert entry["factors"] == { + profile: f"donor_cy2024_to_ty2027_{profile}" + for profile in ("low", "central", "high") + }, family + assert "AWE" in entry["disclosure"], family + assert ( + "provisional pending Max's ruling on non-wage ageing (H3)" + in entry["disclosure"] + ), family + + +def test_donor_money_basis_cites_model_lineage_as_a_declared_convention(): + declaration = _declaration() + evidence = " ".join(declaration["donor_money_period"]["evidence"]) + assert "tools/build_us_fiscal_refresh_release.py" in evidence + assert "PERIOD = 2024" in evidence and "_dataset_from_frame" in evidence + assert "time_period to USSingleYearDataset" in evidence + assert ( + "microcosm.frame.adapters.policyengine_us.PolicyEngineUSEngine" + "._write_and_verify" in evidence + ) + assert "_time_period" in evidence + assert "not authentication" in evidence + disclosure = declaration["notes"] + assert "declared convention covering flows and asset stocks" in disclosure + assert "consistency check, not authentication" in disclosure + assert "No donor file is opened" in disclosure + + +def test_donor_money_basis_evidence_matches_the_code_it_cites(): + # The evidence names symbols, not lines (test_nz_spec_package refuses local + # line citations), so the test checks the cited constant and call + # expressions against the code itself. + repository = paths_for("microcosm-build").repository + tool = ast.parse( + (repository / "tools/build_us_fiscal_refresh_release.py").read_text() + ) + periods = [ + node.value.value + for node in tool.body + if isinstance(node, ast.Assign) + and [getattr(target, "id", None) for target in node.targets] == ["PERIOD"] + ] + assert periods == [_declaration()["donor_money_period"]["value"]] + (builder,) = ( + node + for node in tool.body + if isinstance(node, ast.FunctionDef) and node.name == "_dataset_from_frame" + ) + passed = [ + ast.unparse(keyword.value) + for node in ast.walk(builder) + if isinstance(node, ast.Call) + and getattr(node.func, "id", None) == "USSingleYearDataset" + for keyword in node.keywords + if keyword.arg == "time_period" + ] + assert passed == ["PERIOD"] + adapter = ast.parse( + ( + paths_for("microcosm-frame").package + / "src/microcosm/frame/adapters/policyengine_us.py" + ).read_text() + ) + (engine,) = ( + node + for node in adapter.body + if isinstance(node, ast.ClassDef) and node.name == "PolicyEngineUSEngine" + ) + (writer,) = ( + node + for node in engine.body + if isinstance(node, ast.FunctionDef) and node.name == "_write_and_verify" + ) + stored = [ + ast.unparse(node.args[1]) + for node in ast.walk(writer) + if isinstance(node, ast.Call) + and getattr(node.func, "attr", None) == "put" + and len(node.args) >= 2 + and isinstance(node.args[0], ast.Constant) + and node.args[0].value == "_time_period" + ] + assert stored == ["pd.Series([int(period)])"] + + +def test_scenario_knobs_select_their_own_donor_factor(): + declaration = _declaration() + scenarios = _load("scenarios.json") + assert scenarios["central_knobs"]["aging_index"] == "central" + for scenario in scenarios["scenarios"]: + profile, factors = selected_money_factors(declaration, scenario["knobs"]) + expected = {"V1": "low", "V2": "high"}.get(scenario["id"], "central") + assert profile == expected, scenario["id"] + assert set(factors.values()) == {f"donor_cy2024_to_ty2027_{expected}"} + + +def test_the_scenarios_default_aging_index_is_the_money_central_profile(): + # A scenario that leaves the knob unset selects the declaration's central + # profile, so the scenarios' default must name the same profile. + profile = _declaration()["factor_profile"] + scenarios = _load("scenarios.json") + dimension = profile["dimension"] + assert scenarios["central_knobs"][dimension] == profile["central_profile"] + assert set(scenarios["knob_domains"][dimension]) == set(profile["profiles"]) + + +def test_money_and_the_reference_profiles_age_to_one_target_period(): + declaration = _declaration() + factors = [ + entry["declaration"] for entry in _load("reference_aging.json")["factors"] + ] + donor = {row["id"]: row["target_period"] for row in factors if row["id"] in _DONOR} + assert donor == dict.fromkeys(_DONOR, declaration["target_period"]) + assert { + row["target_period"] for row in factors if row["purpose"] == "profile_input" + } == {declaration["target_period"]} + + +@pytest.mark.parametrize("profile", ["low", "central", "high"]) +def test_a_missing_declared_profile_factor_refuses(profile): + declaration = _load("money_normalization.json") + del declaration["index_family_factors"]["capital_income"]["factors"][profile] + with pytest.raises(ValueError, match="one factor for every declared profile"): + validate_money_normalization(declaration) + + +def test_the_rate_has_one_source_and_the_bridge_points_to_it(): + declaration = _declaration() + rate, evidence = resolve_money_rate(declaration) + assert rate == Fraction(827, 500) + assert evidence["citation"]["url"].startswith("https://www.irs.gov/") + assert evidence["citation"]["column"] == "2024" + occurrences = [ + path.name + for path in sorted(_SPEC.rglob("*.json")) + if "1.654" in path.read_text() + ] + assert occurrences == ["money_normalization.json"] + assert _load("money_normalization.json")["rate"]["text"] == "1.654" + bridge = _load("currency_bridge.json") + assert bridge["rate"] == {"resource": "money_normalization.json", "path": ["rate"]} + assert "awaiting D1" not in bridge["status"] + assert "d1043" in bridge["status"] + assert "method-card-D1-adopted.md" in bridge["status"] + + +def test_the_three_donor_factors_are_exact_and_only_money_may_apply_them(): + declaration = _declaration() + context = _load("reference_aging.json") + assert set(validate_factor_context(context)) >= set(_DONOR) + for factor_id, numerator in _DONOR.items(): + entry = _factor(factor_id) + assert entry["operational_factor"] == { + "numerator": numerator, + "denominator": 1000000, + } + assert check_money_factor(entry, declaration) == (numerator, 6) + receipt = entry["receipt"] + assert Fraction(**receipt["denominator_mean"]) == Fraction("1591.925") + assert [slot["period"]["value"] for slot in receipt["source_schedule"]] == [ + "2024-Q1", + "2024-Q2", + "2024-Q3", + "2024-Q4", + ] + assert [slot["decimal_operand"] for slot in receipt["source_schedule"]] == [ + "1563.54", + "1586.32", + "1595.11", + "1622.73", + ] + # The numerator is the TY2024 factors' N_s for the same profile: + # N_s = 1700.68 * (1 + q + q**2 + q**3) / 4 with q = (1 + g)**(1/4). + profile = factor_id.rsplit("_", 1)[1] + ird = _factor(f"ird_ty2024_to_ty2027_{profile}")["receipt"] + assert receipt["growth"] == ird["growth"] + assert receipt["root_bounds"]["radicand"] == ird["root_bounds"]["radicand"] + assert receipt["target_schedule"] == ird["target_schedule"] + for bound in ("lower", "upper"): + root = Fraction(**receipt["root_bounds"][bound]) + assert Fraction(**receipt["numerator_mean_bounds"][bound]) == ( + Fraction("1700.68") * sum(root**index for index in range(4)) / 4 + ) + assert entry["declaration"]["purpose"] == "money_normalization_input" + + +@pytest.mark.parametrize("profile", ["low", "central", "high"]) +def test_a_tax_year_factor_is_refused_on_donor_money(profile): + entry = _factor(f"ird_ty2024_to_ty2027_{profile}") + with pytest.raises(ValueError, match="applies only money_normalization_input"): + check_money_factor(entry, _declaration()) + # Even relabelled, its April-to-March schedule is not calendar 2024. + relabelled = json.loads(json.dumps(entry)) + relabelled["declaration"]["purpose"] = "money_normalization_input" + with pytest.raises(ValueError, match="not the declared donor money period"): + check_money_factor(relabelled, _declaration()) + + +def test_no_graph_reads_it_yet_so_preflight_names_no_money_gap(): + spec = compose_module.load_transport_spec("nz") + assert compose_module._money_producer_gaps(spec) == [] diff --git a/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_aging.py b/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_aging.py new file mode 100644 index 0000000000..0bf245c6bf --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_aging.py @@ -0,0 +1,351 @@ +"""NZ feed operands, reviewed factors, and the observed-quarter re-declaration.""" + +from __future__ import annotations + +import copy +import json +import os +from datetime import date +from decimal import Decimal, localcontext +from fractions import Fraction +from pathlib import Path + +import pytest + +from microcosm.build import reference_factors +from microcosm.build.ledger_artifact import ( + LexicalFloat, + load_ledger_consumer_artifact, + validate_chronicle_feed_pin, +) +from microcosm.build.ledger_targets import fact_matches_selector +from microcosm.build.reference_factors import ( + replay_reference_factor, + require_current_factors, + resolve_reference_factor, + validate_factor_context, +) +from test_support.paths import paths_for +from tools.generate_nz_reference_aging import ( + _OPERANDS, + build_reference_aging, +) + +_PATHS = paths_for("microcosm-build") +_SPEC = _PATHS.package / "src/microcosm/build/nz" +_EXPECTED = { + "ird_ty2024_to_ty2027_low": 1115056, + "ird_ty2024_to_ty2027_central": 1117109, + "ird_ty2024_to_ty2027_high": 1119158, + "p1_ty2026_to_ty2027_low": 1026690, + "p1_ty2026_to_ty2027_central": 1028580, + "p1_ty2026_to_ty2027_high": 1030467, + "observed_ty2026_over_ty2024": 1086069, + "donor_cy2024_to_ty2027_low": 1077090, + "donor_cy2024_to_ty2027_central": 1079073, + "donor_cy2024_to_ty2027_high": 1081052, +} +_DENOMINATORS = { + "p1_": Fraction("1670.0725"), + "donor_": Fraction("1591.925"), +} +_PURPOSES = {"observed_": "diagnostic", "donor_": "money_normalization_input"} + + +def _document(): + return json.loads((_SPEC / "reference_aging.json").read_text()) + + +def _entry(factor_id="ird_ty2024_to_ty2027_central"): + return next( + entry + for entry in _document()["factors"] + if entry["declaration"]["id"] == factor_id + ) + + +def _fraction(rational): + return Fraction(rational["numerator"], rational["denominator"]) + + +@pytest.fixture(scope="module") +def artifact(): + root = os.environ.get("NZ_CHRONICLE_ARTIFACT") + if not root: + pytest.skip("NZ_CHRONICLE_ARTIFACT is not set") + loaded = load_ledger_consumer_artifact(Path(root), lexical_floats=True) + validate_chronicle_feed_pin(loaded, _document()["feed_identity"]) + return loaded + + +@pytest.fixture(scope="module") +def operand_facts(artifact): + keys = {f"ledger.aggregate_fact.v2:{suffix}" for _, _, suffix, _ in _OPERANDS} + keys.add("ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25") + return tuple(fact for fact in artifact.facts if fact["aggregate_fact_key"] in keys) + + +def test_generated_aging_resource_reproduces_pinned_feed_and_all_receipts(artifact): + assert build_reference_aging(artifact) == _document() + assert set(validate_factor_context(_document())) == set(_EXPECTED) + identity = _document()["feed_identity"] + assert set(identity) == { + "source_commit", + "facts_sha256", + "manifest_sha256", + "artifact_schema_version", + "consumer_fact_schema_versions", + "consumer_fact_schema_sha256", + "fact_row_count", + } + for entry in _document()["factors"]: + assert entry["declaration"]["feed_identity"] == identity + assert entry["receipt"]["feed_identity"] == identity + + +def test_packaged_factor_context_validation_ignores_the_wall_clock(monkeypatch): + class FutureDate(date): + @classmethod + def today(cls): + return cls(2026, 11, 5) + + monkeypatch.setattr(reference_factors, "date", FutureDate) + + assert set(validate_factor_context(_document())) == set(_EXPECTED) + + +@pytest.mark.parametrize( + "today,current", + [ + (date(2026, 10, 22), False), + (date(2026, 10, 23), True), + (date(2026, 11, 3), True), + (date(2026, 11, 4), False), + ], +) +def test_factor_currency_uses_inclusive_declared_boundaries(today, current): + selected = validate_factor_context(_document()) + if current: + require_current_factors(selected, today=today) + else: + with pytest.raises(ValueError, match="reviewed re-declaration") as error: + require_current_factors(selected, today=today) + for factor_id in selected: + assert factor_id in str(error.value) + + +@pytest.mark.parametrize("factor_id", list(_EXPECTED)) +def test_all_nine_factors_and_observed_diagnostic_are_exact(factor_id, operand_facts): + entry = _entry(factor_id) + declaration = entry["declaration"] + expected = {"numerator": _EXPECTED[factor_id], "denominator": 1000000} + factor, receipt = resolve_reference_factor( + declaration, operand_facts, _document()["feed_identity"] + ) + assert factor == expected == declaration["expected_operational_factor"] + assert receipt == entry["receipt"] + assert receipt["operation"] == "projected_period_average_ratio" + assert receipt["operation_version"] == 1 + assert receipt["subperiods_per_year"] == 4 + assert _fraction(receipt["denominator_mean"]) == next( + ( + value + for prefix, value in _DENOMINATORS.items() + if factor_id.startswith(prefix) + ), + Fraction("1537.7225"), + ) + assert declaration["purpose"] == next( + (value for prefix, value in _PURPOSES.items() if factor_id.startswith(prefix)), + "profile_input", + ) + replay_reference_factor( + declaration, operand_facts, _document()["feed_identity"], factor, receipt + ) + + +def test_each_reviewed_operand_resolves_once_with_exact_lexical_evidence(artifact): + for line, period, suffix, expected in _OPERANDS: + fact = artifact.facts[line - 1] + assert fact["aggregate_fact_key"] == f"ledger.aggregate_fact.v2:{suffix}" + assert fact["period"]["value"] == period + assert fact["value"].lexical_token == expected + checked = set() + for entry in _document()["factors"]: + declaration = entry["declaration"] + slots = [*declaration["source_schedule"], *declaration["target_schedule"]] + if declaration["growth"]: + slots.append(declaration["growth"]) + for slot in slots: + identity = json.dumps( + {"selector": slot["selector"], "validation": slot["validation"]}, + sort_keys=True, + ) + if identity in checked: + continue + checked.add(identity) + matches = [ + fact + for fact in artifact.facts + if fact_matches_selector(fact, slot["selector"]) + ] + if not matches: + assert slot in declaration["target_schedule"] + assert slot["projection_offset"] > 0 + continue + (fact,) = matches + validation = slot["validation"] + assert fact["observed_measure"]["unit"] == validation["unit"] + assert fact["source"]["source_sha256"] == validation["source_sha256"] + assert fact["source"]["vintage"] == validation["source_vintage"] + assert fact["entity"]["role"] == validation["entity_role"] + assert fact["assertion"] == validation["assertion"] + assert fact["provenance_class"] == validation["provenance_class"] + for key in ("start_date", "end_date"): + assert ( + fact["period_coverage"][key] == validation["period_coverage"][key] + ) + + +def test_treasury_fy2027_source_column_uses_chronicle_2026_and_2027_refuses( + operand_facts, +): + declaration = _entry()["declaration"] + assert declaration["growth"]["selector"]["period_value"] == 2026 + assert declaration["growth"]["validation"]["source_column_id"] == ( + "year_ending_30_june_2027" + ) + declaration["growth"]["selector"]["period_value"] = 2027 + with pytest.raises(ValueError, match="resolve exactly once"): + resolve_reference_factor( + declaration, operand_facts, _document()["feed_identity"] + ) + + +def test_p1_high_rounds_once_to_1030467_and_old_truncation_refuses(operand_facts): + declaration = _entry("p1_ty2026_to_ty2027_high")["declaration"] + assert declaration["source_period"] == 2026 + assert [ + row["selector"]["period_value"] for row in declaration["source_schedule"] + ] == ["2025-Q2", "2025-Q3", "2025-Q4", "2026-Q1"] + declaration["expected_operational_factor"]["numerator"] = 1030466 + with pytest.raises(ValueError, match="expected factor mismatch"): + resolve_reference_factor( + declaration, operand_facts, _document()["feed_identity"] + ) + + +def test_root_enclosure_certifies_no_rounded_quarters_and_irrational_root(): + for factor_id in list(_EXPECTED)[:6]: + entry = _entry(factor_id) + receipt = entry["receipt"] + delta = Fraction(entry["declaration"]["growth"]["scenario_delta"]) + radicand = 1 + (Fraction("2.7") + delta) / 100 + lower = _fraction(receipt["root_bounds"]["lower"]) + upper = _fraction(receipt["root_bounds"]["upper"]) + assert lower**4 < radicand < upper**4 + assert receipt["root_bounds"]["exact"] is False + denominator = _fraction(receipt["denominator_mean"]) + assert _fraction(receipt["numerator_mean_bounds"]["lower"]) == ( + Fraction("1700.68") * sum(lower**i for i in range(4)) / 4 + ) + for bound in ("lower", "upper"): + value = _fraction(receipt["unrounded_factor_bounds"][bound]) + assert ( + value + == _fraction(receipt["numerator_mean_bounds"][bound]) / denominator + ) + assert round(value * 1000000) == _EXPECTED[factor_id] + + +def test_september_observation_reanchors_and_requires_reviewed_redeclaration( + operand_facts, +): + declaration = _entry()["declaration"] + anchor = next( + fact for fact in operand_facts if fact["period"]["value"] == "2026-Q2" + ) + september = copy.deepcopy(anchor) + september["aggregate_fact_key"] = "ledger.aggregate_fact.v2:synthetic_september" + september["period"]["value"] = "2026-Q3" + september["layout"]["record_set_id"] = "stats_nz.qes.awe.2026q3" + september["period_coverage"].update( + {"start_date": "2026-07-01", "end_date": "2026-09-30"} + ) + september["value"] = LexicalFloat("1750.00") + declaration["feed_identity"]["facts_sha256"] = "9" * 64 + facts = (*operand_facts, september) + with pytest.raises(ValueError, match="expected factor mismatch.*re-declaration"): + resolve_reference_factor(declaration, facts, declaration["feed_identity"]) + with localcontext() as context: + context.prec = 80 + q = (Decimal("1.027").sqrt()).sqrt() + numerator = (Decimal("1700.68") + Decimal(1750) * (1 + q + q * q)) / 4 + expected = round(numerator / Decimal("1537.7225") * 1000000) + declaration["expected_operational_factor"]["numerator"] = expected + factor, receipt = resolve_reference_factor( + declaration, facts, declaration["feed_identity"] + ) + assert factor["numerator"] == expected + assert [slot["status"] for slot in receipt["target_schedule"]] == [ + "observed", + "observed", + "projected", + "projected", + ] + lower = _fraction(receipt["root_bounds"]["lower"]) + assert ( + _fraction(receipt["numerator_mean_bounds"]["lower"]) + == (Fraction("1700.68") + 1750 * (1 + lower + lower**2)) / 4 + ) + for slot in receipt["target_schedule"][2:]: + assert slot["projection_anchor"] == { + "aggregate_fact_key": september["aggregate_fact_key"] + } + + +def test_donor_factors_average_calendar_2024_and_share_the_tax_year_numerator( + operand_facts, +): + """Hub-recomputed values (G8-w rulings 4, H1); the feed calculation governs.""" + for profile, numerator in ( + ("low", 1077090), + ("central", 1079073), + ("high", 1081052), + ): + entry = _entry(f"donor_cy2024_to_ty2027_{profile}") + ird = _entry(f"ird_ty2024_to_ty2027_{profile}") + declaration = entry["declaration"] + assert [ + slot["validation"]["period_coverage"] + for slot in declaration["source_schedule"] + ][0]["start_date"] == "2024-01-01" + assert ( + declaration["source_schedule"][-1]["validation"]["period_coverage"][ + "end_date" + ] + == "2024-12-31" + ) + assert declaration["target_schedule"] == ird["declaration"]["target_schedule"] + assert declaration["growth"] == ird["declaration"]["growth"] + assert declaration["validity"] == ird["declaration"]["validity"] + factor, receipt = resolve_reference_factor( + declaration, operand_facts, _document()["feed_identity"] + ) + assert factor == {"numerator": numerator, "denominator": 1000000} + mean = ( + Fraction("1563.54") + + Fraction("1586.32") + + Fraction("1595.11") + + Fraction("1622.73") + ) / 4 + assert mean == Fraction("1591.925") == _fraction(receipt["denominator_mean"]) + for bound in ("lower", "upper"): + assert ( + round( + _fraction(ird["receipt"]["numerator_mean_bounds"][bound]) + / mean + * 1000000 + ) + == numerator + ) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_intervals.py b/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_intervals.py new file mode 100644 index 0000000000..02908e1510 --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_nz_reference_intervals.py @@ -0,0 +1,340 @@ +"""NZ IRD interval declarations: generated cuts, companions and the declared convention. + +The row formulas below are NZ data, checked here against the packaged +resource; the shared interval module contains none of them. +""" + +from __future__ import annotations + +import json +import os +from fractions import Fraction +from pathlib import Path + +import pytest + +from microcosm.build.ledger_artifact import load_ledger_consumer_artifact +from microcosm.build.reference_intervals import ( + classify_records, + interval_table, + validate_interval_document, +) +from microcosm.build.reference_profiles import resolve_reference_profiles +from test_support.paths import paths_for + +_SPEC = paths_for("microcosm-build").package / "src/microcosm/build/nz" +_REPOSITORY = paths_for("microcosm-build").repository +NATIONAL = "ird_taxable_income_national_ty2024" +AGE = "ird_taxable_income_age_ty2024" +IRD_REASON = ( + "B4: reviewed IRD-definition taxable-income measurement, annual component " + "closure and record universe remain required. Ageing resolution does not " + "establish that measurement." +) + + +def _document(name): + return json.loads((_SPEC / name).read_text()) + + +def _national_upper(row): + if row == 9: + return 100 + if row == 10: + return 1_000 + if row <= 209: + return 1_000 * (row - 9) + if row <= 229: + return 200_000 + 5_000 * (row - 209) + if row <= 243: + return 300_000 + 50_000 * (row - 229) + return None + + +def _age_upper(row): + if row == 7: + return 100 + if row == 8: + return 1_000 + if row <= 187: + return 1_000 * (row - 7) + return None + + +def _tables(): + return { + table["id"]: table for table in _document("reference_intervals.json")["tables"] + } + + +def _upper_dollars(band): + return None if band["upper_cent"] is None else band["upper_cent"] / 100 + + +def test_document_validates_under_the_shared_contract_with_declared_semantics(): + parsed = validate_interval_document(_document("reference_intervals.json")) + assert list(parsed) == [NATIONAL, AGE] + for table in _tables().values(): + assert { + key: table[key] + for key in ( + "entity", + "currency", + "input_unit", + "income_column", + "universe_column", + "source_period", + "target_period", + "positive_band_closure", + "nil_band", + "negative_income_policy", + "first_lower_cut_cents", + "diagnostics", + ) + } == { + "entity": "person", + "currency": "NZD", + "input_unit": "integer_cent", + "income_column": "ird_taxable_income_proxy_cents", + "universe_column": "ird_taxable_income_proxy_universe_status", + "source_period": 2024, + "target_period": 2027, + "positive_band_closure": "right", + "nil_band": "present", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": 0, + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left", + }, + } + assert "scaling" not in table + + +def test_national_table_has_237_count_money_interval_triples_and_no_total_row(): + table = _tables()[NATIONAL] + assert table["source_locator"] == "Income tables 2001-24!BE8:BE244" + bands = table["bands"] + assert [band["id"] for band in bands] == [f"row_{row}" for row in range(8, 245)] + assert not any("246" in json.dumps(band) for band in bands) + assert bands[0] == { + "id": "row_8", + "kind": "nil", + "indicator": "taxable_income_band_row_8_indicator", + "source_label": "nil", + "count_reference": "ird_taxable_income_people_by_band__row_8", + "amount_reference": "ird_taxable_income_total_by_band__row_8", + } + for band in bands[1:]: + row = int(band["id"].removeprefix("row_")) + assert band["kind"] == "positive" + assert _upper_dollars(band) == _national_upper(row) + assert bands[-1]["source_label"] == "Over $1,000,000" + references = { + row["name"]: row + for row in _document("target_references.json")["target_references"] + } + profiles = _document("reference_profiles.json") + for profile in ("low", "central", "high"): + (transformation,) = profiles["dimensions"]["aging_index"]["profiles"][profile][ + "transformations" + ] + assert transformation["interval_table_ids"] == [NATIONAL] + assert transformation["count_reference_names"] == [ + band["count_reference"] for band in bands + ] + assert transformation["reference_names"] == [ + band["amount_reference"] for band in bands + ] + for band in bands: + for name in (band["count_reference"], band["amount_reference"]): + assert references[name]["filter"] == band["indicator"] + assert references[name]["entity"] == "person" + + +def test_age_table_has_183_bands_and_no_companions_or_fit_rows(): + table = _tables()[AGE] + assert table["source_locator"] == "A6:A188" + bands = table["bands"] + assert [band["id"] for band in bands] == [f"row_{row}" for row in range(6, 189)] + assert bands[0]["kind"] == "nil" + for band in bands[1:]: + assert _upper_dollars(band) == _age_upper(int(band["id"].removeprefix("row_"))) + assert bands[-1]["source_label"] == "Over $180,000" + assert all( + "count_reference" not in band and "amount_reference" not in band + for band in bands + ) + indicators = {band["indicator"] for band in bands} + for name in ("target_references.json", "precal_references.json"): + assert not any( + row.get("filter") in indicators + for row in _document(name)["target_references"] + ) + profiles = json.dumps(_document("reference_profiles.json")) + assert AGE not in profiles + + +@pytest.mark.parametrize( + ("table", "rows", "endpoint"), + [ + (NATIONAL, (29, 30), 20_000), + (NATIONAL, (59, 60), 50_000), + (NATIONAL, (89, 90), 80_000), + (NATIONAL, (229, 230), 300_000), + (AGE, (27, 28), 20_000), + (AGE, (57, 58), 50_000), + (AGE, (87, 88), 80_000), + ], +) +def test_shared_endpoint_labels_resolve_to_the_ordered_cuts(table, rows, endpoint): + declaration = _tables()[table] + by_id = {band["id"]: band for band in declaration["bands"]} + lower, upper = (by_id[f"row_{row}"] for row in rows) + label = f"${endpoint:,}" + # Both published labels name the endpoint; the ordered cut gives it to the + # lower band under the declared right-closed convention. + assert lower["source_label"].replace(" ", "").endswith(label) + assert upper["source_label"].replace(" ", "").startswith(label) + assert lower["upper_cent"] == endpoint * 100 + parsed = interval_table(declaration) + positions = classify_records( + parsed, + [endpoint * 100 - 1, endpoint * 100, endpoint * 100 + 1], + ["included"] * 3, + ) + assert [parsed.band_ids[index] for index in positions] == [ + lower["id"], + lower["id"], + upper["id"], + ] + + +def test_central_ageing_keeps_fractional_cent_cuts_exactly(): + _, intervals, receipt = resolve_reference_profiles( + _document("target_references.json"), + _document("reference_profiles.json"), + {}, + _document("reference_intervals.json"), + factor_context=_document("reference_aging.json"), + ) + tables = {table["id"]: table for table in intervals["tables"]} + national = tables[NATIONAL] + by_id = {band["id"]: band for band in national["bands"]} + cuts = { + row: Fraction( + by_id[f"row_{row}"]["upper_cent"]["numerator"], + by_id[f"row_{row}"]["upper_cent"]["denominator"], + ) + for row in (9, 10, 49) + } + # $100, $1,000 and $40,000 at the central factor 1.117109. + assert cuts == { + 9: Fraction("11171.09"), + 10: Fraction("111710.9"), + 49: Fraction(4_468_436), + } + parsed = interval_table(national) + positions = classify_records( + parsed, [11_171, 11_172, 111_710, 111_711], ["included"] * 4 + ) + assert [parsed.band_ids[index] for index in positions] == [ + "row_9", + "row_10", + "row_10", + "row_11", + ] + assert tables[AGE] == _tables()[AGE] + assert receipt["interval_tables"][NATIONAL]["factor_id"] == ( + "ird_ty2024_to_ty2027_central" + ) + + +def test_resolution_is_not_activation_and_no_active_row_requests_the_proxy(): + intervals = _document("reference_intervals.json") + proxy = { + "ird_taxable_income_proxy_cents", + "ird_taxable_income_proxy_universe_status", + } + indicators = { + band["indicator"] for table in intervals["tables"] for band in table["bands"] + } + for profile in ("low", "central", "high"): + effective, _, _ = resolve_reference_profiles( + _document("target_references.json"), + _document("reference_profiles.json"), + {"aging_index": profile}, + intervals, + factor_context=_document("reference_aging.json"), + ) + ird = [ + row + for row in effective["target_references"] + if row["name"].startswith("ird_taxable_income_") + ] + assert len(ird) == 474 + assert all( + row["metadata"]["activation_status"] == "requires_prerequisites" + and row["metadata"]["parking_reason"] == IRD_REASON + for row in ird + ) + for row in effective["target_references"]: + if row["metadata"].get("activation_status", "active") == "active": + assert row.get("filter") not in indicators + assert row.get("measure") not in proxy + bindings = json.dumps(_document("calibration_bindings.json")) + assert not any(name in bindings for name in proxy | indicators) + precal = _document("precal_references.json")["target_references"] + wages = [row for row in precal if row["name"].startswith("ird_wage_salary_")] + assert len(wages) == 235 + assert all( + row["metadata"]["activation_status"] + == "requires_g8r2_source_period_wage_bridge_and_intervals" + for row in wages + ) + + +def test_declared_convention_is_disclosed_in_the_resource_and_the_docs(): + disclosure = _document("reference_intervals.json")["disclosure"] + for phrase in ( + "IRD publishes no band closure or cents semantics", + "Microcosm's declared", + "convention", + "publisher reclassification is unavailable", + ): + assert phrase in disclosure + docs = (_REPOSITORY / "docs/nz-calibration-targets.md").read_text() + assert "IRD publishes no band closure or cents semantics" in docs + assert "declared convention" in docs + + +@pytest.fixture(scope="module") +def artifact(): + path = os.environ.get("NZ_CHRONICLE_ARTIFACT") + if not path: + pytest.skip("NZ_CHRONICLE_ARTIFACT is unset; public NZ feed audit unavailable") + return load_ledger_consumer_artifact( + Path(path), feed_pin=_document("chronicle_feed.json") + ) + + +def test_every_band_label_is_the_feed_label_for_its_row(artifact): + record_sets = { + NATIONAL: "ird_taxable_income_distribution.ty2024.income_bands", + AGE: "ird_taxable_income_distribution.ty2024.age_by_income_band", + } + for table_id, record_set in record_sets.items(): + labels = {} + for fact in artifact.facts: + layout = fact.get("layout", {}) + if ( + layout.get("record_set_id") == record_set + and layout.get("table_record_kind") == "detail" + ): + labels.setdefault(layout["source_row_id"], set()).add( + fact["dimensions"]["taxable_income_band_source_label"] + ) + bands = _tables()[table_id]["bands"] + assert set(labels) == {band["id"] for band in bands} + for band in bands: + assert labels[band["id"]] == {band["source_label"]} diff --git a/packages/microcosm-build/tests/engine_free/shared/test_nz_spec_package.py b/packages/microcosm-build/tests/engine_free/shared/test_nz_spec_package.py index 7e9fba0e58..d8ed8bcfe3 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_nz_spec_package.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_nz_spec_package.py @@ -158,7 +158,11 @@ from hypothesis import assume, given, settings from hypothesis import strategies as st +from microcosm.build.chronicle_epoch import ( + is_accepted_consumer_artifact_schema_version, +) from microcosm.build.country_spec import load_country_spec +from microcosm.build.ledger_artifact import chronicle_feed_identity from microcosm.build.ledger_targets import ( LedgerTargetReference, compile_ledger_target_references, @@ -305,8 +309,9 @@ def rulespec_commit_mentions(value: object) -> set[str]: "rulespec" in any case); - the value, at any depth, of a key that contains "commit" in any case, unless the key's mapping is a foreign record (its repository - attribution lacks "rulespec") and neither the key nor an enclosing - key contains "rulespec". + attribution lacks "rulespec") or the key is one of the seven fields + of a complete Chronicle identity below ``feed_identity``, and neither + the key nor an enclosing key contains "rulespec". A foreign record cancels the context it sits in, except that its commit keys still count below a rulespec key. Integers count where hex strings @@ -316,7 +321,13 @@ def rulespec_commit_mentions(value: object) -> set[str]: found: set[str] = set() - def visit(node: object, *, rulespec_context: bool, commit_value: bool) -> None: + def visit( + node: object, + *, + rulespec_context: bool, + commit_value: bool, + feed_identity_record: bool = False, + ) -> None: if isinstance(node, Mapping): repositories = [ repository @@ -327,6 +338,14 @@ def visit(node: object, *, rulespec_context: bool, commit_value: bool) -> None: rulespec_record = any( "rulespec" in repository.lower() for repository in repositories ) + artifact_identity_fields: set[str] = set() + if feed_identity_record and is_accepted_consumer_artifact_schema_version( + node.get("artifact_schema_version") + ): + try: + artifact_identity_fields = set(chronicle_feed_identity(node)) + except ValueError: + pass foreign = named and not rulespec_record if rulespec_record: commit_value = True @@ -334,16 +353,23 @@ def visit(node: object, *, rulespec_context: bool, commit_value: bool) -> None: commit_value = False for key, child in node.items(): name = str(key).lower() - commit_key = "commit" in name and (rulespec_context or not foreign) + identity_field = key in artifact_identity_fields + field_commit_value = commit_value and ( + rulespec_context or not identity_field + ) + commit_key = "commit" in name and ( + rulespec_context or (not foreign and not identity_field) + ) visit( str(key), rulespec_context=rulespec_context, - commit_value=commit_value, + commit_value=field_commit_value, ) visit( child, rulespec_context=rulespec_context or "rulespec" in name, - commit_value=commit_value or commit_key or "rulespec" in name, + commit_value=field_commit_value or commit_key or "rulespec" in name, + feed_identity_record=name == "feed_identity", ) elif isinstance(node, list): for child in node: @@ -1193,7 +1219,12 @@ def test_each_set_is_a_self_identifying_nz_reference_resource( assert document["country"] == "nz" assert document["reference_set"] == reference_set expected_operations = ( - ["identity", "rolled_forward_by_ratio", "allocated_residual_by_ratio"] + [ + "identity", + "rolled_forward_by_ratio", + "allocated_residual_by_ratio", + "linear_combination", + ] if reference_set == "calibration" else ["identity"] ) @@ -1452,6 +1483,52 @@ def test_gate_profile_requires_exactly_the_calibration_families(self) -> None: class TestRulespecPin: + @pytest.mark.parametrize( + "case", + [ + "canonical", + "unattributed", + "incomplete", + "explicit_commit", + "extra_commit", + "rulespec_extra_revision", + "prose", + "rulespec_context", + "wrong_schema", + ], + ) + def test_chronicle_identity_scope_preserves_rulespec_mentions(self, case) -> None: + identity = _load("reference_aging.json")["feed_identity"] + commit = identity["source_commit"] + payload = {"feed_identity": identity} + expected = set() + if case == "unattributed": + payload = {"other": identity} + expected = {commit} + elif case == "incomplete": + identity.pop("manifest_sha256") + expected = {commit} + elif case == "explicit_commit": + identity["rulespec_nz_commit"] = "c0ffee1" + expected = {"c0ffee1"} + elif case == "extra_commit": + identity["commit"] = "deadbee" + expected = {"deadbee"} + elif case == "rulespec_extra_revision": + identity["revision"] = "deadbee" + payload = {"rulespec": payload} + expected = {commit, "deadbee"} + elif case == "prose": + identity["notes"] = "RuleSpec-nz deadbee" + expected = {"deadbee"} + elif case == "rulespec_context": + payload = {"rulespec": payload} + expected = {commit} + elif case == "wrong_schema": + identity["artifact_schema_version"] = "unknown" + expected = {commit} + assert rulespec_commit_mentions(payload) == expected + def test_every_rulespec_commit_named_anywhere_was_reviewed_on_main(self) -> None: pin = _load("axiom_rules_bindings.json")["rulespec"] mentions = rulespec_commit_mentions(_package_payloads()) @@ -1543,7 +1620,8 @@ def test_scanner_catches_the_phrasings_the_reviews_planted(self) -> None: surface["notes"] = surface["notes"].replace( "git diff 6fe181fc 8dc2507", "git diff 6fe181fc c0ffee1" ) - payloads["currency_bridge.json"]["source"]["rules"] = { + # The IRS rate's source record lives in money_normalization.json (w2a). + payloads["money_normalization.json"]["rate"]["citation"]["rules"] = { "repository": "TheAxiomFoundation/rulespec-nz.git", "revision": "facade9876543", } @@ -3835,13 +3913,24 @@ def test_mc15_ruled_thresholds_and_tracked_targets_are_declared(self) -> None: } def test_currency_bridge_is_one_declared_multiplication(self) -> None: + # The executable rate lives once, as cited decimal text, in + # money_normalization.json; the MC4 record points to it. bridge = _load("currency_bridge.json") assert (bridge["from_currency"], bridge["to_currency"]) == ("USD", "NZD") assert bridge["operation"] == "multiply" - assert bridge["rounding"] == {"decimal_places": 2} - assert isinstance(bridge["rate"], float) and bridge["rate"] > 0 - assert bridge["source"]["url"].startswith("https://www.irs.gov/") - assert bridge["source"]["column"] == str(bridge["rate_year"]) + assert bridge["rate"] == { + "resource": "money_normalization.json", + "path": ["rate"], + } + money = _load("money_normalization.json") + rate = money[bridge["rate"]["path"][0]] + assert money["quote"] == "to_per_from" + assert money["rounding"] == {"digits": 2, "mode": "half_even"} + assert rate["form"] == "decimal_text" + assert isinstance(rate["text"], str) and float(rate["text"]) > 0 + assert rate["citation"]["url"].startswith("https://www.irs.gov/") + assert rate["citation"]["column"] == str(bridge["rate_year"]) + assert "awaiting D1" not in bridge["status"] def test_benefit_unit_rule_declares_the_settled_dependent_child_age(self) -> None: rule = _load("benefit_unit_rule.json") diff --git a/packages/microcosm-build/tests/engine_free/shared/test_nz_target_references.py b/packages/microcosm-build/tests/engine_free/shared/test_nz_target_references.py index 004785b9e9..0a9d1f53a9 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_nz_target_references.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_nz_target_references.py @@ -111,6 +111,8 @@ def _effective_rows(profile="mar"): _document("target_references.json"), _document("reference_profiles.json"), {"census_residual_allocation": profile, "aging_index": "central"}, + _document("reference_intervals.json"), + factor_context=_document("reference_aging.json"), ) return document["target_references"] @@ -202,6 +204,11 @@ def test_residual_profiles_have_exact_recipient_rosters_and_preserve_other_targe if "households_by_size" in row["name"] } == {*REGIONS, "99"} for before, after in zip(raw, effective, strict=True): + if before["family"] == "income": + assert ( + after["metadata"]["activation_status"] == "requires_prerequisites" + ) + continue if ( before["metadata"]["activation_status"] != "requires_reference_profile_resolution" @@ -261,8 +268,13 @@ def test_full_nz_resource_resolves_directly_to_the_strict_compiler_document(prof raw, _document("reference_profiles.json"), {"census_residual_allocation": profile, "aging_index": "central"}, + _document("reference_intervals.json"), + factor_context=_document("reference_aging.json"), + ) + assert receipt["base_intervals_sha256"] == reference_document_sha256( + _document("reference_intervals.json") ) - assert intervals is None + assert ["scaling" in table for table in intervals["tables"]] == [True, False] assert set(effective) == set(raw) & REFERENCE_DOCUMENT_KEYS references = parse_reference_document(effective, country="nz") assert len(references) == 773 @@ -333,6 +345,91 @@ def test_v4_changes_only_the_residual_knob_and_prior_variants_keep_their_meaning assert knobs["census_residual_allocation"] == ( "small_households" if scenario["id"] == "V4" else "mar" ) + assert knobs["aging_index"] == {"V1": "low", "V2": "high"}.get( + scenario["id"], "central" + ) + + +@pytest.mark.parametrize("profile", ["low", "central", "high"]) +def test_ageing_profiles_have_237_explicit_companions_and_keep_b4_parked(profile): + profiles = _document("reference_profiles.json") + (transformation,) = profiles["dimensions"]["aging_index"]["profiles"][profile][ + "transformations" + ] + expected_counts = [ + f"ird_taxable_income_people_by_band__row_{row}" for row in range(8, 245) + ] + expected_money = [ + f"ird_taxable_income_total_by_band__row_{row}" for row in range(8, 245) + ] + assert transformation["count_reference_names"] == expected_counts + assert transformation["reference_names"] == expected_money + assert transformation["interval_table_ids"] == [ + "ird_taxable_income_national_ty2024" + ] + assert transformation["factor_id"] == f"ird_ty2024_to_ty2027_{profile}" + assert not any("246" in name for name in expected_counts + expected_money) + base = _document("target_references.json") + effective, intervals, receipt = resolve_reference_profiles( + base, + profiles, + {"census_residual_allocation": "mar", "aging_index": profile}, + _document("reference_intervals.json"), + factor_context=_document("reference_aging.json"), + ) + assert intervals["tables"][0]["scaling"]["factor_id"] == ( + f"ird_ty2024_to_ty2027_{profile}" + ) + before = {row["name"]: row for row in base["target_references"]} + after = {row["name"]: row for row in effective["target_references"]} + factor = {"low": 1.115056, "central": 1.117109, "high": 1.119158}[profile] + for count_name, money_name in zip(expected_counts, expected_money, strict=True): + for field in ("ledger_selector", "measure", "filter", "period"): + assert after[count_name][field] == before[count_name][field] + assert after[count_name].get("value_operation") == before[count_name].get( + "value_operation" + ) + assert after[count_name].get("value_operands") == before[count_name].get( + "value_operands" + ) + assert after[money_name]["value_operation"] == "linear_combination" + assert after[money_name]["value_operands"] == [ + { + "aggregate_fact_key": before[money_name]["ledger_selector"][ + "aggregate_fact_key" + ], + "expected_member_count": 1, + "weight": factor, + } + ] + for name in (count_name, money_name): + metadata = after[name]["metadata"] + assert metadata["activation_status"] == "requires_prerequisites" + assert json.loads(metadata["reference_prerequisites_remaining"]) == [ + "measurement:ird_taxable_income_ty2027_v1" + ] + assert receipt["selection"]["aging_index"] == profile + + +def test_resolved_money_uses_the_verified_factor_once_and_count_values_are_unchanged( + artifact, facts_by_key +): + rows = [row for row in _effective_rows() if row["family"] == "income"] + # This numerical operation check supplies activation only to its copies; + # the committed and resolved declarations remain parked on B4. + executable = copy.deepcopy(rows) + for row in executable: + row["metadata"]["activation_status"] = "active" + registry = compile_ledger_target_references( + artifact.facts, _parsed(executable), country="nz" + ) + assert len(registry.specs) == 474 + for row, target in zip(rows, registry.specs, strict=True): + source = facts_by_key[row["ledger_selector"]["aggregate_fact_key"]]["value"] + if row["metadata"]["reference_value_kind"] == "count": + assert target.value == source + else: + assert target.value == pytest.approx(source * 1.117109, rel=1e-12) @pytest.mark.parametrize("profile", ["mar", "small_households"]) @@ -555,7 +652,12 @@ def test_grouped_tenure_family_multiplicities_and_parked_definitions(): == "requires_reference_profile_resolution" ) if row["family"] == "income": - assert row["metadata"]["activation_status"].startswith("requires_g8r2") + assert row["metadata"]["activation_status"] == "requires_prerequisites" + assert json.loads(row["metadata"]["reference_prerequisites"]) == [ + "reference_profile:aging_index", + "measurement:ird_taxable_income_ty2027_v1", + ] + assert row["metadata"]["parking_reason"].startswith("B4:") def test_receipt_snapshots_are_split_and_stratified_inputs_stay_parked(): diff --git a/packages/microcosm-build/tests/engine_free/shared/test_reference_factors.py b/packages/microcosm-build/tests/engine_free/shared/test_reference_factors.py new file mode 100644 index 0000000000..635fb6ecf3 --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_reference_factors.py @@ -0,0 +1,627 @@ +"""Exact factor arithmetic, evidence and replay without country assumptions.""" + +from copy import deepcopy +from datetime import date +from decimal import Decimal +from fractions import Fraction + +import pytest +from hypothesis import example, given, settings +from hypothesis import strategies as st + +from microcosm.build.ledger_artifact import LexicalFloat +from microcosm.build.reference_factors import ( + FACTOR_PURPOSES, + certified_nth_root_bounds, + factor_document_sha256, + replay_reference_factor, + require_current_factors, + resolve_reference_factor, + validate_factor_context, +) +from microcosm.graph.canonical import canonical_json +from test_support.microcosm_build.reference_factors import ( + factor_context_fixture, + factor_fixture, +) + + +def _fraction(row): + return Fraction(row["numerator"], row["denominator"]) + + +def _oracle_root(radicand, degree, places=50): + """Independent integer bisection oracle; no production arithmetic helpers.""" + denominator = 10**places + low, high = ( + 0, + max(2, (radicand.numerator // radicand.denominator + 2)) * denominator, + ) + while high - low > 1: + middle = (low + high) // 2 + if ( + middle**degree * radicand.denominator + <= radicand.numerator * denominator**degree + ): + low = middle + else: + high = middle + lower = Fraction(low, denominator) + if lower**degree == radicand: + return lower, lower + return lower, Fraction(high, denominator) + + +def _oracle_round(number, digits): + scaled = number * 10**digits + quotient, remainder = divmod(scaled.numerator, scaled.denominator) + if remainder * 2 > scaled.denominator or ( + remainder * 2 == scaled.denominator and quotient % 2 + ): + quotient += 1 + return quotient + + +def _oracle_factor(source, anchor, growth, frequency, digits, *, places=50): + while places <= 4096: + low, high = _oracle_root(1 + Fraction(growth) / 100, frequency, places) + lower = ( + Fraction(anchor) + / Fraction(source) + * sum(low**index for index in range(frequency)) + / frequency + ) + upper = ( + Fraction(anchor) + / Fraction(source) + * sum(high**index for index in range(frequency)) + / frequency + ) + if _oracle_round(lower, digits) == _oracle_round(upper, digits): + return _oracle_round(lower, digits), (low, high) + places = max(1, places * 2) + raise AssertionError("Independent oracle could not enclose a rounded factor.") + + +def test_independent_oracle_refines_ambiguous_rounding(): + coarse = _oracle_root(Fraction(103, 100), 2, places=1) + assert _oracle_round((1 + coarse[0]) / 2, 6) != _oracle_round( + (1 + coarse[1]) / 2, 6 + ) + expected, bounds = _oracle_factor("1", "1", "3", 2, 6, places=1) + assert expected == 1007445 + assert bounds[1] - bounds[0] < coarse[1] - coarse[0] + + +def test_factor_resolves_exact_operands_once_and_replays(): + context, facts, identity = factor_context_fixture() + entry = context["factors"][0] + receipt = entry["receipt"] + assert entry["operational_factor"] == {"numerator": 1100000, "denominator": 1000000} + assert _fraction(receipt["denominator_mean"]) == 100 + assert receipt["root_bounds"]["exact"] is True + assert [slot["status"] for slot in receipt["target_schedule"]] == [ + "observed", + "projected", + ] + assert all( + slot["numeric_token_proof"]["decimal_values_equal"] + for slot in receipt["source_schedule"] + ) + replay_reference_factor( + entry["declaration"], facts, identity, entry["operational_factor"], receipt + ) + assert validate_factor_context(context, ["toy_factor"]) == {"toy_factor": entry} + + +def test_currency_check_without_selected_factors_is_a_noop(): + require_current_factors({}, today=date(2026, 11, 5)) + + +def test_factor_resolution_and_replay_ignore_caller_feed_pin_prose(): + declaration, facts, identity = factor_fixture() + factor, receipt = resolve_reference_factor(declaration, facts, identity) + pin = { + **identity, + "build": "Reviewed prose edit", + "source_repo": "Reviewed repository description", + } + + revised_factor, revised_receipt = resolve_reference_factor(declaration, facts, pin) + + assert revised_factor == factor + assert revised_receipt == receipt + assert revised_receipt["feed_identity"] == identity + assert declaration["feed_identity"] == identity + replay_reference_factor(declaration, facts, pin, factor, receipt) + + +@pytest.mark.parametrize("container", ["declaration", "context"]) +@pytest.mark.parametrize( + "field", + [ + "source_commit", + "facts_sha256", + "manifest_sha256", + "artifact_schema_version", + "consumer_fact_schema_versions", + "consumer_fact_schema_sha256", + "fact_row_count", + ], +) +def test_factor_identity_requires_every_authenticated_field(container, field): + context, facts, identity = factor_context_fixture() + declaration = context["factors"][0]["declaration"] + document = declaration if container == "declaration" else context + document["feed_identity"] = { + key: value for key, value in document["feed_identity"].items() if key != field + } + + with pytest.raises(ValueError, match="missing fields"): + if container == "declaration": + resolve_reference_factor(declaration, facts, identity) + else: + validate_factor_context(context) + + +@pytest.mark.parametrize("container", ["declaration", "context"]) +@pytest.mark.parametrize("field", ["build", "source_repo"]) +def test_factor_declared_identity_refuses_prose_fields(container, field): + context, facts, identity = factor_context_fixture() + declaration = context["factors"][0]["declaration"] + document = declaration if container == "declaration" else context + document["feed_identity"] = {**document["feed_identity"], field: "Prose"} + + with pytest.raises(ValueError, match="Unknown factor .*feed identity fields"): + if container == "declaration": + resolve_reference_factor(declaration, facts, identity) + else: + validate_factor_context(context) + + +def test_irrational_root_is_an_enclosure_never_an_exact_rational(): + expected, _ = _oracle_factor("100", "110", "3", 4, 6) + declaration, facts, identity = factor_fixture( + frequency=4, growth="3", expected_numerator=expected + ) + _, receipt = resolve_reference_factor(declaration, facts, identity) + bounds = receipt["root_bounds"] + lower, upper = _fraction(bounds["lower"]), _fraction(bounds["upper"]) + assert bounds["exact"] is False + assert lower < upper + assert lower**4 < Fraction(103, 100) < upper**4 + factor_bounds = receipt["unrounded_factor_bounds"] + assert _oracle_round(_fraction(factor_bounds["lower"]), 6) == expected + assert _oracle_round(_fraction(factor_bounds["upper"]), 6) == expected + + +def test_exact_root_is_recognized_without_false_irrationality(): + lower, upper, exact = certified_nth_root_bounds(Fraction(121, 100), 2, 8) + assert lower == upper == Fraction(11, 10) + assert exact is True + + +@pytest.mark.parametrize( + "anchor,expected", [("2469135", 1234568), ("2469133", 1234566)] +) +def test_single_final_rounding_uses_nearest_half_even(anchor, expected): + declaration, facts, identity = factor_fixture( + frequency=1, + source_level="2000000", + anchor_level=anchor, + expected_numerator=expected, + ) + factor, _ = resolve_reference_factor(declaration, facts, identity) + assert factor["numerator"] == expected + + +def test_observed_only_diagnostic_needs_no_projection_operand(): + declaration, facts, identity = factor_fixture() + observed = deepcopy(facts[-2]) + observed["aggregate_fact_key"] = "target-1" + observed["period"]["value"] = declaration["target_schedule"][1]["selector"][ + "period_value" + ] + observed["period_coverage"] = declaration["target_schedule"][1]["validation"][ + "period_coverage" + ] + facts.append(observed) + declaration["purpose"] = "diagnostic" + declaration["growth"] = None + factor, receipt = resolve_reference_factor(declaration, facts, identity) + assert factor["numerator"] == 1100000 + assert receipt["growth"] is None + assert all(slot["status"] == "observed" for slot in receipt["target_schedule"]) + + +@pytest.mark.parametrize( + "field,mutation,message", + [ + ("operation", "expression_eval", "operation"), + ("operation_version", 2, "operation"), + ("schema_version", 2, "schema_version"), + ("unknown", "field", "Unknown"), + ("subperiods_per_year", True, "subperiods_per_year"), + ("subperiods_per_year", 0, "subperiods_per_year"), + ("projection_policy", "june_anchor", "projection policy"), + ("purpose", "execute", "purpose"), + ("source_period", True, "period labels"), + ("target_period", None, "period labels"), + ("source_period", "", "period labels"), + ], +) +def test_closed_declaration_guards(field, mutation, message): + declaration, facts, identity = factor_fixture() + declaration[field] = mutation + with pytest.raises(ValueError, match=message): + resolve_reference_factor(declaration, facts, identity) + + +@pytest.mark.parametrize( + "mutation,message", + [ + ("missing", "exactly once"), + ("duplicate", "Duplicate"), + ("unit", "unit mismatch"), + ("source_sha256", "source_sha256 mismatch"), + ("source_vintage", "source_vintage mismatch"), + ("entity_role", "entity_role mismatch"), + ("coverage", "coverage mismatch"), + ("boolean", "Boolean"), + ("nan", "finite"), + ("float_without_token", "lexical_floats=True"), + ("lossy_float", "does not reproduce"), + ("negative", "positive"), + ], +) +def test_operand_guards(mutation, message): + declaration, facts, identity = factor_fixture() + if mutation == "missing": + facts.pop(0) + elif mutation == "duplicate": + duplicate = deepcopy(facts[0]) + duplicate["aggregate_fact_key"] += "-duplicate" + facts.append(duplicate) + elif mutation == "unit": + facts[0]["observed_measure"]["unit"] = "other" + elif mutation == "source_sha256": + facts[0]["source"]["source_sha256"] = "b" * 64 + elif mutation == "source_vintage": + facts[0]["source"]["vintage"] = "revised" + elif mutation == "entity_role": + facts[0]["entity"]["role"] = "other" + elif mutation == "coverage": + facts[0]["period_coverage"]["end_date"] = "2001-02-01" + # The fixture shares this dictionary with its declaration; keep the pin. + declaration["source_schedule"][0]["validation"]["period_coverage"][ + "end_date" + ] = "2001-01-01" + facts[0]["period_coverage"] = { + "start_date": "2001-01-01", + "end_date": "2001-02-01", + } + elif mutation == "boolean": + facts[0]["value"] = True + elif mutation == "nan": + facts[0]["value"] = Decimal("NaN") + elif mutation == "float_without_token": + facts[0]["value"] = 100.0 + elif mutation == "lossy_float": + facts[0]["value"] = LexicalFloat("100.000000000000000001") + elif mutation == "negative": + facts[0]["value"] = Decimal("-1") + with pytest.raises(ValueError, match=message): + resolve_reference_factor(declaration, facts, identity) + + +def test_lexical_float_conversion_is_proved_and_used_exactly(): + declaration, facts, identity = factor_fixture( + source_level="100.25", anchor_level="110.275", expected_numerator=1100000 + ) + for fact in facts: + fact["value"] = LexicalFloat(str(fact["value"])) + _, receipt = resolve_reference_factor(declaration, facts, identity) + proof = receipt["source_schedule"][0]["numeric_token_proof"] + assert proof == { + "numeric_representation": "binary_float_with_lexical_token", + "lexical_token": "100.25", + "recovered_decimal": "100.25", + "decimal_values_equal": True, + } + assert _fraction(receipt["denominator_mean"]) == Fraction(401, 4) + + +def test_integer_operands_are_exact_beyond_binary_float_precision(): + source = 10**80 + 10 + anchor = source * 11 // 10 + declaration, facts, identity = factor_fixture( + source_level=str(source), + anchor_level=str(anchor), + expected_numerator=1100000, + ) + for fact in facts: + fact["value"] = int(fact["value"]) + + factor, receipt = resolve_reference_factor(declaration, facts, identity) + + assert factor == {"numerator": 1100000, "denominator": 1000000} + assert _fraction(receipt["denominator_mean"]) == source + assert _fraction(receipt["numerator_mean_bounds"]["lower"]) == anchor + assert _fraction(receipt["numerator_mean_bounds"]["upper"]) == anchor + operands = [ + *receipt["source_schedule"], + *(slot for slot in receipt["target_schedule"] if slot["status"] == "observed"), + receipt["growth"], + ] + for operand in operands: + token = operand["decimal_operand"] + assert operand["numeric_token_proof"] == { + "numeric_representation": "exact_integer", + "lexical_token": token, + "recovered_decimal": token, + "decimal_values_equal": True, + } + assert receipt["source_schedule"][0]["decimal_operand"] == str(source) + assert receipt["target_schedule"][0]["decimal_operand"] == str(anchor) + + +def test_source_projection_requires_existing_allow_guard_even_with_exact_key(): + declaration, facts, identity = factor_fixture() + declaration["growth"]["assertion_policy"] = "observed_only" + with pytest.raises(ValueError, match="allow_source_projection"): + resolve_reference_factor(declaration, facts, identity) + + +@pytest.mark.parametrize( + "mutation,message", + [ + ("schedule_order", "misordered"), + ("duplicate_period", "Duplicate"), + ("schedule_gap", "noncontiguous"), + ("negative_weight", "positive"), + ("offset", "projection offsets"), + ("unknown_selector", "selector"), + ("growth_floor", "positive 1"), + ("sensitivity", "sensitivity provenance"), + ("expired", "expired"), + ("wrong_expected", "expected factor mismatch"), + ("wrong_feed", "feed identity"), + ("wrong_growth_period", "exactly once"), + ("source_column", "source_column_id mismatch"), + ], +) +def test_numeric_and_schedule_guards(mutation, message): + declaration, facts, identity = factor_fixture() + if mutation == "schedule_order": + declaration["source_schedule"].reverse() + elif mutation == "duplicate_period": + declaration["source_schedule"][1]["selector"]["period_value"] = "2001-01-01" + elif mutation == "schedule_gap": + declaration["source_schedule"][1]["validation"]["period_coverage"][ + "start_date" + ] = "2001-01-04" + elif mutation == "negative_weight": + declaration["source_schedule"][0]["weight"] = "-1" + elif mutation == "offset": + declaration["target_schedule"][1]["projection_offset"] = 0 + elif mutation == "unknown_selector": + declaration["source_schedule"][0]["selector"]["unit"] = "money_per_week" + elif mutation == "growth_floor": + facts[-1]["value"] = Decimal("-100") + elif mutation == "sensitivity": + declaration["growth"]["sensitivity_provenance"] = [] + elif mutation == "expired": + declaration["validity"]["valid_until"] = "2001-12-31" + elif mutation == "wrong_expected": + declaration["expected_operational_factor"]["numerator"] += 1 + elif mutation == "wrong_feed": + identity = {**identity, "facts_sha256": "wrong"} + elif mutation == "wrong_growth_period": + declaration["growth"]["selector"]["period_value"] = "2003" + elif mutation == "source_column": + declaration["growth"]["validation"]["source_column_id"] = "wrong" + with pytest.raises(ValueError, match=message): + resolve_reference_factor(declaration, facts, identity) + + +def test_observed_preferred_requires_redeclaration_and_reanchors_projection(): + declaration, facts, identity = factor_fixture(frequency=4) + observed = deepcopy(facts[-2]) + observed["aggregate_fact_key"] = "target-1" + observed["value"] = Decimal("120") + observed["period"]["value"] = declaration["target_schedule"][1]["selector"][ + "period_value" + ] + observed["period_coverage"] = declaration["target_schedule"][1]["validation"][ + "period_coverage" + ] + facts.append(observed) + with pytest.raises(ValueError, match="expected factor mismatch"): + resolve_reference_factor(declaration, facts, identity) + # Growth zero makes the latest-anchor formula transparent: (110+120+120+120)/4. + declaration["expected_operational_factor"]["numerator"] = 1175000 + _, receipt = resolve_reference_factor(declaration, facts, identity) + assert _fraction(receipt["numerator_mean_bounds"]["lower"]) == Fraction(470, 4) + assert [slot["projection_offset"] for slot in receipt["target_schedule"]] == [ + 0, + 0, + 1, + 2, + ] + assert receipt["target_schedule"][2]["projection_anchor"] == { + "aggregate_fact_key": "target-1" + } + + +def test_observed_gap_refuses_instead_of_guessing_anchor(): + declaration, facts, identity = factor_fixture(frequency=4) + observed = deepcopy(facts[-2]) + observed["period"]["value"] = declaration["target_schedule"][2]["selector"][ + "period_value" + ] + observed["period_coverage"] = declaration["target_schedule"][2]["validation"][ + "period_coverage" + ] + facts.append(observed) + with pytest.raises(ValueError, match="contiguous from the anchor"): + resolve_reference_factor(declaration, facts, identity) + + +def test_self_consistent_receipt_cannot_replace_compile_replay(): + context, facts, identity = factor_context_fixture() + entry = context["factors"][0] + entry["declaration"]["expected_operational_factor"]["numerator"] = 1200000 + entry["operational_factor"]["numerator"] = 1200000 + entry["receipt"]["operational_factor"]["numerator"] = 1200000 + entry["receipt"]["declaration_sha256"] = factor_document_sha256( + entry["declaration"] + ) + entry["receipt"]["receipt_sha256"] = factor_document_sha256( + { + key: value + for key, value in entry["receipt"].items() + if key != "receipt_sha256" + } + ) + assert validate_factor_context(context, ["toy_factor"]) + with pytest.raises(ValueError, match="expected factor mismatch"): + replay_reference_factor( + entry["declaration"], + facts, + identity, + entry["operational_factor"], + entry["receipt"], + ) + + +def test_receipt_tampering_refuses_and_unselected_edits_are_local(): + context, _, _ = factor_context_fixture() + other = deepcopy(context["factors"][0]) + other["declaration"]["id"] = "unused" + other["receipt"]["receipt_sha256"] = "invalid" + context["factors"].append(other) + assert list(validate_factor_context(context, ["toy_factor"])) == ["toy_factor"] + with pytest.raises(ValueError, match="hash mismatch"): + validate_factor_context(context, ["unused"]) + + +@settings(max_examples=70, deadline=None) +@example(source=32, anchor=1, growth=0, frequency=2, digits=4) +@given( + source=st.integers(1, 10**9), + anchor=st.integers(1, 10**9), + growth=st.integers(-49, 49), + frequency=st.sampled_from([1, 2, 4, 12]), + digits=st.sampled_from([4, 6, 8]), +) +def test_certified_rounding_and_root_bounds_match_independent_fraction_oracle( + source, anchor, growth, frequency, digits +): + source_text, anchor_text = str(Decimal(source) / 100), str(Decimal(anchor) / 100) + expected, oracle_bounds = _oracle_factor( + source_text, anchor_text, str(growth), frequency, digits + ) + declaration, facts, identity = factor_fixture( + source_level=source_text, + anchor_level=anchor_text, + growth=str(growth), + frequency=frequency, + digits=digits, + expected_numerator=expected, + ) + if expected == 0: + with pytest.raises( + ValueError, match="Expected operational factor must be positive" + ): + resolve_reference_factor(declaration, facts, identity) + return + factor, receipt = resolve_reference_factor(declaration, facts, identity) + assert factor["numerator"] == expected + lower, upper = ( + _fraction(receipt["root_bounds"]["lower"]), + _fraction(receipt["root_bounds"]["upper"]), + ) + radicand = 1 + Fraction(growth, 100) + assert lower**frequency <= radicand <= upper**frequency + assert lower <= oracle_bounds[1] and upper >= oracle_bounds[0] + if receipt["root_bounds"]["exact"]: + assert lower == upper and lower**frequency == radicand + else: + assert lower < upper + + +def test_very_large_integer_cross_products_do_not_overflow(): + huge = 10**200 + declaration, facts, identity = factor_fixture( + source_level=str(huge), + anchor_level=str(3 * huge), + frequency=12, + expected_numerator=3000000, + ) + factor, receipt = resolve_reference_factor(declaration, facts, identity) + assert factor["numerator"] == 3000000 + assert _fraction(receipt["denominator_mean"]) == huge + + +@pytest.mark.parametrize( + "frequency,digits,message", + [ + (13, 6, "subperiods_per_year.*1 to 12.*serializable"), + (365, 6, "subperiods_per_year.*1 to 12.*serializable"), + (12, 19, "rounding digits.*0 to 18.*serializable"), + ], +) +def test_unsupported_exact_receipt_schedule_refuses_at_declaration( + frequency, digits, message +): + declaration, facts, identity = factor_fixture() + declaration["subperiods_per_year"] = frequency + declaration["rounding"]["digits"] = digits + # A malformed schedule is intentional: range validation must precede it. + declaration["source_schedule"] = [] + with pytest.raises(ValueError, match=message): + resolve_reference_factor(declaration, facts, identity) + + +def test_largest_supported_schedule_and_rounding_have_serializable_exact_receipt(): + expected, _ = _oracle_factor("100", "110", "3", 12, 18) + declaration, facts, identity = factor_fixture( + frequency=12, growth="3", digits=18, expected_numerator=expected + ) + factor, receipt = resolve_reference_factor(declaration, facts, identity) + assert factor == {"numerator": expected, "denominator": 10**18} + encoded = canonical_json(receipt) + assert encoded and factor_document_sha256(receipt) + replay_reference_factor(declaration, facts, identity, factor, receipt) + + +def test_root_refinement_stays_within_exact_receipt_precision_budget(monkeypatch): + precisions = [] + + def ambiguous_bounds(value, degree, decimal_places): + precisions.append(decimal_places) + return Fraction(1), Fraction(2), False + + monkeypatch.setattr( + "microcosm.build.reference_factors.certified_nth_root_bounds", ambiguous_bounds + ) + declaration, facts, identity = factor_fixture(frequency=12, digits=18) + with pytest.raises(ValueError, match="supported exact receipt precision"): + resolve_reference_factor(declaration, facts, identity) + assert precisions == [26, 52, 104, 208] + + +@pytest.mark.parametrize("purpose", sorted(FACTOR_PURPOSES)) +def test_factor_purposes_are_closed_and_additive(purpose): + assert FACTOR_PURPOSES == { + "profile_input", + "diagnostic", + "money_normalization_input", + } + declaration, facts, identity = factor_fixture() + declaration["purpose"] = purpose + factor, receipt = resolve_reference_factor(declaration, facts, identity) + assert factor == declaration["expected_operational_factor"] + declaration["purpose"] = "money" + with pytest.raises(ValueError, match="Unknown factor purpose"): + resolve_reference_factor(declaration, facts, identity) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_reference_intervals.py b/packages/microcosm-build/tests/engine_free/shared/test_reference_intervals.py new file mode 100644 index 0000000000..71a02b046c --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_reference_intervals.py @@ -0,0 +1,1173 @@ +"""Exact interval cuts, declared semantics, indicators and boundary diagnostics. + +The Hypothesis oracle below reads cuts itself with ``fractions.Fraction`` and +never calls a production helper. +""" + +from __future__ import annotations + +import copy +import json +from decimal import Decimal +from fractions import Fraction + +import numpy as np +import pandas as pd +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from microcosm.build.reference_intervals import ( + BELOW_FIRST_CUT, + EXCLUDED, + IntervalPreparationError, + boundary_diagnostics, + classify_records, + frame_boundary_diagnostics, + interval_document_sha256, + interval_indicators, + interval_input_fingerprint, + interval_table, + scale_interval_document, + scale_interval_table, + source_interval_table, + validate_interval_document, +) +from microcosm.frame import EntitySchema, Frame, WeightKind, Weights + +CENTRAL = {"numerator": 1117109, "denominator": 1000000} +_SETTINGS = settings( + max_examples=60, + database=None, + deadline=None, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +def _table( + cuts=(10_000, 100_000), + *, + closure="right", + nil=True, + first=0, + entity="person", + policy="floor_zero", + table_id="table", + window=100, + income="income_cents", + universe="universe_status", +): + bands = [] + if nil: + bands.append({"id": "nil", "kind": "nil", "indicator": f"{table_id}_nil"}) + for index, cut in enumerate((*cuts, None)): + bands.append( + { + "id": f"b{index}", + "kind": "positive", + "upper_cent": cut, + "indicator": f"{table_id}_b{index}", + } + ) + return { + "id": table_id, + "entity": entity, + "currency": "XTS", + "input_unit": "integer_cent", + "income_column": income, + "universe_column": universe, + "source_period": 2001, + "target_period": 2001, + "positive_band_closure": closure, + "nil_band": "present" if nil else "absent", + "negative_income_policy": policy, + "first_lower_cut_cents": first, + "source_locator": "Toy!A1:A9", + "diagnostics": { + "boundary_window_cents": window, + "alternative_positive_band_closure": "left" + if closure == "right" + else "right", + }, + "bands": bands, + } + + +def _scale(table, factor=CENTRAL): + return scale_interval_table( + table, + factor, + factor_id="toy_factor", + factor_declaration_sha256="a" * 64, + factor_receipt_sha256="b" * 64, + ) + + +def _ids(table, positions): + parsed = interval_table(table) + return [ + {BELOW_FIRST_CUT: "below", EXCLUDED: "excluded"}.get( + position, parsed.band_ids[position] if position >= 0 else None + ) + for position in positions + ] + + +def _classify(table, amounts, universe=None, **kwargs): + universe = ["included"] * len(amounts) if universe is None else universe + return _ids( + table, classify_records(interval_table(table), amounts, universe, **kwargs) + ) + + +# --------------------------------------------------------------------------- +# Example tests +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + ("closure", "expected"), + [ + ( + "right", + ["nil", "nil", "b0", "b0", "b0", "b1", "b1", "b1", "b2"], + ), + ( + "left", + ["nil", "nil", "b0", "b0", "b1", "b1", "b1", "b2", "b2"], + ), + ], +) +def test_each_exact_cut_and_one_cent_either_side(closure, expected): + table = _table(closure=closure) + amounts = [-1, 0, 1, 9_999, 10_000, 10_001, 99_999, 100_000, 100_001] + assert _classify(table, amounts) == expected + + +@pytest.mark.parametrize( + ("closure", "zero", "last", "above"), + [("right", "below", "b1", "b2"), ("left", "b0", "b2", "b2")], +) +def test_first_positive_band_and_open_tail_under_both_closures( + closure, zero, last, above +): + table = _table(closure=closure, nil=False) + assert _classify(table, [0, 1, 100_000, 100_001]) == [zero, "b0", last, above] + + +@pytest.mark.parametrize("closure", ["right", "left"]) +@pytest.mark.parametrize("nil", [False, True]) +def test_a_table_starting_above_zero_reports_records_below_its_first_cut(closure, nil): + table = _table((5_000_000,), closure=closure, nil=nil, first=4_000_000) + amounts = [0, 3_999_999, 4_000_000, 4_000_001, 5_000_000, 5_000_001] + at_first = "below" if closure == "right" else "b0" + at_upper = "b0" if closure == "right" else "b1" + assert _classify(table, amounts) == [ + "nil" if nil else "below", + "below", + at_first, + "b0", + at_upper, + "b1", + ] + report = boundary_diagnostics( + interval_table(table), + list(range(6)), + amounts, + ["included"] * 6, + [1.0] * 6, + weight_record_ids=list(range(6)), + weight_kind="design", + ) + assert report["below_first_cut"]["declared"]["records"] == ( + 2 if closure == "right" else 1 + ) + (0 if nil else 1) + assert Fraction(**report["below_first_cut"]["declared"]["weighted_count"]) == ( + 2 if closure == "right" else 1 + ) + (0 if nil else 1) + assert Fraction( + **report["below_first_cut"]["declared"]["weighted_amount_cents"] + ) == 3_999_999 + (4_000_000 if closure == "right" else 0) + assert report["cuts"][0]["cut"] == "first_lower_cut" + assert report["cuts"][0]["source_cut_cents"] == 4_000_000 + + +def test_nil_and_loss_records_follow_the_declared_negative_policy(): + assert _classify(_table(), [-123_456, 0, 1]) == ["nil", "nil", "b0"] + with pytest.raises(IntervalPreparationError) as refused: + classify_records( + interval_table(_table(policy="refuse")), [-1, 0, 5], ["included"] * 3 + ) + assert refused.value.summary["reasons"] == {"negative_amount_refused": 1} + # Excluded losses never enter a band under either policy. + assert _classify(_table(policy="refuse"), [-1], ["excluded"]) == ["excluded"] + + +@pytest.mark.parametrize("scaled", [False, True]) +def test_left_closed_nil_table_with_a_zero_first_cut_puts_zero_in_nil_only(scaled): + # Zero is nil under either closure, so the first positive band is + # 0 < z < upper. Scaling keeps the zero cut: one cent stays in b0 rather + # than falling below a nonzero stand-in cut. + table = _table((100, 1_000), closure="left") + amounts = [-1, 0, 1, 99, 100, 999, 1_000, 1_001] + if scaled: + table = _scale(table, {"numerator": 2, "denominator": 1}) + assert table["first_lower_cut_cents"] == {"numerator": 0, "denominator": 1} + amounts = [-1, 0, 1, 199, 200, 1_999, 2_000, 2_001] + assert _classify(table, amounts) == [ + "nil", + "nil", + "b0", + "b0", + "b1", + "b1", + "b2", + "b2", + ] + + +def test_left_closed_nil_zero_first_cut_diagnostics_keep_zero_in_nil(): + table = interval_table(_table((100, 1_000), closure="left")) + report = boundary_diagnostics( + table, + [0, 1, 2, 3], + [-5, 0, 1, 100], + ["included"] * 4, + [2.0, 3.0, 5.0, 7.0], + weight_record_ids=[0, 1, 2, 3], + weight_kind="design", + ) + bands = {band["band"]: band for band in report["bands"]} + assert bands["nil"]["declared"]["records"] == 2 + assert Fraction(**bands["nil"]["declared"]["weighted_count"]) == 5 + assert "alternative" not in bands["nil"] + # Declared left: 1 is b0 and 100 is b1. The right-closed alternative puts + # both in b0; zero stays nil and nothing is below the first cut. + assert [bands[band]["declared"]["records"] for band in ("b0", "b1", "b2")] == [ + 1, + 1, + 0, + ] + assert [bands[band]["alternative"]["records"] for band in ("b0", "b1", "b2")] == [ + 2, + 0, + 0, + ] + assert report["below_first_cut"]["declared"]["records"] == 0 + assert report["below_first_cut"]["alternative"]["records"] == 0 + + +def test_left_closed_nil_table_keeps_a_positive_fractional_first_cut_after_scaling(): + effective = _scale( + _table(closure="left", first=1), {"numerator": 1, "denominator": 2} + ) + assert effective["first_lower_cut_cents"] == {"numerator": 1, "denominator": 2} + assert _classify(effective, [-1, 0, 1]) == ["nil", "nil", "b0"] + + +def test_right_closed_nil_diagnostics_keep_zero_out_of_the_left_closed_alternative(): + table = interval_table(_table()) + report = boundary_diagnostics( + table, + [0, 1], + [-5, 0], + ["included", "included"], + [2.0, 3.0], + weight_record_ids=[0, 1], + weight_kind="design", + ) + bands = {band["band"]: band for band in report["bands"]} + assert bands["nil"]["declared"]["records"] == 2 + for band in table.band_ids[1:]: + assert bands[band]["declared"]["records"] == 0 + assert bands[band]["alternative"]["records"] == 0 + assert report["below_first_cut"]["alternative"]["records"] == 0 + + +@pytest.mark.parametrize( + ("closure", "nil", "first", "declared_destination", "alternative_destination"), + [ + ("right", True, 0, "nil", "nil"), + ("right", True, 10, "nil", "nil"), + ("left", True, 1, "nil", "nil"), + ("right", False, 1, "below_first_cut", "below_first_cut"), + ("left", False, 1, "below_first_cut", "below_first_cut"), + ("right", False, 0, "none", "b0"), + ("left", False, 0, "b0", "none"), + ], +) +def test_floored_negative_mass_reports_signed_amounts_by_actual_destination( + closure, nil, first, declared_destination, alternative_destination +): + report = boundary_diagnostics( + interval_table(_table(closure=closure, nil=nil, first=first)), + [0, 1, 2, 3, 4], + [-120, -30, -999, 0, 1], + ["included", "included", "excluded", "included", "included"], + [2.0, 0.5, 99.0, 3.0, 4.0], + weight_record_ids=[0, 1, 2, 3, 4], + weight_kind="design", + ) + floored = report["floored_negative_income"] + expected = { + "records": 2, + "weighted_count": {"numerator": 5, "denominator": 2}, + "pre_floor_amount_cents": -150, + "weighted_pre_floor_amount_cents": {"numerator": -255, "denominator": 1}, + } + assert {key: floored[key] for key in expected} == expected + for evaluation, destination in ( + ("declared", declared_destination), + ("alternative", alternative_destination), + ): + destinations = floored["by_destination"][evaluation] + actual = ( + destinations["positive_bands"][destination] + if destination == "b0" + else destinations[destination] + ) + assert actual == expected + all_destinations = [ + destinations["nil"], + destinations["below_first_cut"], + destinations["none"], + *destinations["positive_bands"].values(), + ] + assert sum(row["records"] for row in all_destinations) == 2 + assert sum(row["pre_floor_amount_cents"] for row in all_destinations) == -150 + for measure in ("weighted_count", "weighted_pre_floor_amount_cents"): + assert sum( + Fraction(**row[measure]) for row in all_destinations + ) == Fraction(**expected[measure]) + + +def test_a_band_id_cannot_merge_with_the_below_first_cut_diagnostic_bucket(): + declaration = _table(first=100) + declaration["bands"][1]["id"] = "below_first_cut" + report = boundary_diagnostics( + interval_table(declaration), + [0, 1], + [50, 101], + ["included", "included"], + [2.0, 3.0], + weight_record_ids=[0, 1], + weight_kind="design", + ) + band = next(row for row in report["bands"] if row["band"] == "below_first_cut") + assert band["declared"]["weighted_count"] == {"numerator": 3, "denominator": 1} + assert report["below_first_cut"]["declared"]["weighted_count"] == { + "numerator": 2, + "denominator": 1, + } + + +def test_fractional_cent_aged_cuts_stay_exact_and_are_never_rounded(): + effective = _scale(_table((10_000, 100_000, 4_000_000))) + uppers = [band.get("upper_cent") for band in effective["bands"]] + assert uppers == [ + None, + {"numerator": 1117109, "denominator": 100}, + {"numerator": 1117109, "denominator": 10}, + {"numerator": 4468436, "denominator": 1}, + None, + ] + assert Fraction(1117109, 100) == Fraction("11171.09") + assert Fraction(1117109, 10) == Fraction("111710.9") + # $111.7109: 11,171 cents is inside the first band and 11,172 above it. + # $1,117.109: 111,710 cents is inside, and 111,711 lies above the cut. + assert _classify(effective, [11_171, 11_172, 111_710, 111_711]) == [ + "b0", + "b1", + "b1", + "b2", + ] + assert _classify(effective, [4_468_436, 4_468_437]) == ["b2", "b3"] + assert effective["scaling"]["operational_factor"] == CENTRAL + assert source_interval_table(effective) == _table((10_000, 100_000, 4_000_000)) + + +def test_included_excluded_and_unresolved_routes(): + table = interval_table(_table()) + indicators = interval_indicators( + table, [0, 0, 500, 500_000], ["included", "excluded", "included", "excluded"] + ) + assert {name: values.tolist() for name, values in indicators.items()} == { + "table_nil": [True, False, False, False], + "table_b0": [False, False, True, False], + "table_b1": [False, False, False, False], + "table_b2": [False, False, False, False], + } + with pytest.raises(IntervalPreparationError) as refused: + interval_indicators( + table, [1, 2, 3, 4], ["included", "unresolved", "maybe", None] + ) + assert refused.value.summary == { + "table": "table", + "entity": "person", + "records": 4, + "refused_records": 3, + "reasons": {"invalid_universe_status": 2, "unresolved_universe_status": 1}, + "universe_statuses": {"included": 1, "invalid": 2, "unresolved": 1}, + } + + +@pytest.mark.parametrize( + ("amount", "reason"), + [ + (True, "boolean_amount"), + (np.bool_(False), "boolean_amount"), + (1.0, "non_integer_amount"), + (np.float64(250.0), "non_integer_amount"), + ("100", "text_amount"), + (None, "missing_amount"), + (float("nan"), "missing_amount"), + (float("inf"), "nonfinite_amount"), + (pd.NA, "missing_amount"), + (Decimal("100"), "non_integer_amount"), + (Fraction(1, 2), "non_integer_amount"), + ], +) +def test_booleans_floats_strings_and_missing_amounts_refuse(amount, reason): + table = interval_table(_table()) + with pytest.raises(IntervalPreparationError) as refused: + interval_indicators(table, [10, amount], ["included", "included"]) + assert refused.value.summary["reasons"] == {reason: 1} + # An excluded record's amount is never read. + assert interval_indicators(table, [10, amount], ["included", "excluded"])[ + "table_b0" + ].tolist() == [True, False] + + +def test_signed_numpy_and_python_integers_classify_identically(): + table = interval_table(_table()) + expected = classify_records(table, [-5, 0, 10_000], ["included"] * 3) + assert ( + classify_records( + table, list(np.asarray([-5, 0, 10_000], dtype=np.int64)), ["included"] * 3 + ) + == expected + ) + + +@pytest.mark.parametrize( + "field", + [ + "positive_band_closure", + "nil_band", + "negative_income_policy", + "first_lower_cut_cents", + "input_unit", + "income_column", + "universe_column", + "entity", + "currency", + "source_period", + "target_period", + "source_locator", + "diagnostics", + ], +) +def test_every_semantic_is_declared_and_none_has_a_default(field): + table = _table() + table.pop(field) + with pytest.raises(ValueError, match="missing declared fields"): + interval_table(table) + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("positive_band_closure", "both"), + ("positive_band_closure", True), + ("nil_band", True), + ("nil_band", "maybe"), + ("negative_income_policy", "absolute"), + ("input_unit", "float_dollar"), + ("first_lower_cut_cents", -1), + ("first_lower_cut_cents", 0.0), + ("first_lower_cut_cents", False), + ("source_period", True), + ("income_column", "universe_status"), + ], +) +def test_invalid_declared_semantics_refuse(field, value): + table = _table() + table[field] = value + with pytest.raises(ValueError): + interval_table(table) + + +def test_the_diagnostic_alternative_is_the_other_closure(): + table = _table(closure="left") + assert table["diagnostics"]["alternative_positive_band_closure"] == "right" + interval_table(table) + table["diagnostics"]["alternative_positive_band_closure"] = "left" + with pytest.raises(ValueError, match="other positive band closure"): + interval_table(table) + + +@pytest.mark.parametrize( + "cut", + [ + 10_000.0, + float("inf"), + float("nan"), + True, + "10000", + json.loads('{"x": Infinity}')["x"], + json.loads('{"x": NaN}')["x"], + -5, + 0, + ], +) +def test_floats_booleans_nonfinite_and_nonpositive_source_cuts_refuse(cut): + table = _table() + table["bands"][1]["upper_cent"] = cut + with pytest.raises(ValueError): + interval_table(table) + + +@pytest.mark.parametrize( + "rational", + [ + {"numerator": 2234218, "denominator": 200}, + {"numerator": 1117109, "denominator": 0}, + {"numerator": 1117109, "denominator": -100}, + {"numerator": 1117109.0, "denominator": 100}, + {"numerator": True, "denominator": 1}, + {"numerator": 1117109, "denominator": 100, "extra": 1}, + ], +) +def test_effective_cuts_must_be_reduced_integer_rationals(rational): + effective = _scale(_table()) + effective["bands"][1]["upper_cent"] = rational + with pytest.raises(ValueError): + interval_table(effective) + + +@pytest.mark.parametrize( + "defect", + [ + "decreasing", + "repeated", + "below_first", + "two_tails", + "tail_not_last", + "no_tail", + "nil_not_first", + "nil_declared_absent", + "nil_missing", + "duplicate_band", + "duplicate_indicator", + "unknown_kind", + "partial_companions", + "mixed_companions", + "unknown_field", + ], +) +def test_malformed_band_structures_refuse(defect): + table = _table((10_000, 100_000, 200_000)) + bands = table["bands"] + if defect == "decreasing": + bands[2]["upper_cent"] = 5_000 + elif defect == "repeated": + bands[2]["upper_cent"] = 10_000 + elif defect == "below_first": + table["first_lower_cut_cents"] = 10_000 + elif defect == "two_tails": + bands[2]["upper_cent"] = None + elif defect == "tail_not_last": + bands[-1], bands[-2] = bands[-2], bands[-1] + elif defect == "no_tail": + bands.pop() + elif defect == "nil_not_first": + bands.append(bands.pop(0)) + elif defect == "nil_declared_absent": + table["nil_band"] = "absent" + elif defect == "nil_missing": + bands.pop(0) + elif defect == "duplicate_band": + bands[2]["id"] = bands[1]["id"] + elif defect == "duplicate_indicator": + bands[2]["indicator"] = bands[1]["indicator"] + elif defect == "unknown_kind": + bands[1]["kind"] = "finite" + elif defect == "partial_companions": + bands[1]["count_reference"] = "count_1" + elif defect == "mixed_companions": + bands[1].update(count_reference="count_1", amount_reference="amount_1") + else: + bands[1]["lower_cent"] = 0 + with pytest.raises(ValueError): + interval_table(table) + + +def test_documents_require_unique_tables_indicators_and_references(): + first = _table(table_id="first") + second = _table(table_id="second", entity="household") + validate_interval_document({"schema_version": 1, "tables": [first, second]}) + for defect in ("table", "indicator", "reference", "schema", "unknown"): + tables = [copy.deepcopy(first), copy.deepcopy(second)] + document = {"schema_version": 1, "tables": tables} + if defect == "table": + tables[1]["id"] = "first" + elif defect == "indicator": + tables[1]["bands"][1]["indicator"] = tables[0]["bands"][1]["indicator"] + elif defect == "reference": + for table in tables: + for index, band in enumerate(table["bands"]): + band.update( + count_reference=f"{table['id']}_count_{index}", + amount_reference=f"{table['id']}_amount_{index}", + ) + tables[1]["bands"][1]["count_reference"] = "first_count_1" + elif defect == "schema": + document["schema_version"] = True + else: + document["defaults"] = {"positive_band_closure": "right"} + with pytest.raises(ValueError): + validate_interval_document(document) + + +def test_scaling_twice_refuses_and_scaled_cuts_bind_their_source(): + source = _table() + effective = _scale(source) + with pytest.raises(ValueError, match="already scaled"): + _scale(effective) + with pytest.raises(ValueError, match="cannot contain scaled tables"): + scale_interval_document( + {"schema_version": 1, "tables": [effective]}, {"table": {}} + ) + twice = copy.deepcopy(effective) + factor = Fraction(1117109, 1000000) + for band in twice["bands"]: + if band.get("upper_cent") is not None: + value = ( + Fraction(band["upper_cent"]["numerator"], 1) + / band["upper_cent"]["denominator"] + ) + value *= factor + band["upper_cent"] = { + "numerator": value.numerator, + "denominator": value.denominator, + } + with pytest.raises(ValueError, match="exactly once"): + interval_table(twice) + for mutation in ("source_cut", "factor", "source_hash", "operation", "missing"): + tampered = copy.deepcopy(effective) + if mutation == "source_cut": + tampered["bands"][1]["source_upper_cent"] = 10_001 + elif mutation == "factor": + tampered["scaling"]["operational_factor"] = { + "numerator": 1117110, + "denominator": 1000000, + } + elif mutation == "source_hash": + tampered["scaling"]["source_table_sha256"] = "0" * 64 + elif mutation == "operation": + tampered["scaling"]["operation"] = "round_to_cents" + else: + tampered["bands"][1].pop("source_upper_cent") + with pytest.raises(ValueError): + interval_table(tampered) + + +def test_document_scaling_changes_only_named_tables(): + document = { + "schema_version": 1, + "tables": [_table(table_id="scaled"), _table(table_id="kept")], + } + effective = scale_interval_document( + document, + { + "scaled": { + "factor": CENTRAL, + "factor_id": "toy_factor", + "factor_declaration_sha256": "a" * 64, + "factor_receipt_sha256": "b" * 64, + } + }, + ) + assert effective["tables"][1] == document["tables"][1] + assert "scaling" in effective["tables"][0] + assert interval_document_sha256(effective) != interval_document_sha256(document) + with pytest.raises(ValueError, match="Unknown interval tables"): + scale_interval_document(document, {"missing": {}}) + + +def test_input_fingerprints_keep_value_types(): + digests = { + interval_input_fingerprint(values) + for values in ([1, 2], [True, 2], [1.0, 2], ["1", 2], [np.int64(1), 2]) + } + assert len(digests) == 4 + assert interval_input_fingerprint([np.int64(1), 2]) == interval_input_fingerprint( + [1, 2] + ) + + +# --------------------------------------------------------------------------- +# Diagnostics on frames +# --------------------------------------------------------------------------- + + +def test_household_entity_table_is_weighted_through_memberships(): + table = interval_table( + _table(entity="household", income="household_income", universe="household_in") + ) + frame = Frame( + { + "person": pd.DataFrame( + {"person_id": [0, 1, 2, 3], "person_household_id": [0, 0, 1, 2]} + ), + "household": pd.DataFrame( + { + "household_id": [0, 1, 2], + "household_income": [0, 10_000, 10_001], + "household_in": ["included", "included", "excluded"], + } + ), + }, + EntitySchema(group_entities=("household",)), + {"person": Weights(np.asarray([2.0, 2.0, 3.0, 5.0]), WeightKind.CALIBRATED)}, + ) + report = frame_boundary_diagnostics(table, frame) + assert report["entity"] == "household" + assert report["weight_kind"] == "calibrated" + bands = {row["band"]: row for row in report["bands"]} + assert bands["nil"]["declared"]["weighted_count"] == { + "numerator": 2, + "denominator": 1, + } + assert bands["b0"]["declared"]["weighted_count"] == { + "numerator": 3, + "denominator": 1, + } + assert report["included_records"] == 2 + # The left-closed alternative moves the exact-cut household to the next band. + assert bands["b0"]["delta"]["weighted_count"] == { + "numerator": -3, + "denominator": 1, + } + assert bands["b1"]["delta"]["weighted_amount_cents"] == { + "numerator": 30_000, + "denominator": 1, + } + + +def test_group_entity_without_weights_resolves_from_the_weighted_household(): + table = interval_table( + _table(entity="tax_unit", income="unit_income", universe="unit_in") + ) + frame = Frame( + { + "person": pd.DataFrame( + { + "person_id": [0, 1, 2], + "person_household_id": [0, 0, 1], + "person_tax_unit_id": [0, 1, 2], + } + ), + "household": pd.DataFrame({"household_id": [0, 1]}), + "tax_unit": pd.DataFrame( + { + "tax_unit_id": [0, 1, 2], + "unit_income": [5, 0, 200_000], + "unit_in": ["included", "excluded", "included"], + } + ), + }, + EntitySchema(group_entities=("household", "tax_unit")), + {"household": Weights(np.asarray([4.0, 6.0]), WeightKind.DESIGN)}, + ) + report = frame_boundary_diagnostics(table, frame) + bands = {row["band"]: row for row in report["bands"]} + assert bands["b0"]["declared"]["weighted_count"]["numerator"] == 4 + assert bands["b2"]["declared"]["weighted_amount_cents"]["numerator"] == 1_200_000 + assert report["weight_kind"] == "design" + + +def test_diagnostics_report_near_cut_mass_digests_and_disclosure(): + effective = interval_table(_scale(_table())) + amounts = [0, 11_071, 11_072, 11_271, 11_272, 111_711] + weights = [1.0, 0.5, 0.25, 0.125, 2.0, 3.0] + report = boundary_diagnostics( + effective, + list(range(6)), + amounts, + ["included"] * 6, + weights, + weight_record_ids=list(range(6)), + weight_kind="design", + ) + near = {row["cut"]: row for row in report["cuts"]} + # |z - 11,171.09| <= 100 cents admits 11,072 and 11,271 only. + assert near["b0"]["near_cut_records"] == 2 + assert Fraction(**near["b0"]["near_cut_weighted_count"]) == Fraction(3, 8) + assert near["b0"]["cut_cents"] == {"numerator": 1117109, "denominator": 100} + assert near["b0"]["source_cut_cents"] == 10_000 + assert near["first_lower_cut"]["near_cut_records"] == 1 + assert report["publisher_reclassification"] == "unavailable" + assert "cannot be recovered" in report["limitation"] + assert report["digests"]["factor"]["operational_factor"] == CENTRAL + assert set(report["digests"]) == { + "table_sha256", + "source_cuts_sha256", + "effective_cuts_sha256", + "factor", + "weights_sha256", + "record_order_sha256", + } + assert ( + report["digests"]["source_cuts_sha256"] + != report["digests"]["effective_cuts_sha256"] + ) + + +def test_misaligned_weights_and_nonfinite_weights_refuse(): + table = interval_table(_table()) + arguments = dict( + record_ids=[0, 1], + amounts=[1, 2], + universe=["included", "included"], + weight_kind="design", + ) + with pytest.raises(ValueError, match="aligned"): + boundary_diagnostics( + table, weights=[1.0, 2.0], weight_record_ids=[1, 0], **arguments + ) + with pytest.raises(ValueError, match="finite"): + boundary_diagnostics( + table, weights=[1.0, float("nan")], weight_record_ids=[0, 1], **arguments + ) + + +# --------------------------------------------------------------------------- +# Hypothesis: an independent Fraction oracle +# --------------------------------------------------------------------------- + + +def _oracle_cut(value) -> Fraction: + if isinstance(value, dict): + return Fraction(value["numerator"], value["denominator"]) + return Fraction(value) + + +def _oracle_cuts(table) -> list[Fraction]: + cuts = [_oracle_cut(table["first_lower_cut_cents"])] + for band in table["bands"]: + if band["kind"] == "positive" and band["upper_cent"] is not None: + cuts.append(_oracle_cut(band["upper_cent"])) + return cuts + + +def _oracle_band(z: Fraction, table) -> str: + """Band id for a nonnegative income under the table's own declaration.""" + if table["nil_band"] == "present" and z == 0: + return "nil" + cuts = _oracle_cuts(table) + positive = [band["id"] for band in table["bands"] if band["kind"] == "positive"] + for index, band_id in enumerate(positive): + lower = cuts[index] + upper = cuts[index + 1] if index + 1 < len(cuts) else None + if table["positive_band_closure"] == "right": + inside = lower < z and (upper is None or z <= upper) + else: + inside = lower <= z and (upper is None or z < upper) + if inside: + return band_id + return "below" + + +@st.composite +def _tables(draw, big=False): + count = draw(st.integers(min_value=1, max_value=6)) + step = st.integers(min_value=1, max_value=10**30 if big else 50_000) + closure = draw(st.sampled_from(["left", "right"])) + nil = draw(st.booleans()) + first = draw(st.one_of(st.just(0), step)) + cuts, previous = [], first + for _ in range(count): + previous += draw(step) + cuts.append(previous) + return _table( + tuple(cuts), + closure=closure, + nil=nil, + first=first, + ) + + +_FACTORS = st.builds( + lambda numerator, denominator: { + "numerator": numerator, + "denominator": denominator, + }, + st.integers(min_value=1, max_value=10**9), + st.one_of( + st.sampled_from([1, 10, 100, 1_000_000]), + st.integers(min_value=1, max_value=10**9), + ), +) + + +def _near_cuts(table, draw, count): + cuts = [int(cut) for cut in _oracle_cuts(table)] + values = st.one_of( + st.integers(min_value=-(10**6), max_value=cuts[-1] * 2 + 10), + st.sampled_from(cuts).flatmap( + lambda cut: st.integers(min_value=cut - 2, max_value=cut + 2) + ), + ) + return draw(st.lists(values, min_size=1, max_size=count)) + + +@given(data=st.data()) +@_SETTINGS +def test_property_included_records_fall_in_exactly_one_band_excluded_in_none(data): + table = data.draw(_tables()) + amounts = _near_cuts(table, data.draw, 24) + universe = data.draw( + st.lists( + st.sampled_from(["included", "excluded"]), + min_size=len(amounts), + max_size=len(amounts), + ) + ) + parsed = interval_table(table) + indicators = interval_indicators(parsed, amounts, universe) + stacked = np.vstack(list(indicators.values())) + for index, (amount, status) in enumerate(zip(amounts, universe, strict=True)): + column = stacked[:, index] + expected = _oracle_band(Fraction(max(0, amount)), table) + if status == "excluded": + assert not column.any() + elif expected == "below": + assert not column.any() + assert _oracle_cuts(table)[0] > 0 or ( + table["nil_band"] == "absent" + and table["positive_band_closure"] == "right" + and amount <= 0 + ) + else: + assert column.sum() == 1 + assert parsed.band_ids[int(np.argmax(column))] == expected + + +@given(data=st.data()) +@_SETTINGS +def test_property_unresolved_records_never_yield_indicators(data): + table = data.draw(_tables()) + amounts = _near_cuts(table, data.draw, 12) + universe = data.draw( + st.lists( + st.sampled_from(["included", "excluded", "unresolved"]), + min_size=len(amounts), + max_size=len(amounts), + ) + ) + unresolved = universe.count("unresolved") + if not unresolved: + universe[data.draw(st.integers(0, len(universe) - 1))] = "unresolved" + unresolved = 1 + with pytest.raises(IntervalPreparationError) as refused: + interval_indicators(interval_table(table), amounts, universe) + assert refused.value.summary["reasons"]["unresolved_universe_status"] == unresolved + + +@given(table=_tables(), factor=_FACTORS) +@_SETTINGS +def test_property_scaling_preserves_order_nil_closure_and_tails(table, factor): + effective = _scale(table, factor) + rational = Fraction(factor["numerator"], factor["denominator"]) + assert [_oracle_cut(cut) for cut in [effective["first_lower_cut_cents"]]] == [ + Fraction(table["first_lower_cut_cents"]) * rational + ] + assert _oracle_cuts(effective) == [cut * rational for cut in _oracle_cuts(table)] + cuts = _oracle_cuts(effective) + assert all(left < right for left, right in zip(cuts, cuts[1:], strict=False)) + for before, after in zip(table["bands"], effective["bands"], strict=True): + assert (before["id"], before["kind"], before["indicator"]) == ( + after["id"], + after["kind"], + after["indicator"], + ) + assert effective["bands"][-1]["upper_cent"] is None + for key in ("positive_band_closure", "nil_band", "negative_income_policy"): + assert effective[key] == table[key] + with pytest.raises(ValueError, match="already scaled"): + _scale(effective, factor) + + +@given(data=st.data(), factor=_FACTORS) +@_SETTINGS +def test_property_scaled_classification_equals_deflated_source_classification( + data, factor +): + table = data.draw(_tables()) + effective = _scale(table, factor) + rational = Fraction(factor["numerator"], factor["denominator"]) + scaled_cuts = _oracle_cuts(effective) + amounts = data.draw( + st.lists( + st.one_of( + st.integers(min_value=-(10**6), max_value=int(scaled_cuts[-1]) * 2 + 2), + st.sampled_from(scaled_cuts).flatmap( + lambda cut: st.integers( + min_value=int(cut) - 2, max_value=int(cut) + 2 + ) + ), + ), + min_size=1, + max_size=24, + ) + ) + actual = _classify(effective, amounts) + expected = [ + _oracle_band(Fraction(max(0, amount)) / rational, table) for amount in amounts + ] + assert actual == expected + + +@given(table=_tables(), factor=_FACTORS) +@_SETTINGS +def test_property_adjacent_attainable_cents_around_fractional_cuts(table, factor): + effective = _scale(table, factor) + for cut in _oracle_cuts(effective): + floor = cut.numerator // cut.denominator + amounts = [floor - 1, floor, floor + 1, floor + 2] + actual = _classify(effective, amounts) + assert actual == [ + _oracle_band(Fraction(max(0, amount)), effective) for amount in amounts + ] + if cut.denominator != 1: + # No attainable cent sits on a fractional cut: floor and floor + 1 + # straddle it under either closure. + assert floor < cut < floor + 1 + + +@given(data=st.data()) +@_SETTINGS +def test_property_permuting_records_and_weights_preserves_diagnostics(data): + table = data.draw(_tables()) + amounts = _near_cuts(table, data.draw, 16) + universe = data.draw( + st.lists( + st.sampled_from(["included", "excluded"]), + min_size=len(amounts), + max_size=len(amounts), + ) + ) + weights = data.draw( + st.lists( + st.floats(min_value=0, max_value=1e6, allow_nan=False), + min_size=len(amounts), + max_size=len(amounts), + ) + ) + ids = list(range(len(amounts))) + order = data.draw(st.permutations(ids)) + parsed = interval_table(table) + + def report(indices, weight_ids=None): + rows = [ids[index] for index in indices] + return boundary_diagnostics( + parsed, + rows, + [amounts[index] for index in indices], + [universe[index] for index in indices], + [weights[index] for index in indices], + weight_record_ids=rows if weight_ids is None else weight_ids, + weight_kind="design", + ) + + original, permuted = report(ids), report(order) + for key in ( + "cuts", + "bands", + "below_first_cut", + "floored_negative_income", + "included_records", + ): + assert original[key] == permuted[key] + if order != ids: + with pytest.raises(ValueError, match="aligned"): + report(order, weight_ids=ids) + + +@given(data=st.data()) +@_SETTINGS +def test_property_alternative_closure_deltas_sum_to_zero_and_nil_is_unchanged(data): + table = data.draw(_tables()) + amounts = _near_cuts(table, data.draw, 20) + weights = data.draw( + st.lists( + st.floats(min_value=0, max_value=1e9, allow_nan=False), + min_size=len(amounts), + max_size=len(amounts), + ) + ) + ids = list(range(len(amounts))) + report = boundary_diagnostics( + interval_table(table), + ids, + amounts, + ["included"] * len(amounts), + weights, + weight_record_ids=ids, + weight_kind="design", + ) + rows = [row for row in report["bands"] if row["kind"] == "positive"] + rows.append(report["below_first_cut"]) + for measure in ("weighted_count", "weighted_amount_cents"): + assert sum(Fraction(**row["delta"][measure]) for row in rows) == 0 + assert sum(row["delta"]["records"] for row in rows) == 0 + nil = [row for row in report["bands"] if row["kind"] == "nil"] + assert all(set(row) == {"band", "kind", "declared"} for row in nil) + # The oracle's exact weighted count per declared band. + for row in report["bands"]: + expected = sum( + ( + Fraction(weight) + for amount, weight in zip(amounts, weights, strict=True) + if _oracle_band(Fraction(max(0, amount)), table) == row["band"] + ), + Fraction(0), + ) + assert Fraction(**row["declared"]["weighted_count"]) == expected + + +@given(data=st.data()) +@_SETTINGS +def test_property_very_large_cross_products_do_not_overflow(data): + table = data.draw(_tables(big=True)) + factor = data.draw( + st.builds( + lambda numerator, denominator: { + "numerator": numerator, + "denominator": denominator, + }, + st.integers(min_value=1, max_value=10**40), + st.integers(min_value=1, max_value=10**40), + ) + ) + effective = _scale(table, factor) + cuts = _oracle_cuts(effective) + amounts = data.draw( + st.lists( + st.one_of( + st.integers(min_value=-(10**45), max_value=10**75), + st.sampled_from(cuts).flatmap( + lambda cut: st.integers( + min_value=int(cut) - 1, max_value=int(cut) + 1 + ) + ), + ), + min_size=1, + max_size=12, + ) + ) + rational = Fraction(factor["numerator"], factor["denominator"]) + assert _classify(effective, amounts) == [ + _oracle_band(Fraction(max(0, amount)) / rational, table) for amount in amounts + ] diff --git a/packages/microcosm-build/tests/engine_free/shared/test_reference_profiles.py b/packages/microcosm-build/tests/engine_free/shared/test_reference_profiles.py index c7324e58d7..e076567f7d 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_reference_profiles.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_reference_profiles.py @@ -3,15 +3,26 @@ from __future__ import annotations import copy +import json +from datetime import date import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st +from microcosm.build.reference_factors import resolve_reference_factor from microcosm.build.reference_profiles import ( REFERENCE_DOCUMENT_KEYS, reference_document_sha256, resolve_reference_profiles, validate_reference_resolution, ) +from microcosm.graph.canonical import canonical_json +from test_support.microcosm_build.reference_factors import ( + factor_context_fixture, + factor_fixture, +) +from test_support.paths import paths_for def _selector(key, category=None): @@ -291,3 +302,1107 @@ def test_effective_document_projects_resource_headers_and_binds_full_base(): effective ) assert validate_reference_resolution(effective, receipt) == receipt + + +def _scaling_inputs(pair_count=2, anchor_level="110"): + context, _, _ = factor_context_fixture(anchor_level=anchor_level) + references = {"country": "toy", "target_references": []} + money, counts = [], [] + for kind, names in (("count", counts), ("money", money)): + for index in range(pair_count): + name = f"{kind}_{index}" + names.append(name) + selector = _selector(f"{kind}-fact-{index}", index) + selector.update( + source_measure_id=kind, + source_concept=f"households.{kind}", + period_value=2001, + ) + row = { + "name": name, + "entity": "household", + "measure": f"prepared_{kind}", + "filter": f"band_{index}", + "period": 2002, + "ledger_selector": selector, + "metadata": { + "activation_status": "requires_prerequisites", + "reference_value_kind": kind, + "required_reference_profile": "aging_index", + "required_measurement_binding": "reviewed_income", + "reference_prerequisites": json.dumps( + ["reference_profile:aging_index", "measurement:reviewed_income"] + ), + "parking_reason": "Reviewed measurement remains unavailable.", + }, + } + if kind == "count": + row.update(value_operation="identity", value_operands=[]) + references["target_references"].append(row) + transformation = { + "operation": "scale_money_and_intervals", + "factor_id": "toy_factor", + "reference_names": money, + "count_reference_names": counts, + "interval_table_ids": [], + "required_measurement_binding": "reviewed_income", + } + declarations = { + "schema_version": 1, + "dimensions": { + "aging_index": { + "default": "central", + "profiles": { + name: {"transformations": [copy.deepcopy(transformation)]} + for name in ("low", "central", "high") + }, + } + }, + } + return references, declarations, context + + +def test_factor_scales_money_once_keeps_counts_and_measurement_prerequisite(): + references, declarations, context = _scaling_inputs() + original = copy.deepcopy((references, declarations, context)) + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + assert (references, declarations, context) == original + assert receipt["resolver_version"] == "reference-profiles-v2" + assert set(receipt["selected_factors"]) == {"toy_factor"} + for source, row in zip( + references["target_references"], effective["target_references"], strict=True + ): + metadata = row["metadata"] + assert metadata["activation_status"] == "requires_prerequisites" + assert metadata["parking_reason"] == source["metadata"]["parking_reason"] + assert json.loads(metadata["reference_prerequisites_remaining"]) == [ + "measurement:reviewed_income" + ] + assert row["ledger_selector"] == source["ledger_selector"] + if row["measure"] == "prepared_count": + assert {key: value for key, value in row.items() if key != "metadata"} == { + key: value for key, value in source.items() if key != "metadata" + } + assert receipt["transformations"][row["name"]]["operation"] == "identity" + else: + assert row["value_operation"] == "linear_combination" + assert row["value_operands"] == [ + { + "aggregate_fact_key": source["ledger_selector"][ + "aggregate_fact_key" + ], + "expected_member_count": 1, + "weight": 1.1, + } + ] + assert validate_reference_resolution(effective, receipt) == receipt + with pytest.raises(ValueError, match="already been resolved"): + resolve_reference_profiles(effective, declarations, {}, factor_context=context) + + +@given( + pair_count=st.integers(min_value=1, max_value=16), + anchor=st.integers(min_value=1, max_value=500), +) +# Two integers are cheap to draw; a 31 s draw under host load tripped +# too_slow in review, so only wall-clock health is suppressed. +@settings( + max_examples=16, + database=None, + deadline=None, + suppress_health_check=[HealthCheck.too_slow], +) +def test_counts_are_invariant_and_every_money_row_uses_one_common_factor( + pair_count, anchor +): + references, declarations, context = _scaling_inputs(pair_count, str(anchor)) + effective, _, _ = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + counts = references["target_references"][:pair_count] + effective_counts = effective["target_references"][:pair_count] + for before, after in zip(counts, effective_counts, strict=True): + assert before["ledger_selector"] == after["ledger_selector"] + assert before["value_operation"] == after["value_operation"] + assert before["value_operands"] == after["value_operands"] + weights = [ + row["value_operands"] for row in effective["target_references"][pair_count:] + ] + assert all(len(operands) == 1 for operands in weights) + rational = context["factors"][0]["operational_factor"] + assert {operands[0]["weight"] for operands in weights} == { + rational["numerator"] / rational["denominator"] + } + with pytest.raises(ValueError, match="already been resolved"): + resolve_reference_profiles(effective, declarations, {}, factor_context=context) + + +@pytest.mark.parametrize( + "mutation", + [ + "missing_count", + "swapped_roles", + "missing_both", + "mismatched_filter", + "overlap", + "duplicate", + "duplicate_count", + "source_period", + "target_period", + "diagnostic_factor", + "money_already_scaled", + "arbitrary_parking", + "missing_prerequisites", + "wrong_measurement", + "override_prerequisites", + "override_measurement", + "missing_context", + "wrong_factor", + "wrong_receipt", + "wrong_profile_factor", + ], +) +def test_invalid_factor_profiles_and_companions_refuse(mutation): + references, declarations, context = _scaling_inputs() + transform = declarations["dimensions"]["aging_index"]["profiles"]["central"][ + "transformations" + ][0] + if mutation == "missing_count": + transform["count_reference_names"].pop() + elif mutation == "swapped_roles": + transform["count_reference_names"], transform["reference_names"] = ( + transform["reference_names"], + transform["count_reference_names"], + ) + elif mutation == "missing_both": + transform["count_reference_names"].pop() + transform["reference_names"].pop() + elif mutation == "mismatched_filter": + references["target_references"][0]["filter"] = "wrong_band" + elif mutation == "overlap": + declarations["dimensions"]["another"] = copy.deepcopy( + declarations["dimensions"]["aging_index"] + ) + elif mutation == "duplicate": + transform["reference_names"].append(transform["reference_names"][0]) + elif mutation == "duplicate_count": + transform["count_reference_names"].append(transform["count_reference_names"][0]) + elif mutation in {"source_period", "target_period"}: + key = "ledger_selector" if mutation == "source_period" else "period" + for row in references["target_references"]: + if key == "ledger_selector": + row[key]["period_value"] = 1999 + else: + row[key] = 1999 + elif mutation == "diagnostic_factor": + entry = context["factors"][0] + entry["declaration"]["purpose"] = "diagnostic" + from microcosm.build.reference_factors import factor_document_sha256 + + entry["receipt"]["declaration_sha256"] = factor_document_sha256( + entry["declaration"] + ) + body = { + key: value + for key, value in entry["receipt"].items() + if key != "receipt_sha256" + } + entry["receipt"]["receipt_sha256"] = factor_document_sha256(body) + elif mutation == "money_already_scaled": + row = references["target_references"][-1] + row["value_operation"] = "linear_combination" + row["value_operands"] = [ + { + "aggregate_fact_key": row["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": 1.1, + } + ] + elif mutation == "arbitrary_parking": + references["target_references"][0]["metadata"]["activation_status"] = ( + "requires_arbitrary_evidence" + ) + elif mutation == "missing_prerequisites": + references["target_references"][0]["metadata"].pop("reference_prerequisites") + elif mutation == "wrong_measurement": + references["target_references"][0]["metadata"][ + "required_measurement_binding" + ] = "other" + elif mutation in {"override_prerequisites", "override_measurement"}: + key = ( + "reference_prerequisites" + if mutation == "override_prerequisites" + else "required_measurement_binding" + ) + transform["metadata"] = {key: "forged"} + elif mutation == "missing_context": + context = None + elif mutation == "wrong_factor": + context["factors"][0]["operational_factor"]["numerator"] += 1 + elif mutation == "wrong_receipt": + context["factors"][0]["receipt"]["receipt_sha256"] = "0" * 64 + elif mutation == "wrong_profile_factor": + transform["factor_id"] = "missing" + with pytest.raises(ValueError): + resolve_reference_profiles(references, declarations, {}, factor_context=context) + + +def _interval_document(pair_count=2, *, table_id="reviewed_table", companions=True): + bands = [] + for index in range(pair_count): + band = { + "id": f"band_{index}", + "kind": "positive", + "upper_cent": None if index == pair_count - 1 else 10_000 * (index + 1), + "indicator": f"band_{index}", + } + if companions: + band.update( + count_reference=f"count_{index}", amount_reference=f"money_{index}" + ) + bands.append(band) + return { + "schema_version": 1, + "tables": [ + { + "id": table_id, + "entity": "household", + "currency": "XTS", + "input_unit": "integer_cent", + "income_column": "household_income_cents", + "universe_column": "household_income_universe", + "source_period": 2001, + "target_period": 2002, + "positive_band_closure": "right", + "nil_band": "absent", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": 0, + "source_locator": "Toy!A1:A2", + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left", + }, + "bands": bands, + } + ], + } + + +def _interval_inputs(pair_count=2): + references, declarations, context = _scaling_inputs(pair_count) + for profile in declarations["dimensions"]["aging_index"]["profiles"].values(): + profile["transformations"][0]["interval_table_ids"] = ["reviewed_table"] + return references, declarations, context, _interval_document(pair_count) + + +def _effective_cuts(intervals, table_id="reviewed_table"): + from fractions import Fraction + + table = next(table for table in intervals["tables"] if table["id"] == table_id) + return [ + None + if band["upper_cent"] is None + else Fraction( + band["upper_cent"]["numerator"], band["upper_cent"]["denominator"] + ) + for band in table["bands"] + ] + + +def test_named_interval_tables_scale_once_with_their_money_factor(): + references, declarations, context, intervals = _interval_inputs(3) + original = copy.deepcopy((references, declarations, context, intervals)) + effective, effective_intervals, receipt = resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + assert (references, declarations, context, intervals) == original + rational = context["factors"][0]["operational_factor"] + factor = rational["numerator"] / rational["denominator"] + # One factor covers each money/cut triple. + money = [ + row + for row in effective["target_references"] + if row["name"] in {"money_0", "money_1", "money_2"} + ] + assert {row["value_operands"][0]["weight"] for row in money} == {factor} + assert _effective_cuts(effective_intervals) == [11_000, 22_000, None] + table = effective_intervals["tables"][0] + assert table["scaling"]["factor_id"] == "toy_factor" + assert table["scaling"]["operational_factor"] == rational + assert [band.get("source_upper_cent") for band in table["bands"]] == [ + 10_000, + 20_000, + None, + ] + assert receipt["base_intervals_sha256"] == reference_document_sha256(intervals) + assert receipt["effective_intervals_sha256"] == reference_document_sha256( + effective_intervals + ) + assert receipt["effective_intervals_sha256"] != receipt["base_intervals_sha256"] + assert receipt["interval_tables"]["reviewed_table"]["factor_id"] == "toy_factor" + assert receipt["interval_tables_sha256"] == reference_document_sha256( + receipt["interval_tables"] + ) + assert receipt["receipt_sha256"] not in json.dumps(effective_intervals) + for row in effective["target_references"]: + assert ( + row["metadata"]["effective_intervals_sha256"] + == (receipt["effective_intervals_sha256"]) + ) + if row["metadata"]["reference_value_kind"] == "count": + source = next( + item + for item in references["target_references"] + if item["name"] == row["name"] + ) + assert row["value_operation"] == source["value_operation"] + assert row["value_operands"] == source["value_operands"] + assert ( + validate_reference_resolution( + effective, receipt, interval_document=effective_intervals + ) + == receipt + ) + with pytest.raises(ValueError, match="interval document hash mismatch"): + validate_reference_resolution(effective, receipt, interval_document=intervals) + with pytest.raises(ValueError, match="already been resolved"): + resolve_reference_profiles( + effective, declarations, {}, effective_intervals, factor_context=context + ) + with pytest.raises(ValueError, match="cannot contain scaled tables"): + resolve_reference_profiles( + references, declarations, {}, effective_intervals, factor_context=context + ) + + +@pytest.mark.parametrize( + "mutation", + [ + "missing_document", + "unknown_table", + "duplicate_table_id", + "omitted_band", + "extra_band", + "cross_pair", + "swapped_roles", + "duplicate_reference", + "wrong_filter", + "wrong_entity", + "no_companions", + "source_period", + "target_period", + "second_transformation", + ], +) +def test_interval_companions_must_be_exactly_the_scaled_rosters(mutation): + references, declarations, context, intervals = _interval_inputs(3) + transform = declarations["dimensions"]["aging_index"]["profiles"]["central"][ + "transformations" + ][0] + table = intervals["tables"][0] + bands = table["bands"] + if mutation == "missing_document": + intervals = None + elif mutation == "unknown_table": + transform["interval_table_ids"] = ["missing_table"] + elif mutation == "duplicate_table_id": + transform["interval_table_ids"] = ["reviewed_table", "reviewed_table"] + elif mutation == "omitted_band": + bands.pop(1) + elif mutation == "extra_band": + bands.insert( + 2, + { + "id": "band_extra", + "kind": "positive", + "upper_cent": 30_000, + "indicator": "band_extra", + "count_reference": "other_count", + "amount_reference": "other_money", + }, + ) + elif mutation == "cross_pair": + bands[0]["amount_reference"], bands[1]["amount_reference"] = ( + bands[1]["amount_reference"], + bands[0]["amount_reference"], + ) + elif mutation == "swapped_roles": + bands[0]["amount_reference"], bands[0]["count_reference"] = ( + bands[0]["count_reference"], + bands[0]["amount_reference"], + ) + elif mutation == "duplicate_reference": + bands[1]["count_reference"] = bands[0]["count_reference"] + elif mutation == "wrong_filter": + bands[0]["indicator"] = "another_indicator" + elif mutation == "wrong_entity": + table["entity"] = "person" + elif mutation == "no_companions": + intervals = _interval_document(3, companions=False) + elif mutation in {"source_period", "target_period"}: + table[mutation] = 1999 + else: + declarations["dimensions"]["another"] = { + "default": "only", + "profiles": {"only": {"transformations": [copy.deepcopy(transform)]}}, + } + with pytest.raises(ValueError): + resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + + +def test_tables_without_companions_validate_but_profiles_do_not_scale_them(): + references, declarations, context, intervals = _interval_inputs(2) + unscaled = _interval_document(3, table_id="declared_only", companions=False)[ + "tables" + ][0] + for band in unscaled["bands"]: + band["indicator"] = f"declared_{band['indicator']}" + intervals["tables"].append(unscaled) + _, effective_intervals, receipt = resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + assert effective_intervals["tables"][1] == unscaled + assert set(receipt["interval_tables"]) == {"reviewed_table"} + + +def test_unnamed_interval_tables_pass_through_and_effective_equals_base(): + references, declarations, context = _scaling_inputs() + intervals = _interval_document(2) + _, effective_intervals, receipt = resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + assert effective_intervals == intervals + assert "interval_tables" not in receipt + assert receipt["effective_intervals_sha256"] == receipt["base_intervals_sha256"] + + +def test_interval_scaling_is_local_to_the_selected_profile(): + references, declarations, context, intervals = _interval_inputs(2) + declaration, facts, identity = factor_fixture(anchor_level="105") + declaration["id"] = "toy_low" + factor, factor_receipt = resolve_reference_factor(declaration, facts, identity) + context["factors"].append( + { + "declaration": declaration, + "operational_factor": factor, + "receipt": factor_receipt, + } + ) + profiles = declarations["dimensions"]["aging_index"]["profiles"] + profiles["low"]["transformations"][0]["factor_id"] = "toy_low" + low = resolve_reference_profiles( + references, + declarations, + {"aging_index": "low"}, + intervals, + factor_context=context, + ) + central = resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + assert _effective_cuts(low[1]) == [10_500, None] + assert _effective_cuts(central[1]) == [11_000, None] + profiles["high"]["notes"] = "edited unselected high declaration" + assert ( + resolve_reference_profiles( + references, + declarations, + {"aging_index": "low"}, + intervals, + factor_context=context, + ) + == low + ) + assert ( + resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + == central + ) + + +@pytest.mark.parametrize( + "mutation", + [ + "record_factor", + "record_profile", + "record_hash", + "drop_records", + "document_table", + "unscaled_document", + ], +) +def test_rehashed_interval_scaling_records_still_refuse(mutation): + references, declarations, context, intervals = _interval_inputs(2) + effective, effective_intervals, receipt = resolve_reference_profiles( + references, declarations, {}, intervals, factor_context=context + ) + receipt = copy.deepcopy(receipt) + document = copy.deepcopy(effective_intervals) + record = receipt["interval_tables"]["reviewed_table"] + if mutation == "record_factor": + record["operational_factor"] = {"numerator": 1, "denominator": 1} + receipt["interval_tables_sha256"] = reference_document_sha256( + receipt["interval_tables"] + ) + elif mutation == "record_profile": + record["profile"] = "high" + receipt["interval_tables_sha256"] = reference_document_sha256( + receipt["interval_tables"] + ) + elif mutation == "record_hash": + record["effective_table_sha256"] = "0" * 64 + elif mutation == "drop_records": + receipt.pop("interval_tables") + receipt.pop("interval_tables_sha256") + elif mutation == "document_table": + document["tables"][0]["scaling"]["factor_receipt_sha256"] = "0" * 64 + receipt["effective_intervals_sha256"] = reference_document_sha256(document) + else: + document = copy.deepcopy(intervals) + receipt["effective_intervals_sha256"] = reference_document_sha256(document) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError): + validate_reference_resolution(effective, receipt, interval_document=document) + + +def test_factor_evidence_and_unselected_profile_locality(): + references, declarations, context = _scaling_inputs() + low = resolve_reference_profiles( + references, declarations, {"aging_index": "low"}, factor_context=context + ) + central = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + declarations["dimensions"]["aging_index"]["profiles"]["high"]["notes"] = ( + "edited unselected declaration" + ) + unselected = copy.deepcopy(context["factors"][0]) + unselected["declaration"]["id"] = "unselected_invalid_factor" + context["factors"].append(unselected) + assert ( + resolve_reference_profiles( + references, declarations, {"aging_index": "low"}, factor_context=context + ) + == low + ) + assert ( + resolve_reference_profiles(references, declarations, {}, factor_context=context) + == central + ) + # Factor evidence is part of selection identity, not merely an opaque attachment. + receipt = copy.deepcopy(central[2]) + receipt["selected_factor_identities"]["toy_factor"]["receipt_sha256"] = "0" * 64 + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="factor evidence identity"): + validate_reference_resolution(central[0], receipt) + + +def test_legacy_receipt_remains_version_one_without_factor_fields(): + references, declarations = _inputs() + _, _, receipt = resolve_reference_profiles(references, declarations, {}) + assert receipt["resolver_version"] == "reference-profiles-v1" + assert "selected_factors" not in receipt + assert "selected_factor_identities" not in receipt + assert "factor_companions" not in receipt + assert "factor_companions_sha256" not in receipt + # Canonical bytes produced by the v1 resolver at 1d5fff46. + assert reference_document_sha256(receipt) == ( + "753fe7ddb2bc5e00cc4d70a74e0cac8295af266ffbc05511101540e39ae21508" + ) + + +@pytest.mark.parametrize( + ("selection", "digest"), + [ + ({}, "09fb37b862d2daaa24f62ad2a1660f794885f201f25157bcf7db885fbd521d38"), + ( + {"aging_index": "low"}, + "50e9554eef9624e5e0d6ae3f3e8e28c1c01925150010db0e58df7f8a2def8254", + ), + ( + {"aging_index": "high"}, + "ebb6f0955d8865f6e8b445db74bd0f8143853852d25bd88374495d1d923c126e", + ), + ], +) +def test_nz_factor_receipts_are_byte_identical_after_additive_factors( + selection, digest +): + # Canonical v2 receipts, with the interval document, produced at #1394's + # 75c27688, before the donor factors and the money_normalization_input + # purpose were added to the factor document. + spec = paths_for("microcosm-build").package / "src/microcosm/build/nz" + _, _, receipt = resolve_reference_profiles( + json.loads((spec / "target_references.json").read_text()), + json.loads((spec / "reference_profiles.json").read_text()), + selection, + json.loads((spec / "reference_intervals.json").read_text()), + factor_context=json.loads((spec / "reference_aging.json").read_text()), + ) + assert receipt["resolver_version"] == "reference-profiles-v2" + assert receipt["receipt_sha256"] == digest + + +def test_legacy_receipt_without_transformed_names_refuses_as_a_value_error(): + references, declarations = _inputs() + effective, _, receipt = resolve_reference_profiles(references, declarations, {}) + receipt.pop("transformed_names") + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="transformed names"): + validate_reference_resolution(effective, receipt) + + +def test_unnamed_unresolved_rows_still_validate_without_factor_evidence(): + references, declarations = _inputs() + effective, _, receipt = resolve_reference_profiles(references, declarations, {}) + effective["target_references"].extend( + [ + {"entity": "household", "metadata": {}}, + {"entity": "household", "metadata": {}}, + ] + ) + receipt.pop("receipt_sha256") + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt["receipt_sha256"] = reference_document_sha256(receipt) + assert validate_reference_resolution(effective, receipt) == receipt + + +def test_duplicate_unresolved_factor_companion_refuses(): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + duplicate = copy.deepcopy(effective["target_references"][0]) + assert duplicate["name"] == "count_0" + duplicate["metadata"].pop("reference_profiles_resolved") + effective["target_references"].append(duplicate) + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="Duplicate factor companion"): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize("with_factors", [False, True], ids=["v1", "v2"]) +@pytest.mark.parametrize( + "mutation", + [ + "transformations_list", + "transformations_null", + "transformation_scalar", + "resolved_name_missing", + "resolved_name_null", + "resolved_name_empty", + "resolved_name_scalar", + "resolved_name_list", + ], +) +def test_malformed_resolution_shapes_refuse_as_value_errors(with_factors, mutation): + if with_factors: + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + else: + references, declarations = _inputs() + effective, _, receipt = resolve_reference_profiles(references, declarations, {}) + row = effective["target_references"][0] + if mutation == "transformations_list": + receipt["transformations"] = [] + elif mutation == "transformations_null": + receipt["transformations"] = None + elif mutation == "transformation_scalar": + receipt["transformations"][row["name"]] = 7 + elif mutation == "resolved_name_missing": + row.pop("name") + else: + row["name"] = { + "resolved_name_null": None, + "resolved_name_empty": "", + "resolved_name_scalar": 7, + "resolved_name_list": [], + }[mutation] + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="transformations|reference names"): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize( + "name_fields", + [{}, {"name": None}, {"name": ""}, {"name": 7}, {"name": []}], + ids=["missing", "null", "empty", "int", "list"], +) +def test_unnamed_unresolved_factor_rows_refuse_as_value_errors(name_fields): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + effective["target_references"].append( + {"entity": "household", "metadata": {}, **name_fields} + ) + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="reference names"): + validate_reference_resolution(effective, receipt) + + +def test_nz_default_factor_receipt_is_compact_and_covers_the_exact_roster(): + spec = paths_for("microcosm-build").package / "src/microcosm/build/nz" + base = json.loads((spec / "target_references.json").read_text()) + declarations = json.loads((spec / "reference_profiles.json").read_text()) + context = json.loads((spec / "reference_aging.json").read_text()) + intervals = json.loads((spec / "reference_intervals.json").read_text()) + _, _, receipt = resolve_reference_profiles( + base, declarations, {}, intervals, factor_context=context + ) + assert set(receipt["interval_tables"]) == {"ird_taxable_income_national_ty2024"} + assert ( + receipt["interval_tables"]["ird_taxable_income_national_ty2024"]["bands"] == 237 + ) + assert "selected_declarations" not in receipt + assert "base_reference_document" not in receipt + companions = receipt["factor_companions"] + expected_names = { + f"ird_taxable_income_{kind}_by_band__row_{index}" + for kind in ("people", "total") + for index in range(8, 245) + } + assert len(companions) == 474 + assert set(companions) == expected_names + source_rows = {row["name"]: row for row in base["target_references"]} + for name, companion in companions.items(): + source = source_rows[name] + role = source["metadata"]["reference_value_kind"] + assert companion == { + "role": role, + "factor_id": "ird_ty2024_to_ty2027_central", + "dimension": "aging_index", + "profile": "central", + "source": { + "ledger_selector": source["ledger_selector"], + "period": source.get("period"), + "value_operation": source.get("value_operation", "identity"), + "value_operands": source.get("value_operands", []), + "metadata": {"reference_value_kind": role}, + }, + } + assert receipt["factor_companions_sha256"] == reference_document_sha256(companions) + assert len(canonical_json(receipt)) < 2_600_000 + + +def test_nz_default_factor_resolution_validates_after_declaration_expiry(monkeypatch): + from microcosm.build import reference_factors + + class LaterDate(date): + @classmethod + def today(cls): + return cls(2026, 11, 5) + + monkeypatch.setattr(reference_factors, "date", LaterDate) + spec = paths_for("microcosm-build").package / "src/microcosm/build/nz" + base = json.loads((spec / "target_references.json").read_text()) + declarations = json.loads((spec / "reference_profiles.json").read_text()) + context = json.loads((spec / "reference_aging.json").read_text()) + intervals = json.loads((spec / "reference_intervals.json").read_text()) + effective, effective_intervals, receipt = resolve_reference_profiles( + base, declarations, {}, intervals, factor_context=context + ) + assert receipt["selected_factors"] + assert ( + validate_reference_resolution( + effective, receipt, interval_document=effective_intervals + ) + == receipt + ) + + +def test_unrelated_direct_fact_references_are_outside_companion_rosters(): + references, declarations, context = _scaling_inputs() + direct = { + "name": "other_direct_fact", + "ledger_fact_key": "other", + "entity": "household", + } + references["target_references"].append(direct) + effective, _, _ = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + assert effective["target_references"][-1] == direct + + +def test_self_consistent_effective_money_tampering_still_refuses(): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + effective["target_references"][-1]["value_operands"][0]["weight"] *= 1.1 + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="must be applied exactly once"): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize("target", ["count", "money"]) +def test_rehashed_factor_application_cannot_bypass_companion_role(target): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + row = next( + row for row in effective["target_references"] if row["name"] == f"{target}_0" + ) + row["value_operation"] = "linear_combination" + row["value_operands"] = [ + { + "aggregate_fact_key": row["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": 2, + } + ] + if target == "money": + receipt["transformations"][row["name"]]["operation"] = "identity" + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="roster role|count value semantics"): + validate_reference_resolution(effective, receipt) + + +def test_rehashed_action_label_cannot_change_money_into_a_count(): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + row = next( + row for row in effective["target_references"] if row["name"] == "money_0" + ) + source = receipt["factor_companions"][row["name"]]["source"] + row["metadata"]["reference_value_kind"] = "count" + row["value_operation"] = source["value_operation"] + row["value_operands"] = copy.deepcopy(source["value_operands"]) + action = receipt["transformations"][row["name"]] + action["operation"] = "identity" + action["operand_selectors"] = copy.deepcopy(source["value_operands"]) + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="roster role"): + validate_reference_resolution(effective, receipt) + + +def test_count_preserves_original_weighted_operation_and_refuses_changed_weight(): + references, declarations, context = _scaling_inputs() + source = references["target_references"][0] + source["value_operation"] = "linear_combination" + source["value_operands"] = [ + { + "aggregate_fact_key": source["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": 1, + } + ] + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + row = effective["target_references"][0] + assert row["value_operation"] == source["value_operation"] + assert row["value_operands"] == source["value_operands"] + assert row["metadata"]["reference_value_kind"] == "count" + action = receipt["transformations"][row["name"]] + assert action["operation"] == "identity" + assert action["operand_selectors"] == source["value_operands"] + assert validate_reference_resolution(effective, receipt) == receipt + + row["value_operands"][0]["weight"] = 2 + action["operand_selectors"][0]["weight"] = 2 + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="count value semantics"): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize( + "mutation", + [ + "count_action", + "count_kind", + "outside_roster", + "outside_roster_without_factor", + "outside_roster_with_residual_label", + "source_values", + "roster", + ], +) +def test_factor_roles_and_count_semantics_use_hash_bound_companion_record(mutation): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + row = effective["target_references"][0] + if mutation == "count_action": + receipt["transformations"][row["name"]]["operation"] = "linear_combination" + elif mutation == "count_kind": + row["metadata"]["reference_value_kind"] = "money" + elif mutation == "outside_roster": + previous = row["name"] + row["name"] = "unlisted_count" + receipt["transformations"][row["name"]] = receipt["transformations"].pop( + previous + ) + receipt["transformed_names"] = sorted(receipt["transformations"]) + elif mutation in { + "outside_roster_without_factor", + "outside_roster_with_residual_label", + }: + extra = copy.deepcopy(row) + extra["name"] = "unlisted_count" + effective["target_references"].append(extra) + receipt["transformations"][extra["name"]] = copy.deepcopy( + receipt["transformations"][row["name"]] + ) + receipt["transformations"][extra["name"]].pop("factor_id") + if mutation == "outside_roster_with_residual_label": + receipt["transformations"][extra["name"]]["operation"] = ( + "allocated_residual_by_ratio" + ) + receipt["transformed_names"] = sorted(receipt["transformations"]) + elif mutation == "source_values": + receipt["factor_companions"][row["name"]]["source"]["value_operands"] = [ + {"aggregate_fact_key": "count-fact-0", "weight": 2} + ] + elif mutation == "roster": + receipt["factor_companions"]["unlisted_count"] = receipt[ + "factor_companions" + ].pop(row["name"]) + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises( + ValueError, + match=( + "roster|companion.*hash|source.*identity|source semantics" + "|selection identity mismatch" + ), + ): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize("target", ["money", "count"]) +def test_rehashed_forged_companion_role_requires_the_bound_selection_identity(target): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + row = next( + row for row in effective["target_references"] if row["name"] == f"{target}_0" + ) + companion = receipt["factor_companions"][row["name"]] + forged_role = "count" if target == "money" else "money" + companion["role"] = forged_role + companion["source"]["metadata"]["reference_value_kind"] = forged_role + row["metadata"]["reference_value_kind"] = forged_role + action = receipt["transformations"][row["name"]] + if forged_role == "money": + rational = action["operational_factor"] + operation = "linear_combination" + operands = [ + { + "aggregate_fact_key": row["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": rational["numerator"] / rational["denominator"], + } + ] + else: + operation = companion["source"]["value_operation"] + operands = companion["source"]["value_operands"] + row["value_operation"] = operation + row["value_operands"] = copy.deepcopy(operands) + action["operation"] = "linear_combination" if forged_role == "money" else "identity" + action["operand_selectors"] = copy.deepcopy(operands) + # Rehash every mutable attachment while retaining the original selection. + receipt["factor_companions_sha256"] = reference_document_sha256( + receipt["factor_companions"] + ) + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="selection identity mismatch"): + validate_reference_resolution(effective, receipt) + + +def test_factor_companion_hash_must_be_covered_by_the_selection_identity(): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + incomplete_identity = { + key: receipt[key] + for key in ( + "resolver_version", + "base_references_sha256", + "base_intervals_sha256", + "selected_declarations_sha256", + "selection", + "selected_factor_identities", + "transformed_names", + ) + } + selection_hash = reference_document_sha256(incomplete_identity) + receipt["reference_selection_sha256"] = selection_hash + for row in effective["target_references"]: + row["metadata"]["reference_selection_sha256"] = selection_hash + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="selection identity mismatch"): + validate_reference_resolution(effective, receipt) + + +@pytest.mark.parametrize("mutation", ["missing_record", "untransformed_record"]) +def test_factor_companion_record_and_transformed_rows_cover_each_other(mutation): + references, declarations, context = _scaling_inputs() + effective, _, receipt = resolve_reference_profiles( + references, declarations, {}, factor_context=context + ) + companions = receipt["factor_companions"] + if mutation == "missing_record": + companions.pop("count_0") + else: + companions["untransformed_count"] = copy.deepcopy(companions["count_0"]) + receipt["factor_companions_sha256"] = reference_document_sha256(companions) + # Rebind the changed record so this checks roster coverage after identity. + identity = { + key: receipt[key] + for key in ( + "resolver_version", + "base_references_sha256", + "base_intervals_sha256", + "selected_declarations_sha256", + "selection", + "selected_factor_identities", + "factor_companions_sha256", + "transformed_names", + ) + } + selection_hash = reference_document_sha256(identity) + receipt["reference_selection_sha256"] = selection_hash + for row in effective["target_references"]: + row["metadata"]["reference_selection_sha256"] = selection_hash + receipt["effective_references_sha256"] = reference_document_sha256(effective) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="roster|missing factor companion"): + validate_reference_resolution(effective, receipt) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_spec_engine_country_bundles.py b/packages/microcosm-build/tests/engine_free/shared/test_spec_engine_country_bundles.py index 927db55d16..6a04c198bf 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_spec_engine_country_bundles.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_spec_engine_country_bundles.py @@ -233,7 +233,10 @@ def test_nz_generation_zero_views_come_from_the_country_spec_seam() -> None: "gates.json", "geography_spine.json", "holdout_references.json", + "money_normalization.json", "precal_references.json", + "reference_aging.json", + "reference_intervals.json", "reference_inventory.json", "reference_profiles.json", "release_contract.json", diff --git a/packages/microcosm-build/tests/engine_free/shared/test_spec_source_citations.py b/packages/microcosm-build/tests/engine_free/shared/test_spec_source_citations.py index 240ad69690..9e1a37565f 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_spec_source_citations.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_spec_source_citations.py @@ -59,6 +59,7 @@ from __future__ import annotations import ast +import importlib import inspect import json import re @@ -271,6 +272,19 @@ def test_an_unresolvable_dotted_symbol_raises( resolve_dotted_symbol(dotted) +@pytest.mark.parametrize( + "dotted", + [ + "microcosm.frame.concepts.TransportRule.CARRY", + "microcosm.frame.adapters.policyengine_us.PolicyEngineUSEngine._write_and_verify", + ], +) +def test_a_class_attribute_resolves_through_its_module(dotted: str) -> None: + module, cls, attribute = dotted.rsplit(".", 2) + expected = getattr(getattr(importlib.import_module(module), cls), attribute) + assert resolve_dotted_symbol(dotted) is expected + + # --------------------------------------------------------------------------- # Validator properties # --------------------------------------------------------------------------- diff --git a/packages/microcosm-build/tests/engine_free/shared/test_transport_composed_graph.py b/packages/microcosm-build/tests/engine_free/shared/test_transport_composed_graph.py index 051e1c4cb7..e4df2e7599 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_transport_composed_graph.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_transport_composed_graph.py @@ -43,16 +43,20 @@ import shutil import time from dataclasses import replace +from datetime import UTC, date, datetime, timedelta, timezone from pathlib import Path from unittest.mock import Mock import numpy as np +import pandas as pd import pytest from hypothesis import HealthCheck, given, settings from hypothesis import strategies as st +from microcosm.build.reference_profiles import reference_document_sha256 from microcosm.build.transport import cli as cli_module from microcosm.build.transport import compose as compose_module +from microcosm.build.transport import target_kernels as target_kernels_module from microcosm.build.transport.artifact_types import COMPARISON_TYPE from microcosm.build.transport.cli import ( EXPORT_FILENAME, @@ -70,6 +74,7 @@ ) from microcosm.build.transport.population_kernels import TRANSPORT_CURRENCY from microcosm.build.transport.takeup_kernels import GAP_TYPE +from microcosm.build.transport.target_kernels import CALIBRATION_PREPARE from microcosm.build.transport.terminal_kernels import materialize_export from microcosm.frame.kernels import SimulateRulesKernel from microcosm.graph import ( @@ -80,6 +85,7 @@ Determinism, GraphError, KernelBase, + KernelContext, KernelRegistry, Node, NodeRejectedError, @@ -94,6 +100,15 @@ from microcosm.graph.canonical import canonical_json from microcosm.graph.keys import node_key, source_content_key from test_support.microcosm_build.chronicle_feed import write_pinned_artifact +from test_support.microcosm_build.reference_factor_transport import ( + factor_transport_fixture, +) +from test_support.microcosm_build.reference_intervals import ( + INCOME_COLUMN, + UNIVERSE_COLUMN, + interval_bindings, + interval_profile_fixture, +) from test_support.microcosm_build.transport_composed import ( FAMILY_INPUTS, PROGRAMS, @@ -964,6 +979,304 @@ def test_preflight_checks_the_selected_variant_reference_status(composed): validate_transport_activation(spec) +def test_factor_context_is_read_without_composition_feed_io(tmp_path, monkeypatch): + _, pin, _, _, spec = factor_transport_fixture(tmp_path) + spec["resources"]["chronicle_feed"] = pin + + def no_feed_io(*args, **kwargs): + raise AssertionError("Composition must not load the consumer artifact.") + + monkeypatch.setattr( + "microcosm.build.ledger_artifact.load_ledger_consumer_artifact", no_feed_io + ) + view = compose_module.reference_profile_view(spec) + assert view[2]["selected_factors"]["toy_factor"]["operational_factor"] == { + "numerator": 1100000, + "denominator": 1000000, + } + assert all( + row["metadata"]["activation_status"] == "requires_prerequisites" + for row in view[0]["target_references"] + ) + assert compose_module.transport_resource(spec, "target_references") == view[0] + + +def test_factor_context_ignores_feed_pin_prose(tmp_path): + _, pin, _, _, spec = factor_transport_fixture(tmp_path) + spec["resources"]["chronicle_feed"] = pin + before = compose_module.reference_profile_view(spec) + pin["build"] = "Reviewed prose edit" + pin["source_repo"] = "Reviewed repository description" + assert compose_module.reference_profile_view(spec) == before + + +@pytest.mark.parametrize( + "defect", + ["missing", "wrong_feed", "wrong_commit", "wrong_manifest", "wrong_factor"], +) +def test_preflight_refuses_missing_or_incompatible_default_factor_context( + tmp_path, defect +): + _, pin, _, _, spec = factor_transport_fixture(tmp_path) + spec["resources"]["chronicle_feed"] = pin + if defect == "missing": + spec["resources"].pop("factor_context") + elif defect == "wrong_feed": + spec["resources"]["chronicle_feed"] = {**pin, "facts_sha256": "0" * 64} + elif defect == "wrong_commit": + pin["source_commit"] = "d" * 40 + elif defect == "wrong_manifest": + pin["manifest_sha256"] = "0" * 64 + else: + spec["resources"]["factor_context"]["factors"][0]["operational_factor"][ + "numerator" + ] += 1 + with pytest.raises( + ValueError, match="missing resource|incompatible|factor differs" + ): + validate_transport_activation(spec) + + +def test_preflight_resolves_empty_knobs_only_once(tmp_path, monkeypatch): + _, _, _, _, spec = factor_transport_fixture(tmp_path) + original = compose_module.reference_profile_view + selections = [] + + def recording_view(spec, selection=None): + selections.append(selection) + return original(spec, selection) + + monkeypatch.setattr(compose_module, "reference_profile_view", recording_view) + with pytest.raises(ValueError, match="Transport spec is not activated"): + validate_transport_activation(spec) + assert selections + assert all(selection is None for selection in selections) + + +def test_preflight_validates_scenario_factor_context_before_activation(tmp_path): + _, _, _, _, spec = factor_transport_fixture(tmp_path) + profiles = spec["resources"]["reference_profiles"]["dimensions"]["index"][ + "profiles" + ] + profiles["low"]["transformations"][0]["factor_id"] = "missing-scenario-factor" + assert compose_module.reference_profile_view(spec)[2]["selection"] == { + "index": "central" + } + spec["resources"]["scenarios"]["scenarios"].append( + {"id": "variant", "knobs": {"index": "low"}} + ) + with pytest.raises(ValueError, match="Missing selected factor"): + validate_transport_activation(spec) + + +def test_declared_ageing_scenarios_bind_selected_evidence_and_preserve_locality(): + spec = load_transport_spec("nz") + profiles = spec["resources"]["reference_profiles"] + receipts = {} + for scenario in spec["resources"]["scenarios"]["scenarios"]: + receipt = compose_module.reference_profile_view(spec, scenario["knobs"])[2] + receipts[scenario["id"]] = receipt + profile = {"V1": "low", "V2": "high"}.get(scenario["id"], "central") + factor_id = f"ird_ty2024_to_ty2027_{profile}" + assert receipt["selection"] == { + "aging_index": profile, + "census_residual_allocation": ( + "small_households" if scenario["id"] == "V4" else "mar" + ), + } + assert set(receipt["selected_factors"]) == {factor_id} + entry = next( + entry + for entry in spec["resources"]["reference_aging"]["factors"] + if entry["declaration"]["id"] == factor_id + ) + assert receipt["selected_factors"][factor_id] == entry + chosen = { + dimension: { + "name": name, + "declaration": profiles["dimensions"][dimension]["profiles"][name], + } + for dimension, name in receipt["selection"].items() + } + assert ( + receipt["selected_declarations_sha256"] + == hashlib.sha256(canonical_json(chosen)).hexdigest() + ) + profiles["dimensions"]["aging_index"]["profiles"]["high"]["notes"] = ( + "Reviewed edit confined to the unselected high profile." + ) + for scenario in spec["resources"]["scenarios"]["scenarios"]: + revised = compose_module.reference_profile_view(spec, scenario["knobs"])[2] + if scenario["id"] == "V2": + assert ( + revised["reference_selection_sha256"] + != receipts["V2"]["reference_selection_sha256"] + ) + else: + assert revised == receipts[scenario["id"]] + + +def _interval_prepare_spec(): + base, profiles, context, intervals = interval_profile_fixture() + table = intervals["tables"][0] + spec = { + "country": "xx", + "resources": { + "target_references": base, + "reference_profiles": profiles, + "factor_context": context, + "profile_intervals": intervals, + "calibration_bindings": interval_bindings(table), + }, + } + row = { + "id": "prepare", + "kernel": "calibration.prepare@1", + "population": "open", + "params": { + "bindings": {"resource": "calibration_bindings", "encoding": "json"}, + "references": {"resource": "target_references", "encoding": "json"}, + "references_sha256": { + "resource": "target_references", + "encoding": "sha256", + }, + }, + "outputs": [ + {"entity": "person", "column": band["indicator"], "dtype": "bool"} + for band in table["bands"] + ], + } + return spec, row + + +_INTERVAL_CONFIG = TransportGraphConfig( + engine_refs={}, create_outputs=(Owned("person", "age", "int64"),) +) + + +def _prepare_node(spec, row, knobs, *, interval_knobs="same"): + params = { + name: compose_module._resource_param(spec, value, profile_selection=knobs) + for name, value in row["params"].items() + } + if interval_knobs != "same": + params["intervals"] = compose_module._resource_param( + spec, + {"resource": "profile_intervals", "encoding": "json"}, + profile_selection=interval_knobs, + ) + return compose_module._node( + spec, + row, + _INTERVAL_CONFIG, + {}, + resolved_params=params, + profile_selection=knobs, + ) + + +def _run_prepare(node, incomes): + tables = { + "person": pd.DataFrame( + { + "person_id": range(len(incomes)), + INCOME_COLUMN: incomes, + UNIVERSE_COLUMN: ["included"] * len(incomes), + } + ) + } + return CALIBRATION_PREPARE.run( + KernelContext( + node=node, + tables=tables, + weights={}, + strata=pd.Series([], dtype=object, name="stratum"), + params=node.params, + rng=np.random.default_rng(0), + sources={}, + artifacts={}, + ) + ) + + +def test_composition_supplies_each_variant_its_own_effective_interval_companion(): + spec, row = _interval_prepare_spec() + incomes = [104_999, 107_000, 112_000, 114_000, 116_000] + first_band = {} + for profile, knobs in ( + ("central", None), + ("low", {"index": "low"}), + ("high", {"index": "high"}), + ): + node = _prepare_node(spec, row, knobs) + view = compose_module.reference_profile_view(spec, knobs) + assert node.params["intervals"] == canonical_json(view[1]).decode("utf-8") + assert json.loads(node.params["reference_resolution"]) == view[2] + result = _run_prepare(node, incomes) + first_band[profile] = result.columns["person", "toy_income_band_b0"].tolist() + evidence = json.loads(result.artifacts["preparation"]) + assert ( + evidence["effective_intervals_sha256"] + == view[2]["effective_intervals_sha256"] + ) + # 100,000 source cents become 105,000, 110,000 and 115,000: each variant + # regenerates its indicators from its own cuts rather than reusing S0's. + assert first_band == { + "low": [True, False, False, False, False], + "central": [True, True, False, False, False], + "high": [True, True, True, True, False], + } + + +def test_composition_refuses_a_supplied_interval_document_from_another_profile(): + spec, row = _interval_prepare_spec() + row = copy.deepcopy(row) + row["params"]["intervals"] = {"resource": "profile_intervals", "encoding": "json"} + node = _prepare_node(spec, row, {"index": "low"}) + assert ( + json.loads(node.params["intervals"])["tables"][0]["scaling"]["factor_id"] + == "toy_low" + ) + with pytest.raises(ValueError, match="differs from its effective interval"): + _prepare_node(spec, row, {"index": "low"}, interval_knobs=None) + + +def test_declared_scenarios_scale_national_cuts_with_their_own_factor(): + from fractions import Fraction + + spec = load_transport_spec("nz") + source = spec["resources"]["reference_intervals"] + by_id = {table["id"]: table for table in source["tables"]} + seen = {} + for scenario in spec["resources"]["scenarios"]["scenarios"]: + _, intervals, receipt = compose_module.reference_profile_view( + spec, scenario["knobs"] + ) + profile = {"V1": "low", "V2": "high"}.get(scenario["id"], "central") + tables = {table["id"]: table for table in intervals["tables"]} + national = tables["ird_taxable_income_national_ty2024"] + assert national["scaling"]["factor_id"] == f"ird_ty2024_to_ty2027_{profile}" + assert ( + tables["ird_taxable_income_age_ty2024"] + == by_id["ird_taxable_income_age_ty2024"] + ) + factor = national["scaling"]["operational_factor"] + rational = Fraction(factor["numerator"], factor["denominator"]) + for band, original in zip( + national["bands"], + by_id["ird_taxable_income_national_ty2024"]["bands"], + strict=True, + ): + if original.get("upper_cent") is not None: + upper = band["upper_cent"] + assert Fraction(upper["numerator"], upper["denominator"]) == ( + original["upper_cent"] * rational + ) + seen.setdefault(profile, set()).add(receipt["effective_intervals_sha256"]) + assert all(len(hashes) == 1 for hashes in seen.values()) + assert len(set().union(*seen.values())) == 3 + + class _IllegalOwner(KernelBase): ref = "test.illegal_owner@1" capabilities = Capabilities(Determinism.DETERMINISTIC) @@ -1314,7 +1627,10 @@ def test_packaged_spec_refuses_before_reading_any_source(tmp_path): ) -def test_cli_main_refuses_the_packaged_spec_and_writes_nothing(tmp_path, capsys): +def test_cli_main_refuses_the_packaged_spec_and_writes_nothing( + tmp_path, monkeypatch, capsys +): + monkeypatch.setattr(cli_module, "_today", lambda: date(2026, 10, 24)) out = tmp_path / "out" assert cli_module.main(["--country", "nz", "--out", str(out)]) == 1 message = capsys.readouterr().err @@ -1503,6 +1819,104 @@ def injected_registry(bindings, root, *, unit_rule, engines_by_binding=None): assert not (out / EXPORT_FILENAME).exists() +_AFTER_FACTOR_EXPIRY = date(2100, 1, 1) + + +@pytest.mark.parametrize("resume", ["auto", "forbid"]) +@pytest.mark.parametrize("selection", ["default", "scenario"]) +def test_cli_main_refuses_expired_factors_before_create( + tmp_path, monkeypatch, capsys, resume, selection +): + _, _, _, _, spec = factor_transport_fixture(tmp_path) + if selection == "scenario": + spec["resources"]["reference_profiles"]["dimensions"]["index"]["profiles"][ + "central" + ]["transformations"] = [] + spec["resources"]["scenarios"]["scenarios"].append( + {"id": "variant", "knobs": {"index": "low"}} + ) + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + monkeypatch.setattr(cli_module, "load_transport_spec", lambda source: spec) + later = _mock_steps_after_the_currency_check(monkeypatch) + out = tmp_path / "out" + assert ( + cli_module.main(["--country", "xx", "--out", str(out), "--resume", resume]) == 1 + ) + error = capsys.readouterr().err + assert "toy_factor" in error + assert "reviewed re-declaration" in error + for step in later: + step.assert_not_called() + assert not out.exists() + + +def _mock_steps_after_the_currency_check(monkeypatch): + """Activation, the registry, the CREATE inventory and CREATE, each refusing.""" + steps = [] + for owner, name in ( + (cli_module, "validate_transport_activation"), + (cli_module, "build_transport_registry"), + (cli_module, "prepare_create_outputs"), + (cli_module.TRANSPORT_CREATE, "run"), + ): + step = Mock(side_effect=AssertionError(f"Must refuse before {name}.")) + monkeypatch.setattr(owner, name, step) + steps.append(step) + return steps + + +@pytest.mark.parametrize("resume", ["auto", "forbid"]) +@pytest.mark.parametrize("selection", ["default", "scenario"]) +def test_preparation_refuses_expired_factors_for_library_callers( + tmp_path, monkeypatch, resume, selection +): + # prepare_transport_build is exported: a caller that skips main must meet + # the same run-entry check, before activation, the registry or CREATE. + _, _, _, _, spec = factor_transport_fixture(tmp_path) + if selection == "scenario": + spec["resources"]["reference_profiles"]["dimensions"]["index"]["profiles"][ + "central" + ]["transformations"] = [] + spec["resources"]["scenarios"]["scenarios"].append( + {"id": "variant", "knobs": {"index": "low"}} + ) + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + later = _mock_steps_after_the_currency_check(monkeypatch) + with pytest.raises(ValueError, match="toy_factor") as error: + cli_module.prepare_transport_build(spec, sources={}, resume=resume) + assert "reviewed re-declaration" in str(error.value) + for step in later: + step.assert_not_called() + + +def test_preparation_lets_require_reach_stored_work_after_expiry(tmp_path, monkeypatch): + # require skips the currency check and goes on to activation and the store. + _, _, _, _, spec = factor_transport_fixture(tmp_path) + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + reached = Mock(side_effect=LookupError("activation reached")) + monkeypatch.setattr(cli_module, "validate_transport_activation", reached) + with pytest.raises(LookupError, match="activation reached"): + cli_module.prepare_transport_build(spec, sources={}, resume="require") + reached.assert_called_once_with(spec) + + +def test_preparation_checks_resume_before_currency(tmp_path, monkeypatch): + _, _, _, _, spec = factor_transport_fixture(tmp_path) + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + with pytest.raises(ValueError, match="resume must be"): + cli_module.prepare_transport_build(spec, sources={}, resume="sometimes") + + +def test_cli_factor_currency_decision_allows_historical_replay(tmp_path, monkeypatch): + # The factor fixture has parked measurement rows and no runnable skeleton. + # Exercise the CLI decision directly; the warm-store test below verifies + # actual require execution with the engine-free composed skeleton. + _, _, _, _, spec = factor_transport_fixture(tmp_path) + assert compose_module.reference_profile_view(spec)[2]["selected_factors"] + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + cli_module._require_current_run_factors(spec, resume="require") + + def test_cli_main_and_path_loader_success(composed, tmp_path, monkeypatch, capsys): # Real Path loading, registry preparation and execution; only the injected # engine seam replaces Axiom with the fixture's pure-Python adapters. @@ -1551,9 +1965,199 @@ def counted(context, _run=run, _ref=ref): assert (out / EXPORT_FILENAME).is_file() assert json.loads((out / "manifest.json").read_bytes())["nodes"] observe_warm.append(True) + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + assert cli_module.main([*argv, "--resume", "require"]) == 0 + assert capsys.readouterr().err == "" + assert calls == [] + + +def test_cli_run_date_is_the_utc_date(monkeypatch): + # 11:30pm in New York on 3 November is already 4 November in UTC, where CI runs. + instant = datetime(2026, 11, 4, 3, 30, tzinfo=UTC) + + class Clock(datetime): + @classmethod + def now(cls, tz=None): + return instant.astimezone(tz or timezone(timedelta(hours=-5))) + + monkeypatch.setattr(cli_module, "datetime", Clock) + assert cli_module._today() == date(2026, 11, 4) + + +def test_cli_currency_check_walks_the_views_activation_walks(tmp_path, monkeypatch): + # Activation resolves scenario views only when both resources exist and + # validates their shapes; the run-entry check shares that walk. + _, _, _, _, spec = factor_transport_fixture(tmp_path) + del spec["resources"]["scenarios"] + assert len(compose_module.reference_profile_views(spec)) == 1 + monkeypatch.setattr(cli_module, "_today", lambda: date(2050, 1, 1)) + cli_module._require_current_run_factors(spec, resume="auto") + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) + with pytest.raises(ValueError, match="toy_factor"): + cli_module._require_current_run_factors(spec, resume="auto") + spec["resources"]["scenarios"] = {"scenarios": {"id": "malformed"}} + with pytest.raises(TypeError) as activation: + compose_module.validate_transport_activation(spec) + with pytest.raises(TypeError) as run_entry: + cli_module._require_current_run_factors(spec, resume="auto") + assert str(run_entry.value) == str(activation.value) == "scenarios must be a list." + + +def test_cli_require_replays_a_warm_store_after_selected_factors_expire( + composed, tmp_path, monkeypatch, capsys +): + # End to end: warm a real store while a selected factor is current, then + # replay it past valid_until. require verifies and runs nothing; a new run + # of the same spec refuses on that factor before preparation work. + facts, pin, _, _, factors = factor_transport_fixture(tmp_path / "factor") + spec = copy.deepcopy(composed.spec) + spec["resources"].update( + factor_target_references=factors["resources"]["target_references"], + factor_context=factors["resources"]["factor_context"], + factor_feed=pin, + reference_profiles={ + **factors["resources"]["reference_profiles"], + "reference_resource": "factor_target_references", + }, + ) + profile_view = compose_module.reference_profile_view + + def measured_view(spec, selection=None): + # As in factor_transport_fixture(activate=True), supply a synthetic + # reviewed measurement closure. The real resolver still selects and + # binds the factors; composition must attach its rehashed receipt. + # Copies, so the resolver's own return values are never edited. + references, intervals, receipt = profile_view(spec, selection) + references, receipt = copy.deepcopy(references), copy.deepcopy(receipt) + for row in references["target_references"]: + row["metadata"]["activation_status"] = "active" + receipt["effective_references_sha256"] = reference_document_sha256(references) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + return references, intervals, receipt + + monkeypatch.setattr(compose_module, "reference_profile_view", measured_view) + declaration = spec["resources"]["transport_graph"] + compiler_id = "nz.factor.targets.compile" + declaration["sources"].append( + { + **next( + source + for source in declaration["sources"] + if source["name"] == "nz_calibration_facts" + ), + "name": "factor_facts", + } + ) + declaration["nodes"].append( + { + "id": compiler_id, + "kernel": "targets.compile@1", + "population": "nz.open", + "sources": ["factor_facts"], + "params": { + "country": factors["country"], + "references": { + "resource": "factor_target_references", + "encoding": "json", + }, + "references_sha256": { + "resource": "factor_target_references", + "encoding": "sha256", + }, + "chronicle_feed_pin": {"resource": "factor_feed", "encoding": "json"}, + }, + } + ) + # This factor-bound compiler is an explicit CLI terminal endpoint, so its + # target surface must actually be compiled and cached during the warm run. + declaration["endpoints"]["terminal"].append(compiler_id) + compiler = compose_transport_graph(spec, composed.config).node(compiler_id) + assert compiler.kernel == "targets.compile@1" + assert "reference_resolution" in compiler.params + resolution = json.loads(compiler.params["reference_resolution"]) + assert resolution["selected_factors"] + assert json.loads(compiler.params["chronicle_feed_pin"]) == pin + directory = _write_local_transport_spec(tmp_path / "spec", spec) + build_registry = cli_module.build_transport_registry + calls = [] + replay = Mock( + wraps=target_kernels_module.reference_factors_module.replay_reference_factor + ) + monkeypatch.setattr( + target_kernels_module.reference_factors_module, + "replay_reference_factor", + replay, + ) + + def injected_registry(bindings, root, *, unit_rule, engines_by_binding=None): + registry = build_registry( + bindings, + root, + unit_rule=unit_rule, + engines_by_binding=composed.engines_by_binding, + ) + for ref in registry.kernels.refs(): + kernel = registry.kernels.get(ref) + run = kernel.run + + def counted(context, _run=run): + calls.append(context.node.id) + return _run(context) + + monkeypatch.setattr(kernel, "run", counted) + return registry + + monkeypatch.setattr(cli_module, "build_transport_registry", injected_registry) + out = tmp_path / "out" + argv = ["--spec-dir", str(directory), "--out", str(out)] + for name, path in {**composed.sources, "factor_facts": facts}.items(): + argv.extend(["--source", f"{name}={path}"]) + monkeypatch.setattr(cli_module, "_today", lambda: date(2050, 1, 1)) + assert cli_module.main(argv) == 0 + assert capsys.readouterr().err == "" + assert calls.count(compiler_id) == 1 + replay.assert_called_once() + factor = resolution["selected_factors"]["toy_factor"] + # The replay ran against the authenticated factor feed: the facts an + # independent load of the pinned factor_facts source yields. + fed_facts = ( + target_kernels_module.ledger_artifact_module.load_ledger_consumer_artifact( + facts, feed_pin=pin, lexical_floats=True + ).facts + ) + assert fed_facts + replay.assert_called_once_with( + factor["declaration"], + fed_facts, + factor["declaration"]["feed_identity"], + factor["operational_factor"], + factor["receipt"], + ) + warm_node = json.loads((out / "manifest.json").read_bytes())["nodes"][compiler_id] + assert warm_node["receipt"]["n_targets"] == 2 + exported = (out / EXPORT_FILENAME).read_bytes() + calls.clear() + replay.reset_mock() + monkeypatch.setattr(cli_module, "_today", lambda: _AFTER_FACTOR_EXPIRY) assert cli_module.main([*argv, "--resume", "require"]) == 0 assert capsys.readouterr().err == "" + # require reads back the stored run's export and rewrites nothing. + assert (out / EXPORT_FILENAME).read_bytes() == exported + assert calls == [] + replay.assert_not_called() + stored_node = json.loads((out / "manifest.json").read_bytes())["nodes"][compiler_id] + assert stored_node["hit"] is True + assert stored_node["key"] == warm_node["key"] + assert stored_node["opaque_artifacts"] == warm_node["opaque_artifacts"] + later = _mock_steps_after_the_currency_check(monkeypatch) + assert cli_module.main([*argv, "--resume", "auto"]) == 1 + error = capsys.readouterr().err + assert "toy_factor" in error + assert "reviewed re-declaration" in error assert calls == [] + for step in later: + step.assert_not_called() def test_cli_main_invalid_arguments_are_a_refusal(capsys): diff --git a/packages/microcosm-build/tests/engine_free/shared/test_transport_money_kernels.py b/packages/microcosm-build/tests/engine_free/shared/test_transport_money_kernels.py new file mode 100644 index 0000000000..1149b9d419 --- /dev/null +++ b/packages/microcosm-build/tests/engine_free/shared/test_transport_money_kernels.py @@ -0,0 +1,804 @@ +"""``money.normalize@1``, its opt-in producer preflight and the toy lineage run. + +Invariants executed here: + +- Each declared money column is normalized exactly once per lineage: the + kernel rewrites it once with its integer companion, composition refuses a + second normalization in any version's lineage (a FILTER carries every + column of its base version), and a consumed column needs exactly one + declared producer. +- Factor authenticity: a factor whose receipt is self-consistent but whose + operands differ from the authenticated feed refuses on replay. +- The donor money period recorded by CREATE must equal the declaration's. +- A scenario's profile knob selects its own factor (V1 low, V2 high). +- Opt-in: without a money-normalization resource, preflight and composition + are byte-identical and the toy keeps ``transport.currency@1``. +""" + +from __future__ import annotations + +import copy +import json +from dataclasses import replace +from fractions import Fraction + +import numpy as np +import pandas as pd +import pytest + +from microcosm.build.reference_factors import ( + resolve_reference_factor, + validate_factor_context, +) +from microcosm.build.transport import compose as compose_module +from microcosm.build.transport import money_kernels as money_kernels_module +from microcosm.build.transport.artifact_types import ( + INCOME_CELLS_TYPE, + TARGET_SURFACE_TYPE, +) +from microcosm.build.transport.codecs import register_transport_codecs +from microcosm.build.transport.compose import ( + SKELETON_KERNELS, + validate_transport_activation, +) +from microcosm.build.transport.money_kernels import MONEY_NORMALIZE +from microcosm.build.transport.registry import register_transport_population_kernels +from microcosm.graph import ( + SOURCE_CODECS, + ArtifactInput, + ArtifactOutput, + ContentStore, + KernelContext, + KernelRegistry, + KernelResult, + Node, + Owned, + Slice, + compile_graph, + graph_to_json, + run_graph, +) +from microcosm.graph.canonical import canonical_json +from test_support.microcosm_build.money_normalization import ( + DIMENSION, + GRAPH_NORMALIZED, + RAW_FARM, + RAW_RENTAL, + ToyMoneyConsumer, + add_money_columns, + factor_id, + graph_declaration, + money_factor_context, + money_graph, + money_registry, + money_sources, + normalize_node, + oracle_factor, + rate_feed_form, + write_money_feed, +) +from test_support.microcosm_build.transport_composed import make_composed_fixture +from test_support.microcosm_build.transport_population import ( + write_population_sources, +) + +_VALUES = { + "interest_income": [3.0, 5.0, 0.1], + "rental_income": [-120.5, 0.0, 35.25], + "farm_self_employment_income": [0.0, -2.675, 1000000.0], +} + + +@pytest.fixture(scope="module") +def feed(tmp_path_factory): + path, pin, identity, facts = write_money_feed( + tmp_path_factory.mktemp("money-feed"), rate="1.25" + ) + return path, pin, identity, facts, money_factor_context(identity, facts) + + +def _oracle_cents(value: float, rate: Fraction, factor: Fraction) -> int: + return round(round(Fraction(repr(value)) * rate * 100) * factor) + + +def _context( + feed, + *, + declaration=None, + factor_context=None, + selection=None, + metadata=None, + pin=True, + node=None, +): + path, feed_pin, _, _, context = feed + declaration = graph_declaration() if declaration is None else declaration + factor_context = context if factor_context is None else factor_context + node = node or normalize_node( + "toy.normalize", + "toy.central", + declaration, + factor_context, + feed_pin, + selection=selection, + ) + params = dict(node.params) + if not pin: + params.pop("chronicle_feed_pin") + return KernelContext( + node=node, + tables={ + "person": pd.DataFrame({"person_id": [11, 12, 13], **_VALUES}), + }, + weights={}, + strata=pd.Series([], dtype=object), + params=params, + rng=np.random.default_rng(0), + sources={"money_facts": path}, + frame_metadata=({"donor_time_period": 2024} if metadata is None else metadata), + ) + + +# --------------------------------------------------------------------------- +# The kernel +# --------------------------------------------------------------------------- + + +def test_kernel_rewrites_each_column_once_with_its_companion_and_receipt(feed): + result = MONEY_NORMALIZE.run(_context(feed)) + factor = oracle_factor("central") + for column, values in _VALUES.items(): + cents = [_oracle_cents(value, Fraction(5, 4), factor) for value in values] + assert result.columns["person", f"{column}_cents"].tolist() == cents + assert result.columns["person", column].tolist() == [c / 100 for c in cents] + assert result.columns["person", column].index.tolist() == [11, 12, 13] + # 3 * 1.25 * 1.1 = 4.125 ties to even; -2.675 * 1.25 = -3.34375. + assert result.columns["person", "interest_income_cents"].tolist()[0] == 412 + assert result.columns["person", "farm_self_employment_income_cents"].tolist()[ + 1 + ] == round(-334 * Fraction(11, 10)) + receipt = result.receipt + assert receipt["profile"] == "central" + assert receipt["recorded_donor_time_period"] == 2024 + assert receipt["rate"]["text"] == "1.25" + assert {row["factor_id"] for row in receipt["factors"].values()} == { + factor_id("central") + } + assert [row["column"] for row in receipt["columns"]] == GRAPH_NORMALIZED["person"] + assert all(row["n"] == 3 for row in receipt["columns"]) + + +@pytest.mark.parametrize( + ("selection", "profile"), + [ + ({}, "central"), + ({DIMENSION: "low"}, "low"), + ({DIMENSION: "high"}, "high"), + ({DIMENSION: "central", "unrelated": "x"}, "central"), + ], +) +def test_the_profile_knob_selects_each_variants_own_factor(feed, selection, profile): + result = MONEY_NORMALIZE.run(_context(feed, selection=selection)) + assert result.receipt["profile"] == profile + expected = [ + _oracle_cents(value, Fraction(5, 4), oracle_factor(profile)) + for value in _VALUES["rental_income"] + ] + assert result.columns["person", "rental_income_cents"].tolist() == expected + + +def test_an_unknown_profile_refuses(feed): + with pytest.raises(ValueError, match="Unknown money normalization profile"): + MONEY_NORMALIZE.run(_context(feed, selection={DIMENSION: "bogus"})) + + +def test_a_missing_declared_profile_factor_refuses(feed): + declaration = graph_declaration() + del declaration["index_family_factors"]["capital_income"]["factors"]["low"] + with pytest.raises(ValueError, match="one factor for every declared profile"): + MONEY_NORMALIZE.run( + _context(feed, declaration=declaration, selection={DIMENSION: "low"}) + ) + + +@pytest.mark.parametrize( + ("metadata", "message"), + [ + ({"donor_time_period": 2023}, "differs from the declared calendar_year 2024"), + ({"donor_time_period": "2024"}, "differs from the declared"), + ({"donor_time_period": None}, "records no money period"), + ({}, "needs the frame's recorded donor_time_period"), + ], +) +def test_a_donor_period_mismatch_or_null_refuses(feed, metadata, message): + with pytest.raises(ValueError, match=message): + MONEY_NORMALIZE.run(_context(feed, metadata=metadata)) + + +def test_a_self_consistent_tampered_factor_refuses_on_feed_replay(feed): + _, _, identity, facts, context = feed + forged_facts = copy.deepcopy(list(facts)) + anchor = next( + row for row in forged_facts if row["aggregate_fact_key"] == "anchor-0" + ) + anchor["value"] = 120 + forged = copy.deepcopy(context) + entry = forged["factors"][1] + entry["declaration"]["expected_operational_factor"]["numerator"] = 1200000 + factor, receipt = resolve_reference_factor( + entry["declaration"], forged_facts, identity + ) + entry["operational_factor"], entry["receipt"] = factor, receipt + # The forged context is internally consistent ... + assert set(validate_factor_context(forged)) == { + factor_id(profile) for profile in ("low", "central", "high") + } + # ... but replay against the authenticated feed refuses it. + with pytest.raises(ValueError, match="expected factor mismatch|replay differs"): + MONEY_NORMALIZE.run(_context(feed, factor_context=forged)) + + +def test_without_a_feed_pin_the_factor_source_commit_must_be_authenticated(feed): + with pytest.raises(ValueError, match="cannot be authenticated without a feed pin"): + MONEY_NORMALIZE.run(_context(feed, pin=False)) + + +def test_a_profile_input_factor_is_never_applied_to_donor_money(feed): + _, _, identity, facts, _ = feed + context = money_factor_context(identity, facts, purpose="profile_input") + with pytest.raises(ValueError, match="applies only money_normalization_input"): + MONEY_NORMALIZE.run(_context(feed, factor_context=context)) + + +def test_a_feed_selected_rate_replays_through_the_kernel(feed): + _, _, identity, _, _ = feed + declaration = graph_declaration() + declaration["rate"] = rate_feed_form(identity, "1.25") + result = MONEY_NORMALIZE.run(_context(feed, declaration=declaration)) + assert result.receipt["rate"]["form"] == "feed_selector" + assert result.receipt["rate"]["operand"]["decimal_operand"] == "1.25" + assert result.columns["person", "interest_income_cents"].tolist()[0] == 412 + declaration["rate"]["expected_decimal"] = "1.26" + with pytest.raises(ValueError, match="replay differs"): + MONEY_NORMALIZE.run(_context(feed, declaration=declaration)) + + +def test_outputs_and_slices_must_equal_the_declared_columns(feed): + _, pin, _, _, context = feed + node = normalize_node( + "toy.normalize", "toy.central", graph_declaration(), context, pin + ) + short = Node( + node.id, + node.kernel, + population=node.population, + sources=node.sources, + inputs=node.inputs, + outputs=node.outputs[:-1], + params=node.params, + ) + with pytest.raises(ValueError, match="rewrite exactly the declared money columns"): + MONEY_NORMALIZE.run(_context(feed, node=short)) + wide = Node( + node.id, + node.kernel, + population=node.population, + sources=node.sources, + inputs=(Slice("person", (*GRAPH_NORMALIZED["person"], "age")),), + outputs=node.outputs, + params=node.params, + ) + with pytest.raises(ValueError, match="slices exactly the declared money columns"): + MONEY_NORMALIZE.run(_context(feed, node=wide)) + typed = Node( + node.id, + node.kernel, + population=node.population, + sources=node.sources, + inputs=node.inputs, + outputs=node.outputs, + params=node.params, + artifact_outputs=(ArtifactOutput("surface", TARGET_SURFACE_TYPE),), + ) + with pytest.raises(ValueError, match="no artifacts"): + MONEY_NORMALIZE.run(_context(feed, node=typed)) + + +def test_implementation_hash_binds_the_pure_module_and_factor_replay(monkeypatch): + seen = [] + + def recording(*objects, dependencies=()): + seen.extend(objects) + return "0" * 64 + + monkeypatch.setattr(money_kernels_module, "source_hash", recording) + MONEY_NORMALIZE.implementation_hash() + names = {getattr(item, "__name__", "") for item in seen} + assert { + "microcosm.build.money_normalization", + "microcosm.build.reference_factors", + "microcosm.build.ledger_artifact", + "microcosm.build.ledger_targets", + "microcosm.frame.concepts", + } <= names + monkeypatch.undo() + assert ( + MONEY_NORMALIZE.implementation_hash() == MONEY_NORMALIZE.implementation_hash() + ) + + +def test_the_kernel_is_registered_and_a_skeleton_kernel(): + registry = KernelRegistry() + register_transport_population_kernels(registry) + assert "money.normalize@1" in registry.refs() + assert "money.normalize@1" in SKELETON_KERNELS + assert "nz" not in MONEY_NORMALIZE.ref + + +def test_composition_binds_the_country_feed_pin_to_the_normalizer(): + spec = { + "country": "xx", + "resources": {"chronicle_feed": {"source_commit": "c" * 40, "x": 1}}, + } + params = compose_module._target_feed_params(spec, "money.normalize@1", {}) + assert json.loads(params["chronicle_feed_pin"])["source_commit"] == "c" * 40 + assert "chronicle_feed_pin" not in compose_module._target_feed_params( + spec, "transport.currency@1", {} + ) + + +# --------------------------------------------------------------------------- +# The opt-in producer preflight +# --------------------------------------------------------------------------- + + +def _row(node_id, kernel, population, inputs=(), outputs=(), **fields): + return { + "id": node_id, + "kernel": kernel, + "population": population, + "inputs": [{"entity": e, "columns": list(c)} for e, c in inputs], + "outputs": [ + {"entity": e, "column": c, "dtype": "float64", "rewrite": True} + for e, c in outputs + ], + **fields, + } + + +def _structural(node_id, base, kind="filter"): + return { + "id": node_id, + "kernel": "transport.boundary@1", + "structural": kind, + "base": base, + } + + +_CREATE = {"id": "c", "kernel": "transport.create@1", "structural": "create"} + + +def _spec(*rows, declaration=None): + return { + "country": "xx", + "resources": { + "money_normalization": graph_declaration() + if declaration is None + else declaration, + "transport_graph": {"nodes": [_CREATE, _structural("open", "c"), *rows]}, + }, + } + + +def _normalizer(node_id, population): + return _row( + node_id, + "money.normalize@1", + population, + inputs=[("person", GRAPH_NORMALIZED["person"])], + outputs=[("person", column) for column in GRAPH_NORMALIZED["person"]], + ) + + +def _reader(node_id, population, *columns, entity="person"): + return _row(node_id, "concepts.encode@1", population, inputs=[(entity, columns)]) + + +def test_a_normalized_column_read_after_its_normalizer_has_no_gap(): + spec = _spec( + _structural("central", "open"), + _normalizer("central.n", "central"), + _reader("central.read", "central", "interest_income", "rental_income"), + ) + assert compose_module._money_producer_gaps(spec) == [] + + +def test_a_consumed_pending_column_is_a_named_gap(): + spec = _spec(_reader("open.read", "open", "dividend_income")) + assert compose_module._money_producer_gaps(spec) == [ + "money column person.dividend_income awaits a donor that carries it " + "(read by ['open.read'])" + ] + + +def test_a_consumed_quantile_mapped_column_without_its_map_names_what_it_awaits(): + spec = _spec( + _reader("open.read", "open", "employment_income"), + _reader("open.rent", "open", "rent", entity="household"), + ) + assert compose_module._money_producer_gaps(spec) == [ + "money column household.rent awaits quantile map <- toy rent bands " + "(read by ['open.rent'])", + "money column person.employment_income awaits quantile map <- toy wage " + "bands (read by ['open.read'])", + ] + + +def test_raw_donor_money_read_without_its_normalizer_is_a_gap(): + gaps = compose_module._money_producer_gaps( + _spec(_reader("open.read", "open", "interest_income")) + ) + assert gaps == [ + "money column person.interest_income has no money.normalize@1 producer " + "(read by ['open.read'])" + ] + + +def test_a_second_normalization_in_one_lineage_is_a_gap(): + spec = _spec( + _normalizer("open.n", "open"), + _structural("central", "open"), + _normalizer("central.n", "central"), + _reader("central.read", "central", "interest_income"), + ) + (gap,) = compose_module._money_producer_gaps(spec) + assert gap.startswith( + "money column person.interest_income has 2 producers in one lineage " + "['central.n', 'open.n']" + ) + + +def test_variant_lineages_are_independent(): + # A V1-style FILTER on the shared version carries its base, never a sibling. + spec = _spec( + _structural("central", "open"), + _normalizer("central.n", "central"), + _reader("central.read", "central", "interest_income"), + _structural("v1", "open"), + _normalizer("v1.n", "v1"), + _reader("v1.read", "v1", "interest_income"), + _structural("v1.after", "v1"), + _reader("v1.after.read", "v1.after", "interest_income"), + ) + assert compose_module._money_producer_gaps(spec) == [] + + +def test_only_the_two_money_producers_may_produce_a_money_column(): + spec = _spec( + _row( + "open.currency", + "transport.currency@1", + "open", + inputs=[("person", ["interest_income"])], + outputs=[("person", "interest_income")], + ), + _reader("open.read", "open", "interest_income"), + ) + (gap,) = compose_module._money_producer_gaps(spec) + assert "produced by transport.currency@1 node 'open.currency'" in gap + + +def test_a_quantile_map_cannot_produce_a_normalized_column(): + spec = _spec( + _row( + "open.qmap", + "transport.quantile_map@1", + "open", + inputs=[("person", ["interest_income"])], + outputs=[("person", "interest_income")], + ), + _reader("open.read", "open", "interest_income"), + ) + (gap,) = compose_module._money_producer_gaps(spec) + assert "declared normalized but produced by transport.quantile_map@1" in gap + + +def test_a_quantile_mapped_drop_default_column_has_its_map_as_sole_producer(): + declaration = graph_declaration() + declaration["quantile_mapped"]["person"]["public_pension_income"] = { + "awaits": ["toy pension bands"], + "reason": "Quantile-mapped although its concept default is drop.", + } + spec = _spec( + _row( + "open.qmap", + "transport.quantile_map@1", + "open", + inputs=[("person", ["public_pension_income"])], + outputs=[("person", "public_pension_income")], + ), + _reader("open.read", "open", "public_pension_income"), + declaration=declaration, + ) + assert compose_module._money_producer_gaps(spec) == [] + + +def test_a_new_destination_money_output_is_derived_instead_of_donor_money(): + derived = _row( + "open.pension", + "simulate.rules_by_ref@1", + "open", + outputs=[("person", "public_pension_income")], + ) + derived["outputs"][0]["rewrite"] = False + spec = _spec( + derived, + _reader("open.read", "open", "public_pension_income"), + ) + assert compose_module._money_producer_gaps(spec) == [] + + +def test_an_invalid_declaration_is_a_named_preflight_gap(): + declaration = graph_declaration() + declaration["rate"]["text"] = 1.25 + (gap,) = compose_module._money_producer_gaps(_spec(declaration=declaration)) + assert gap.startswith("money_normalization.json: ") + + +def test_without_the_resource_preflight_reads_nothing(): + spec = {"country": "xx", "resources": {"transport_graph": {"nodes": "malformed"}}} + assert compose_module._money_producer_gaps(spec) == [] + compose_module._check_single_money_normalization(spec, None, None) + + +def test_activation_names_money_gaps_only_when_the_toy_opts_in(tmp_path, monkeypatch): + fixture = make_composed_fixture(tmp_path / "toy") + validate_transport_activation(fixture.spec) + graph = fixture.graph() + kernels = {node.kernel for node in graph.nodes} + assert "transport.currency@1" in kernels + assert "money.normalize@1" not in kernels + plain = graph_to_json(graph) + extended = graph_to_json(fixture.graph(extended=True)) + # With the hooks removed, composition and activation are byte-identical. + with monkeypatch.context() as patch: + patch.setattr(compose_module, "_money_producer_gaps", lambda spec: []) + patch.setattr( + compose_module, "_check_single_money_normalization", lambda *args: None + ) + assert graph_to_json(fixture.graph()) == plain + assert graph_to_json(fixture.graph(extended=True)) == extended + validate_transport_activation(fixture.spec) + opted = copy.deepcopy(fixture.spec) + opted["resources"]["money_normalization"] = graph_declaration() + with pytest.raises(ValueError) as error: + validate_transport_activation(opted) + assert ( + "money column person.interest_income is produced by transport.currency@1 " + "node 'nz.transport.currency'" + ) in str(error.value) + + +# --------------------------------------------------------------------------- +# The toy lineage run: CREATE -> open -> {central, v1} +# --------------------------------------------------------------------------- + + +@pytest.fixture(scope="module") +def toy_run(tmp_path_factory, feed): + root = tmp_path_factory.mktemp("money-graph") + population = write_population_sources( + root / "population", time_period=2024, edit_tables=add_money_columns + ) + path, pin, _, _, context = feed + declaration = graph_declaration() + register_transport_codecs(SOURCE_CODECS) + graph = money_graph(population, declaration, context, pin) + manifest = run_graph( + compile_graph(graph), + sources=money_sources(population, path), + store=ContentStore(root / "store"), + kernels=money_registry(), + ) + return population, declaration, context, pin, graph, manifest + + +def test_each_branch_normalizes_once_with_its_own_factor(toy_run): + population, declaration, _, _, graph, manifest = toy_run + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + compose_module._check_single_money_normalization(spec, graph, compile_graph(graph)) + created = manifest.population("nz.create").table("person") + raw = {column: created[column].tolist() for column in GRAPH_NORMALIZED["person"]} + assert raw["rental_income"] == RAW_RENTAL + assert raw["farm_self_employment_income"] == RAW_FARM + for branch, profile in ( + ("toy.central", "central"), + ("toy.v1", "low"), + ("toy.v2", "high"), + ): + receipt = manifest.nodes[f"{branch}.consume"].receipt + factor = oracle_factor(profile) + for column in GRAPH_NORMALIZED["person"]: + cents = [ + _oracle_cents(value, Fraction(5, 4), factor) for value in raw[column] + ] + assert list(receipt[f"{column}_cents"]) == cents, (branch, column) + assert list(receipt[column]) == [c / 100 for c in cents] + normalize = manifest.nodes[f"{branch}.normalize"].receipt + assert normalize["profile"] == profile + assert normalize["recorded_donor_time_period"] == 2024 + # The shared version is untouched: normalization lives on each branch. + assert ( + manifest.population("toy.open").table("person")["rental_income"].tolist() + == RAW_RENTAL + ) + assert "rental_income_cents" not in manifest.population("toy.open").table("person") + + +def test_create_records_the_donor_money_period_for_the_branches(toy_run): + *_, manifest = toy_run + assert manifest.nodes["nz.create"].receipt["donor_time_period"] == 2024 + for version in ("nz.create", "toy.open", "toy.central", "toy.v1", "toy.v2"): + assert manifest.population(version).metadata["donor_time_period"] == 2024 + + +def test_a_second_normalization_in_a_shared_version_refuses_at_composition(toy_run): + population, declaration, context, pin, _, _ = toy_run + doubled = money_graph(population, declaration, context, pin, normalize_open=True) + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + with pytest.raises(ValueError, match="normalized more than once in the lineage"): + compose_module._check_single_money_normalization( + spec, doubled, compile_graph(doubled) + ) + + +class _ToyBaselineCells(ToyMoneyConsumer): + """Read normalized branch money and exchange baseline reporting evidence.""" + + ref = "toy.baseline_cells@1" + + def run(self, context): + result = super().run(context) + if context.artifacts: + baseline = json.loads(context.artifacts["baseline_cells"].payload) + return KernelResult( + receipt={**result.receipt, "baseline_cells": baseline["person_id"]} + ) + return replace( + result, + artifacts={"baseline_cells": canonical_json(dict(result.receipt))}, + ) + + +def test_s0_v1_normalize_once_despite_a_cross_population_baseline_artifact( + tmp_path, toy_run, feed +): + population, declaration, _, _, graph, _ = toy_run + baseline = replace( + graph.node("toy.central.consume"), + kernel=_ToyBaselineCells.ref, + artifact_outputs=(ArtifactOutput("baseline_cells", INCOME_CELLS_TYPE),), + ) + variant = replace( + graph.node("toy.v1.consume"), + kernel=_ToyBaselineCells.ref, + artifact_inputs=( + ArtifactInput( + "baseline_cells", baseline.id, "baseline_cells", INCOME_CELLS_TYPE + ), + ), + ) + graph = replace( + graph, + nodes=tuple( + baseline + if node.id == baseline.id + else variant + if node.id == variant.id + else node + for node in graph.nodes + ), + ) + compiled = compile_graph(graph) + # The artifact makes both normalizers DAG ancestors of V1's consumer. + # It does not make S0 a structural base of V1 or transfer its ownership. + ancestors = compose_module._ancestor_closure(compiled.predecessors, {variant.id}) + assert {"toy.central.normalize", "toy.v1.normalize"} <= ancestors + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + compose_module._check_single_money_normalization(spec, graph, compiled) + registry = money_registry() + registry.register(_ToyBaselineCells()) + manifest = run_graph( + compiled, + sources=money_sources(population, feed[0]), + store=ContentStore(tmp_path / "store"), + kernels=registry, + ) + assert ( + manifest.nodes[variant.id].receipt["baseline_cells"] + == manifest.nodes[baseline.id].receipt["person_id"] + ) + assert manifest.nodes["toy.v1.normalize"].receipt["profile"] == "low" + assert manifest.nodes["toy.central.normalize"].receipt["profile"] == "central" + + +def test_currency_then_normalization_refuses_on_the_same_money_lineage(toy_run): + population, declaration, context, pin, _, _ = toy_run + graph = money_graph(population, declaration, context, pin) + currency = Node( + "toy.open.currency", + "transport.currency@1", + population="toy.open", + inputs=(Slice("person", ("interest_income",)),), + outputs=(Owned("person", "interest_income", "float64", rewrite=True),), + ) + graph = replace(graph, nodes=(*graph.nodes, currency)) + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + with pytest.raises(ValueError, match="normalized more than once.*interest_income"): + compose_module._check_single_money_normalization( + spec, graph, compile_graph(graph) + ) + + +def test_derived_money_does_not_add_a_donor_conversion_to_a_compiled_lineage(toy_run): + _, declaration, _, _, graph, _ = toy_run + derived = Node( + "toy.central.pension", + "simulate.rules_by_ref@1", + population="toy.central", + inputs=(Slice("person", ("interest_income",)),), + outputs=(Owned("person", "public_pension_income", "float64"),), + ) + reader = Node( + "toy.central.pension.read", + "toy.money_consumer@1", + population="toy.central", + inputs=(Slice("person", ("public_pension_income",)),), + ) + graph = replace(graph, nodes=(*graph.nodes, derived, reader)) + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + compose_module._check_single_money_normalization(spec, graph, compile_graph(graph)) + + +def test_each_compiled_producer_must_match_its_declared_route(toy_run): + population, declaration, context, pin, graph, _ = toy_run + qmap = Node( + "toy.v1.qmap", + "transport.quantile_map@1", + population="toy.v1", + inputs=(Slice("person", ("employment_income",)),), + outputs=(Owned("person", "employment_income", "float64", rewrite=True),), + ) + spec = {"country": "xx", "resources": {"money_normalization": declaration}} + graph_with_map = type(graph)(graph.country, graph.sources, (*graph.nodes, qmap)) + compose_module._check_single_money_normalization( + spec, graph_with_map, compile_graph(graph_with_map) + ) + wrong = copy.deepcopy(declaration) + wrong["quantile_mapped"]["person"].pop("employment_income") + wrong["pending"]["person"]["employment_income"] = { + "awaits": ["x"], + "reason": "x", + } + with pytest.raises(ValueError, match="declared pending but"): + compose_module._check_single_money_normalization( + {"country": "xx", "resources": {"money_normalization": wrong}}, + graph_with_map, + compile_graph(graph_with_map), + ) + + +def test_a_toy_graph_without_a_donor_period_refuses_at_the_normalizer(tmp_path, feed): + population = write_population_sources( + tmp_path / "population", edit_tables=add_money_columns + ) + path, pin, _, _, context = feed + register_transport_codecs(SOURCE_CODECS) + graph = money_graph(population, graph_declaration(), context, pin) + with pytest.raises(Exception, match="records no money period"): + run_graph( + compile_graph(graph), + sources=money_sources(population, path), + store=ContentStore(tmp_path / "store"), + kernels=money_registry(), + ) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_transport_population_kernels.py b/packages/microcosm-build/tests/engine_free/shared/test_transport_population_kernels.py index a6600315f2..90165c090c 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_transport_population_kernels.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_transport_population_kernels.py @@ -136,11 +136,12 @@ def test_kernel_registration_is_explicit_idempotent_and_country_neutral(): ) -def test_aggregate_population_registration_installs_exactly_ten_kernels(): +def test_aggregate_population_registration_installs_exactly_eleven_kernels(): registry = KernelRegistry() assert register_transport_population_kernels(registry) is registry assert register_transport_population_kernels(registry) is registry assert set(registry.refs()) == { + "money.normalize@1", "transport.create@1", "transport.boundary@1", "transport.currency@1", @@ -857,3 +858,23 @@ def test_create_artifacts_pin_python_string_storage_with_pyarrow_installed(tmp_p frame = run.manifest.population(node) for entity, column in text: assert frame.table(entity)[column].dtype == pinned, (node, column) + + +@pytest.mark.parametrize("time_period", [2024, None]) +def test_create_records_the_donor_money_period_in_metadata_and_receipt( + tmp_path, time_period +): + sources = write_population_sources(tmp_path / "sources", time_period=time_period) + node = create_node(sources) + context = KernelContext( + node=node, + tables={}, + weights={}, + strata=pd.Series([], dtype=object), + params=node.params, + rng=np.random.default_rng(0), + sources={"donor": sources.donor, "facts": sources.facts}, + ) + result = TRANSPORT_CREATE.run(context) + assert result.frame.metadata["donor_time_period"] == time_period + assert result.receipt["donor_time_period"] == time_period diff --git a/packages/microcosm-build/tests/engine_free/shared/test_transport_target_kernels.py b/packages/microcosm-build/tests/engine_free/shared/test_transport_target_kernels.py index 9550f1ccaf..10c2d3c7df 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_transport_target_kernels.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_transport_target_kernels.py @@ -20,7 +20,9 @@ from __future__ import annotations import json +from copy import deepcopy from dataclasses import replace +from pathlib import Path import numpy as np import pandas as pd @@ -28,6 +30,10 @@ from hypothesis import HealthCheck, given, settings from hypothesis import strategies as st +import microcosm.build.reference_factors as reference_factors_module +import microcosm.build.reference_intervals as reference_intervals_module +import microcosm.build.reference_profiles as reference_profiles_module +import microcosm.build.transport.target_kernels as target_kernels_module from microcosm.build.ledger_artifact import ChronicleFeedPinMismatchError from microcosm.build.reference_profiles import ( reference_document_sha256, @@ -62,6 +68,15 @@ ) from microcosm.graph.canonical import canonical_json from test_support.microcosm_build.chronicle_feed import write_pinned_artifact +from test_support.microcosm_build.reference_factor_transport import ( + factor_transport_fixture, +) +from test_support.microcosm_build.reference_intervals import ( + INCOME_COLUMN, + UNIVERSE_COLUMN, + interval_bindings, + interval_table_fixture, +) from test_support.microcosm_build.transport_graph import ( CALIBRATION_TARGETS, COUNTRY, @@ -167,6 +182,300 @@ def test_kernels_declare_honest_country_neutral_contracts() -> None: ) +@pytest.mark.parametrize( + "kernel", [TARGETS_COMPILE, TAKEUP_COMPARE], ids=lambda k: k.ref +) +def test_factor_verifier_source_changes_compilation_identity( + tmp_path, monkeypatch, kernel +): + before = kernel.implementation_hash() + source = Path(reference_factors_module.__file__) + changed = tmp_path / source.name + changed.write_bytes(source.read_bytes() + b"\n# verifier implementation mutation\n") + monkeypatch.setattr(reference_factors_module, "__file__", str(changed)) + assert kernel.implementation_hash() != before + + +def test_compiler_replays_factor_and_applies_one_common_money_weight(tmp_path): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + payload, registry = compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + assert registry.specs[0].value == 3 + assert registry.specs[1].value == pytest.approx(110) + assert decode_target_surface(payload).reference_resolution == receipt + + +def test_compile_replay_refuses_self_consistent_forged_factor(tmp_path): + path, pin, document, receipt, _ = factor_transport_fixture( + tmp_path, forged=True, activate=True + ) + # Composition has accepted all declaration/receipt hashes; only feed replay + # can distinguish this forged level from the authenticated source operand. + with pytest.raises(ValueError, match="expected factor mismatch"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +def test_compile_replays_correct_factor_without_pin_when_manifest_binds_commit( + tmp_path, +): + path, _, document, receipt, _ = factor_transport_fixture( + tmp_path, activate=True, manifest_commit=True + ) + _, registry = compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + reference_resolution=receipt, + ) + assert registry.specs[1].value == pytest.approx(110) + + +@pytest.mark.parametrize("manifest_commit", [False, True]) +def test_compile_without_pin_refuses_unauthenticated_or_different_feed( + tmp_path, manifest_commit +): + path, _, document, receipt, _ = factor_transport_fixture( + tmp_path, activate=True, manifest_commit=manifest_commit + ) + if manifest_commit: + # The declaration still has a valid receipt for its original artifact. + manifest_path = path / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["source_commit"] = "d" * 40 + manifest_path.write_text(json.dumps(manifest)) + with pytest.raises(ValueError, match="cannot be authenticated|manifest_sha256"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + reference_resolution=receipt, + ) + + +def test_compile_factor_ignores_pin_prose(tmp_path): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + arguments = dict( + country=COUNTRY, + references_sha256=reference_document_sha256(document), + reference_resolution=receipt, + ) + before, _ = compile_target_surface(path, document, feed_pin=pin, **arguments) + after, _ = compile_target_surface( + path, document, feed_pin={**pin, "build": "Reviewed prose edit"}, **arguments + ) + assert before == after + + +@pytest.mark.parametrize("field", ["source_commit", "facts_sha256", "manifest_sha256"]) +def test_compile_factor_refuses_changed_pin_identity(tmp_path, field): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + pin[field] = "d" * len(pin[field]) + with pytest.raises(ValueError, match="feed identity mismatch|pin mismatch"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +def test_compile_replay_refuses_tampered_evidence_with_recomputed_hashes(tmp_path): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + factor_receipt = receipt["selected_factors"]["toy_factor"]["receipt"] + factor_receipt["source_schedule"][0]["decimal_operand"] = "99" + factor_receipt.pop("receipt_sha256") + factor_receipt["receipt_sha256"] = reference_document_sha256(factor_receipt) + receipt["selected_factor_identities"]["toy_factor"]["receipt_sha256"] = ( + factor_receipt["receipt_sha256"] + ) + receipt["reference_selection_sha256"] = reference_document_sha256( + reference_profiles_module._selection_identity(receipt) + ) + for row in document["target_references"]: + row["metadata"]["reference_selection_sha256"] = receipt[ + "reference_selection_sha256" + ] + receipt["effective_references_sha256"] = reference_document_sha256(document) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="Factor replay differs"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +@pytest.mark.parametrize("target", ["count", "money"]) +def test_compile_replay_refuses_rehashed_factor_application_tampering(tmp_path, target): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + row = next( + row for row in document["target_references"] if row["name"] == f"toy-{target}" + ) + row["value_operation"] = "linear_combination" + row["value_operands"] = [ + { + "aggregate_fact_key": row["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": 2, + } + ] + if target == "money": + receipt["transformations"][row["name"]]["operation"] = "identity" + receipt["effective_references_sha256"] = reference_document_sha256(document) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="roster role|count value semantics"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +@pytest.mark.parametrize("target", ["count", "money"]) +def test_compile_refuses_rehashed_companion_role_with_unchanged_selection( + tmp_path, target +): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + name = f"toy-{target}" + companion = receipt["factor_companions"][name] + role = "count" if target == "money" else "money" + companion["role"] = role + companion["source"]["metadata"]["reference_value_kind"] = role + row = next(row for row in document["target_references"] if row["name"] == name) + action = receipt["transformations"][name] + # Make the forged role internally consistent. Its original selection hash + # must still refuse even when the action and effective row agree with it. + operation = "linear_combination" if role == "money" else "identity" + rational = action["operational_factor"] + operands = ( + [ + { + "aggregate_fact_key": row["ledger_selector"]["aggregate_fact_key"], + "expected_member_count": 1, + "weight": rational["numerator"] / rational["denominator"], + } + ] + if role == "money" + else [] + ) + row["metadata"]["reference_value_kind"] = role + row["value_operation"] = operation + row["value_operands"] = operands + action["operation"] = operation + action["operand_selectors"] = operands + receipt["factor_companions_sha256"] = reference_document_sha256( + receipt["factor_companions"] + ) + receipt["effective_references_sha256"] = reference_document_sha256(document) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises(ValueError, match="Reference selection identity mismatch"): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +def test_compile_refuses_outside_roster_row_with_residual_action(tmp_path): + path, pin, document, receipt, _ = factor_transport_fixture(tmp_path, activate=True) + extra = deepcopy(document["target_references"][0]) + previous = extra["name"] + extra["name"] = "unlisted-count" + document["target_references"].append(extra) + action = deepcopy(receipt["transformations"][previous]) + action.pop("factor_id") + action["operation"] = "allocated_residual_by_ratio" + receipt["transformations"][extra["name"]] = action + receipt["transformed_names"] = sorted(receipt["transformations"]) + receipt["effective_references_sha256"] = reference_document_sha256(document) + receipt.pop("receipt_sha256") + receipt["receipt_sha256"] = reference_document_sha256(receipt) + with pytest.raises( + ValueError, match="selection identity|outside its companion roster" + ): + compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + feed_pin=pin, + reference_resolution=receipt, + ) + + +@pytest.mark.parametrize( + "resolved", [False, True], ids=["unresolved", "legacy-resolution"] +) +def test_no_factor_compilation_is_identical_with_or_without_lexical_loading( + tmp_path, monkeypatch, resolved +): + facts = toy_facts(CALIBRATION_TARGETS) + path = write_facts(tmp_path / "facts.jsonl", facts) + document = references_for(CALIBRATION_TARGETS) + receipt = None + if resolved: + document, _, receipt = resolve_reference_profiles( + document, {"schema_version": 1, "dimensions": {}}, {} + ) + original_loader = target_kernels_module.load_ledger_consumer_artifact + requests = [] + outputs = [] + registries = [] + for force_lexical in (False, True): + + def load_with_selected_representation(*args, lexical=force_lexical, **kwargs): + requests.append(kwargs.get("lexical_floats", False)) + kwargs["lexical_floats"] = lexical + return original_loader(*args, **kwargs) + + monkeypatch.setattr( + target_kernels_module, + "load_ledger_consumer_artifact", + load_with_selected_representation, + ) + payload, registry = compile_target_surface( + path, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + reference_resolution=receipt, + ) + outputs.append(payload) + registries.append([spec.value for spec in registry.specs]) + assert requests == [False, False] + assert outputs[0] == outputs[1] + assert registries[0] == registries[1] + + def test_compile_emits_a_traced_surface_bound_to_facts_not_paths(tmp_path) -> None: payload = _surface(tmp_path) surface = decode_target_surface(payload) @@ -582,10 +891,15 @@ def test_surface_decode_refuses_a_tampered_trace_or_registry(tmp_path) -> None: ) +# Base/current seeded draws have identical inputs and measured generation cost. +# Host contention can exceed Hypothesis' wall-clock generation health check. @settings( max_examples=40, deadline=None, - suppress_health_check=(HealthCheck.function_scoped_fixture,), + suppress_health_check=( + HealthCheck.function_scoped_fixture, + HealthCheck.too_slow, + ), ) @given( chosen=st.lists( @@ -802,3 +1116,257 @@ def test_property_comparison_reports_the_fact_and_the_weighted_model( assert row["reference"] == row["name"] == HOLDOUT_TARGETS[0][0] assert document["weight_kind"] == "calibrated" assert result.receipt["n_comparisons"] == 1 + + +# --------------------------------------------------------------------------- +# Interval preparation and pre-fit boundary diagnostics +# --------------------------------------------------------------------------- + + +def _interval_document(): + return { + "schema_version": 1, + "tables": [interval_table_fixture(source_period=2002)], + } + + +def _interval_preparation_node(document, receipt, intervals, bindings=None): + table = intervals["tables"][0] + bindings = interval_bindings(table) if bindings is None else bindings + params = { + "bindings": canonical_text(bindings), + "references": canonical_text(document), + "references_sha256": reference_document_sha256(document), + "intervals": canonical_text(intervals), + } + if receipt is not None: + params["reference_resolution"] = canonical_text(receipt) + return Node( + "prepare", + "calibration.prepare@1", + population="open", + params=params, + outputs=tuple( + Owned("person", band["indicator"], dtype="bool") for band in table["bands"] + ), + artifact_outputs=(ArtifactOutput("preparation", CALIBRATION_PREPARATION_TYPE),), + ) + + +def _with_interval_inputs(tables, persons): + pattern = [0, -400, 99_999, 100_000, 100_001, 1_000_000, 1_000_001, 7] + statuses = ["included", "included", "included", "excluded"] + tables["person"][INCOME_COLUMN] = [pattern[i % 8] for i in range(persons)] + tables["person"][UNIVERSE_COLUMN] = [statuses[i % 4] for i in range(persons)] + + +def _interval_case(tmp_path): + intervals = _interval_document() + document, effective, receipt = _profile_document(intervals=intervals) + assert effective == intervals + facts = write_facts(tmp_path / "facts.jsonl", toy_facts(CALIBRATION_TARGETS)) + payload, _ = compile_target_surface( + facts, + document, + country=COUNTRY, + references_sha256=reference_document_sha256(document), + reference_resolution=receipt, + ) + frame = toy_frame() + persons = len(frame.table("person")) + context = _context( + _interval_preparation_node(document, receipt, intervals), frame=frame + ) + _with_interval_inputs(context.tables, persons) + prepared = CALIBRATION_PREPARE.run(context) + return intervals, document, receipt, payload, frame, prepared + + +def _interval_problem_context(frame, payload, preparation, prepared_columns): + context = _context( + _problem_node(), + frame=frame, + artifacts={ + "surface": _artifact(payload, TARGET_SURFACE_TYPE), + "preparation": _artifact(preparation, CALIBRATION_PREPARATION_TYPE), + }, + ) + _with_interval_inputs(context.tables, len(frame.table("person"))) + for (entity, name), values in prepared_columns.items(): + context.tables[entity][name] = values.to_numpy(copy=True) + return context + + +def test_interval_preparation_carries_digest_inputs_and_payload(tmp_path): + intervals, _, receipt, _, frame, prepared = _interval_case(tmp_path) + evidence = json.loads(prepared.artifacts["preparation"]) + assert evidence["effective_intervals_sha256"] == reference_document_sha256( + intervals + ) + assert evidence["effective_intervals"] == intervals + (row,) = evidence["interval_inputs"] + persons = frame.table("person") + assert row["table"] == "toy_income" and row["entity"] == "person" + assert row["rows"] == len(persons) + assert row["income_values_sha256"] == ( + reference_intervals_module.interval_input_fingerprint( + [ + [0, -400, 99_999, 100_000, 100_001, 1_000_000, 1_000_001, 7][i % 8] + for i in range(len(persons)) + ] + ) + ) + nil = prepared.columns["person", "toy_income_band_nil"] + b0 = prepared.columns["person", "toy_income_band_b0"] + statuses = [ + ["included", "included", "included", "excluded"][i % 4] + for i in range(len(persons)) + ] + incomes = [ + [0, -400, 99_999, 100_000, 100_001, 1_000_000, 1_000_001, 7][i % 8] + for i in range(len(persons)) + ] + assert nil.tolist() == [ + status == "included" and income <= 0 + for status, income in zip(statuses, incomes, strict=True) + ] + assert b0.tolist() == [ + status == "included" and 0 < income <= 100_000 + for status, income in zip(statuses, incomes, strict=True) + ] + + +def test_problem_binds_boundary_diagnostics_without_changing_targets(tmp_path): + _, document, receipt, payload, frame, prepared = _interval_case(tmp_path) + result = TARGETS_PROBLEM.run( + _interval_problem_context( + frame, payload, prepared.artifacts["preparation"], prepared.columns + ) + ) + decoded = decode_problem(result.artifacts["problem"]) + report = decoded.bindings["interval_diagnostics"] + assert decoded.bindings["interval_diagnostics_sha256"] == reference_document_sha256( + report + ) + assert result.receipt["interval_diagnostics"] == report + assert report["effective_intervals_sha256"] == receipt["effective_intervals_sha256"] + (table,) = report["tables"] + assert table["table"] == "toy_income" + assert table["weight_kind"] == "design" + assert table["publisher_reclassification"] == "unavailable" + evidence = json.loads(prepared.artifacts["preparation"]) + assert ( + table["digests"]["record_order_sha256"] + == (evidence["interval_inputs"][0]["entity_ids_sha256"]) + ) + # The same surface prepared without interval outputs has identical targets. + plain = CALIBRATION_PREPARE.run( + _context( + _preparation_node(document, receipt, _interval_document()), frame=frame + ) + ) + context = _context( + _problem_node(), + frame=frame, + artifacts={ + "surface": _artifact(payload, TARGET_SURFACE_TYPE), + "preparation": _artifact( + plain.artifacts["preparation"], CALIBRATION_PREPARATION_TYPE + ), + }, + ) + context.tables["household"]["prepared_marker"] = 1 + baseline = TARGETS_PROBLEM.run(context) + without = decode_problem(baseline.artifacts["problem"]) + assert "interval_diagnostics" not in without.bindings + np.testing.assert_array_equal( + decoded.problem.target_vector, without.problem.target_vector + ) + np.testing.assert_array_equal( + decoded.problem.target_vector, + [spec.value for spec in decode_target_surface(payload).registry.specs], + ) + + +@pytest.mark.parametrize( + "defect", + [ + "income", + "universe", + "payload", + "dropped_inputs", + "dropped_payload", + "missing_income_column", + ], +) +def test_problem_refuses_stale_interval_preparation(tmp_path, defect): + _, _, _, payload, frame, prepared = _interval_case(tmp_path) + evidence = json.loads(prepared.artifacts["preparation"]) + if defect == "payload": + evidence["effective_intervals"]["tables"][0]["bands"][1]["upper_cent"] = 99_000 + elif defect == "dropped_inputs": + evidence.pop("interval_inputs") + elif defect == "dropped_payload": + evidence.pop("effective_intervals") + context = _interval_problem_context( + frame, payload, canonical_json(evidence), prepared.columns + ) + if defect == "income": + context.tables["person"].loc[0, INCOME_COLUMN] = 100_001 + elif defect == "universe": + context.tables["person"].loc[0, UNIVERSE_COLUMN] = "excluded" + elif defect == "missing_income_column": + context.tables["person"].pop(INCOME_COLUMN) + with pytest.raises(ValueError, match="preparation|missing"): + TARGETS_PROBLEM.run(context) + + +def test_missing_diagnostic_entity_refuses_as_missing_evidence(): + table = reference_intervals_module.interval_table( + interval_table_fixture(entity="tax_unit", source_period=2002) + ) + with pytest.raises(ValueError, match="missing evidence"): + target_kernels_module._interval_diagnostics( + toy_frame(), [table], ENTITIES, "0" * 64 + ) + + +@pytest.mark.parametrize( + "defect", ["wrong_document", "no_receipt", "no_document", "unresolved"] +) +def test_preparation_refuses_wrong_unbound_or_unresolved_intervals(tmp_path, defect): + intervals = _interval_document() + document, _, receipt = _profile_document(intervals=intervals) + supplied = deepcopy(intervals) + if defect == "wrong_document": + supplied["tables"][0]["bands"][1]["upper_cent"] = 99_000 + node = _interval_preparation_node( + document, None if defect == "no_receipt" else receipt, supplied + ) + if defect == "no_document": + document, _, receipt = _profile_document() + node = _interval_preparation_node(document, receipt, intervals) + node = replace( + node, params={k: v for k, v in node.params.items() if k != "intervals"} + ) + frame = toy_frame() + context = _context(node, frame=frame) + _with_interval_inputs(context.tables, len(frame.table("person"))) + if defect == "unresolved": + context.tables["person"].loc[1, UNIVERSE_COLUMN] = "unresolved" + with pytest.raises(ValueError): + CALIBRATION_PREPARE.run(context) + + +@pytest.mark.parametrize( + "kernel", [CALIBRATION_PREPARE, TARGETS_PROBLEM], ids=lambda k: k.ref +) +def test_interval_module_source_changes_preparation_and_problem_identity( + tmp_path, monkeypatch, kernel +): + before = kernel.implementation_hash() + source = Path(reference_intervals_module.__file__) + changed = tmp_path / source.name + changed.write_bytes(source.read_bytes() + b"\n# interval helper mutation\n") + monkeypatch.setattr(reference_intervals_module, "__file__", str(changed)) + assert kernel.implementation_hash() != before diff --git a/packages/microcosm-build/tests/golden/nz_country_spec.json b/packages/microcosm-build/tests/golden/nz_country_spec.json index fb0a05908f..66feb8a5f4 100644 --- a/packages/microcosm-build/tests/golden/nz_country_spec.json +++ b/packages/microcosm-build/tests/golden/nz_country_spec.json @@ -1,6 +1,6 @@ { "country": "nz", - "fingerprint": "39da98e8f11e7503245582f0b1f3422f23657ddcc2237d10b99dc59d275fad30", + "fingerprint": "3988856778646df45db3b99a1518b53178439f1625dbe8eacd4676c03d2209f5", "gate_ids": [ "calibration_per_family_fit", "demography_per_family_fit", @@ -46,15 +46,18 @@ "benefit_unit_rule.json": "a9e8cbfd38a64d2f8db0002fe8c31bb5a18fb34dbe59fa943336487ce95e2ca8", "calibration_bindings.json": "2293672e396407a24caaa5788638e7a1e7fc962575bc2551f500fbc309c3fe32", "chronicle_feed.json": "60a80dd0042838b0412baf9dd0985c961b4253a6876d16bcdbb8a2c6c03de0c7", - "country_package.json": "0c367ae03f22c39080693472ecad948df3c1629564130ec13765047346e5d71d", - "currency_bridge.json": "aa6a4b286a6a1a015088f32ac013148553422c3d45eeef7d7a15af08388857ca", + "country_package.json": "5ccb980ee050f0de3221f01ada8303fc722fb9e0296c2c5cb312bb5aec5e35c9", + "currency_bridge.json": "c72732cf36fa60938a6950e4543b6adfbc98b823572c2a6acc517cbe34d20bad", "export_contract.json": "2c7414fefe395c4f5822643dcd3bb3544ebb2040259aa007b828a99bd01ae210", "gates.json": "fa08b508ce1c93ef7627ca06f3a9e808f8fa6ea6f23ce24fa54c58241bbb9f7f", "geography_spine.json": "5f2b1bdd9f8768249912ffd0fbcb2f7d4ed493b51c74306f793ec12441fd383f", "holdout_references.json": "52b5a2360502156da1392a68350e4e7f61b5fd420fa74428819b65886a9f8655", + "money_normalization.json": "799540e5ea5004a39210c60df199b8d15e6b185b3aa68f0daabf44435779aeb6", "precal_references.json": "420361e424405df2312bd21808d6b6f4c3428b6fe90c44c37ab156ce197aa25d", - "reference_inventory.json": "a1356d3c7f91f4025a31a1edc68e2fe78017ec7a5eeb497d91031d4166e3765e", - "reference_profiles.json": "b0f23587f707c4af71eedfc450eae9175886f92e77d732a0e24758df83ec505d", + "reference_aging.json": "e7619ec7a02ae5f460a46d160e89c9c9836c8b1d03140aed2348761e590504f0", + "reference_intervals.json": "fcc88c0efd5f416aa15a7d5afbeee1d103dd78a136e13b1efc3973d203188412", + "reference_inventory.json": "d16ac4e95f5427c7bb1382ff6ae90e93d4b48344d810755fcc1dc6514430244a", + "reference_profiles.json": "cbdcb3bc9fe9c64f0f9280b40996545a8b20ec3a3d173fbdc1080c683715e6d5", "release_contract.json": "6cd5e3a4a2bf30e4025e04e964c393b24040937d13db7e6a07b3966a55f3b114", "reporting_classifiers.json": "2700a8dab0bcc186a0ce354a09c7cab127e8b07589b15fb7d63c7f1581041ad8", "scenarios.json": "3f1b5e40d283d7a1baf1a6990f2ca08a0ca39151a8cff028a49455f95e82e9c7", @@ -65,7 +68,7 @@ "spec/sources.yaml": "afdaa4bef98cf520a1c72b6013d1e453162c761cd4b3c1548620184f6ba41297", "spec/spine.yaml": "b3e6a2832ff7671777cae113b7fc45f5e3492bb519526f229118d0851083533e", "spec/vintages.yaml": "1ecc1a3f952bd25b834c394314e8c933ddb5b095fd64ba492dddb03e3cdd803e", - "target_references.json": "39c40fbd2277d68304b141dedd8e01800fd93f58d1950ad7cce8e103e4daeeca" + "target_references.json": "72b2d3b667c782b81b33cc850cd7f8b4014fc4d9fd50e74cb98c29dae1d46a46" }, "resources": [ "spec/bundle.yaml", @@ -87,7 +90,10 @@ "gates.json", "geography_spine.json", "holdout_references.json", + "money_normalization.json", "precal_references.json", + "reference_aging.json", + "reference_intervals.json", "reference_inventory.json", "reference_profiles.json", "release_contract.json", diff --git a/packages/microcosm-frame/src/microcosm/frame/transport.py b/packages/microcosm-frame/src/microcosm/frame/transport.py index 8353dfa07f..e55fa54746 100644 --- a/packages/microcosm-frame/src/microcosm/frame/transport.py +++ b/packages/microcosm-frame/src/microcosm/frame/transport.py @@ -472,6 +472,8 @@ class DonorBank: The household table carries ``source_household_id`` only when explicit source year and raw household lineage are available. Its typed JSON key merges clones across support channels and distinguishes source vintages. + ``time_period`` is the donor file's recorded model ``_time_period`` year, + or ``None`` when absent. It does not authenticate the amounts' money basis. """ tables: Mapping[str, pd.DataFrame] @@ -482,6 +484,7 @@ class DonorBank: donor_country: str = "us" currency: str = "USD" dropped_parent_cycle_edges: int = 0 + time_period: int | None = None #: Line numbers and pointers are validated as float64 (pointer columns can hold @@ -785,6 +788,19 @@ def _drop_two_person_parent_cycles(person: pd.DataFrame) -> int: return int(drop.sum()) +def _donor_time_period(store: pd.HDFStore) -> int | None: + """The exporter's one-row ``_time_period`` series, or ``None`` when absent.""" + if "/_time_period" not in store.keys(): + return None + values = store["_time_period"] + if not isinstance(values, pd.Series) or len(values) != 1: + raise ValueError("Donor _time_period must hold exactly one value.") + value = values.iloc[0] + if isinstance(value, (bool, np.bool_)) or not isinstance(value, (int, np.integer)): + raise ValueError("Donor _time_period must be an integer year.") + return int(value) + + def read_populace_us_donor(path: str | Path, *, sha256: str, size: int) -> DonorBank: """Authenticate a local donor H5, then decode its person/household inputs. @@ -824,7 +840,9 @@ def read_populace_us_donor(path: str | Path, *, sha256: str, size: int) -> Donor Donor-only enrichment is staged before ``decode``; all work after that seam reads concept names. The result declares transport content with a - US donor and unchanged source-currency amounts and design weights. + US donor and unchanged source-currency amounts and design weights. The + exporter's ``_time_period`` (a one-row integer series) is returned as + ``time_period``; a file without it reads as ``None``. """ path = Path(path) if isinstance(size, bool) or not isinstance(size, int) or size < 0: @@ -845,6 +863,7 @@ def read_populace_us_donor(path: str | Path, *, sha256: str, size: int) -> Donor entity: read_frame_table(store, entity) for entity in ("person", "household") } + time_period = _donor_time_period(store) staged = _donor_extras(raw["person"], raw["household"]) tables = POLICYENGINE_US_CONCEPT_MAPPING.decode(raw) del raw @@ -869,6 +888,7 @@ def read_populace_us_donor(path: str | Path, *, sha256: str, size: int) -> Donor staged["support_strata"], staged["source_person_ids"], dropped_parent_cycle_edges=dropped, + time_period=time_period, ) diff --git a/packages/microcosm-frame/tests/engine_free/shared/test_transport_donor.py b/packages/microcosm-frame/tests/engine_free/shared/test_transport_donor.py index 71257bbddb..15751153d2 100644 --- a/packages/microcosm-frame/tests/engine_free/shared/test_transport_donor.py +++ b/packages/microcosm-frame/tests/engine_free/shared/test_transport_donor.py @@ -77,6 +77,9 @@ def __exit__(self, *_): def __getitem__(self, key): return tables[key].copy(deep=True) + def keys(self): + return [f"/{name}" for name in tables] + def get_storer(self, _): return SimpleNamespace(attrs=SimpleNamespace()) @@ -132,6 +135,7 @@ def spy_decode(mapping, tables): assert donor.donor_country == "us" assert donor.currency == "USD" assert donor.dropped_parent_cycle_edges == 0 + assert donor.time_period is None assert validate_concept_tables(donor.tables) == () assert "A_LINENO" not in person and "bank_account_assets" not in person assert "household_weight" not in donor.tables["household"] @@ -197,6 +201,36 @@ def test_synthetic_donor_transport_and_mapping_round_trip(tmp_path): np.testing.assert_array_equal(donor.weights, original_weights) +@pytest.mark.parametrize("year", [2024, 2023]) +def test_reader_returns_the_exporters_time_period(tmp_path, year): + # The US exporter writes a one-row table-format integer series under + # ``/_time_period`` (adapters/policyengine_us.py); the reader returns it. + path = tmp_path / "dated.h5" + _write_h5(path, _tables()) + with pd.HDFStore(path, mode="a") as store: + store.put("_time_period", pd.Series([year]), format="table") + donor = read_populace_us_donor(path, **_pin(path)) + assert donor.time_period == year and type(donor.time_period) is int + + +@pytest.mark.parametrize( + "series", + [ + pd.Series([2024, 2025]), + pd.Series(["2024"]), + pd.Series([2024.5]), + pd.Series([True]), + ], +) +def test_reader_refuses_a_malformed_time_period(tmp_path, series): + path = tmp_path / "malformed.h5" + _write_h5(path, _tables()) + with pd.HDFStore(path, mode="a") as store: + store.put("_time_period", series, format="table") + with pytest.raises(ValueError, match="_time_period"): + read_populace_us_donor(path, **_pin(path)) + + @pytest.mark.parametrize("mismatch", ["size", "sha256"]) def test_reader_refuses_pin_mismatch_before_hdf_parse(tmp_path, monkeypatch, mismatch): path = tmp_path / "not-an-hdf.h5" diff --git a/test_support/microcosm_build/money_normalization.py b/test_support/microcosm_build/money_normalization.py new file mode 100644 index 0000000000..cf11cca876 --- /dev/null +++ b/test_support/microcosm_build/money_normalization.py @@ -0,0 +1,593 @@ +"""Engine-free fixtures for exact donor money normalization. + +Everything here is synthetic: the destination currency is ``XTS`` (the ISO +4217 code reserved for testing), the toy feed holds invented half-year pay +levels for calendar 2024 and 2027, and the three toy factors are chosen so +their growth roots are exact (``1 + g = q**2`` for ``q`` = 1.01, 1.02 and +1.03). The toy graph reads the frame package's six invented US support +records; none of it describes a country. +""" + +from __future__ import annotations + +import json +from copy import deepcopy +from fractions import Fraction +from pathlib import Path + +import numpy as np + +from microcosm.build.ledger_artifact import ( + chronicle_feed_identity, + load_ledger_consumer_artifact, +) +from microcosm.build.reference_factors import resolve_reference_factor +from microcosm.build.transport.money_kernels import register_money_kernels +from microcosm.graph import ( + Capabilities, + Determinism, + Graph, + KernelBase, + KernelResult, + Node, + Owned, + Slice, + SourceRef, + StructuralDelta, + source_hash, +) +from microcosm.graph.canonical import canonical_json +from test_support.microcosm_build.chronicle_feed import write_pinned_artifact +from test_support.microcosm_build.transport_population import ( + PopulationSources, + create_node, + population_registry, +) + +PROFILES = ("low", "central", "high") +DIMENSION = "toy_index" +#: (entity, column) of every money concept the toy declaration normalizes. +NORMALIZED = { + "person": [ + "nonfarm_self_employment_income", + "farm_self_employment_income", + "interest_income", + "dividend_income", + "rental_income", + "realized_capital_gains", + "private_pension_income", + ], + "household": ["mortgage_interest", "mortgage_principal", "property_tax"], +} +FAMILIES = ("mixed_income", "capital_income", "pensions", "owner_housing_costs") +#: The money columns the synthetic donor carries once ``add_money_columns`` +#: adds two signed raw columns; the graph declaration normalizes these. +GRAPH_NORMALIZED = { + "person": ["interest_income", "rental_income", "farm_self_employment_income"] +} +_GRAPH_ABSENT = { + "person": [ + "nonfarm_self_employment_income", + "dividend_income", + "realized_capital_gains", + "private_pension_income", + ], + "household": ["mortgage_interest", "mortgage_principal", "property_tax"], +} +#: Raw donor rows (decoded by the frame package's US concept mapping) that add +#: signed rental and farm amounts, including a half-cent tie and a negative. +RAW_RENTAL = [-120.5, 0.0, 0.0, 35.25, 0.0, 10.0] +RAW_FARM = [0.0, -2.675, 0.0, 0.0, 0.0, 1000000.0] +_GROWTH = "4.04" +_DELTAS = {"low": "-2.03", "central": "0", "high": "2.05"} +_SOURCE_LEVELS = ("100", "102") +_ANCHOR = "110" +_VALIDATION = { + "unit": "money_per_week", + "source_sha256": "a" * 64, + "source_vintage": "reviewed", + "entity_role": "employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", +} + + +def factor_id(profile: str) -> str: + return f"toy_donor_{profile}" + + +def oracle_factor(profile: str) -> Fraction: + """The rounded toy factor from first principles (exact roots, 6 dp).""" + q = {"low": Fraction(101, 100), "central": Fraction(102, 100)}.get( + profile, Fraction(103, 100) + ) + numerator = Fraction(_ANCHOR) * (1 + q) / 2 + denominator = sum(Fraction(level) for level in _SOURCE_LEVELS) / 2 + return Fraction(round(numerator / denominator * 10**6), 10**6) + + +def toy_declaration( + *, + rate: str = "1.25", + quote: str = "to_per_from", + identity_families: tuple[str, ...] = (), +) -> dict: + """A complete toy declaration; callers edit copies for negative cases.""" + families = { + family: ( + {"identity": True, "reason": "Toy family left unaged on purpose."} + if family in identity_families + else { + "factors": {profile: factor_id(profile) for profile in PROFILES}, + "disclosure": f"Toy {family} aged by the toy pay index.", + } + ) + for family in FAMILIES + } + return { + "schema_version": 1, + "from_currency": "USD", + "to_currency": "XTS", + "rate": { + "form": "decimal_text", + "text": rate, + "citation": { + "publisher": "Toy office", + "title": "Invented yearly average rate", + "url": "https://example.invalid/toy-rate", + }, + }, + "quote": quote, + "rounding": {"digits": 2, "mode": "half_even"}, + "donor_money_period": { + "type": "calendar_year", + "value": 2024, + "basis": "declared_convention", + "evidence": ["Synthetic fixture convention; no donor authentication."], + }, + "target_period": 2027, + "columns": deepcopy(NORMALIZED), + "index_family_factors": families, + "factor_profile": { + "dimension": DIMENSION, + "profiles": list(PROFILES), + "central_profile": "central", + }, + "cents_suffix": "_cents", + "quantile_mapped": { + "person": { + "employment_income": { + "awaits": ["toy wage bands"], + "reason": "Toy earnings map onto invented bands.", + }, + "liquid_financial_assets": { + "awaits": ["toy asset bands"], + "reason": "Toy assets map onto invented bands.", + }, + }, + "household": { + "rent": { + "awaits": ["toy rent bands"], + "reason": "Toy rents map onto invented bands.", + } + }, + }, + "pending": {}, + } + + +def graph_declaration() -> dict: + """The toy declaration restricted to the columns the synthetic donor has. + + Money concepts the donor lacks are declared pending, so the roster stays + complete; nothing in the toy graph reads them. + """ + declaration = toy_declaration() + declaration["columns"] = deepcopy(GRAPH_NORMALIZED) + declaration["index_family_factors"] = { + family: declaration["index_family_factors"][family] + for family in ("capital_income", "mixed_income") + } + declaration["pending"] = { + entity: { + column: { + "awaits": ["a donor that carries it"], + "reason": "The synthetic donor has no such column.", + } + for column in columns + } + for entity, columns in _GRAPH_ABSENT.items() + } + return declaration + + +def add_money_columns(tables) -> None: + """``edit_tables`` hook: add the signed raw rental and farm columns.""" + tables["person"]["rental_income"] = RAW_RENTAL + tables["person"]["farm_operations_income"] = RAW_FARM + + +def _slot(period: str, start: str, end: str, sequence: int, *, target: bool) -> dict: + slot = { + "selector": { + "source_name": "toy_office", + "source_measure_id": "weekly_pay", + "entity_name": "person", + "period_type": "half", + "period_value": period, + "assertion": "observation", + "dimensions": [], + }, + "validation": { + **_VALIDATION, + "period_coverage": {"start_date": start, "end_date": end}, + }, + "weight": "1", + "sequence": sequence, + } + if target: + slot["projection_offset"] = sequence + return slot + + +def _fact(key: str, value: str, period: str, start: str, end: str) -> dict: + return { + "aggregate_fact_key": key, + "value": float(value), + "assertion": "observation", + "provenance_class": "survey_aggregate", + "period": {"type": "half", "value": period}, + "period_coverage": {"start_date": start, "end_date": end}, + "entity": {"name": "person", "role": "employee"}, + "dimensions": {}, + "source": { + "source_name": "toy_office", + "source_sha256": "a" * 64, + "vintage": "reviewed", + }, + "observed_measure": { + "source_measure_id": "weekly_pay", + "unit": "money_per_week", + }, + } + + +_SOURCE = ( + ("2024-H1", "2024-01-01", "2024-06-30"), + ("2024-H2", "2024-07-01", "2024-12-31"), +) +_TARGET = ( + ("2027-H1", "2027-01-01", "2027-06-30"), + ("2027-H2", "2027-07-01", "2027-12-31"), +) + + +def money_feed_facts(*, rate: str | None = None) -> list[dict]: + """The toy feed: two 2024 source halves, one 2027 anchor and a growth row.""" + facts = [ + _fact(f"source-{index}", level, *_SOURCE[index]) + for index, level in enumerate(_SOURCE_LEVELS) + ] + facts.append(_fact("anchor-0", _ANCHOR, *_TARGET[0])) + facts.append( + { + "aggregate_fact_key": "growth", + "value": float(_GROWTH), + "assertion": "source_projection", + "provenance_class": "model_output", + "period": {"type": "year", "value": "2027"}, + "period_coverage": {"start_date": "2027-01-01", "end_date": "2027-12-31"}, + "entity": {"name": "person", "role": "employee"}, + "dimensions": {}, + "source": { + "source_name": "toy_office", + "source_sha256": "a" * 64, + "vintage": "reviewed", + }, + "observed_measure": {"source_measure_id": "growth", "unit": "percent"}, + "layout": {"source_column_id": "annual_projection"}, + } + ) + if rate is not None: + facts.append( + { + "aggregate_fact_key": "toy-rate", + "value": float(rate), + "assertion": "observation", + "provenance_class": "administrative", + "period": {"type": "year", "value": "2024"}, + "period_coverage": { + "start_date": "2024-01-01", + "end_date": "2024-12-31", + }, + "entity": {"name": "currency", "role": "rate"}, + "dimensions": {}, + "source": { + "source_name": "toy_bank", + "source_sha256": "b" * 64, + "vintage": "reviewed", + }, + "observed_measure": { + "source_measure_id": "toy_rate", + "unit": "xts_per_usd", + }, + } + ) + return facts + + +def rate_feed_form(identity: dict, expected: str) -> dict: + """A feed-selected rate declaration matching :func:`money_feed_facts`.""" + return { + "form": "feed_selector", + "selector": {"aggregate_fact_key": "toy-rate", "period_value": "2024"}, + "validation": { + "unit": "xts_per_usd", + "source_sha256": "b" * 64, + "source_vintage": "reviewed", + "entity_role": "rate", + "provenance_class": "administrative", + "assertion": "observation", + "period_coverage": {"start_date": "2024-01-01", "end_date": "2024-12-31"}, + }, + "assertion_policy": "observed_only", + "feed_identity": identity, + "expected_decimal": expected, + } + + +def money_factor_declaration( + profile: str, + identity: dict, + *, + purpose: str = "money_normalization_input", + source=_SOURCE, +) -> dict: + numerator = oracle_factor(profile) * 10**6 + return { + "schema_version": 1, + "id": factor_id(profile), + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": purpose, + "feed_identity": deepcopy(identity), + "source_period": "toy-2024", + "target_period": 2027, + "source_schedule": [ + _slot(*row, index, target=False) for index, row in enumerate(source) + ], + "target_schedule": [ + _slot(*row, index, target=True) for index, row in enumerate(_TARGET) + ], + "growth": { + "selector": {"aggregate_fact_key": "growth", "period_value": "2027"}, + "validation": { + **_VALIDATION, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "period_coverage": { + "start_date": "2027-01-01", + "end_date": "2027-12-31", + }, + "source_column_id": "annual_projection", + }, + "unit_multiplier": "0.01", + "scenario_delta": _DELTAS[profile], + "sensitivity_provenance": ["toy reviewed sensitivity"], + "assertion_policy": "allow_source_projection", + }, + "subperiods_per_year": 2, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": {"digits": 6, "mode": "nearest_half_even"}, + "expected_operational_factor": { + "numerator": int(numerator), + "denominator": 10**6, + }, + "validity": { + "declared_on": "2025-01-01", + "valid_from": "2025-01-01", + "valid_until": "2099-12-31", + "as_of": "2025-01-01", + }, + "evidence": ["toy published pay levels"], + "decision_references": ["toy reviewed decision"], + } + + +def write_money_feed(root: Path, *, rate: str | None = None): + """Write the toy feed; return ``(path, pin, identity, lexical facts)``.""" + path, pin = write_pinned_artifact(root, money_feed_facts(rate=rate)) + pin["source_commit"] = "c" * 40 + artifact = load_ledger_consumer_artifact(path, feed_pin=pin, lexical_floats=True) + return path, pin, chronicle_feed_identity(pin), artifact.facts + + +def money_factor_context(identity: dict, facts, **declaration_options) -> dict: + """Resolve the three toy factors through the public factor API.""" + entries = [] + for profile in PROFILES: + declaration = money_factor_declaration(profile, identity, **declaration_options) + factor, receipt = resolve_reference_factor(declaration, facts, identity) + entries.append( + { + "declaration": declaration, + "operational_factor": factor, + "receipt": receipt, + } + ) + return {"schema_version": 1, "feed_identity": identity, "factors": entries} + + +def text(document) -> str: + return canonical_json(document).decode("utf-8") + + +def digest(document) -> str: + import hashlib + + return hashlib.sha256(canonical_json(document)).hexdigest() + + +def normalize_node( + node_id: str, + population: str, + declaration: dict, + context: dict, + pin: dict, + *, + selection: dict | None = None, +) -> Node: + """A ``money.normalize@1`` node owning exactly the declared outputs.""" + suffix = declaration["cents_suffix"] + columns = declaration["columns"] + return Node( + node_id, + "money.normalize@1", + population=population, + sources=("money_facts",), + inputs=tuple(Slice(entity, tuple(names)) for entity, names in columns.items()), + outputs=tuple( + owned + for entity, names in columns.items() + for name in names + for owned in ( + Owned(entity, name, "float64", rewrite=True), + Owned(entity, f"{name}{suffix}", "int64"), + ) + ), + params={ + "declaration": text(declaration), + "declaration_sha256": digest(declaration), + "factor_context": text(context), + "factor_context_sha256": digest(context), + "profile_selection": text({} if selection is None else selection), + "chronicle_feed_pin": text(pin), + }, + ) + + +class ToyMoneyConsumer(KernelBase): + """A test-only reader: totals of one normalized column and its companion.""" + + ref = "toy.money_consumer@1" + capabilities = Capabilities(Determinism.DETERMINISTIC, dependencies=("pandas",)) + + def implementation_hash(self) -> str: + return source_hash(type(self), dependencies=self.capabilities.dependencies) + + def run(self, context) -> KernelResult: + table = context.tables["person"] + return KernelResult( + receipt={ + "person_id": [int(value) for value in table["person_id"]], + **{ + column: [float(value) for value in table[column]] + for column in GRAPH_NORMALIZED["person"] + }, + **{ + f"{column}_cents": [ + int(value) for value in table[f"{column}_cents"] + ] + for column in GRAPH_NORMALIZED["person"] + }, + } + ) + + +def consumer_node(node_id: str, population: str) -> Node: + columns = GRAPH_NORMALIZED["person"] + return Node( + node_id, + ToyMoneyConsumer.ref, + population=population, + inputs=( + Slice("person", (*columns, *(f"{column}_cents" for column in columns))), + ), + ) + + +def money_graph( + population: PopulationSources, + declaration: dict, + context: dict, + pin: dict, + *, + variant_selection: dict | None = None, + normalize_open: bool = False, +) -> Graph: + """CREATE -> open -> {central, v1, v2} with one normalization per lineage. + + ``normalize_open`` adds a normalization in the shared open version, so + each branch's lineage normalizes twice. + """ + create = create_node(population) + open_ = Node( + "toy.open", + "transport.boundary@1", + structural=StructuralDelta.FILTER, + base=create.id, + inputs=(Slice("person", ("age",)),), + ) + nodes = [create, open_] + if normalize_open: + nodes.append( + normalize_node("toy.open.normalize", open_.id, declaration, context, pin) + ) + for branch, selection in ( + ("toy.central", None), + ( + "toy.v1", + {DIMENSION: "low"} if variant_selection is None else variant_selection, + ), + ("toy.v2", {DIMENSION: "high"}), + ): + nodes.append( + Node( + branch, + "transport.boundary@1", + structural=StructuralDelta.FILTER, + base=open_.id, + inputs=(Slice("person", ("age",)),), + ) + ) + nodes.append( + normalize_node( + f"{branch}.normalize", + branch, + declaration, + context, + pin, + selection=selection, + ) + ) + nodes.append(consumer_node(f"{branch}.consume", branch)) + return Graph( + "synthetic-money", + ( + SourceRef("donor", "populace-us-h5-v1"), + SourceRef("facts", "ledger-consumer-artifact-v1"), + SourceRef("money_facts", "ledger-consumer-artifact-v1"), + ), + tuple(nodes), + ) + + +def money_registry(): + registry = population_registry() + register_money_kernels(registry) + registry.register(ToyMoneyConsumer()) + return registry + + +def as_float_list(values) -> list[float]: + return [float(value) for value in np.asarray(values, dtype=np.float64)] + + +def dump(document) -> str: + return json.dumps(document, sort_keys=True) + + +def money_sources(population: PopulationSources, feed: Path) -> dict[str, Path]: + """The three sources :func:`money_graph` declares.""" + return {"donor": population.donor, "facts": population.facts, "money_facts": feed} diff --git a/test_support/microcosm_build/reference_factor_transport.py b/test_support/microcosm_build/reference_factor_transport.py new file mode 100644 index 0000000000..1fe2f52bb4 --- /dev/null +++ b/test_support/microcosm_build/reference_factor_transport.py @@ -0,0 +1,169 @@ +"""Authenticated toy feed and companion references for transport factor checks.""" + +import json +from copy import deepcopy + +from microcosm.build.ledger_artifact import ( + chronicle_feed_identity, + load_ledger_consumer_artifact, +) +from microcosm.build.reference_factors import resolve_reference_factor +from microcosm.build.reference_profiles import ( + reference_document_sha256, + resolve_reference_profiles, +) +from test_support.microcosm_build.chronicle_feed import write_pinned_artifact +from test_support.microcosm_build.reference_factors import factor_fixture +from test_support.microcosm_build.transport_graph import reference_document + + +def factor_transport_fixture( + tmp_path, *, forged=False, activate=False, manifest_commit=False +): + """Build evidence from loaded lexical tokens; optionally forge its source claim.""" + _, facts, _ = factor_fixture() + references = [] + for kind, value in (("count", 3), ("money", 100)): + key = f"toy-{kind}" + selector = { + "aggregate_fact_key": key, + "source_name": "toy_office", + "source_measure_id": kind, + "source_concept": f"toy.{kind}", + "entity_name": "person", + "period_type": "year", + "period_value": 2001, + "dimensions": ["band"], + "dimension_values": {"band": "one"}, + } + facts.append( + { + "aggregate_fact_key": key, + "label": f"Toy published {kind}", + "value": value, + "assertion": "observation", + "period": {"type": "year", "value": 2001}, + "geography": { + "level": "country", + "id": "XX", + "name": "Toy country", + "vintage": "toy", + }, + "entity": {"name": "person"}, + "dimensions": {"band": "one"}, + "dimension_labels": {"band": "Band"}, + "dimension_value_labels": {"band": {"one": "One"}}, + "aggregation": {"method": "sum"}, + "source": {"source_name": "toy_office"}, + "observed_measure": { + "source_measure_id": kind, + "source_concept": f"toy.{kind}", + "unit": kind, + }, + } + ) + references.append( + { + "name": key, + "entity": "person", + "measure": "people" if kind == "count" else "employment_income", + "family": "toy", + "category_id": "toy.population", + "period": 2002, + "period_match_policy": "latest_not_after", + "ledger_selector": selector, + "metadata": { + "contract_target_id": key, + "reference_value_kind": kind, + "activation_status": "requires_prerequisites", + "required_reference_profile": "index", + "required_measurement_binding": "reviewed_measurement", + "reference_prerequisites": json.dumps( + ["reference_profile:index", "measurement:reviewed_measurement"] + ), + "parking_reason": "Reviewed measurement is required.", + }, + } + ) + path, pin = write_pinned_artifact( + tmp_path / "factor-artifact", json.loads(json.dumps(facts, default=float)) + ) + pin["source_commit"] = "c" * 40 + if manifest_commit: + import hashlib + + manifest_path = path / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["source_commit"] = pin["source_commit"] + manifest_path.write_text(json.dumps(manifest, sort_keys=True) + "\n") + pin["manifest_sha256"] = hashlib.sha256(manifest_path.read_bytes()).hexdigest() + artifact = load_ledger_consumer_artifact(path, feed_pin=pin, lexical_floats=True) + identity = chronicle_feed_identity(pin) + declaration, _, _ = factor_fixture(feed_identity=identity) + evidence_facts = artifact.facts + if forged: + # This evidence is internally consistent but falsely claims the original pin. + evidence_facts = deepcopy(artifact.facts) + next(row for row in evidence_facts if row["aggregate_fact_key"] == "target-0")[ + "value" + ] = 120 + declaration["expected_operational_factor"]["numerator"] = 1200000 + factor, receipt = resolve_reference_factor(declaration, evidence_facts, identity) + context = { + "schema_version": 1, + "feed_identity": identity, + "factors": [ + { + "declaration": declaration, + "operational_factor": factor, + "receipt": receipt, + } + ], + } + transformation = { + "operation": "scale_money_and_intervals", + "factor_id": declaration["id"], + "reference_names": ["toy-money"], + "count_reference_names": ["toy-count"], + "interval_table_ids": [], + "required_measurement_binding": "reviewed_measurement", + } + profiles = { + "schema_version": 1, + "reference_resource": "target_references", + "factor_context_resource": "factor_context", + "dimensions": { + "index": { + "default": "central", + "profiles": { + name: {"transformations": [deepcopy(transformation)]} + for name in ("low", "central", "high") + }, + } + }, + } + base = reference_document(references) + base["hierarchy"]["target_labels"] = { + row["name"]: f"Aged toy {row['name']}" for row in references + } + document, _, resolution = resolve_reference_profiles( + base, profiles, {}, factor_context=context + ) + if activate: + # Test-only future measurement closure lets the compiler test its own replay + # and arithmetic independently of this lane's parked country declarations. + for row in document["target_references"]: + row["metadata"]["activation_status"] = "active" + resolution["effective_references_sha256"] = reference_document_sha256(document) + resolution.pop("receipt_sha256") + resolution["receipt_sha256"] = reference_document_sha256(resolution) + spec = { + "country": "xx", + "resources": { + "target_references": base, + "reference_profiles": profiles, + "factor_context": context, + "scenarios": {"scenarios": [{"id": "base", "knobs": {}}]}, + }, + } + return path, pin, document, resolution, spec diff --git a/test_support/microcosm_build/reference_factors.py b/test_support/microcosm_build/reference_factors.py new file mode 100644 index 0000000000..ca962f70ab --- /dev/null +++ b/test_support/microcosm_build/reference_factors.py @@ -0,0 +1,185 @@ +"""Small engine-free declarations for exercising public factor interfaces.""" + +from __future__ import annotations + +from copy import deepcopy +from datetime import date, timedelta +from decimal import Decimal + +from microcosm.build.ledger_artifact import CONSUMER_ARTIFACT_SCHEMA_VERSION +from microcosm.build.reference_factors import resolve_reference_factor + + +def factor_fixture( + *, + feed_identity=None, + frequency=2, + source_level="100", + anchor_level="110", + growth="0", + digits=6, + expected_numerator=None, +): + """Return a declaration, exact numeric facts and a matching feed identity. + + Slots are single days: period coverage, rather than calendar conventions, + establishes contiguity. Callers may serialize facts into their toy artifact. + """ + identity = ( + { + "source_commit": "c" * 40, + "facts_sha256": "a" * 64, + "manifest_sha256": "b" * 64, + "artifact_schema_version": CONSUMER_ARTIFACT_SCHEMA_VERSION, + "consumer_fact_schema_versions": ["chronicle.consumer_fact.v4"], + "consumer_fact_schema_sha256": "d" * 64, + "fact_row_count": frequency + 2, + } + if feed_identity is None + else deepcopy(feed_identity) + ) + facts, source, target = [], [], [] + validation = { + "unit": "money_per_week", + "source_sha256": "a" * 64, + "source_vintage": "reviewed", + "entity_role": "employee", + "provenance_class": "survey_aggregate", + "assertion": "observation", + } + for schedule, start, prefix, level in ( + (source, date(2001, 1, 1), "source", source_level), + (target, date(2002, 1, 1), "target", anchor_level), + ): + for index in range(frequency): + period = (start + timedelta(days=index)).isoformat() + coverage = {"start_date": period, "end_date": period} + selector = { + "source_name": "toy_office", + "source_measure_id": "weekly_pay", + "entity_name": "person", + "period_type": "day", + "period_value": period, + "assertion": "observation", + "dimensions": [], + } + slot = { + "selector": selector, + "validation": {**validation, "period_coverage": coverage}, + "weight": "1", + "sequence": index, + } + if schedule is target: + slot["projection_offset"] = index + schedule.append(slot) + if schedule is source or index == 0: + facts.append( + { + "aggregate_fact_key": f"{prefix}-{index}", + "value": Decimal(level), + "assertion": "observation", + "provenance_class": "survey_aggregate", + "period": {"type": "day", "value": period}, + "period_coverage": coverage, + "entity": {"name": "person", "role": "employee"}, + "dimensions": {}, + "source": { + "source_name": "toy_office", + "source_sha256": "a" * 64, + "vintage": "reviewed", + }, + "observed_measure": { + "source_measure_id": "weekly_pay", + "unit": "money_per_week", + }, + } + ) + growth_period = "2002" + growth_validation = { + **validation, + "unit": "percent", + "assertion": "source_projection", + "provenance_class": "model_output", + "period_coverage": {"start_date": "2002-01-01", "end_date": "2002-12-31"}, + "source_column_id": "annual_projection", + } + facts.append( + { + "aggregate_fact_key": "growth", + "value": Decimal(growth), + "assertion": "source_projection", + "provenance_class": "model_output", + "period": {"type": "year", "value": growth_period}, + "period_coverage": growth_validation["period_coverage"], + "entity": {"name": "person", "role": "employee"}, + "dimensions": {}, + "source": { + "source_name": "toy_office", + "source_sha256": "a" * 64, + "vintage": "reviewed", + }, + "observed_measure": {"source_measure_id": "growth", "unit": "percent"}, + "layout": {"source_column_id": "annual_projection"}, + } + ) + if expected_numerator is None: + expected_numerator = int( + Decimal(anchor_level) / Decimal(source_level) * 10**digits + ) + declaration = { + "schema_version": 1, + "id": "toy_factor", + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": identity, + "source_period": 2001, + "target_period": 2002, + "source_schedule": source, + "target_schedule": target, + "growth": { + "selector": {"aggregate_fact_key": "growth", "period_value": growth_period}, + "validation": growth_validation, + "unit_multiplier": "0.01", + "scenario_delta": "0", + "sensitivity_provenance": ["toy reviewed sensitivity"], + "assertion_policy": "allow_source_projection", + }, + "subperiods_per_year": frequency, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": {"digits": digits, "mode": "nearest_half_even"}, + "expected_operational_factor": { + "numerator": expected_numerator, + "denominator": 10**digits, + }, + "validity": { + "declared_on": "2000-01-01", + "valid_from": "2000-01-01", + "valid_until": "2099-12-31", + "as_of": "2002-01-01", + }, + "evidence": ["toy published levels"], + "decision_references": ["toy reviewed decision"], + } + return declaration, facts, identity + + +def factor_context_fixture(**kwargs): + """Return a context with a factor genuinely resolved through the public API.""" + declaration, facts, identity = factor_fixture(**kwargs) + factor, receipt = resolve_reference_factor(declaration, facts, identity) + return ( + { + "schema_version": 1, + "feed_identity": identity, + "factors": [ + { + "declaration": declaration, + "operational_factor": factor, + "receipt": receipt, + } + ], + }, + facts, + identity, + ) diff --git a/test_support/microcosm_build/reference_intervals.py b/test_support/microcosm_build/reference_intervals.py new file mode 100644 index 0000000000..ad7d8bf489 --- /dev/null +++ b/test_support/microcosm_build/reference_intervals.py @@ -0,0 +1,190 @@ +"""Toy interval tables and ageing profiles for exercising interval plumbing.""" + +from __future__ import annotations + +import json +from copy import deepcopy + +from microcosm.build.reference_factors import resolve_reference_factor +from test_support.microcosm_build.reference_factors import factor_fixture +from test_support.microcosm_build.transport_graph import reference_document + +#: Anchor levels against a source level of 100: factors 1.05, 1.1 and 1.15. +PROFILE_ANCHORS = {"low": "105", "central": "110", "high": "115"} +INCOME_COLUMN = "toy_income_cents" +UNIVERSE_COLUMN = "toy_income_universe" + + +def interval_table_fixture( + *, + table_id="toy_income", + entity="person", + cuts=(100_000, 1_000_000), + nil=True, + closure="right", + first=0, + source_period=2001, + target_period=2002, + companions=False, + indicator_prefix="toy_income_band", +): + """One declared table; companions name ``toy_{count,money}_``.""" + bands = [] + if nil: + bands.append( + {"id": "nil", "kind": "nil", "indicator": f"{indicator_prefix}_nil"} + ) + for index, upper in enumerate((*cuts, None)): + bands.append( + { + "id": f"b{index}", + "kind": "positive", + "upper_cent": upper, + "indicator": f"{indicator_prefix}_b{index}", + } + ) + if companions: + for band in bands: + band.update( + count_reference=f"toy_count_{band['id']}", + amount_reference=f"toy_money_{band['id']}", + ) + return { + "id": table_id, + "entity": entity, + "currency": "XTS", + "input_unit": "integer_cent", + "income_column": INCOME_COLUMN, + "universe_column": UNIVERSE_COLUMN, + "source_period": source_period, + "target_period": target_period, + "positive_band_closure": closure, + "nil_band": "present" if nil else "absent", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": first, + "source_locator": "Toy!A1:A9", + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left" + if closure == "right" + else "right", + }, + "bands": bands, + } + + +def interval_bindings(table, *, entity=None): + """Calibration bindings whose outputs are the table's band indicators.""" + entity = table["entity"] if entity is None else entity + return { + "schema_version": 1, + "required_inputs": {entity: [f"{entity}_id"]}, + "input_domains": {entity: {f"{entity}_id": {"kind": "identity"}}}, + "columns": [ + { + "entity": entity, + "name": band["indicator"], + "interval": {"table": table["id"], "band": band["id"]}, + } + for band in table["bands"] + ], + } + + +def _factor_entry(profile): + declaration, facts, identity = factor_fixture(anchor_level=PROFILE_ANCHORS[profile]) + declaration["id"] = f"toy_{profile}" + factor, receipt = resolve_reference_factor(declaration, facts, identity) + return identity, { + "declaration": declaration, + "operational_factor": factor, + "receipt": receipt, + } + + +def interval_profile_fixture(): + """References, profiles, factor context and intervals for low/central/high. + + Every band has parked count and money companions filtered on its own + indicator; each profile scales the table with its own factor. + """ + table = interval_table_fixture(companions=True) + references = [] + for band in table["bands"]: + for kind in ("count", "money"): + name = f"toy_{kind}_{band['id']}" + references.append( + { + "name": name, + "entity": "person", + "measure": "people" if kind == "count" else "toy_income", + "filter": band["indicator"], + "period": 2002, + "family": "toy", + "category_id": "toy.population", + "period_match_policy": "latest_not_after", + "ledger_selector": { + "aggregate_fact_key": f"toy-{kind}-{band['id']}", + "source_name": "toy_office", + "source_measure_id": kind, + "source_concept": f"toy.{kind}", + "entity_name": "person", + "period_type": "year", + "period_value": 2001, + "dimensions": ["band"], + "dimension_values": {"band": band["id"]}, + }, + "metadata": { + "contract_target_id": name, + "reference_value_kind": kind, + "activation_status": "requires_prerequisites", + "required_reference_profile": "index", + "required_measurement_binding": "reviewed_measurement", + "reference_prerequisites": json.dumps( + [ + "reference_profile:index", + "measurement:reviewed_measurement", + ] + ), + "parking_reason": "Reviewed measurement is required.", + }, + } + ) + identity, factors = None, [] + for profile in PROFILE_ANCHORS: + identity, entry = _factor_entry(profile) + factors.append(entry) + context = {"schema_version": 1, "feed_identity": identity, "factors": factors} + transformation = { + "operation": "scale_money_and_intervals", + "reference_names": [ + row["name"] for row in references if row["name"].startswith("toy_money") + ], + "count_reference_names": [ + row["name"] for row in references if row["name"].startswith("toy_count") + ], + "interval_table_ids": [table["id"]], + "required_measurement_binding": "reviewed_measurement", + } + profiles = { + "schema_version": 1, + "reference_resource": "target_references", + "factor_context_resource": "factor_context", + "interval_resource": "profile_intervals", + "dimensions": { + "index": { + "default": "central", + "profiles": { + profile: { + "transformations": [ + {**deepcopy(transformation), "factor_id": f"toy_{profile}"} + ] + } + for profile in PROFILE_ANCHORS + }, + } + }, + } + base = reference_document(references) + intervals = {"schema_version": 1, "tables": [table]} + return base, profiles, context, intervals diff --git a/test_support/microcosm_build/spec_source_citations.py b/test_support/microcosm_build/spec_source_citations.py index f4ae81e01e..bda0528c37 100644 --- a/test_support/microcosm_build/spec_source_citations.py +++ b/test_support/microcosm_build/spec_source_citations.py @@ -1309,9 +1309,16 @@ def resolve_dotted_symbol(dotted: str) -> object: try: target: object = importlib.import_module(name) except ModuleNotFoundError as error: - if error.name != name: + # ``pkg.mod.Class.method`` fails one level up: importing it first + # imports ``pkg.mod.Class``, which is not a module. A missing + # dependency of a module that does exist still raises. + if error.name is None or not ( + name == error.name or name.startswith(error.name + ".") + ): raise continue + if cut == 1: + raise ModuleNotFoundError(dotted, name=dotted) for attribute in parts[cut:]: target = getattr(target, attribute) return target diff --git a/test_support/microcosm_build/transport_population.py b/test_support/microcosm_build/transport_population.py index 60a48bd026..cd6ae806e6 100644 --- a/test_support/microcosm_build/transport_population.py +++ b/test_support/microcosm_build/transport_population.py @@ -116,11 +116,14 @@ def write_population_sources( permutation=None, band_entity: str = "person", edit_tables=None, + time_period: int | None = None, ) -> PopulationSources: """Write the synthetic donor H5 and toy facts. ``edit_tables`` may change the raw donor tables (a dict of DataFrames) in - place before they are written, for a test-local donor. + place before they are written, for a test-local donor. ``time_period`` + writes the exporter's one-row ``_time_period`` table; by default the + donor records none. """ root.mkdir(parents=True, exist_ok=True) document = json.loads(SYNTHETIC_DONOR.read_text()) @@ -135,6 +138,8 @@ def write_population_sources( with pd.HDFStore(donor, mode="w") as store: for entity, table in tables.items(): store.put(entity, table, format="table") + if time_period is not None: + store.put("_time_period", pd.Series([time_period]), format="table") return PopulationSources( donor=donor, facts=write_facts( diff --git a/tools/generate_nz_reference_aging.py b/tools/generate_nz_reference_aging.py new file mode 100644 index 0000000000..9030534aa6 --- /dev/null +++ b/tools/generate_nz_reference_aging.py @@ -0,0 +1,284 @@ +"""Generate NZ's reviewed ageing declarations and authenticated factor receipts. + +Run with ``NZ_CHRONICLE_ARTIFACT`` or ``--artifact``. The nine operational +expectations are assertions of the feed calculation, never arithmetic inputs. +The observed-only ratio is diagnostic and is not available to profiles. The +three donor factors age calendar-2024 donor money to tax year 2026-27; only +money normalization may apply them. +""" + +from __future__ import annotations + +import argparse +import copy +import json +import os +from pathlib import Path + +from microcosm.build.ledger_artifact import ( + chronicle_feed_identity, + load_ledger_consumer_artifact, + validate_chronicle_feed_pin, +) +from microcosm.build.reference_factors import resolve_reference_factor +from tools.generate_nz_target_references import SPEC, _selector + +_QES_2025_HASH = "25053dcbd343320cad72e2896475eaebaf5f5bb0b5965ca788506ca15efcea30" +_QES_2026_HASH = "1af2e7e37f1c108a2656842cf1f519c903e1a982bcdc03ee02d0ad888ebc3a97" +_TREASURY_HASH = "e2486375dde515d0722f8c3022f481804bb014beb83c81b8bfc99bc9426544d9" +_OPERANDS = ( + (12028, "2023-Q2", "cfbce1343aa646fce0411631", "1502.27"), + (12030, "2023-Q3", "f9f4f127d080fe2f06617947", "1528.92"), + (12032, "2023-Q4", "fb5938810b7b37ae8534e5f7", "1556.16"), + (12034, "2024-Q1", "854e91ec1dd95e396659ad8e", "1563.54"), + (12036, "2024-Q2", "c07fd9ce3137fa20386bafe0", "1586.32"), + (12038, "2024-Q3", "54a0eeb64049ba4c15e397bf", "1595.11"), + (12040, "2024-Q4", "7fc35ba9682ceb04504f2ef9", "1622.73"), + (12044, "2025-Q2", "86ce22ba42a6e2a36b889f9b", "1652.72"), + (12046, "2025-Q3", "434e7b2736b4b16589034c72", "1661.12"), + (12048, "2025-Q4", "bc87e53157eaf53c846879dc", "1681.06"), + (12050, "2026-Q1", "78e735569814069f8066ba9f", "1685.39"), + (12052, "2026-Q2", "91583e3f39d11b8b4b2923d2", "1700.68"), +) +_TY2024 = ("2023-Q2", "2023-Q3", "2023-Q4", "2024-Q1") +_TY2026 = ("2025-Q2", "2025-Q3", "2025-Q4", "2026-Q1") +_CY2024 = ("2024-Q1", "2024-Q2", "2024-Q3", "2024-Q4") +_TARGET = ("2026-Q2", "2026-Q3", "2026-Q4", "2027-Q1") +_EXPECTED = { + "ird_ty2024_to_ty2027_low": 1115056, + "ird_ty2024_to_ty2027_central": 1117109, + "ird_ty2024_to_ty2027_high": 1119158, + "p1_ty2026_to_ty2027_low": 1026690, + "p1_ty2026_to_ty2027_central": 1028580, + "p1_ty2026_to_ty2027_high": 1030467, + "observed_ty2026_over_ty2024": 1086069, + "donor_cy2024_to_ty2027_low": 1077090, + "donor_cy2024_to_ty2027_central": 1079073, + "donor_cy2024_to_ty2027_high": 1081052, +} +_DECISIONS = [ + "d1599; IRD band ageing factors, 22 October 2026", + "G8-r2 hub rulings 3, 23 October 2026, sections 1–2", +] +_DONOR_DECISIONS = [ + "d1952 card; NZ money year build item: donor money is calendar-2024 dollars", + "G8-w hub rulings 5, 24 October 2026, items H1–H3", + "G8-r2 hub rulings 3, 23 October 2026, section 1", +] + + +def _coverage(period): + year, quarter = period.split("-Q") + year, quarter = int(year), int(quarter) + start_month = 3 * quarter - 2 + end_day = (31, 30, 30, 31)[quarter - 1] + return { + "start_date": f"{year:04d}-{start_month:02d}-01", + "end_date": f"{year:04d}-{start_month + 2:02d}-{end_day}", + } + + +def _validation(fact): + return { + "unit": fact["observed_measure"]["unit"], + "source_sha256": fact["source"]["source_sha256"], + "source_vintage": fact["source"]["vintage"], + "entity_role": fact["entity"]["role"], + "provenance_class": fact["provenance_class"], + "assertion": fact["assertion"], + "period_coverage": { + key: fact["period_coverage"][key] for key in ("start_date", "end_date") + }, + "source_column_id": fact["layout"]["source_column_id"], + } + + +def build_aging_declarations(artifact, *, package_dir=SPEC): + """Author the reviewed fixed selectors, refusing a changed operand or pin.""" + feed_pin = json.loads((package_dir / "chronicle_feed.json").read_text()) + validate_chronicle_feed_pin(artifact, feed_pin) + feed_pin = chronicle_feed_identity(feed_pin) + facts_by_key = {fact["aggregate_fact_key"]: fact for fact in artifact.facts} + qes = {} + for _line, period, suffix, expected in _OPERANDS: + fact = facts_by_key[f"ledger.aggregate_fact.v2:{suffix}"] + if str(fact["value"]) != expected or fact["period"]["value"] != period: + raise ValueError(f"Reviewed QES operand changed for {period}") + historical = period in _TY2024 + expected_hash = _QES_2025_HASH if historical else _QES_2026_HASH + if ( + fact["source"]["source_sha256"] != expected_hash + or fact["observed_measure"]["source_measure_id"] != "SBSZ9A" + or fact["observed_measure"]["unit"] != "nzd_per_week" + or fact["assertion"] != "observation" + ): + raise ValueError(f"Reviewed QES evidence changed for {period}") + qes[period] = fact + treasury = facts_by_key["ledger.aggregate_fact.v2:aba7d3d54489b068116e6a25"] + if ( + str(treasury["value"]) != "2.7" + or treasury["period"]["value"] != 2026 + or treasury["source"]["source_sha256"] != _TREASURY_HASH + or treasury["layout"]["source_column_id"] != "year_ending_30_june_2027" + ): + raise ValueError("Reviewed PREFU operand changed") + + def schedule(periods, *, target=False): + result = [] + for index, period in enumerate(periods): + fact = qes.get(period, qes["2026-Q2"]) + selector = _selector(fact, pin_key=period in qes) + selector["assertion"] = "observation" + selector["period_value"] = period + selector["record_set_id"] = ( + f"stats_nz.qes.awe.{period.lower().replace('-', '')}" + ) + validation = _validation(fact) + validation["period_coverage"] = _coverage(period) + slot = { + "selector": selector, + "validation": validation, + "weight": "1", + "sequence": index, + } + if target: + slot["projection_offset"] = index + result.append(slot) + return result + + growth_selector = _selector(treasury) + growth_selector["assertion"] = "source_projection" + growth_selector["source_table"] = treasury["observed_measure"]["source_table"] + declarations = [] + for prefix, periods, source_period in ( + ("ird_ty2024", _TY2024, 2024), + ("p1_ty2026", _TY2026, 2026), + ): + for profile, delta in (("low", "-0.5"), ("central", "0"), ("high", "0.5")): + factor_id = f"{prefix}_to_ty2027_{profile}" + declarations.append( + { + "schema_version": 1, + "id": factor_id, + "operation": "projected_period_average_ratio", + "operation_version": 1, + "purpose": "profile_input", + "feed_identity": copy.deepcopy(feed_pin), + "source_period": source_period, + "target_period": 2027, + "source_schedule": schedule(periods), + "target_schedule": schedule(_TARGET, target=True), + "growth": { + "selector": copy.deepcopy(growth_selector), + "validation": _validation(treasury), + "unit_multiplier": "0.01", + "scenario_delta": delta, + "assertion_policy": "allow_source_projection", + "sensitivity_provenance": [ + "d1599: symmetric ±0.5 percentage-point Microcosm sensitivity; high is not a Treasury forecast path" + ], + }, + "subperiods_per_year": 4, + "projection_policy": "observed_preferred_latest_contiguous", + "rounding": {"digits": 6, "mode": "nearest_half_even"}, + "expected_operational_factor": { + "numerator": _EXPECTED[factor_id], + "denominator": 1000000, + }, + "validity": { + "declared_on": "2026-10-23", + "valid_from": "2026-10-23", + "valid_until": "2026-11-03", + "as_of": "2026-10-23", + }, + "evidence": [ + "Stats NZ QES Table 7; QEMQ.SBSZ9A; ordinary-time FTE, all sectors, actual", + "Treasury PREFU 2026 Table 4.1; year ending 30 June 2027; Chronicle period 2026", + ], + "decision_references": list(_DECISIONS), + } + ) + diagnostic = copy.deepcopy(declarations[1]) + diagnostic.update( + { + "id": "observed_ty2026_over_ty2024", + "purpose": "diagnostic", + "target_period": 2026, + "target_schedule": schedule(_TY2026, target=True), + "growth": None, + "expected_operational_factor": { + "numerator": _EXPECTED["observed_ty2026_over_ty2024"], + "denominator": 1000000, + }, + } + ) + declarations.append(diagnostic) + for profile in ("low", "central", "high"): + donor = copy.deepcopy( + next( + row + for row in declarations + if row["id"] == f"ird_ty2024_to_ty2027_{profile}" + ) + ) + factor_id = f"donor_cy2024_to_ty2027_{profile}" + donor.update( + { + "id": factor_id, + "purpose": "money_normalization_input", + "source_period": "CY2024", + "source_schedule": schedule(_CY2024), + "expected_operational_factor": { + "numerator": _EXPECTED[factor_id], + "denominator": 1000000, + }, + "evidence": [ + *donor["evidence"], + "Declared calendar-2024 money convention (H1), covering " + "flows and asset stocks; the recorded _time_period is a " + "consistency check, not authentication", + ], + "decision_references": list(_DONOR_DECISIONS), + } + ) + declarations.append(donor) + return feed_pin, declarations + + +def build_reference_aging(artifact, *, package_dir=SPEC): + feed_pin, declarations = build_aging_declarations(artifact, package_dir=package_dir) + records = [] + for declaration in declarations: + factor, receipt = resolve_reference_factor( + declaration, artifact.facts, feed_pin + ) + records.append( + { + "declaration": declaration, + "operational_factor": factor, + "receipt": receipt, + } + ) + return {"schema_version": 1, "feed_identity": feed_pin, "factors": records} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--artifact", default=os.environ.get("NZ_CHRONICLE_ARTIFACT")) + parser.add_argument("--out", type=Path, default=SPEC / "reference_aging.json") + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + if not args.artifact: + parser.error("--artifact or NZ_CHRONICLE_ARTIFACT is required") + artifact = load_ledger_consumer_artifact(args.artifact, lexical_floats=True) + document = build_reference_aging(artifact) + encoded = json.dumps(document, ensure_ascii=False, indent=2) + "\n" + if args.check: + if args.out.read_text() != encoded: + raise SystemExit(f"Stale generated resource: {args.out}") + else: + args.out.write_text(encoded) + + +if __name__ == "__main__": + main() diff --git a/tools/generate_nz_target_references.py b/tools/generate_nz_target_references.py index 1a3f309a81..38d31b7384 100644 --- a/tools/generate_nz_target_references.py +++ b/tools/generate_nz_target_references.py @@ -12,7 +12,9 @@ import json import math import os +import re from collections import Counter +from decimal import Decimal from pathlib import Path from microcosm.build.ledger_artifact import load_ledger_consumer_artifact @@ -79,7 +81,39 @@ "disclosed national-growth proxy." ) FAMILY_REASON = "Stats NZ Census-family nucleus standard and deterministic roster producer are not encoded; MC7 benefit units and family labels cannot substitute for Census multiplicities." -IRD_REASON = "G8-r2: reviewed IRD-definition taxable-income binding, component closure, record universe, numerical intervals and B5 ageing remain required." +IRD_REASON = "B4: reviewed IRD-definition taxable-income measurement, annual component closure and record universe remain required. Ageing resolution does not establish that measurement." +IRD_MEASUREMENT_BINDING = "ird_taxable_income_ty2027_v1" +# Reviewed national detail-row map: nil row 8 through open tail row 244. +# Published national total row 246 is diagnostic and is deliberately absent. +IRD_NATIONAL_ROWS = tuple(f"row_{row}" for row in range(8, 245)) +IRD_AGE_ROWS = tuple(f"row_{row}" for row in range(6, 189)) +IRD_NATIONAL_TABLE = "ird_taxable_income_national_ty2024" +IRD_AGE_TABLE = "ird_taxable_income_age_ty2024" +# Columns name the d1952 stand-in measure, not IRD's taxable-income definition. +IRD_PROXY_INCOME_COLUMN = "ird_taxable_income_proxy_cents" +IRD_PROXY_UNIVERSE_COLUMN = "ird_taxable_income_proxy_universe_status" +# Rows whose published lower label repeats the previous row's upper endpoint. +IRD_SHARED_ENDPOINT_ROWS = { + IRD_NATIONAL_TABLE: ("row_30", "row_60", "row_90", "row_230"), + IRD_AGE_TABLE: ("row_28", "row_58", "row_88"), +} +INTERVAL_DISCLOSURE = ( + "IRD publishes no band closure or cents semantics for these tables. The " + "cuts, closure, nil band and integer-cent arithmetic are Microcosm's declared " + "convention (G8-r open rulings 2, section 1, 23 October 2026), not IRD's own " + "semantics: t = max(0, x) in integer cents with no dollar rounding; t == 0 is " + "the nil band, including qualifying loss records; each positive band is " + "previous_upper < t <= upper; and the open tail is t > its lower cut. Four " + "published label pairs share an endpoint ($20,000, $50,000, $80,000 and " + "$300,000: national rows 29/30, 59/60, 89/90 and 229/230; age rows 27/28, " + "57/58 and 87/88); the ordered right-closed cuts assign each shared endpoint " + "to the lower band. Ageing scales every finite cut exactly by the selected " + "factor and keeps fractional cents. The income and universe columns name the " + "d1952 stand-in measure rather than IRD's definition; no producer exists yet " + "(G8-r2c) and no active row requests them. Boundary diagnostics report " + "weighted mass within $1 of every cut and the fixed-weight change under the " + "left-closed alternative; publisher reclassification is unavailable." +) RECEIPT_REASON = "G8-w: age-stratified receipt assignment is not supported by the current total-only receipt kernel; preserve the source snapshot and age filters." NZS_REASON = "NZ Super receipt binding and prepared receipt flag are not implemented." VP_REASON = "Veterans Pension is unsupported: no veteran facts or producer; a missing flag must refuse, never default to false." @@ -537,8 +571,10 @@ def build_reference_documents(artifact, *, package_dir=SPEC): (f for f in facts if f["layout"]["table_record_kind"] == "detail"), key=lambda f: int(f["layout"]["source_row_id"].removeprefix("row_")), ) - if len(details) != 237: - raise ValueError("Expected 237 taxable-income detail bands") + if tuple(f["layout"]["source_row_id"] for f in details) != IRD_NATIONAL_ROWS: + raise ValueError( + "Expected the reviewed 237 taxable-income detail rows 8–244" + ) for fact in details: row_id = fact["layout"]["source_row_id"] targets.append( @@ -550,12 +586,26 @@ def build_reference_documents(artifact, *, package_dir=SPEC): measure=column, family="income", period=2027, - status="requires_g8r2_taxable_income_contract_intervals_and_ageing", + status="requires_prerequisites", reason=IRD_REASON, filter_column=f"taxable_income_band_{row_id}_indicator", label=f"TY2026-27 {contract.replace('_', ' ')} {fact['dimensions']['taxable_income_band_source_label']} (parked)", ) ) + targets[-1]["metadata"].update( + { + "reference_prerequisites": json.dumps( + [ + "reference_profile:aging_index", + f"measurement:{IRD_MEASUREMENT_BINDING}", + ], + separators=(",", ":"), + ), + "required_reference_profile": "aging_index", + "required_measurement_binding": IRD_MEASUREMENT_BINDING, + "reference_value_kind": "count" if column == "people" else "money", + } + ) wage_facts = inventory.cells( "ird_wage_salary_distribution.ty2026.income_bands", measure="individuals" ) @@ -705,6 +755,7 @@ def build_reference_documents(artifact, *, package_dir=SPEC): "identity", "rolled_forward_by_ratio", "allocated_residual_by_ratio", + "linear_combination", ] if reference_set == "calibration" else ["identity"], @@ -713,12 +764,182 @@ def build_reference_documents(artifact, *, package_dir=SPEC): } documents["calibration_bindings.json"] = calibration_bindings() documents["reference_profiles.json"] = _reference_profiles(inventory, targets) + documents["reference_intervals.json"] = _reference_intervals(inventory) documents["reference_inventory.json"] = _receipts( inventory, erp, documents, feed_pin, ta_facts ) return documents +def _national_upper_dollars(row): + if row == 9: + return 100 + if row == 10: + return 1000 + if 11 <= row <= 209: + return 1000 * (row - 9) + if 210 <= row <= 229: + return 200000 + 5000 * (row - 209) + if 230 <= row <= 243: + return 300000 + 50000 * (row - 229) + if row == 244: + return None + raise ValueError(f"No national taxable-income cut for row {row}") + + +def _age_upper_dollars(row): + if row == 7: + return 100 + if row == 8: + return 1000 + if 9 <= row <= 187: + return 1000 * (row - 7) + if row == 188: + return None + raise ValueError(f"No age-table taxable-income cut for row {row}") + + +def _label_bounds(label): + """Read a published band label as (lower, upper) dollars; upper None is open.""" + if label == "nil": + return None + match = re.fullmatch(r"\$([\d,]+(?:\.\d+)?)\s*-\s*\$([\d,]+)", label) + if match: + lower, upper = match.groups() + return Decimal(lower.replace(",", "")), Decimal(upper.replace(",", "")) + match = re.fullmatch(r"Over \$([\d,]+)", label) + if match: + return Decimal(match.group(1).replace(",", "")), None + raise ValueError(f"Unreadable taxable-income band label {label!r}") + + +def _band_labels(facts, rows): + labels = {} + for fact in facts: + if fact["layout"]["table_record_kind"] != "detail": + continue + row = fact["layout"]["source_row_id"] + label = fact["dimensions"]["taxable_income_band_source_label"] + if labels.setdefault(row, label) != label: + raise ValueError(f"Taxable-income row {row} has inconsistent labels") + if tuple(sorted(labels, key=lambda row: int(row[4:]))) != rows: + raise ValueError("Taxable-income detail rows differ from the reviewed roster") + return labels + + +def _interval_table(table_id, rows, labels, upper_dollars, locator, companions): + """Generate cuts from the row formulas and check them against the labels.""" + bands, previous, shared = [], Decimal(0), [] + for row_id in rows: + row = int(row_id.removeprefix("row_")) + label = labels[row_id] + bounds = _label_bounds(label) + indicator = ( + f"taxable_income_band_{row_id}_indicator" + if companions + else f"taxable_income_age_table_band_{row_id}_indicator" + ) + band = {"id": row_id} + if bounds is None: + if row_id != rows[0]: + raise ValueError("Only the first taxable-income row is nil") + band.update(kind="nil") + else: + upper = upper_dollars(row) + lower, published_upper = bounds + if published_upper != (None if upper is None else Decimal(upper)): + raise ValueError(f"Row {row_id} label {label!r} disagrees with its cut") + if lower == previous: + shared.append(row_id) + elif lower != previous + (Decimal("0.01") if previous == 0 else 1): + raise ValueError(f"Row {row_id} label {label!r} leaves a gap") + band.update( + kind="positive", upper_cent=None if upper is None else upper * 100 + ) + if upper is not None: + previous = Decimal(upper) + band.update(indicator=indicator, source_label=label) + if companions: + band.update( + count_reference=f"ird_taxable_income_people_by_band__{row_id}", + amount_reference=f"ird_taxable_income_total_by_band__{row_id}", + ) + bands.append(band) + # "Over $X" names its own lower cut, so the open tail is not a shared label. + shared = tuple(row for row in shared if row != rows[-1]) + if shared != IRD_SHARED_ENDPOINT_ROWS[table_id]: + raise ValueError(f"{table_id} shared endpoint rows changed: {shared}") + return { + "id": table_id, + "entity": "person", + "currency": "NZD", + "input_unit": "integer_cent", + "income_column": IRD_PROXY_INCOME_COLUMN, + "universe_column": IRD_PROXY_UNIVERSE_COLUMN, + "source_period": 2024, + "target_period": 2027, + "positive_band_closure": "right", + "nil_band": "present", + "negative_income_policy": "floor_zero", + "first_lower_cut_cents": 0, + "source_locator": locator, + "diagnostics": { + "boundary_window_cents": 100, + "alternative_positive_band_closure": "left", + }, + "bands": bands, + } + + +def _reference_intervals(inventory): + national = _band_labels( + inventory.cells( + "ird_taxable_income_distribution.ty2024.income_bands", + measure="individuals", + ), + IRD_NATIONAL_ROWS, + ) + age = _band_labels( + inventory.cells("ird_taxable_income_distribution.ty2024.age_by_income_band"), + IRD_AGE_ROWS, + ) + tables = [ + _interval_table( + IRD_NATIONAL_TABLE, + IRD_NATIONAL_ROWS, + national, + _national_upper_dollars, + "Income tables 2001-24!BE8:BE244", + companions=True, + ), + _interval_table( + IRD_AGE_TABLE, + IRD_AGE_ROWS, + age, + _age_upper_dollars, + "A6:A188", + companions=False, + ), + ] + tables[0]["description"] = ( + "National TY2024 taxable-income bands, rows 8-244: one nil band, 235 " + "finite bands and the open tail over $1,000,000. Every band names its " + "parked count and amount references." + ) + tables[1]["description"] = ( + "Age-by-income TY2024 taxable-income bands, rows 6-188: one nil band, " + "181 finite bands and the open tail over $180,000. Declared and " + "validated only; no reference companions and no fit rows." + ) + return { + "schema_version": 1, + "country": "nz", + "description": "Generated by tools/generate_nz_target_references.py from the row formulas and checked against the pinned feed's own band labels.", + "disclosure": INTERVAL_DISCLOSURE, + "tables": tables, + } + + def _allocation_operands(inventory, row, recipients): selector = row["ledger_selector"] region = selector["geography_id"] @@ -791,14 +1012,32 @@ def _reference_profiles(inventory, rows): profiles[profile] = {"transformations": transformations} return { "schema_version": 1, + "factor_context_resource": "reference_aging.json", + "interval_resource": "reference_intervals.json", "dimensions": { "census_residual_allocation": {"default": "mar", "profiles": profiles}, "aging_index": { "default": "central", "profiles": { - "central": {"transformations": [], "deferred": True}, - "low": {"available": False, "transformations": []}, - "high": {"available": False, "transformations": []}, + profile: { + "transformations": [ + { + "operation": "scale_money_and_intervals", + "factor_id": f"ird_ty2024_to_ty2027_{profile}", + "reference_names": [ + f"ird_taxable_income_total_by_band__{row_id}" + for row_id in IRD_NATIONAL_ROWS + ], + "count_reference_names": [ + f"ird_taxable_income_people_by_band__{row_id}" + for row_id in IRD_NATIONAL_ROWS + ], + "interval_table_ids": [IRD_NATIONAL_TABLE], + "required_measurement_binding": IRD_MEASUREMENT_BINDING, + } + ] + } + for profile in ("low", "central", "high") }, }, }, @@ -1017,7 +1256,7 @@ def _receipts(inventory, erp, documents, feed_pin, ta_facts): ), ( "qes_prefu_and_b5_ageing", - "G8-r2; QES/PREFU rows are present in the pinned artifact but unused by this residual-allocation task.", + "QES/PREFU feed rows support the declared factor calculations in reference_aging.json; they never become direct calibration references or carried target values.", ), ( "wff_tripwires",