{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://law.arxo.io/schema/population-impact-report.schema.json",
  "title": "Arxo population impact report 0.1",
  "description": "Report of the population-impact comparison. reportCore is deterministic and reproducible: reportCoreHash is the sha256 of its canonical bytes; receipt is the execution receipt outside the core and the hash. Row classes are a mechanical projection of closed evaluation documents by a fixed classification matrix, not a legal answer; changing the matrix requires a new schemaVersion.",
  "$comment": "DECISION-0255 §§4–9; SPEC §208; SPEC §5",
  "type": "object",
  "additionalProperties": false,
  "required": ["reportCore", "reportCoreHash", "receipt"],
  "properties": {
    "reportCore": {
      "$ref": "#/$defs/ReportCore"
    },
    "reportCoreHash": {
      "$ref": "#/$defs/Digest"
    },
    "receipt": {
      "$ref": "#/$defs/Receipt"
    }
  },
  "$defs": {
    "Digest": {
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$"
    },
    "Decimal": {
      "description": "Canonical decimal string, not less than zero.",
      "$comment": "SPEC §208",
      "type": "string",
      "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]*[1-9])?$"
    },
    "SignedDecimal": {
      "description": "Canonical signed decimal string, without negative zero.",
      "$comment": "SPEC §208",
      "type": "string",
      "pattern": "^(0|-?(0\\.[0-9]*[1-9]|[1-9][0-9]*(\\.[0-9]*[1-9])?))$"
    },
    "Tally": {
      "type": "object",
      "additionalProperties": false,
      "required": ["rows", "weight"],
      "properties": {
        "rows": {
          "type": "integer",
          "minimum": 0
        },
        "weight": {
          "$ref": "#/$defs/Decimal"
        }
      }
    },
    "RowClass": {
      "enum": [
        "VALUE_ON_BOTH",
        "VALUE_BEFORE_ONLY",
        "VALUE_AFTER_ONLY",
        "NO_COMPARABLE_VALUE",
        "EXCLUDED",
        "FAILED"
      ]
    },
    "ReportCore": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "schemaVersion",
        "bank",
        "worlds",
        "measure",
        "rows",
        "coverage",
        "verdict",
        "semdiff"
      ],
      "properties": {
        "schemaVersion": {
          "enum": ["law.population.impact-report/0.1", "law.population.impact-report/0.2"]
        },
        "proofMode": {
          "const": "omitted",
          "description": "Proof-omitted mode (`--no-proof`): the documents' proof graph was neither encoded nor hashed; sides have no documentRef, the record address is outcomeHash. The field is absent in full mode.",
          "$comment": "DECISION-0275"
        },
        "bank": {
          "$ref": "#/$defs/BankSection"
        },
        "worlds": {
          "type": "object",
          "additionalProperties": false,
          "required": ["before", "after"],
          "properties": {
            "before": {
              "$ref": "population-bank.schema.json#/$defs/WorldPin"
            },
            "after": {
              "$ref": "population-bank.schema.json#/$defs/WorldPin"
            }
          }
        },
        "measure": {
          "$ref": "population-bank.schema.json#/$defs/Measure"
        },
        "rows": {
          "description": "Rows in the byte order of rowId of the canonical form.",
          "$comment": "SPEC §208",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Row"
          }
        },
        "coverage": {
          "$ref": "#/$defs/Coverage"
        },
        "aggregate": {
          "$ref": "#/$defs/Aggregate"
        },
        "verdict": {
          "$ref": "#/$defs/Verdict"
        },
        "semdiff": {
          "$ref": "#/$defs/SemanticDiff"
        },
        "distribution": {
          "$ref": "#/$defs/Distribution"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "schemaVersion": {
                "const": "law.population.impact-report/0.2"
              }
            }
          },
          "then": {
            "required": ["distribution"]
          },
          "else": {
            "not": {
              "required": ["distribution"]
            }
          }
        }
      ],
      "if": {
        "properties": {
          "verdict": {
            "properties": {
              "verdict": {
                "const": "INCOMPLETE"
              }
            }
          }
        }
      },
      "then": {
        "not": {
          "required": ["aggregate"]
        }
      },
      "else": {
        "required": ["aggregate"]
      }
    },
    "BankSection": {
      "type": "object",
      "additionalProperties": false,
      "required": ["bankId", "contentHash", "rows", "weightTotal", "legalTime"],
      "properties": {
        "bankId": {
          "$ref": "population-bank.schema.json#/$defs/Name"
        },
        "contentHash": {
          "$ref": "#/$defs/Digest"
        },
        "rows": {
          "type": "integer",
          "minimum": 1
        },
        "weightTotal": {
          "$ref": "#/$defs/Decimal"
        },
        "legalTime": {
          "description": "Distribution of legalTime across row cases: minimum, maximum, and the number of distinct values; null means no row supplied a date.",
          "$comment": "DECISION-0255 §2",
          "type": "object",
          "additionalProperties": false,
          "required": ["min", "max", "distinct"],
          "properties": {
            "min": {
              "type": ["string", "null"]
            },
            "max": {
              "type": ["string", "null"]
            },
            "distinct": {
              "type": "integer",
              "minimum": 0
            }
          }
        }
      }
    },
    "Row": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "rowId",
        "case",
        "weight",
        "legalTime",
        "class",
        "parameterGap",
        "before",
        "after"
      ],
      "properties": {
        "rowId": {
          "$ref": "population-bank.schema.json#/$defs/Name"
        },
        "case": {
          "$ref": "population-bank.schema.json#/$defs/CaseName"
        },
        "weight": {
          "$ref": "#/$defs/Decimal"
        },
        "legalTime": {
          "type": ["string", "null"]
        },
        "class": {
          "$ref": "#/$defs/RowClass"
        },
        "excludedKey": {
          "description": "Key of the EXCLUDED class: side and state (when both sides are excluded — before).",
          "type": "string",
          "pattern": "^(before|after):.+$"
        },
        "parameterGap": {
          "description": "MISSING_PARAMETER_VALUE in the document of at least one side.",
          "$comment": "SPEC §175; E-0171",
          "type": "boolean"
        },
        "outcomeUnchanged": {
          "description": "The sides' outcomeHash matched; present only when both sides have a document. Not the same as the aggregate unchanged tally.",
          "$comment": "DECISION-0255 §4; SPEC §6",
          "type": "boolean"
        },
        "rank": {
          "description": "State of the auxiliary rank query on the basis side: a separate ask document with its own documentRef, classified by the same row matrix, value is Money in the currency of distribution.rank.",
          "$comment": "DECISION-0265 clarifications; SPEC §5 of DECISION-0255",
          "$ref": "#/$defs/Side"
        },
        "size": {
          "type": "object",
          "additionalProperties": false,
          "required": ["adults", "children"],
          "properties": {
            "adults": {
              "$ref": "#/$defs/SizeSide"
            },
            "children": {
              "$ref": "#/$defs/SizeSide"
            }
          }
        },
        "group": {
          "description": "Group number from 1 to the group count for a rankable row; absent for an unrankable one.",
          "$comment": "DECISION-0265 §3",
          "type": "integer",
          "minimum": 1
        },
        "unrankedReason": {
          "enum": ["RANK_NO_VALUE", "RANK_SIZE", "RANK_FAILED"]
        },
        "delta": {
          "description": "after − before; present only for VALUE_ON_BOTH.",
          "$ref": "#/$defs/SignedDecimal"
        },
        "before": {
          "$ref": "#/$defs/Side"
        },
        "after": {
          "$ref": "#/$defs/Side"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "class": {
                "const": "EXCLUDED"
              }
            }
          },
          "then": {
            "required": ["excludedKey"]
          },
          "else": {
            "not": {
              "required": ["excludedKey"]
            }
          }
        },
        {
          "if": {
            "properties": {
              "class": {
                "const": "VALUE_ON_BOTH"
              }
            }
          },
          "then": {
            "required": ["delta"]
          },
          "else": {
            "not": {
              "required": ["delta"]
            }
          }
        }
      ]
    },
    "Side": {
      "type": "object",
      "additionalProperties": false,
      "required": ["state"],
      "properties": {
        "state": {
          "enum": ["VALUE", "NO_VALUE", "EXCLUDED", "FAILED"]
        },
        "excluded": {
          "description": "<status>[/<code of the first blocking issue>], COMPUTED/<code>, AMBIGUOUS_MEASURE, MEASURE_KIND (element is not Money), or MEASURE_CURRENCY (different currency).",
          "$comment": "DECISION-0255 §5, H1",
          "type": "string",
          "minLength": 1
        },
        "evaluationStatus": {
          "$ref": "evaluation.schema.json#/$defs/EvaluationStatus"
        },
        "elements": {
          "description": "Number of quantity elements in the result (0 or 1 for a term).",
          "type": "integer",
          "minimum": 0
        },
        "value": {
          "description": "Sum of the side's Money in the measure's currency.",
          "$ref": "#/$defs/SignedDecimal"
        },
        "blockingIssues": {
          "description": "Codes of the document's error- and fatal-level issues, in document order.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "outcomeHash": {
          "$ref": "#/$defs/Digest"
        },
        "documentRef": {
          "$ref": "#/$defs/Digest"
        },
        "error": {
          "type": "object",
          "additionalProperties": false,
          "required": ["code", "message"],
          "properties": {
            "code": {
              "type": "string"
            },
            "message": {
              "type": "string"
            }
          }
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "state": {
                "const": "FAILED"
              }
            }
          },
          "then": {
            "required": ["error"],
            "not": {
              "anyOf": [
                {
                  "required": ["documentRef"]
                },
                {
                  "required": ["outcomeHash"]
                },
                {
                  "required": ["evaluationStatus"]
                }
              ]
            }
          },
          "else": {
            "required": ["evaluationStatus", "blockingIssues", "outcomeHash"],
            "not": {
              "required": ["error"]
            }
          }
        },
        {
          "if": {
            "properties": {
              "state": {
                "const": "VALUE"
              }
            }
          },
          "then": {
            "required": ["value", "elements"]
          },
          "else": {
            "not": {
              "required": ["value"]
            }
          }
        },
        {
          "if": {
            "properties": {
              "state": {
                "const": "EXCLUDED"
              }
            }
          },
          "then": {
            "required": ["excluded"]
          },
          "else": {
            "not": {
              "required": ["excluded"]
            }
          }
        }
      ]
    },
    "Coverage": {
      "type": "object",
      "additionalProperties": false,
      "required": ["rows", "weight", "classes", "excluded", "parameterGap"],
      "properties": {
        "rows": {
          "type": "integer",
          "minimum": 0
        },
        "weight": {
          "$ref": "#/$defs/Decimal"
        },
        "classes": {
          "$ref": "#/$defs/ClassTallies"
        },
        "excluded": {
          "description": "EXCLUDED rows keyed by excludedKey.",
          "type": "object",
          "propertyNames": {
            "pattern": "^(before|after):.+$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/Tally"
          }
        },
        "parameterGap": {
          "$ref": "#/$defs/Tally"
        }
      }
    },
    "Aggregate": {
      "description": "Sums over VALUE_ON_BOTH rows; winners/losers/unchanged are by the sign of the delta, unchanged being VALUE_ON_BOTH rows with a zero delta (not the same as the row's outcomeUnchanged flag); comparableWeight is the weight of VALUE_ON_BOTH.",
      "$comment": "DECISION-0255 §6",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "comparableWeight",
        "weightedSignedDelta",
        "weightedAbsoluteDelta",
        "winners",
        "losers",
        "unchanged"
      ],
      "properties": {
        "comparableWeight": {
          "$ref": "#/$defs/Decimal"
        },
        "weightedSignedDelta": {
          "$ref": "#/$defs/SignedDecimal"
        },
        "weightedAbsoluteDelta": {
          "$ref": "#/$defs/Decimal"
        },
        "winners": {
          "$ref": "#/$defs/Tally"
        },
        "losers": {
          "$ref": "#/$defs/Tally"
        },
        "unchanged": {
          "$ref": "#/$defs/Tally"
        }
      }
    },
    "Verdict": {
      "description": "Verdict precedence: 4, then 1, then 3, then 0 (refusal 2 is not printed in the report).",
      "$comment": "DECISION-0255 §7",
      "type": "object",
      "additionalProperties": false,
      "required": ["verdict", "exitCode"],
      "properties": {
        "verdict": {
          "enum": ["INCOMPLETE", "CHANGE", "NO_COMPARABLE_WEIGHT", "NO_WEIGHTED_CHANGE"]
        },
        "exitCode": {
          "enum": [4, 1, 3, 0]
        }
      },
      "allOf": [
        {
          "if": {"properties": {"verdict": {"const": "INCOMPLETE"}}},
          "then": {"properties": {"exitCode": {"const": 4}}}
        },
        {
          "if": {"properties": {"verdict": {"const": "CHANGE"}}},
          "then": {"properties": {"exitCode": {"const": 1}}}
        },
        {
          "if": {"properties": {"verdict": {"const": "NO_COMPARABLE_WEIGHT"}}},
          "then": {"properties": {"exitCode": {"const": 3}}}
        },
        {
          "if": {"properties": {"verdict": {"const": "NO_WEIGHTED_CHANGE"}}},
          "then": {"properties": {"exitCode": {"const": 0}}}
        }
      ]
    },
    "SemanticDiff": {
      "description": "Semantic difference of the sides' linked worlds: which rules, norms, and texts were added, removed, changed, or renamed.",
      "$comment": "SPEC §215",
      "type": "object",
      "additionalProperties": false,
      "required": ["entries", "semanticChange"],
      "properties": {
        "entries": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["kind", "id"],
            "properties": {
              "kind": {
                "enum": ["added", "removed", "changed", "renamed", "presentation"]
              },
              "id": {
                "type": "string",
                "minLength": 1
              },
              "newId": {
                "type": "string",
                "minLength": 1
              },
              "hashBefore": {
                "$ref": "#/$defs/Digest"
              },
              "hashAfter": {
                "$ref": "#/$defs/Digest"
              }
            }
          }
        },
        "semanticChange": {
          "type": "boolean"
        }
      }
    },
    "Receipt": {
      "description": "Execution receipt: outside the core and the hash, mandatory. Completion order, chunk size, and worker count do not affect the core.",
      "$comment": "DECISION-0255 §8",
      "type": "object",
      "additionalProperties": false,
      "required": ["implementation", "build", "schemaVersions", "workers", "startedAt", "finishedAt"],
      "properties": {
        "implementation": {
          "enum": ["lawref", "lawc"]
        },
        "build": {
          "type": "string",
          "minLength": 1
        },
        "schemaVersions": {
          "type": "object",
          "additionalProperties": false,
          "required": ["bank", "report"],
          "properties": {
            "bank": {
              "enum": ["law.population.bank/0.1", "law.population.bank/0.2"]
            },
            "report": {
              "enum": ["law.population.impact-report/0.1", "law.population.impact-report/0.2"]
            }
          }
        },
        "workers": {
          "type": "integer",
          "minimum": 1
        },
        "storeMode": {
          "description": "Run storage mode: full means documents are recorded by documentRef; none means only addresses are kept, and the document is reproduced by replaying the population from the bank's pins. Outside the core and the hash.",
          "$comment": "DECISION-0271",
          "enum": ["full", "none"]
        },
        "startedAt": {
          "$ref": "evaluation.schema.json#/$defs/Instant"
        },
        "failures": {
          "description": "Details of FAILED sides, outside the core: compiler diagnostic text, system error, or crash. In reportCore, a FAILED side carries only code and a fixed message keyed by the code. The field is present only when a FAILED occurred.",
          "$comment": "DECISION-0255 §5",
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["case", "side", "code", "detail"],
            "properties": {
              "case": {
                "$ref": "population-bank.schema.json#/$defs/CaseName"
              },
              "side": {
                "enum": ["before", "after"]
              },
              "code": {
                "type": "string",
                "minLength": 1
              },
              "detail": {
                "type": "string"
              }
            }
          }
        },
        "finishedAt": {
          "$ref": "evaluation.schema.json#/$defs/Instant"
        }
      }
    },
    "ClassTallies": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "VALUE_ON_BOTH",
        "VALUE_BEFORE_ONLY",
        "VALUE_AFTER_ONLY",
        "NO_COMPARABLE_VALUE",
        "EXCLUDED",
        "FAILED"
      ],
      "properties": {
        "VALUE_ON_BOTH": {
          "$ref": "#/$defs/Tally"
        },
        "VALUE_BEFORE_ONLY": {
          "$ref": "#/$defs/Tally"
        },
        "VALUE_AFTER_ONLY": {
          "$ref": "#/$defs/Tally"
        },
        "NO_COMPARABLE_VALUE": {
          "$ref": "#/$defs/Tally"
        },
        "EXCLUDED": {
          "$ref": "#/$defs/Tally"
        },
        "FAILED": {
          "$ref": "#/$defs/Tally"
        }
      }
    },
    "AuxState": {
      "enum": ["VALUE", "NO_VALUE", "EXCLUDED", "FAILED"]
    },
    "SizeSide": {
      "description": "State of the size.adults / size.children query: the same document projection as Side, but value is an integer of zero or more.",
      "$comment": "DECISION-0265 clarifications",
      "type": "object",
      "additionalProperties": false,
      "required": ["state"],
      "properties": {
        "state": {
          "$ref": "#/$defs/AuxState"
        },
        "evaluationStatus": {
          "$ref": "evaluation.schema.json#/$defs/EvaluationStatus"
        },
        "blockingIssues": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "outcomeHash": {
          "$ref": "#/$defs/Digest"
        },
        "excluded": {
          "type": "string",
          "minLength": 1
        },
        "value": {
          "type": "integer",
          "minimum": 0
        },
        "documentRef": {
          "$ref": "#/$defs/Digest"
        },
        "error": {
          "type": "object",
          "additionalProperties": false,
          "required": ["code", "message"],
          "properties": {
            "code": {
              "type": "string"
            },
            "message": {
              "type": "string"
            }
          }
        }
      }
    },
    "Rational": {
      "description": "Exact rational pair of reduced integers: denominator above zero.",
      "$comment": "DECISION-0265 §4",
      "type": "object",
      "additionalProperties": false,
      "required": ["numerator", "denominator"],
      "properties": {
        "numerator": {
          "type": "string",
          "pattern": "^-?(0|[1-9][0-9]*)$"
        },
        "denominator": {
          "type": "string",
          "pattern": "^[1-9][0-9]*$"
        }
      }
    },
    "Group": {
      "description": "Distribution group: aggregate sums run over the group's VALUE_ON_BOTH rows; no sums when INCOMPLETE.",
      "$comment": "DECISION-0265 §§3–4; DECISION-0255 §6",
      "type": "object",
      "additionalProperties": false,
      "required": ["index", "rows", "weight", "bounds", "classes"],
      "properties": {
        "index": {
          "type": "integer",
          "minimum": 1
        },
        "rows": {
          "type": "integer",
          "minimum": 0
        },
        "weight": {
          "$ref": "#/$defs/Decimal"
        },
        "bounds": {
          "type": "object",
          "additionalProperties": false,
          "required": ["min", "max"],
          "properties": {
            "min": {
              "oneOf": [
                {
                  "$ref": "#/$defs/Rational"
                },
                {
                  "type": "null"
                }
              ]
            },
            "max": {
              "oneOf": [
                {
                  "$ref": "#/$defs/Rational"
                },
                {
                  "type": "null"
                }
              ]
            }
          }
        },
        "classes": {
          "$ref": "#/$defs/ClassTallies"
        },
        "comparableWeight": {
          "$ref": "#/$defs/Decimal"
        },
        "weightedSignedDelta": {
          "$ref": "#/$defs/SignedDecimal"
        },
        "weightedAbsoluteDelta": {
          "$ref": "#/$defs/Decimal"
        },
        "winners": {
          "$ref": "#/$defs/Tally"
        },
        "losers": {
          "$ref": "#/$defs/Tally"
        },
        "unchanged": {
          "$ref": "#/$defs/Tally"
        },
        "meanSignedDelta": {
          "oneOf": [
            {
              "$ref": "#/$defs/Rational"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "Distribution": {
      "description": "Income-group distribution section of form 0.2 of the impact comparison.",
      "$comment": "DECISION-0265 §4",
      "type": "object",
      "additionalProperties": false,
      "required": ["basis", "groups", "scale", "rankMeasure", "byGroup", "unranked", "ties"],
      "properties": {
        "basis": {
          "const": "before"
        },
        "groups": {
          "type": "integer",
          "minimum": 2
        },
        "scale": {
          "$ref": "population-bank.schema.json#/$defs/ScalePin"
        },
        "rankMeasure": {
          "type": "object",
          "additionalProperties": false,
          "required": ["name", "stage", "currency"],
          "properties": {
            "name": {
              "type": "string"
            },
            "stage": {
              "type": "string"
            },
            "currency": {
              "type": "string"
            }
          }
        },
        "byGroup": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Group"
          }
        },
        "unranked": {
          "type": "object",
          "additionalProperties": false,
          "required": ["rows", "weight", "reasons"],
          "properties": {
            "rows": {
              "type": "integer",
              "minimum": 0
            },
            "weight": {
              "$ref": "#/$defs/Decimal"
            },
            "reasons": {
              "type": "object",
              "additionalProperties": false,
              "required": ["RANK_NO_VALUE", "RANK_SIZE", "RANK_FAILED"],
              "properties": {
                "RANK_NO_VALUE": {
                  "$ref": "#/$defs/Tally"
                },
                "RANK_SIZE": {
                  "$ref": "#/$defs/Tally"
                },
                "RANK_FAILED": {
                  "$ref": "#/$defs/Tally"
                }
              }
            }
          }
        },
        "ties": {
          "type": "object",
          "additionalProperties": false,
          "required": ["blocks", "rows", "weight"],
          "properties": {
            "blocks": {
              "type": "integer",
              "minimum": 0
            },
            "rows": {
              "type": "integer",
              "minimum": 0
            },
            "weight": {
              "$ref": "#/$defs/Decimal"
            }
          }
        }
      }
    }
  }
}
