{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://law.arxo.io/schema/decision-table.schema.json",
  "title": "Arxo A3 decision table 0.1",
  "description": "Canonical typed decision table: the authoring surface of the A3 format.",
  "$comment": "SPEC §A3.3",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "schemaVersion": {
      "const": "law.decision/0.1"
    },
    "kind": {
      "const": "decision-table"
    },
    "id": {
      "$ref": "#/$defs/StableId"
    },
    "mode": {
      "enum": [
        "function",
        "rules"
      ]
    },
    "hitPolicy": {
      "enum": [
        "unique",
        "first",
        "any",
        "priority",
        "collect",
        "multiple"
      ]
    },
    "priorityOrder": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/LiteralTerm"
      }
    },
    "total": {
      "type": "boolean"
    },
    "aggregation": {
      "enum": [
        "list",
        "count",
        "sum",
        "min",
        "max"
      ]
    },
    "inputs": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/InputColumn"
      }
    },
    "outputs": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/OutputColumn"
      }
    },
    "emit": {
      "$ref": "#/$defs/EmitTarget"
    },
    "rows": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/DecisionRow"
      }
    },
    "labels": {
      "type": "array",
      "description": "Labels of the table ITSELF — they propagate to its symbol_decl. They do not spill onto rows: a row has its own carrier, because the table generates a node for each row, and the table label would give them all the same name.",
      "$comment": "SPEC §218; E-0056",
      "items": {
        "$ref": "#/$defs/Label"
      }
    },
    "metadata": {
      "type": "object",
      "additionalProperties": true
    }
  },
  "required": [
    "schemaVersion",
    "kind",
    "id",
    "mode",
    "hitPolicy",
    "total",
    "inputs",
    "outputs",
    "rows"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "hitPolicy": {
            "const": "priority"
          }
        },
        "required": [
          "hitPolicy"
        ]
      },
      "then": {
        "required": [
          "priorityOrder"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "mode": {
            "const": "rules"
          }
        },
        "required": [
          "mode"
        ]
      },
      "then": {
        "required": [
          "emit"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "hitPolicy": {
            "const": "collect"
          }
        },
        "required": [
          "hitPolicy"
        ]
      },
      "then": {
        "required": [
          "aggregation"
        ]
      }
    }
  ],
  "$defs": {
    "StableId": {
      "type": "string",
      "minLength": 1
    },
    "LocalId": {
      "type": "string",
      "pattern": "^[A-Za-z_][A-Za-z0-9_.-]*$"
    },
    "TypeRef": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "$ref": "#/$defs/StableId"
        },
        "args": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/TypeRef"
          }
        },
        "optional": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "name"
      ]
    },
    "JsonValue": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "boolean"
        },
        {
          "type": "integer"
        },
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "$ref": "#/$defs/JsonValue"
          }
        },
        {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/JsonValue"
          }
        }
      ]
    },
    "LiteralTerm": {
      "additionalProperties": false,
      "properties": {
        "currency": {
          "type": "string"
        },
        "display": {
          "type": "string"
        },
        "kind": {
          "const": "value"
        },
        "type": {
          "$ref": "#/$defs/TypeRef"
        },
        "unit": {
          "type": "string"
        },
        "value": {
          "$ref": "#/$defs/JsonValue"
        },
        "factors": {
          "description": "The multiset of base units of a quantity. `unit` is not parsed on read but is CHECKED against its rendering: the \"base _per_ base\" grammar is ambiguous.",
          "$comment": "SPEC §49.1; E-0136",
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "unit": {
                "type": "string"
              },
              "exp": {
                "type": "integer"
              }
            },
            "required": [
              "unit",
              "exp"
            ]
          }
        },
        "lower": {
          "description": "Lower bound of the interval, an irreducible fraction `<n>/<d>`.",
          "$comment": "SPEC §259.2; E-0136",
          "type": "string",
          "pattern": "^-?[0-9]+/[1-9][0-9]*$"
        },
        "upper": {
          "description": "Upper bound of the interval.",
          "$comment": "SPEC §259.2; E-0136",
          "type": "string",
          "pattern": "^-?[0-9]+/[1-9][0-9]*$"
        },
        "derivation": {
          "description": "The derivation canon. The bounds are RECOMPUTED from it, not checked field by field.",
          "$comment": "SPEC §259.2; E-0136",
          "type": "string",
          "minLength": 1
        },
        "poly": {
          "description": "Algebraic minimal-polynomial coefficients in ascending order, as ASCII-int decimal strings; verified through the canon funnel on read, never trusted.",
          "$comment": "DECISION-0437; 0.4.1",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[+-]?[0-9]+$"
          }
        },
        "root": {
          "description": "1-based index of the real root in ascending order; range-checked against the Sturm count on read.",
          "$comment": "DECISION-0437; 0.4.1",
          "type": "integer",
          "minimum": 1
        },
        "expr": {
          "description": "Real-expression canon string; verified through the constructor funnel on read, never trusted.",
          "$comment": "DECISION-0438; 0.5; SPEC §1",
          "type": "string",
          "minLength": 1
        }
      },
      "required": [
        "kind",
        "type"
      ],
      "type": "object"
    },
    "Operand": {
      "oneOf": [
        {
          "$ref": "#/$defs/LiteralTerm"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "parameter"
            },
            "id": {
              "$ref": "#/$defs/StableId"
            }
          },
          "required": [
            "kind",
            "id"
          ]
        }
      ]
    },
    "Condition": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "any"
            }
          },
          "required": [
            "kind"
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "comparison"
            },
            "op": {
              "enum": [
                "eq",
                "neq",
                "lt",
                "lte",
                "gt",
                "gte"
              ]
            },
            "value": {
              "$ref": "#/$defs/Operand"
            }
          },
          "required": [
            "kind",
            "op",
            "value"
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "interval"
            },
            "lower": {
              "$ref": "#/$defs/Operand"
            },
            "upper": {
              "$ref": "#/$defs/Operand"
            },
            "lowerInclusive": {
              "type": "boolean"
            },
            "upperInclusive": {
              "type": "boolean"
            }
          },
          "required": [
            "kind",
            "lowerInclusive",
            "upperInclusive"
          ],
          "anyOf": [
            {
              "required": [
                "lower"
              ]
            },
            {
              "required": [
                "upper"
              ]
            }
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "in"
            },
            "values": {
              "type": "array",
              "minItems": 1,
              "uniqueItems": true,
              "items": {
                "$ref": "#/$defs/LiteralTerm"
              }
            }
          },
          "required": [
            "kind",
            "values"
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "and"
            },
            "items": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/Condition"
              }
            }
          },
          "required": [
            "kind",
            "items"
          ]
        }
      ]
    },
    "InputColumn": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/LocalId"
        },
        "name": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "$ref": "#/$defs/TypeRef"
        }
      },
      "required": [
        "id",
        "type"
      ]
    },
    "OutputColumn": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/LocalId"
        },
        "name": {
          "type": "string",
          "minLength": 1
        },
        "type": {
          "$ref": "#/$defs/TypeRef"
        }
      },
      "required": [
        "id",
        "type"
      ]
    },
    "InputCell": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "input": {
          "$ref": "#/$defs/LocalId"
        },
        "condition": {
          "$ref": "#/$defs/Condition"
        }
      },
      "required": [
        "input",
        "condition"
      ]
    },
    "OutputCell": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "output": {
          "$ref": "#/$defs/LocalId"
        },
        "value": {
          "$ref": "#/$defs/Operand"
        }
      },
      "required": [
        "output",
        "value"
      ]
    },
    "DecisionRow": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/LocalId"
        },
        "when": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/InputCell"
          }
        },
        "then": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/OutputCell"
          }
        },
        "anchors": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/StableId"
          }
        },
        "labels": {
          "type": "array",
          "description": "Labels of a table row. They propagate to the nodes generated by THIS row (in mode=rules — to its rule, in mode=function — to its helper symbol); the table label does NOT spill onto rows, otherwise every row would be named after the table. Outside the semantic hash, inside the content hash of the node.",
          "$comment": "SPEC §218; E-0056; SPEC §209; E-0010; SPEC §202",
          "items": {
            "$ref": "#/$defs/Label"
          }
        }
      },
      "required": [
        "id",
        "when",
        "then",
        "anchors"
      ]
    },
    "LanguageTag": {
      "type": "string",
      "pattern": "^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$"
    },
    "Label": {
      "type": "object",
      "additionalProperties": false,
      "description": "A copy of the label definition from the intermediate-representation schema: A3 schemas are self-contained and do not borrow even their own primitives (StableId, LiteralTerm, TypeRef).",
      "$comment": "SPEC §218",
      "properties": {
        "grammaticalForms": {
          "type": "object",
          "description": "Morphological forms of the label; the key is a slot from the closed dictionary of its language tag, the value is a non-empty form.",
          "$comment": "SPEC §218.1",
          "propertyNames": {
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "additionalProperties": {
            "type": "string",
            "minLength": 1
          },
          "minProperties": 1
        },
        "language": {
          "$ref": "#/$defs/LanguageTag"
        },
        "status": {
          "enum": [
            "official",
            "unofficial",
            "translation"
          ]
        },
        "text": {
          "type": "string"
        }
      },
      "required": [
        "language",
        "status",
        "text"
      ]
    },
    "EmitArgument": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "input"
            },
            "id": {
              "$ref": "#/$defs/LocalId"
            }
          },
          "required": [
            "kind",
            "id"
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "output"
            },
            "id": {
              "$ref": "#/$defs/LocalId"
            }
          },
          "required": [
            "kind",
            "id"
          ]
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "kind": {
              "const": "literal"
            },
            "value": {
              "$ref": "#/$defs/LiteralTerm"
            }
          },
          "required": [
            "kind",
            "value"
          ]
        }
      ]
    },
    "EmitTarget": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "predicate": {
          "$ref": "#/$defs/StableId"
        },
        "polarity": {
          "enum": [
            "positive",
            "negative"
          ]
        },
        "args": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/EmitArgument"
          }
        }
      },
      "required": [
        "predicate",
        "polarity",
        "args"
      ]
    }
  }
}
