{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://retirementwealth.com.au/api/v2/schema/registry.json",
  "title": "Registry of selected Australian retirement rules and rates",
  "description": "The shape of registry/figures.json, version 2. scripts/validate-registry.mjs checks the registry against this schema and then runs further checks that a schema cannot express: dates, history order, ids, derivations and text copied from notes. See docs/registry-schema.md.",
  "type": "object",
  "required": [
    "registry_version",
    "last_reviewed",
    "figures"
  ],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string"
    },
    "registry_version": {
      "const": 2
    },
    "last_reviewed": {
      "$ref": "#/$defs/date"
    },
    "figures": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/figure"
      }
    }
  },
  "$defs": {
    "date": {
      "description": "A calendar date, YYYY-MM-DD.",
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
    },
    "id": {
      "description": "A permanent identifier in snake_case. Never reused or changed once published; a figure that must be renamed keeps its old id in former_ids.",
      "type": "string",
      "pattern": "^[a-z0-9]+(_[a-z0-9]+)*$"
    },
    "text": {
      "type": "string",
      "minLength": 1
    },
    "https_url": {
      "type": "string",
      "pattern": "^https://"
    },
    "unit": {
      "description": "A unit code from registry/units.json.",
      "enum": [
        "AUD",
        "AUD_per_year",
        "AUD_per_fortnight",
        "AUD_per_day",
        "AUD_per_fortnight_per_1000",
        "cents_per_dollar",
        "percent",
        "percent_per_year",
        "percent_of_balance",
        "years",
        "business_days",
        "days",
        "hours",
        "income_year"
      ]
    },
    "resets": {
      "description": "When the official figure is reviewed: day and month pairs, legislated (changes only when the law changes) or ad_hoc (no fixed date).",
      "enum": [
        "1_july",
        "20_september",
        "20_march_20_september",
        "20_march_1_july_20_september",
        "1_january_1_april_1_july_1_october",
        "legislated",
        "ad_hoc"
      ]
    },
    "source": {
      "type": "object",
      "required": [
        "title",
        "publisher",
        "url",
        "type"
      ],
      "additionalProperties": false,
      "properties": {
        "title": {
          "$ref": "#/$defs/text"
        },
        "publisher": {
          "$ref": "#/$defs/text"
        },
        "url": {
          "$ref": "#/$defs/https_url"
        },
        "type": {
          "enum": [
            "primary",
            "secondary"
          ]
        }
      }
    },
    "verification": {
      "type": "object",
      "required": [
        "status",
        "checked_on"
      ],
      "additionalProperties": false,
      "properties": {
        "status": {
          "enum": [
            "verified_primary",
            "secondary_only",
            "needs_verification"
          ]
        },
        "checked_on": {
          "$ref": "#/$defs/date"
        }
      }
    },
    "past_value": {
      "description": "An earlier value. Entries are newest first; an entry whose start date the source does not state has effective_from null and goes last.",
      "type": "object",
      "required": [
        "value",
        "effective_from",
        "source",
        "verification"
      ],
      "additionalProperties": false,
      "properties": {
        "value": {
          "type": "number"
        },
        "effective_from": {
          "anyOf": [
            {
              "$ref": "#/$defs/date"
            },
            {
              "type": "null"
            }
          ]
        },
        "source": {
          "$ref": "#/$defs/source"
        },
        "verification": {
          "$ref": "#/$defs/verification"
        },
        "notes": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "future_value": {
      "description": "A value that is already law but has not started. Entries are soonest first.",
      "type": "object",
      "required": [
        "value",
        "effective_from",
        "source",
        "verification"
      ],
      "additionalProperties": false,
      "properties": {
        "value": {
          "type": "number"
        },
        "effective_from": {
          "$ref": "#/$defs/date"
        },
        "source": {
          "$ref": "#/$defs/source"
        },
        "verification": {
          "$ref": "#/$defs/verification"
        },
        "notes": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "dimensions": {
      "description": "What tells this figure apart from the other figures with the same concept.",
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "household": {
          "description": "single; couple; couple_illness_separated (a couple living apart because of illness); couple_separated (a couple living apart because of illness, respite care or prison, as the Commonwealth Seniors Health Card income test describes it).",
          "enum": [
            "single",
            "couple",
            "couple_illness_separated",
            "couple_separated"
          ]
        },
        "basis": {
          "description": "For couples: combined (both partners together) or each (for each partner).",
          "enum": [
            "combined",
            "each"
          ]
        },
        "homeowner": {
          "type": "boolean"
        },
        "age_band": {
          "description": "Ages in whole years, inclusive. null means no lower or no upper limit.",
          "type": "object",
          "required": [
            "min",
            "max"
          ],
          "additionalProperties": false,
          "properties": {
            "min": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "null"
                }
              ]
            },
            "max": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "null"
                }
              ]
            }
          }
        },
        "tier": {
          "description": "The position of a threshold in a numbered series: 1 for the first threshold.",
          "type": "integer",
          "minimum": 1
        },
        "cohort": {
          "description": "The group of people a figure applies to, as the source names it.",
          "enum": [
            "fee_arrangements_2014_07_01",
            "fee_arrangements_2025_11_01",
            "first_entered_residential_care_from_2025_11_01"
          ]
        },
        "service_category": {
          "enum": [
            "clinical",
            "independence",
            "everyday_living"
          ]
        },
        "means_status": {
          "enum": [
            "full_pensioner",
            "not_eligible_for_pension_or_cshc"
          ]
        },
        "bring_forward_period_years": {
          "enum": [
            2,
            3
          ]
        },
        "balance_threshold": {
          "description": "Division 296: large or very large super balance threshold.",
          "enum": [
            "large",
            "very_large"
          ]
        },
        "deeming_band": {
          "enum": [
            "lower",
            "upper"
          ]
        }
      }
    },
    "applies_by": {
      "description": "Which value applies, when that is not simply the value in force on the date in question. text is copied from the figure's notes.",
      "type": "object",
      "required": [
        "selector",
        "text"
      ],
      "additionalProperties": false,
      "properties": {
        "selector": {
          "description": "date_of_birth: the value depends on the person's date of birth. financial_year: the value for the financial year, measured as the text says. event_date: the value in force on the date of an event, such as when a contribution is made. fixed_at_event: the value in force at an event stays fixed for that person.",
          "enum": [
            "date_of_birth",
            "financial_year",
            "event_date",
            "fixed_at_event"
          ]
        },
        "text": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "statement": {
      "description": "A sentence copied word for word from the figure's notes.",
      "type": "object",
      "required": [
        "text"
      ],
      "additionalProperties": false,
      "properties": {
        "text": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "exception": {
      "description": "A case where the figure differs or does not apply, copied word for word from the figure's notes. modelled is false while no rule module in this registry covers the case.",
      "type": "object",
      "required": [
        "text",
        "modelled"
      ],
      "additionalProperties": false,
      "properties": {
        "text": {
          "$ref": "#/$defs/text"
        },
        "modelled": {
          "type": "boolean"
        }
      }
    },
    "caveat": {
      "description": "Something to know about the source of the figure, copied word for word from its notes.",
      "type": "object",
      "required": [
        "type",
        "text"
      ],
      "additionalProperties": false,
      "properties": {
        "type": {
          "description": "source_conflict: two official sources differ. indicative_source: the source describes its own figures as indicative. use_not_stated: the source does not say how the figure is used. source_scope: the figure is stated on a related page. derived_effective_date: the effective date is worked out, not stated.",
          "enum": [
            "source_conflict",
            "indicative_source",
            "use_not_stated",
            "source_scope",
            "derived_effective_date"
          ]
        },
        "text": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "legal_basis": {
      "description": "A law the figure's notes or source cite. role: sets_value (the provision that sets the figure), indexation (the provision that indexes it) or effective_date (the law the effective date comes from).",
      "type": "object",
      "required": [
        "instrument",
        "role"
      ],
      "additionalProperties": false,
      "properties": {
        "instrument": {
          "$ref": "#/$defs/text"
        },
        "provision": {
          "$ref": "#/$defs/text"
        },
        "role": {
          "enum": [
            "sets_value",
            "indexation",
            "effective_date"
          ]
        }
      }
    },
    "logic": {
      "description": "A JSON Logic expression. Supported operations: var, +, -, *, /, min, max, round_up_to, round_down_to. round_up_to and round_down_to take [number, multiple].",
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "object"
        }
      ]
    },
    "derivation": {
      "description": "How the value can be worked out from other figures. Each variable takes the input figure's value in its own unit (for example 12 for 12%). The validator checks the published value against this calculation. basis: stated (the figure's notes, or the source given here, describe the calculation), implied (it follows from the inputs' own rules, with no rounding) or observed (it matches the published values; the calculation is not taken from an official source).",
      "type": "object",
      "required": [
        "expression",
        "inputs",
        "basis",
        "description"
      ],
      "additionalProperties": false,
      "properties": {
        "expression": {
          "$ref": "#/$defs/logic"
        },
        "inputs": {
          "type": "object",
          "minProperties": 1,
          "additionalProperties": {
            "$ref": "#/$defs/id"
          }
        },
        "basis": {
          "enum": [
            "stated",
            "implied",
            "observed"
          ]
        },
        "valid_from": {
          "$ref": "#/$defs/date"
        },
        "description": {
          "$ref": "#/$defs/text"
        },
        "source": {
          "$ref": "#/$defs/source"
        }
      }
    },
    "figure": {
      "type": "object",
      "required": [
        "id",
        "label",
        "category",
        "value",
        "unit",
        "effective_from",
        "effective_from_basis",
        "resets",
        "concept",
        "kind",
        "source",
        "verification",
        "notes"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/id"
        },
        "former_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "label": {
          "$ref": "#/$defs/text"
        },
        "category": {
          "$ref": "#/$defs/id"
        },
        "value": {
          "type": "number"
        },
        "unit": {
          "$ref": "#/$defs/unit"
        },
        "effective_from": {
          "anyOf": [
            {
              "$ref": "#/$defs/date"
            },
            {
              "type": "null"
            }
          ]
        },
        "effective_from_basis": {
          "description": "What effective_from means. value: the day this value started to apply. provision: the day the current provision started; the number itself is older. as_at: a day on which the source confirms the value applied; the day it started is not recorded.",
          "enum": [
            "value",
            "provision",
            "as_at"
          ]
        },
        "resets": {
          "$ref": "#/$defs/resets"
        },
        "concept": {
          "description": "One key for every variant of the same quantity, for example age_pension.assets_test.cut_off. Variants differ in dimensions.",
          "type": "string",
          "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)+$"
        },
        "kind": {
          "description": "amount: a payment, fee or contribution amount. threshold: a level at which a rule changes. cap: an upper limit. rate: a percentage applied to an amount. taper: a reduction for each unit above a threshold. factor: a share of an account balance. age: an age in years. duration: a length of time. increment: a rounding or indexation step. year: an income year.",
          "enum": [
            "amount",
            "threshold",
            "cap",
            "rate",
            "taper",
            "factor",
            "age",
            "duration",
            "increment",
            "year"
          ]
        },
        "dimensions": {
          "$ref": "#/$defs/dimensions"
        },
        "applies_by": {
          "$ref": "#/$defs/applies_by"
        },
        "source": {
          "$ref": "#/$defs/source"
        },
        "verification": {
          "$ref": "#/$defs/verification"
        },
        "notes": {
          "anyOf": [
            {
              "$ref": "#/$defs/text"
            },
            {
              "type": "null"
            }
          ]
        },
        "conditions": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/statement"
          }
        },
        "exceptions": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/exception"
          }
        },
        "caveats": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/caveat"
          }
        },
        "legal_basis": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/legal_basis"
          }
        },
        "derivation": {
          "$ref": "#/$defs/derivation"
        },
        "history": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/past_value"
          }
        },
        "upcoming": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/future_value"
          }
        }
      }
    }
  }
}
