{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://earthsciml.org/schemas/esm-integrator/0.1.0/plan.schema.json",
  "title": "EarthSciIntegrator plan",
  "description": "The planner's output (spec/plan.md). M2 draft: every field is specified there except the checkpoint actions, which arrive at M4. A plan is serialized as RFC 8785 canonical JSON and must be byte-identical between the Julia and Rust planners, so it records nothing implementation-specific.",
  "type": "object",
  "required": ["esm_integrator", "inputs", "time", "operators", "schedule", "step", "derived"],
  "additionalProperties": false,
  "properties": {
    "esm_integrator": {"$ref": "run-config.schema.json#/$defs/semver"},
    "inputs": {
      "type": "object",
      "required": ["model", "run_config"],
      "additionalProperties": false,
      "properties": {
        "model": {"$ref": "#/$defs/fileRef"},
        "run_config": {"$ref": "#/$defs/fileRef"}
      }
    },
    "operators": {
      "type": "object",
      "minProperties": 1,
      "propertyNames": {"$ref": "run-config.schema.json#/$defs/operatorName"},
      "additionalProperties": {"$ref": "#/$defs/operatorPlan"}
    },
    "schedule": {
      "description": "The resolved schedule: the run-config grammar with every 'auto' replaced and every leaf's options made explicit (spec/plan.md §3). Every node has an id.",
      "allOf": [{"$ref": "#/$defs/resolvedNode"}, {"$ref": "#/$defs/noAuto"}]
    },
    "time": {
      "description": "The resolved run interval in seconds (spec/run-config.md §1.1, D60). Which source gave it is not recorded, so the same interval always gives the same plan.",
      "type": "object",
      "required": ["start", "end"],
      "additionalProperties": false,
      "properties": {
        "start": {"type": "number"},
        "end": {"type": "number"}
      }
    },
    "step": {
      "allOf": [{"$ref": "run-config.schema.json#/$defs/step"}, {"$ref": "#/$defs/noAuto"}]
    },
    "partition": {"$ref": "#/$defs/partitionPlan"},
    "precision": {
      "description": "Resolved precision roles per leaf, keyed by node id. Every role is present.",
      "type": "object",
      "propertyNames": {"$ref": "run-config.schema.json#/$defs/nodeKey"},
      "additionalProperties": {
        "allOf": [
          {"$ref": "run-config.schema.json#/$defs/precisionRoles"},
          {"required": ["state", "rhs", "jacobian", "factors", "tables"]}
        ]
      }
    },
    "checkpoint_schedule": {"$ref": "#/$defs/checkpointSchedule"},
    "derived": {
      "description": "Every derived document the plan refers to, by path relative to plan.json.",
      "type": "object",
      "propertyNames": {"type": "string", "pattern": "^derived/[A-Za-z0-9_.-]+\\.esm$"},
      "additionalProperties": {
        "type": "object",
        "required": ["kind", "operator", "sha256"],
        "additionalProperties": false,
        "properties": {
          "kind": {"$ref": "#/$defs/derivedKind"},
          "operator": {"$ref": "run-config.schema.json#/$defs/operatorName"},
          "sha256": {"$ref": "#/$defs/sha256"}
        }
      }
    },
    "validation": {
      "description": "Pass 8's checks (spec/plan.md §8). Only outcomes are recorded, never measured errors, which differ between implementations. rhs_sum is one check over the whole model and names no operator; band_model and non_empty name theirs.",
      "type": "array",
      "items": {
        "type": "object",
        "required": ["check", "passed"],
        "additionalProperties": false,
        "properties": {
          "check": {"enum": ["rhs_sum", "band_model", "non_empty"]},
          "operator": {"$ref": "run-config.schema.json#/$defs/operatorName"},
          "passed": {"const": true}
        },
        "if": {"properties": {"check": {"const": "rhs_sum"}}},
        "then": {"not": {"required": ["operator"]}},
        "else": {"required": ["operator"]}
      }
    }
  },

  "$defs": {
    "resolvedNode": {
      "description": "A run-config schedule node with an id, whose child nodes are resolved nodes too. A leaf's or coupled node's operators are always a list (spec/plan.md §7.1).",
      "$ref": "run-config.schema.json#/$defs/node",
      "required": ["id"],
      "properties": {
        "leaf": {"type": "array"},
        "coupled": {"type": "array"},
        "sequence": {"items": {"$ref": "#/$defs/resolvedNode"}},
        "strang": {"items": {"$ref": "#/$defs/resolvedNode"}},
        "subcycle": {"$ref": "#/$defs/resolvedNode"},
        "every": {"$ref": "#/$defs/resolvedNode"}
      }
    },
    "noAuto": {
      "description": "No value anywhere inside is the string 'auto' (spec/plan.md §3). Operator names and node ids are never 'auto' (spec/run-config.md §2.1), so this cannot misfire on a name.",
      "not": {"const": "auto"},
      "items": {"$ref": "#/$defs/noAuto"},
      "additionalProperties": {"$ref": "#/$defs/noAuto"}
    },
    "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
    "fileRef": {
      "type": "object",
      "required": ["path", "sha256"],
      "additionalProperties": false,
      "properties": {
        "path": {"type": "string", "minLength": 1},
        "sha256": {"$ref": "#/$defs/sha256"}
      }
    },
    "derivedKind": {
      "enum": [
        "rhs", "jacobian", "frozen_jacobian", "vjp", "second_order", "param_jacobian", "forcing_jacobian",
        "residual", "map", "limit", "condition"
      ]
    },
    "operatorPlan": {
      "type": "object",
      "required": ["kind", "locality", "writes", "reads", "terms", "structure", "stiffness"],
      "additionalProperties": false,
      "properties": {
        "kind": {"enum": ["rhs", "algebraic", "map", "limit", "condition"]},
        "locality": {
          "type": "object",
          "required": ["class"],
          "additionalProperties": false,
          "properties": {
            "class": {"$ref": "run-config.schema.json#/$defs/locality"},
            "radius": {
              "description": "Stencil reach per horizontal axis: [cells toward lower index, cells toward higher index]. Present only for class 'stencil', with every horizontal axis (spec/plan.md §5.2.4).",
              "type": "object",
              "minProperties": 1,
              "propertyNames": {"$ref": "run-config.schema.json#/$defs/identifier"},
              "additionalProperties": {
                "type": "array", "items": {"type": "integer", "minimum": 0}, "minItems": 2, "maxItems": 2
              }
            }
          },
          "if": {"properties": {"class": {"const": "stencil"}}},
          "then": {"required": ["radius"]},
          "else": {"not": {"required": ["radius"]}}
        },
        "writes": {"$ref": "#/$defs/sortedNames"},
        "reads": {"$ref": "#/$defs/sortedNames"},
        "terms": {
          "description": "The operator's terms (spec/plan.md §5.1), in flattened-equation order, then contribution, then summand.",
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "object",
            "required": ["variable", "contribution", "summand", "provenance", "locality"],
            "additionalProperties": false,
            "properties": {
              "variable": {"$ref": "run-config.schema.json#/$defs/qualifiedName"},
              "contribution": {"type": "integer", "minimum": 0},
              "summand": {"type": "integer", "minimum": 0},
              "provenance": {
                "description": "A component path, coupling:<k>, or several joined by + (D64).",
                "type": "string",
                "pattern": "^([A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*|coupling:[0-9]+)(\\+([A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*|coupling:[0-9]+))*$"
              },
              "locality": {"$ref": "run-config.schema.json#/$defs/locality"}
            }
          }
        },
        "structure": {"$ref": "#/$defs/structure"},
        "stiffness": {
          "type": "object",
          "required": ["class", "source"],
          "additionalProperties": false,
          "properties": {
            "class": {"enum": ["stiff", "nonstiff"]},
            "source": {"enum": ["reaction_systems", "solver_hint", "gershgorin", "run_config"]}
          }
        }
      },
      "allOf": [
        {
          "if": {"properties": {"locality": {"properties": {"class": {"const": "stencil"}}}}},
          "then": {"properties": {"structure": {"required": ["stencil"]}}},
          "else": {"properties": {"structure": {"not": {"required": ["stencil"]}}}}
        },
        {
          "if": {"properties": {"locality": {"properties": {"class": {"const": "column"}}}}},
          "then": {"properties": {"structure": {"required": ["column"]}}},
          "else": {"properties": {"structure": {"not": {"required": ["column"]}}}}
        },
        {
          "if": {"not": {"properties": {"locality": {"properties": {"class": {"const": "pointwise"}}}}}},
          "then": {"properties": {"structure": {"not": {"required": ["block"]}}}}
        }
      ]
    },
    "structure": {
      "description": "Pass 4 (spec/plan.md §5.4).",
      "type": "object",
      "required": ["class", "linear_in_u", "depends_on_t", "bands"],
      "additionalProperties": false,
      "properties": {
        "class": {"enum": ["block_diagonal", "banded", "general", "empty"]},
        "linear_in_u": {"type": "boolean"},
        "depends_on_t": {"type": "boolean"},
        "bands": {
          "description": "Band positions in field order: band k is J_k of the Jacobian document (EarthSciASTDiff serialize_band without coef).",
          "type": "array",
          "items": {
            "type": "object",
            "required": ["row", "col", "rows", "row_idx", "col_idx"],
            "additionalProperties": false,
            "properties": {
              "row": {"$ref": "run-config.schema.json#/$defs/qualifiedName"},
              "col": {"$ref": "run-config.schema.json#/$defs/qualifiedName"},
              "rows": {
                "type": "array",
                "items": {"type": "array", "items": {"type": "integer"}, "minItems": 2, "maxItems": 2}
              },
              "row_idx": {"type": "array", "items": {"type": ["string", "integer"]}},
              "col_idx": {"type": "array"},
              "contracted": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "array", "minItems": 3, "maxItems": 3,
                  "prefixItems": [{"type": "string"}, {"type": "integer"}, {"type": "integer"}]
                }
              }
            }
          }
        },
        "block": {
          "description": "EarthSciASTDiff's block-diagonal factorization plan, 1-based (esm-jacobian-spec §6, without its type tag).",
          "type": "object",
          "required": ["vars", "ordering", "pattern", "lu_pattern"],
          "additionalProperties": false,
          "properties": {
            "vars": {"type": "array", "items": {"$ref": "run-config.schema.json#/$defs/qualifiedName"}, "minItems": 1},
            "ordering": {"type": "array", "items": {"type": "integer", "minimum": 1}, "minItems": 1},
            "pattern": {"$ref": "#/$defs/pairs"},
            "lu_pattern": {"$ref": "#/$defs/pairs"}
          }
        },
        "column": {
          "type": "object",
          "required": ["axis", "kl", "ku"],
          "additionalProperties": false,
          "properties": {
            "axis": {"$ref": "run-config.schema.json#/$defs/identifier"},
            "kl": {"type": "integer", "minimum": 0},
            "ku": {"type": "integer", "minimum": 0}
          }
        },
        "stencil": {
          "type": "object",
          "required": ["footprint"],
          "additionalProperties": false,
          "properties": {
            "footprint": {
              "description": "Distinct reduced horizontal offset vectors, in axes.horizontal order, sorted lexicographically (spec/plan.md §5.4).",
              "type": "array",
              "minItems": 1,
              "items": {"type": "array", "items": {"type": "integer"}, "minItems": 1, "maxItems": 2}
            }
          }
        }
      }
    },
    "pairs": {
      "type": "array",
      "items": {"type": "array", "items": {"type": "integer", "minimum": 1}, "minItems": 2, "maxItems": 2}
    },
    "sortedNames": {
      "description": "Qualified names, sorted by Unicode code point (spec/plan.md §2).",
      "type": "array",
      "items": {"$ref": "run-config.schema.json#/$defs/qualifiedName"},
      "uniqueItems": true
    },
    "partitionPlan": {
      "type": "object",
      "required": ["ranks", "tiles"],
      "additionalProperties": false,
      "properties": {
        "ranks": {"$ref": "run-config.schema.json#/$defs/grid2"},
        "tiles": {"$ref": "run-config.schema.json#/$defs/grid2"},
        "halo": {
          "description": "Halo width per horizontal axis, the maximum radius over the stencil operators (architecture §7.1).",
          "type": "object",
          "propertyNames": {"$ref": "run-config.schema.json#/$defs/identifier"},
          "additionalProperties": {"type": "integer", "minimum": 0}
        },
        "shards": {
          "description": "Shard count per pointwise or column leaf, by node id (spec/plan.md §7.4).",
          "type": "object",
          "propertyNames": {"$ref": "run-config.schema.json#/$defs/nodeKey"},
          "additionalProperties": {"type": "integer", "minimum": 1}
        },
        "output_shard": {
          "description": "The output stores' shard length per axis; rank block boundaries fall on its multiples (U12).",
          "type": "object",
          "propertyNames": {"$ref": "run-config.schema.json#/$defs/identifier"},
          "additionalProperties": {"type": "integer", "minimum": 1}
        }
      }
    },
    "checkpointSchedule": {
      "description": "D34: computed once by the planner, executed by both runtimes. Until M4 it is the placeholder {kind: all, macro_steps: 0} (D78); the action vocabulary is provisional until then.",
      "type": "object",
      "required": ["kind", "macro_steps"],
      "additionalProperties": false,
      "properties": {
        "kind": {"enum": ["all", "revolve", "multilevel"]},
        "macro_steps": {"type": "integer", "minimum": 0},
        "snapshots": {"type": "integer", "minimum": 1},
        "actions": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["op", "step"],
            "additionalProperties": false,
            "properties": {
              "op": {"enum": ["store", "restore", "advance", "reverse", "free"]},
              "step": {"type": "integer", "minimum": 0},
              "to": {"type": "integer", "minimum": 0},
              "slot": {"type": "integer", "minimum": 0},
              "level": {"enum": ["memory", "disk"]}
            }
          }
        }
      }
    }
  }
}
