{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.modernedi.com/docs/scenarios/schemas/scenario-definition-v1.schema.json",
  "title": "ModernEDI ScenarioDefinition v1",
  "description": "Maps define how documents are processed; scenarios define and prove the business conversation those maps implement. A ScenarioDefinition is the portable recipe for that conversation: who exchanges each X12 business document, which documents belong together, what order and repetition are allowed, and what evidence must pass. Tenant partners, syntax trees, mappings, and executable targets belong in a ScenarioBinding.",
  "type": "object",
  "additionalProperties": false,
  "required": ["apiVersion", "kind", "metadata", "spec"],
  "properties": {
    "$schema": {
      "const": "https://www.modernedi.com/docs/scenarios/schemas/scenario-definition-v1.schema.json",
      "description": "Optional portable tooling hint. It does not participate in the definition's canonical content hash."
    },
    "apiVersion": {
      "const": "modernedi.com/scenario/v1",
      "description": "Wire-contract version for the generally available ScenarioDefinition contract."
    },
    "kind": {
      "const": "ScenarioDefinition",
      "description": "Discriminator for a scenario definition document."
    },
    "metadata": {
      "$ref": "#/$defs/metadata"
    },
    "spec": {
      "$ref": "#/$defs/spec"
    }
  },
  "$defs": {
    "token": {
      "type": "string",
      "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,127}$",
      "description": "A stable identifier beginning with a letter and containing at most 128 letters, digits, underscores, or hyphens."
    },
    "namespace": {
      "type": "string",
      "maxLength": 253,
      "pattern": "^[a-z0-9]+(?:[.-][a-z0-9]+)*$",
      "description": "A lowercase, DNS-like ownership namespace."
    },
    "artifactVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^(?!\\s)(?!.*\\s$)[^\\u0000-\\u001F\\u007F]+$",
      "description": "A trim-exact artifact version with no control characters. This versions a published definition; it is separate from apiVersion."
    },
    "positiveDuration": {
      "type": "string",
      "format": "duration",
      "pattern": "^P(?=.+)(?!0+(?:D|T(?:0+H)?(?:0+M)?(?:0+(?:\\.0+)?S)?$))(?:[0-9]+D)?(?:T(?=[0-9])(?:[0-9]+H)?(?:[0-9]+M)?(?:[0-9]+(?:\\.[0-9]+)?S)?)?$",
      "description": "A positive ISO 8601 duration accepted by java.time.Duration, no longer than P365D. Examples: PT30M, PT4H, P2D. The server enforces the upper bound."
    },
    "metadata": {
      "title": "Definition metadata",
      "description": "Immutable identity of a published definition. The identity tuple is namespace, key, and version; contentSha256 is computed when the definition is published.",
      "type": "object",
      "additionalProperties": false,
      "required": ["namespace", "key", "version"],
      "properties": {
        "namespace": {
          "$ref": "#/$defs/namespace"
        },
        "key": {
          "$ref": "#/$defs/token",
          "description": "Stable definition key within the namespace."
        },
        "version": {
          "$ref": "#/$defs/artifactVersion"
        }
      }
    },
    "spec": {
      "title": "Scenario specification",
      "description": "The business-conversation recipe. Actors name the business roles, steps name their X12 document exchanges, and transitions state how those exchanges relate. ModernEDI also verifies unique IDs, references, a connected conversation, and the absence of ordinary transition cycles during Validate and Publish. Omitted transitions, assertions, parameters, checkpoints, or branches mean empty arrays; published JSON materializes every collection.",
      "type": "object",
      "additionalProperties": false,
      "required": ["actors", "steps"],
      "properties": {
        "actors": {
          "type": "array",
          "description": "Business roles participating in the conversation. Every actor ID must be unique.",
          "minItems": 1,
          "maxItems": 100,
          "items": {
            "$ref": "#/$defs/actor"
          }
        },
        "steps": {
          "type": "array",
          "description": "Business-document exchanges. Every step ID must be unique and must reference two different declared actors.",
          "minItems": 1,
          "maxItems": 500,
          "items": {
            "$ref": "#/$defs/step"
          }
        },
        "transitions": {
          "type": "array",
          "description": "Rules connecting document steps. For example, an 810 invoice can follow its matching 850 purchase order, a later 860 can supersede an earlier order change, and an ocean 303 can cancel a matching 301 booking confirmation. Transition IDs must be unique. Except for a same-step supersedes revision chain, ordinary next-document paths cannot loop and every step must belong to one connected conversation.",
          "default": [],
          "maxItems": 2000,
          "items": {
            "$ref": "#/$defs/transition"
          }
        },
        "assertions": {
          "type": "array",
          "description": "Run-level facts that must hold across one or more observed step occurrences. Assertion IDs must be unique.",
          "default": [],
          "maxItems": 1000,
          "items": {
            "$ref": "#/$defs/assertion"
          }
        },
        "parameters": {
          "type": "array",
          "description": "Prepared values supplied when a run starts. Parameters declare only their semantic type, required flag, and optional default; bindings and runtime APIs supply values.",
          "default": [],
          "maxItems": 100,
          "items": {"$ref": "#/$defs/parameter"}
        },
        "checkpoints": {
          "type": "array",
          "description": "Named completion events emitted after a step finishes a declared processing stage. A checkpoint can trigger a choice between allowed next steps. For example, a bookingDisposition checkpoint emitted after an X12 301 booking confirmation can choose between an X12 303 cancellation and X12 304 shipping instructions.",
          "default": [],
          "maxItems": 100,
          "items": {"$ref": "#/$defs/checkpoint"}
        },
        "branches": {
          "type": "array",
          "description": "Explicit choices between allowed next-document transitions, evaluated only after a named checkpoint is emitted. For example, after an X12 301 booking confirmation emits bookingDisposition, cancelRequested=true can select the X12 303 cancellation path and otherwise can select X12 304 shipping instructions. Choices are evaluated in deterministic rounds from the steps already available at the start of that round; a destination document cannot provide the fact that selects its own path.",
          "default": [],
          "maxItems": 100,
          "items": {"$ref": "#/$defs/branch"}
        }
      }
    },
    "actor": {
      "title": "Actor",
      "description": "A business role. Bindings later connect each role to this workspace or a trading partner.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id"],
      "properties": {
        "id": {
          "$ref": "#/$defs/token"
        }
      }
    },
    "step": {
      "title": "Step",
      "description": "One directed X12 business-document exchange. Functional acknowledgments (997/999) are evidence requirements, not business steps.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "fromActor", "toActor", "transaction", "occurrence", "facts", "pipeline"],
      "properties": {
        "id": {
          "$ref": "#/$defs/token",
          "description": "Stable ID used by transitions, assertions, and bindings."
        },
        "fromActor": {
          "$ref": "#/$defs/token",
          "description": "ID of the actor sending this business document."
        },
        "toActor": {
          "$ref": "#/$defs/token",
          "description": "ID of the actor receiving this business document; it must differ from fromActor."
        },
        "transaction": {
          "$ref": "#/$defs/transaction"
        },
        "occurrence": {
          "$ref": "#/$defs/occurrence"
        },
        "assurance": {
          "$ref": "#/$defs/assurance"
        },
        "facts": {
          "type": "array",
          "description": "Typed semantic facts exposed by this step. Use an empty array when the step exposes none. Declarations name the stable ABI only; extraction provenance belongs to runtime capability and binding layers.",
          "maxItems": 100,
          "items": {"$ref": "#/$defs/factDeclaration"}
        },
        "pipeline": {
          "type": "array",
          "description": "Ordered abstract processing stages for each step occurrence. Stages describe behavior without naming mapper or transport artifacts.",
          "minItems": 1,
          "maxItems": 20,
          "items": {"$ref": "#/$defs/pipelineStage"}
        }
      }
    },
    "transaction": {
      "title": "Transaction",
      "description": "The business document carried by a step. The server verifies the transaction-set code against ModernEDI's X12 catalog.",
      "type": "object",
      "additionalProperties": false,
      "required": ["standard", "transactionSet", "businessUsage"],
      "properties": {
        "standard": {
          "const": "X12",
          "description": "Transaction standard. v1 supports X12."
        },
        "transactionSet": {
          "type": "string",
          "pattern": "^(?!(?:997|999)$)[0-9]{3}$",
          "description": "Known three-digit X12 business transaction set, such as 850, 855, 856, 810, 875, 880, 940, 945, 204, 214, 300, 301, or 315. Use assurance for 997/999 acknowledgments."
        },
        "businessUsage": {
          "$ref": "#/$defs/token",
          "description": "Scenario-specific semantic role, such as purchase_order, shipment_notice, or ocean_status. It is intentionally more precise than the transaction-set code."
        }
      }
    },
    "occurrence": {
      "title": "Occurrence bounds",
      "description": "How many documents this step may contribute to one run and the rule that says when no more are expected. One observed document is one occurrence. For example, an 850 order can be exactly once while 856 shipment notices can repeat. The server also enforces min <= max.",
      "type": "object",
      "additionalProperties": false,
      "required": ["min", "max"],
      "properties": {
        "min": {
          "type": "integer",
          "minimum": 0,
          "maximum": 1000,
          "description": "Minimum accepted count once a reachable step closes. Zero is a no-document outcome only when expected_count resolves to 0 or branch selection makes the step unreachable; zero alone does not close a stream."
        },
        "max": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "description": "Structural per-step ceiling for authored occurrences. Validate and Apply also enforce one conservative run-state capacity budget across all steps, paired documents, checks, and durable evidence."
        },
        "closure": {
          "$ref": "#/$defs/occurrenceClosure",
          "description": "The closure rule: the objective signal that no more documents are expected for this step. It may be omitted while authoring; Publish writes fixed when min equals max and max_reached otherwise. Because max_reached waits for the maximum, a min-zero unbranched step must instead use expected_count so a zero-document outcome is explicit."
        }
      }
    },
    "assurance": {
      "title": "Evidence requirements",
      "description": "Operational evidence required for each occurrence of this step. Duplicate requirement types are rejected by the server.",
      "type": "object",
      "additionalProperties": false,
      "required": ["requirements"],
      "properties": {
        "requirements": {
          "type": "array",
          "minItems": 1,
          "maxItems": 4,
          "items": {
            "$ref": "#/$defs/assuranceRequirement"
          },
          "allOf": [
            {
              "contains": {"type": "object", "properties": {"type": {"const": "mapping_succeeded"}}},
              "minContains": 0,
              "maxContains": 1
            },
            {
              "contains": {"type": "object", "properties": {"type": {"const": "transport_receipt_accepted"}}},
              "minContains": 0,
              "maxContains": 1
            },
            {
              "contains": {"type": "object", "properties": {"type": {"const": "interchange_acknowledgment_accepted"}}},
              "minContains": 0,
              "maxContains": 1
            },
            {
              "contains": {"type": "object", "properties": {"type": {"const": "functional_or_implementation_acknowledgment_accepted"}}},
              "minContains": 0,
              "maxContains": 1
            }
          ]
        }
      }
    },
    "assuranceRequirement": {
      "title": "Evidence requirement",
      "description": "One observable proof required for a step. For example, mapping_succeeded proves that the selected map ran, while functional_or_implementation_acknowledgment_accepted proves accepted 997 or 999 evidence. Outgoing mapping proof may predate the document occurrence because mapping runs immediately before exchange; receipts and acknowledgments may not. A deadline is measured from the document occurrence to the terminal evidence time, and requires that evidence's own timestamp. Terminal evidence timestamped after the run's evaluation time is invalid.",
      "type": "object",
      "additionalProperties": false,
      "required": ["type"],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "mapping_succeeded",
            "transport_receipt_accepted",
            "interchange_acknowledgment_accepted",
            "functional_or_implementation_acknowledgment_accepted"
          ],
          "description": "mapping_succeeded proves mapper execution; transport_receipt_accepted proves the AS2 receipt; interchange_acknowledgment_accepted proves TA1 acceptance; functional_or_implementation_acknowledgment_accepted proves accepted 997 or 999 evidence."
        },
        "within": {
          "$ref": "#/$defs/positiveDuration",
          "description": "Optional deadline from the business document occurrence to terminal evidence. An outgoing mapping completed before exchange satisfies the lower timing boundary, but its own source timestamp is still required; missing terminal source time cannot prove a deadline."
        }
      }
    },
    "transition": {
      "title": "Transition",
      "description": "A rule from one document step to another. Correlations identify which actual documents belong together. follows models ordinary order, such as a matching 810 invoice after an 850 order. supersedes records a replacement, such as a later 860 order change replacing the prior 850 or 860. cancels records a removal, such as an ocean 303 cancellation for a 301 booking confirmation. Supersedes and cancels inspect every stored observation before any replacement or cancellation is applied; they do not make an otherwise unavailable next-document path available and cannot themselves be branch alternatives.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "fromStep", "toStep", "relationship", "matching"],
      "properties": {
        "id": {
          "$ref": "#/$defs/token"
        },
        "fromStep": {
          "$ref": "#/$defs/token",
          "description": "Predecessor step ID."
        },
        "toStep": {
          "$ref": "#/$defs/token",
          "description": "Successor step ID. It may equal fromStep only for a supersedes transition that uses previous matching; cancellation is represented by a distinct cancellation-document step."
        },
        "relationship": {
          "type": "string",
          "enum": ["follows", "supersedes", "cancels"],
          "description": "Choose follows when a matched later document must occur at or after the earlier one, such as an 810 invoice after its 850 order. Choose supersedes when the later document replaces the matched earlier occurrence, such as an 860 order change replacing an 850 or prior 860. Choose cancels when the later document cancels the matched earlier occurrence, such as an ocean 303 cancelling a 301 booking confirmation. supersedes and cancels require effect to say when the change begins and whether later checks keep using the original document or reevaluate with the replacement/removal. Business labels such as acknowledges, reports status, or settles belong in the transition ID and prose unless an assertion or evidence requirement actually verifies them."
        },
        "within": {
          "$ref": "#/$defs/positiveDuration",
          "description": "Optional maximum elapsed time from the correlated fromStep occurrence to the toStep occurrence."
        },
        "correlations": {
          "type": "array",
          "description": "Conditions the scenario evaluator uses to pair actual source and target documents. For example, compare the purchase-order number extracted from an 850 with the purchase-order number extracted from an 810. Correlation IDs must be unique within this transition.",
          "minItems": 0,
          "maxItems": 100,
          "items": {
            "$ref": "#/$defs/correlation"
          }
        },
        "matching": {
          "$ref": "#/$defs/occurrenceMatching",
          "description": "Required quantification and pairing semantics for concrete occurrences; the server does not infer them from correlations."
        },
        "effect": {
          "$ref": "#/$defs/revisionEffect",
          "description": "Required effective-time and downstream behavior for supersedes and cancels. Forbidden for follows."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {"relationship": {"const": "follows"}},
            "required": ["relationship"]
          },
          "then": {"not": {"required": ["effect"]}}
        },
        {
          "if": {
            "properties": {"relationship": {"enum": ["supersedes", "cancels"]}},
            "required": ["relationship"]
          },
          "then": {"required": ["effect"]}
        }
      ]
    },
    "correlation": {
      "title": "Correlation",
      "description": "A condition that decides whether two actual documents belong to the same transition pair. equal can match the purchase-order number on an 850 and 810; same_set can compare their item sets; reply_link uses ModernEDI's stored reply relationship and accepts no fact operands.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operator"],
      "properties": {
        "id": {
          "$ref": "#/$defs/token"
        },
        "operator": {
          "type": "string",
          "enum": ["equal", "same_set", "reply_link"]
        },
        "left": {
          "$ref": "#/$defs/factOperand",
          "description": "For fact operators, must reference the transition's fromStep."
        },
        "right": {
          "$ref": "#/$defs/factOperand",
          "description": "For fact operators, must reference the transition's toStep."
        }
      },
      "oneOf": [
        {
          "properties": {
            "operator": {"enum": ["equal", "same_set"]}
          },
          "required": ["left", "right"]
        },
        {
          "properties": {
            "operator": {"const": "reply_link"}
          },
          "not": {
            "anyOf": [
              {"required": ["left"]},
              {"required": ["right"]}
            ]
          }
        }
      ]
    },
    "assertion": {
      "title": "Assertion",
      "description": "A fact rule that must hold for the run to pass. For example, exists can require every 810 invoice to expose invoiceTotal, sum_equal can compare shipped and invoiced totals, and monotonic can require successive 315 status timestamps to move forward. Binary operators require right; unary operators reject it.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operator", "left"],
      "properties": {
        "id": {
          "$ref": "#/$defs/token"
        },
        "operator": {
          "type": "string",
          "enum": [
            "equal",
            "not_equal",
            "exists",
            "unique",
            "same_set",
            "subset",
            "sum_equal",
            "less_than_or_equal",
            "greater_than_or_equal",
            "monotonic"
          ]
        },
        "left": {
          "$ref": "#/$defs/valueOperand"
        },
        "right": {
          "$ref": "#/$defs/valueOperand"
        }
      },
      "oneOf": [
        {
          "properties": {
            "operator": {
              "enum": [
                "equal",
                "not_equal",
                "same_set",
                "subset",
                "sum_equal",
                "less_than_or_equal",
                "greater_than_or_equal"
              ]
            }
          },
          "required": ["right"]
        },
        {
          "properties": {
            "operator": {"enum": ["exists", "unique", "monotonic"]}
          },
          "not": {"required": ["right"]}
        }
      ]
    },
    "valueType": {
      "type": "string",
      "enum": ["string", "decimal", "date", "time", "timestamp", "boolean", "list", "set"],
      "description": "Closed evaluator value type. list and set contain scalar values only and require elementType."
    },
    "scalarType": {
      "type": "string",
      "enum": ["string", "decimal", "date", "time", "timestamp", "boolean"],
      "description": "Closed scalar type used directly or as the element type of list and set values."
    },
    "factProjection": {
      "type": "string",
      "enum": ["value", "list", "set", "first", "latest"],
      "description": "Select one declared per-occurrence fact value, aggregate values across occurrences as a list or set, or select the first/latest observed occurrence. Correlations require value."
    },
    "decimalJson": {
      "description": "A finite JSON number or exact base-10 string. The scenario evaluator normalizes scale without precision loss.",
      "anyOf": [
        {"type": "number"},
        {"type": "string", "pattern": "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$"}
      ]
    },
    "parameter": {
      "title": "Prepared run parameter",
      "description": "A typed value supplied when a run starts through the Workspace UI or API, or resolved from its declared default when optional.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "type", "required"],
      "properties": {
        "id": {"$ref": "#/$defs/token", "description": "Stable name used by prepare stages, expected-count closure, and parameter operands."},
        "type": {"$ref": "#/$defs/valueType"},
        "elementType": {"$ref": "#/$defs/scalarType", "description": "Required exactly when type is list or set."},
        "required": {"type": "boolean", "description": "Whether the run must explicitly provide this value. A required parameter cannot also have a default."},
        "default": {"description": "Optional JSON value used when the run omits a non-required parameter."}
      },
      "allOf": [
        {
          "if": {"properties": {"type": {"enum": ["list", "set"]}}, "required": ["type"]},
          "then": {"required": ["elementType"]},
          "else": {"not": {"required": ["elementType"]}}
        },
        {
          "if": {"properties": {"required": {"const": true}}, "required": ["required"]},
          "then": {"not": {"required": ["default"]}}
        },
        {
          "if": {"properties": {"type": {"const": "string"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "string"}}}
        },
        {
          "if": {"properties": {"type": {"const": "decimal"}}, "required": ["type"]},
          "then": {"properties": {"default": {"$ref": "#/$defs/decimalJson"}}}
        },
        {
          "if": {"properties": {"type": {"const": "date"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "string", "format": "date"}}}
        },
        {
          "if": {"properties": {"type": {"const": "time"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "string", "format": "time"}}}
        },
        {
          "if": {"properties": {"type": {"const": "timestamp"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "string", "format": "date-time"}}}
        },
        {
          "if": {"properties": {"type": {"const": "boolean"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "boolean"}}}
        },
        {
          "if": {"properties": {"type": {"enum": ["list", "set"]}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "array"}}}
        },
        {
          "if": {"properties": {"type": {"const": "set"}}, "required": ["type"]},
          "then": {"properties": {"default": {"type": "array", "uniqueItems": true}}}
        }
      ]
    },
    "checkpoint": {
      "title": "Checkpoint declaration",
      "description": "A named completion event emitted by exactly one checkpoint pipeline stage after its preceding stages finish. For example, an X12 301 booking-confirmation step can emit bookingDisposition after exchange and fact extraction, allowing the run to choose the cancellation or shipping-instructions path.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id"],
      "properties": {"id": {"$ref": "#/$defs/token"}}
    },
    "factDeclaration": {
      "title": "Typed fact declaration",
      "description": "Stable semantic ABI for one fact exposed by a step occurrence. The binding/runtime capability separately defines extraction provenance.",
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "type", "cardinality", "normalization", "sensitivity"],
      "properties": {
        "name": {"$ref": "#/$defs/token", "description": "Stable fact name used by discriminated fact operands and binding-side fact sources."},
        "type": {"$ref": "#/$defs/valueType"},
        "elementType": {"$ref": "#/$defs/scalarType", "description": "Required exactly when type is list or set; nested collections are not supported."},
        "cardinality": {
          "type": "string",
          "enum": ["one", "optional"],
          "description": "Presence of the typed value on each occurrence. Collection multiplicity is expressed by type list/set, never by a separate many cardinality."
        },
        "normalization": {
          "type": "array",
          "description": "Ordered, deterministic normalization applied before typed evaluation.",
          "maxItems": 5,
          "uniqueItems": true,
          "items": {"type": "string", "enum": ["trim", "collapse_whitespace", "uppercase", "lowercase", "decimal_canonical"]}
        },
        "sensitivity": {
          "type": "string",
          "enum": ["business", "personal", "financial", "health", "restricted"],
          "description": "Data-handling classification carried with evidence and diagnostics."
        }
      },
      "allOf": [
        {
          "if": {"properties": {"type": {"enum": ["list", "set"]}}, "required": ["type"]},
          "then": {"required": ["elementType"]},
          "else": {"not": {"required": ["elementType"]}}
        }
      ]
    },
    "pipelineStage": {
      "title": "Composed step pipeline stage",
      "description": "One named action in the ordered processing of an observed document. A definition says prepare, map, exchange, extract facts, check evidence, or emit a checkpoint; the binding later selects the concrete map, partner, or adapter. Keeping those artifact IDs out of the definition lets the same conversation recipe be reused by another tenant.",
      "oneOf": [
        {
          "title": "Prepare stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind", "parameters"],
          "properties": {
            "id": {"$ref": "#/$defs/token"},
            "kind": {"const": "prepare", "description": "Journal that named run parameters are available. Every referenced parameter must either be required at run start or declare a default."},
            "parameters": {"type": "array", "minItems": 1, "maxItems": 100, "uniqueItems": true, "items": {"$ref": "#/$defs/token"}}
          }
        },
        {
          "title": "Map stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind"],
          "properties": {"id": {"$ref": "#/$defs/token"}, "kind": {"const": "map", "description": "Confirm the exact runtime mapping selected by an applied binding. A step may declare this stage once, immediately next to exchange. Apply requires it before exchange for outgoing documents and after exchange for incoming documents."}}
        },
        {
          "title": "Exchange stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind"],
          "properties": {"id": {"$ref": "#/$defs/token"}, "kind": {"const": "exchange", "description": "Observe or perform the business-document exchange. Exactly one is required per step."}}
        },
        {
          "title": "Extract stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind", "facts"],
          "properties": {
            "id": {"$ref": "#/$defs/token"},
            "kind": {"const": "extract", "description": "Materialize declared semantic facts using binding/runtime-provided sources. Every declared fact must appear in exactly one extract stage."},
            "facts": {"type": "array", "minItems": 1, "maxItems": 100, "uniqueItems": true, "items": {"$ref": "#/$defs/token"}}
          }
        },
        {
          "title": "Assure stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind"],
          "properties": {"id": {"$ref": "#/$defs/token"}, "kind": {"const": "assure", "description": "Evaluate the step's declared operational evidence requirements. A step may declare this stage at most once."}}
        },
        {
          "title": "Checkpoint stage",
          "type": "object",
          "additionalProperties": false,
          "required": ["id", "kind", "checkpoint"],
          "properties": {
            "id": {"$ref": "#/$defs/token"},
            "kind": {"const": "checkpoint", "description": "Emit a declared global checkpoint after the preceding stages complete. In v1, the containing step must have occurrence max 1 so the emitting occurrence is unambiguous."},
            "checkpoint": {"$ref": "#/$defs/token"}
          }
        }
      ]
    },
    "occurrenceClosure": {
      "title": "Occurrence closure",
      "description": "The objective rule that tells a run no more documents are expected for one repeated step. fixed closes at the required fixed count; max_reached closes only at the authored maximum; expected_count closes at a required or defaulted run parameter, including an explicit zero. Branch selection supplies the no-document outcome for an unselected destination. There is no manual-close operation in v1.",
      "oneOf": [
        {
          "title": "Fixed closure",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind"],
          "properties": {"kind": {"const": "fixed", "description": "Close after the fixed min=max count is observed. Example: an 850 step with min=1 and max=1 closes after its one purchase order."}}
        },
        {
          "title": "Maximum-reached closure",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind"],
          "properties": {"kind": {"const": "max_reached", "description": "Close only when the declared maximum count is observed. Example: a 315 status step with max=2 closes after the second status document."}}
        },
        {
          "title": "Expected-count closure",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "parameter"],
          "properties": {
            "kind": {"const": "expected_count", "description": "Close when the number of occurrences reaches an exact integer supplied by a required run parameter or a declared default. The value must fit this step's occurrence bounds; steps sharing one count parameter must have overlapping bounds. Example: expectedShipmentCount=2 closes a repeated 945 step after two shipping-advice documents."},
            "parameter": {"$ref": "#/$defs/token"}
          }
        }
      ]
    },
    "matchQuantifier": {
      "title": "Occurrence match quantifier",
      "description": "An explicit requirement over source occurrences, target occurrences, matched pairs, or matches per endpoint occurrence.",
      "oneOf": [
        {
          "title": "Any quantifier",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "scope"],
          "properties": {"kind": {"const": "any"}, "scope": {"type": "string", "enum": ["from", "to", "pairs", "per_from", "per_to"]}}
        },
        {
          "title": "Every quantifier",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "scope"],
          "properties": {"kind": {"const": "every"}, "scope": {"type": "string", "enum": ["from", "to", "pairs", "per_from", "per_to"]}}
        },
        {
          "title": "Exact-count quantifier",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "scope", "count"],
          "properties": {
            "kind": {"const": "exactly"},
            "scope": {"type": "string", "enum": ["from", "to", "pairs", "per_from", "per_to"]},
            "count": {"type": "integer", "minimum": 1, "maximum": 1000}
          }
        }
      ]
    },
    "occurrenceMatching": {
      "title": "Occurrence matching",
      "description": "How actual repeated documents are paired across a transition, plus what happens to documents with no match. correlate pairs documents whose declared fact or reply-link conditions pass; position pairs by observation order; previous pairs each revision with the immediately preceding occurrence of the same step, such as the second 860 order change with the first.",
      "oneOf": [
        {
          "title": "Correlation matching",
          "type": "object",
          "additionalProperties": false,
          "required": ["strategy", "quantifiers", "unmatched"],
          "properties": {
            "strategy": {"const": "correlate", "description": "Pair occurrences whose declared correlation conditions pass."},
            "quantifiers": {"type": "array", "minItems": 1, "maxItems": 10, "items": {"$ref": "#/$defs/matchQuantifier"}},
            "unmatched": {"type": "string", "enum": ["fail", "record"]}
          }
        },
        {
          "title": "Positional matching",
          "type": "object",
          "additionalProperties": false,
          "required": ["strategy", "quantifiers", "unmatched"],
          "properties": {
            "strategy": {"const": "position", "description": "Pair source and target occurrences by stable observation order; correlations are forbidden."},
            "quantifiers": {"type": "array", "minItems": 1, "maxItems": 10, "items": {"$ref": "#/$defs/matchQuantifier"}},
            "unmatched": {"type": "string", "enum": ["fail", "record"]}
          }
        },
        {
          "title": "Previous-occurrence matching",
          "description": "For a same-step supersedes transition whose step occurrence max is at least 2, pair occurrence n with n-1. The first occurrence is recorded as the initial revision, so both matching.unmatched and effect.unmatched must be record. A separate cross-step transition can link an initial 850 to the first 860. cancels uses a distinct cancellation-document step and cannot use previous matching.",
          "type": "object",
          "additionalProperties": false,
          "required": ["strategy", "quantifiers", "unmatched"],
          "properties": {
            "strategy": {"const": "previous"},
            "quantifiers": {
              "type": "array",
              "minItems": 1,
              "maxItems": 1,
              "items": {
                "type": "object",
                "additionalProperties": false,
                "required": ["kind", "scope"],
                "properties": {"kind": {"const": "every"}, "scope": {"const": "to"}}
              }
            },
            "unmatched": {"const": "record", "description": "Record the first occurrence because it has no predecessor; later occurrences must match their immediate predecessor."}
          }
        }
      ]
    },
    "factOperand": {
      "title": "Fact operand",
      "description": "A declared semantic fact from a concrete step occurrence.",
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "stepId", "fact", "projection"],
      "properties": {
        "kind": {"const": "fact"},
        "stepId": {"$ref": "#/$defs/token"},
        "fact": {"$ref": "#/$defs/token", "description": "Name from the referenced step's facts declaration."},
        "projection": {"$ref": "#/$defs/factProjection", "description": "How values are selected or aggregated across this step's occurrences. list/set require occurrence max greater than 1 and a scalar declared fact."}
      }
    },
    "parameterOperand": {
      "title": "Parameter operand",
      "description": "A prepared run parameter usable in assertions, branch predicates, and effective-time expressions.",
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "parameter"],
      "properties": {"kind": {"const": "parameter"}, "parameter": {"$ref": "#/$defs/token"}}
    },
    "literalOperand": {
      "title": "Typed literal operand",
      "description": "A closed typed JSON literal. list and set literals require scalar elementType and use a JSON array value.",
      "type": "object",
      "additionalProperties": false,
      "required": ["kind", "type", "value"],
      "properties": {
        "kind": {"const": "literal"},
        "type": {"$ref": "#/$defs/valueType"},
        "elementType": {"$ref": "#/$defs/scalarType"},
        "value": {"description": "JSON value matching type and elementType. The server performs format and exact type validation."}
      },
      "allOf": [
        {
          "if": {"properties": {"type": {"enum": ["list", "set"]}}, "required": ["type"]},
          "then": {"required": ["elementType"], "properties": {"value": {"type": "array"}}},
          "else": {"not": {"required": ["elementType"]}}
        },
        {
          "if": {"properties": {"type": {"const": "string"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "string"}}}
        },
        {
          "if": {"properties": {"type": {"const": "decimal"}}, "required": ["type"]},
          "then": {"properties": {"value": {"$ref": "#/$defs/decimalJson"}}}
        },
        {
          "if": {"properties": {"type": {"const": "date"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "string", "format": "date"}}}
        },
        {
          "if": {"properties": {"type": {"const": "time"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "string", "format": "time"}}}
        },
        {
          "if": {"properties": {"type": {"const": "timestamp"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "string", "format": "date-time"}}}
        },
        {
          "if": {"properties": {"type": {"const": "boolean"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "boolean"}}}
        },
        {
          "if": {"properties": {"type": {"const": "set"}}, "required": ["type"]},
          "then": {"properties": {"value": {"type": "array", "uniqueItems": true}}}
        }
      ]
    },
    "valueOperand": {
      "title": "Value operand",
      "description": "Closed discriminated union used by assertions, branch predicates, and effective-time expressions.",
      "oneOf": [
        {"$ref": "#/$defs/factOperand"},
        {"$ref": "#/$defs/parameterOperand"},
        {"$ref": "#/$defs/literalOperand"}
      ]
    },
    "effectiveAt": {
      "title": "Revision effective time",
      "description": "When a replacement or cancellation begins to govern the conversation. observed_at uses the time ModernEDI observed the later document. value uses an explicit timestamp, such as effectiveTimestamp extracted from an X12 860 order change. The value must be a timestamp, not only a date or clock time. A fact value belongs to the matched revision pair and must come from its source or later document; for a previous-matched same-step revision, it is read from the later revision occurrence.",
      "oneOf": [
        {
          "title": "Time ModernEDI observed the revision",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind"],
          "properties": {"kind": {"const": "observed_at"}}
        },
        {
          "title": "Business effective timestamp",
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "value"],
          "properties": {
            "kind": {"const": "value"},
            "value": {
              "$ref": "#/$defs/valueOperand",
              "description": "Must resolve to a timestamp. For example, bind it to effectiveTimestamp extracted from the later X12 860. A fact operand requires value projection and one of the revision transition's endpoint steps; for a previous-matched same-step revision, it is read from the later revision occurrence."
            }
          }
        }
      ]
    },
    "revisionEffect": {
      "title": "Revision or cancellation effect",
      "description": "What a matched replacement or cancellation changes. Raw immutable observation history means every received document remains stored unchanged; the revision rule changes only the run's interpreted business view. A revision transition does not open a next-document path by itself. Each branch-decision round first snapshots the steps already allowed; its conditions see only active reevaluate effects whose original and later-document steps are both in that snapshot, and a chosen path becomes available only in the next round. After branch selection, a revision affects checks only when both endpoint steps remain on the selected conversation path. If several active reevaluate effects target the same original document, the later effectiveAt wins; equal latest times fail with REVISION_EFFECT_CONFLICT instead of choosing arbitrarily.",
      "type": "object",
      "additionalProperties": false,
      "required": ["effectiveAt", "downstream", "unmatched"],
      "properties": {
        "effectiveAt": {
          "$ref": "#/$defs/effectiveAt",
          "description": "When this replacement or cancellation begins to govern checks. Later active reevaluate effects take precedence for the same original document."
        },
        "downstream": {
          "type": "string",
          "enum": ["retain", "reevaluate"],
          "description": "retain records the change and effective time but keeps using the original document for branch conditions, ordinary follows transitions, and assertions; for example, record an advisory 860 while later shipment checks still use the original 850. reevaluate recomputes those same consumers with the replacement or removal; for example, use the revised 860 values for later 856/810 checks, or remove a 301 booking confirmation after its 303 cancellation."
        },
        "unmatched": {"type": "string", "enum": ["fail", "record"], "description": "Behavior when no prior effective occurrence can be revised or cancelled. previous matching requires record because the first occurrence is the initial revision and necessarily has no prior occurrence."}
      }
    },
    "predicate": {
      "title": "Branch predicate",
      "description": "A yes/no condition evaluated after the branch's named checkpoint is emitted. For example, after an X12 301 emits bookingDisposition, compare the cancelRequested run parameter with true to select the X12 303 path. A fact condition must read the common source step of every alternative and that fact's extract stage must precede the checkpoint; it cannot read a destination document or later pipeline evidence to make that destination eligible. Every condition sees the conversation view from the start of the decision round, so one branch cannot use evidence made available by itself or a sibling branch in that round.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operator", "left"],
      "properties": {
        "id": {"$ref": "#/$defs/token"},
        "operator": {"type": "string", "enum": ["equal", "not_equal", "exists", "unique", "same_set", "subset", "sum_equal", "less_than_or_equal", "greater_than_or_equal", "monotonic"]},
        "left": {"$ref": "#/$defs/valueOperand"},
        "right": {"$ref": "#/$defs/valueOperand"}
      },
      "oneOf": [
        {"properties": {"operator": {"enum": ["equal", "not_equal", "same_set", "subset", "sum_equal", "less_than_or_equal", "greater_than_or_equal"]}}, "required": ["right"]},
        {"properties": {"operator": {"enum": ["exists", "unique", "monotonic"]}}, "not": {"required": ["right"]}}
      ]
    },
    "branchAlternative": {
      "title": "Branch alternative",
      "description": "One ordinary follows transition that a branch may select, either when its conditions pass or as the explicit otherwise fallback. For example, cancelRequested=true selects the 301-to-303 follows transition; otherwise selects 301-to-304. supersedes and cancels transitions are revision records, not branch alternatives.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "kind", "transitionId", "conditions"],
      "properties": {
        "id": {"$ref": "#/$defs/token"},
        "kind": {"type": "string", "enum": ["when", "otherwise"]},
        "transitionId": {"$ref": "#/$defs/token", "description": "ID of a follows transition selected by this branch; supersedes and cancels transitions are not allowed."},
        "conditions": {"type": "array", "maxItems": 20, "items": {"$ref": "#/$defs/predicate"}, "description": "Conditions may use parameters, literals, or facts from the alternatives' shared fromStep. They cannot read a destination step to select that destination."}
      },
      "oneOf": [
        {"properties": {"kind": {"const": "when"}, "conditions": {"type": "array", "minItems": 1}}},
        {"properties": {"kind": {"const": "otherwise"}, "conditions": {"type": "array", "maxItems": 0}}}
      ]
    },
    "branch": {
      "title": "Explicit branch",
      "description": "A choice between mutually exclusive next-document paths after a named checkpoint. The checkpoint must be emitted by the alternatives' shared source step, so the decision precedes every controlled destination. The emitter must also necessarily observe at least one document; expected_count closure with occurrence min 0 cannot emit a branch checkpoint. All alternatives leave the same source step. Example: after a 301 booking confirmation emits bookingDisposition, choose the 303 cancellation path when cancelRequested=true; otherwise choose 304 shipping instructions. exactly_one must choose one path and therefore requires an otherwise fallback. at_most_one may choose no path. A round evaluates only evidence already available when that round began; its selection can make later steps available in the next round.",
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "mode", "checkpoint", "alternatives"],
      "properties": {
        "id": {"$ref": "#/$defs/token"},
        "mode": {"type": "string", "enum": ["exactly_one", "at_most_one"]},
        "checkpoint": {"$ref": "#/$defs/token"},
        "alternatives": {"type": "array", "minItems": 1, "maxItems": 20, "items": {"$ref": "#/$defs/branchAlternative"}, "description": "at_most_one accepts one or more when alternatives; exactly_one requires at least two alternatives and exactly one otherwise."}
      }
    }
  },
  "x-modernedi-docs": {
    "customerIntroduction": {
      "exampleTitle": "Start with one real conversation: an X12 850 order and its 810 invoice",
      "exampleSummary": "Suppose a retailer sends your company an X12 850 purchase order and your company later returns an X12 810 invoice. A scenario turns that familiar exchange into a repeatable proof that the right documents, maps, partner route, acknowledgments, timing, and business values all agree.",
      "exampleSteps": [
        {
          "term": "Definition",
          "description": "Describe the reusable business conversation: the retailer sends one 850, the supplier returns one matching 810 within 30 days, both carry the same purchase-order number, the selected maps succeed, and required AS2 receipts plus accepted 997 or 999 acknowledgments exist. No tenant-specific partner or map ID appears here."
        },
        {
          "term": "Binding",
          "description": "Connect that recipe to this tenant's real platform resources: the retailer partner, the supplier workspace, the inbound 850 map, the outbound 810 map, the fact expressions that read each purchase-order number, and any explicitly pinned X12 syntax-tree version (the exact parsing structure used for that document). Applying the binding freezes those exact dependencies for repeatable runs."
        },
        {
          "term": "Run",
          "description": "Start one evaluation of the applied binding and attach the persisted 850 and 810 transactions. ModernEDI checks that the documents followed the allowed path, matched by purchase-order number, arrived in time, used the frozen dependencies, and satisfied every required check."
        },
        {
          "term": "Evidence",
          "description": "Inspect the durable proof: persisted transaction and map identities, transport-receipt and 997 or 999 assurance outcomes, redacted fact subjects, matching-rule and business-check results, evidence references, timestamps, and the immutable evidence report for the terminal run. Raw X12 payloads and extracted fact values remain in authorized platform and transaction views; the run and downloadable report do not copy them."
        }
      ],
      "mentalModel": [
        {
          "term": "Definition",
          "description": "The reusable recipe for an X12 business conversation: roles, document steps, allowed order and repetition, matching rules, evidence requirements, and business checks."
        },
        {
          "term": "Binding",
          "description": "The tenant-specific wiring that selects the partners, workspace, maps, fact expressions, server-side execution implementation, and optional X12 syntax-tree versions used by that recipe."
        },
        {
          "term": "Run",
          "description": "One timestamped evaluation of an applied binding against persisted transactions and platform evidence."
        },
        {
          "term": "Evidence",
          "description": "The durable, hash-identified record of what happened and why the run passed, failed, was cancelled, or remained inconclusive."
        }
      ],
      "glossary": [
        {
          "term": "Conversation graph",
          "description": "The actors, document steps, and directed relationship rules in a definition. It is a model of the business conversation, not a separate customer API."
        },
        {
          "term": "Actor",
          "description": "A business role that sends or receives documents, such as retailer, supplier, warehouse, or carrier. A binding later connects each role to a tenant partner or the tenant workspace."
        },
        {
          "term": "Step",
          "description": "One kind and direction of X12 business document in the conversation, such as an inbound 850 order or outbound 810 invoice."
        },
        {
          "term": "Occurrence",
          "description": "One actual persisted document assigned to a step during a run. Two 856 shipment notices are two occurrences of the same step."
        },
        {
          "term": "Transition",
          "description": "A rule connecting two steps. follows requires a matched later document; supersedes records a replacement such as a later 860 order change; cancels records a removal such as an ocean 303 cancelling a 301 booking confirmation."
        },
        {
          "term": "Fact",
          "description": "A typed business value extracted from one document occurrence by an X12 Mapper expression, such as purchaseOrderNumber, invoiceTotal, or statusTimestamp."
        },
        {
          "term": "Correlation",
          "description": "A matching rule that decides which concrete documents belong together, such as equal purchase-order numbers on an 850 and 810."
        },
        {
          "term": "Assertion",
          "description": "A business fact rule that must pass, such as shipped quantity equaling invoiced quantity or successive 315 status timestamps increasing."
        },
        {
          "term": "Parameter",
          "description": "A typed value supplied when a run starts rather than extracted from a document, such as expectedShipmentCount or cancelRequested."
        },
        {
          "term": "Matching strategy",
          "description": "The rule for pairing repeated documents: by declared correlations, by observation position, or by the immediately previous occurrence in a same-step revision chain."
        },
        {
          "term": "Quantifier",
          "description": "The scope of a repeated check, such as every pair must match, any pair may match, or exactly a declared number must match."
        },
        {
          "term": "Closure",
          "description": "The objective signal that no more documents are expected for one repeated step: a fixed count, the authored maximum, or a required/defaulted expected count."
        },
        {
          "term": "Checkpoint",
          "description": "A named completion event emitted after a declared processing stage. It can trigger a branch; for example, bookingDisposition after processing a 301 can choose the 303 or 304 path."
        },
        {
          "term": "Branch",
          "description": "An explicit choice between mutually exclusive next-document paths after a checkpoint, such as choosing a 303 cancellation instead of 304 shipping instructions."
        },
        {
          "term": "Revision effect",
          "description": "The rule for when a superseding or cancelling document begins to apply and whether later checks keep the original document or reevaluate with the replacement or removal."
        },
        {
          "term": "Pipeline stage",
          "description": "One ordered processing action for a document occurrence: prepare, map, exchange, extract facts, check evidence, or emit a checkpoint."
        },
        {
          "term": "Runtime target",
          "description": "The server-side implementation selected by a binding to prepare, map, exchange, and evaluate the scenario's document steps."
        },
        {
          "term": "Runtime capacity",
          "description": "The bounded amount of durable run state a definition may require. Validate and Apply reject a definition whose worst-case structure would exceed that budget."
        },
        {
          "term": "Publish",
          "description": "Create an immutable, content-hashed definition revision after validation succeeds."
        },
        {
          "term": "Apply",
          "description": "Create an immutable binding revision after ModernEDI resolves and validates its tenant-owned dependencies."
        }
      ],
      "apiBoundary": "The signed-in Workspace API is the customer HTTP API for definitions, bindings, runs, context, operations, and evidence reports. A conversation graph is only the definition's model of actors, document steps, and directed rules; the scenario runtime is the server-side evaluator that executes supported applied bindings. Some generated OpenAPI component names begin with ScenarioGraph because they represent conversation-graph state inside a run; they remain part of the Workspace API, not a separate Graph API."
    },
    "filePattern": "*.scenario.json",
    "exampleFile": "../examples/order-invoice.scenario.json",
    "authoringWorkflow": [
      "Create a *.scenario.json draft in Mapper Explorer. JSON Schema supplies completion, hover help, and immediate structural diagnostics.",
      "Run Validate to check references, X12 catalog values, closure and branch rules, typed operands, and whether the current scenario runtime can execute the definition.",
      "Publish only after validation succeeds. Publish creates an immutable definition identity and content hash for bindings.",
      "Create and Apply a matching *.binding.json to resolve tenant partners, mappings, adapters, and optional syntax-tree choices.",
      "Start a scenario run with the applied binding, then inspect each occurrence, evidence item, correlation, and assertion in Implementation Verification."
    ],
    "serverSemanticChecks": [
      "Actor, step, transition, parameter, checkpoint, branch, predicate, fact, and pipeline-stage IDs are unique in their documented scopes.",
      "Every referenced actor, step, fact, checkpoint, transition, and parameter exists, and every document step goes from one actor to a different actor. A correlation may read facts only from its two connected steps. A branch condition may read facts only from the common source step and cannot use a destination document to select itself.",
      "An occurrence minimum cannot exceed its maximum; every repeated step has an objective closure rule; repeated-document pairing and every/any/count behavior are explicit; durations are positive and no longer than P365D. A min-zero step uses expected_count because zero observations cannot satisfy max_reached, unless every incoming follows path is branch-controlled so an unselected path supplies the no-document outcome. expected_count uses an exact integer parameter that is required at run start or has a default within the step bounds; steps sharing that parameter must have overlapping bounds.",
      "Transaction sets exist in the X12 catalog, while 997 and 999 are represented as assurance evidence.",
      "Ordinary next-document paths cannot loop, and every step belongs to the same connected conversation. Only supersedes with previous matching may form a same-step revision chain, and that repeated step must allow at least two occurrences; cancels identifies a distinct cancellation-document step.",
      "Each operator receives compatible typed values from facts, run parameters, or literals; list and set elements must use one exact scalar type.",
      "Each composed processing pipeline contains one exchange stage in valid order and at most one map and assure stage. map must be directly beside exchange; Apply requires it before exchange for an outgoing runtime mapping and after exchange for an incoming one. Every parameter named by prepare is required at run start or has a default. Every declared fact appears in exactly one extract stage. Every declared checkpoint has exactly one emitting stage, a step that emits a checkpoint can occur only once, and a branch fact is extracted before its decision checkpoint.",
      "supersedes and cancels require a revision effect; follows forbids one. previous matching is supersedes-only, requires a step occurrence max of at least 2, and requires record for both matching and effect because the first occurrence is the initial revision. Revisions always match the unchanged observation history, cannot be branch alternatives, and cannot open or schedule a next-document path. Each branch checkpoint must be emitted by the alternatives' shared source step and must necessarily observe at least one document, so expected_count closure with occurrence min 0 is rejected. Each branch-decision round uses only steps already allowed at its start; a selected path becomes available in the next round. After branch selection, a revision affects checks only when both endpoint steps remain on the selected path. retain keeps the original document for branch conditions, follows transitions, and assertions; reevaluate uses the replacement or removal instead. A later effective time wins, and equal latest times fail with REVISION_EFFECT_CONFLICT.",
      "The definition's combined worst-case occurrence summaries, transition pairs, checks, and compact evidence references must fit the v1 durable run-state budget. Per-step JSON Schema maxima for fact declarations and occurrences are structural ceilings, not separately available runtime quotas."
    ],
    "runtimeCapacity": {
      "source": "ScenarioGraphRuntimeCapacity",
      "durableProjectionBudgetBytes": 480000,
      "structuralFactDeclarationsPerStepMaximum": 100,
      "structuralPerStepOccurrenceMaximum": 1000,
      "stableDiagnostic": "runtime_capacity_exceeded",
      "description": "Validate and Apply conservatively estimate deterministic durable run-state growth rather than accepting each step independently. The estimate includes occurrence summaries, compact evidence references, checks, and quadratic transition-pair and revision-check growth. It does not reserve the maximum runtime value size for every declared fact; actual Mapper-derived values and evidence are checked against separate per-observation limits before persistence. A definition is rejected with runtime_capacity_exceeded when its combined structural projection exceeds the v1 durable run-state budget."
    }
  }
}
