{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.modernedi.com/docs/scenarios/schemas/configuration-scenario-binding-v1.schema.json",
  "title": "ModernEDI configuration ScenarioBinding",
  "description": "Optional scenario configuration in the same reviewed bundle as connections, partners and maps. Retirement removes current configuration without deleting published revisions or run evidence.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "apiVersion",
    "kind",
    "metadata",
    "spec"
  ],
  "properties": {
    "apiVersion": {
      "const": "modernedi.com/v1"
    },
    "kind": {
      "const": "ScenarioBinding"
    },
    "metadata": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key"
      ],
      "properties": {
        "key": {
          "type": "string",
          "format": "uuid",
          "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
          "description": "Stable resource key; retain it when copying configuration to another workspace."
        }
      }
    },
    "spec": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "source"
      ],
      "properties": {
        "source": {
          "title": "ModernEDI ScenarioBinding v1",
          "description": "A tenant-scoped authoring binding that connects every actor and step in one immutable ScenarioDefinition to current workspace partners and executable targets. It references tenant artifacts by identity and contains no copied runtime configuration, credentials, private keys, or other secrets.",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "apiVersion",
            "kind",
            "metadata",
            "spec"
          ],
          "properties": {
            "$schema": {
              "const": "https://www.modernedi.com/docs/scenarios/schemas/scenario-binding-v1.schema.json",
              "description": "Optional portable tooling hint. It does not participate in the binding's canonical content hash."
            },
            "apiVersion": {
              "const": "modernedi.com/scenario-binding-authoring/v1",
              "description": "Wire-contract version for tenant-authored scenario bindings."
            },
            "kind": {
              "const": "ScenarioBinding",
              "description": "Discriminator for a scenario binding 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": "Lowercase namespace of the published ScenarioDefinition."
    },
    "artifactVersion": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^(?!\\s)(?!.*\\s$)[^\\u0000-\\u001F\\u007F]+$",
      "description": "Exact published definition version."
    },
    "metadata": {
      "title": "Binding metadata",
      "description": "Editable identity and display name for this binding draft. Apply creates an immutable binding revision.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/token",
          "description": "Stable binding ID within the workspace."
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 160,
          "pattern": "\\S",
          "description": "Human-readable binding name. The server trims leading and trailing whitespace when canonicalizing."
        }
      }
    },
    "spec": {
      "title": "Binding specification",
      "description": "References one exact published definition and binds all of its actors and steps. Apply resolves and freezes authoritative artifacts for runtime use.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "definition",
        "environment",
        "actors",
        "steps"
      ],
      "properties": {
        "regressionCases": {
          "$ref": "#/$defs/regressionCases"
        },
        "definition": {
          "$ref": "#/$defs/definitionReference"
        },
        "environment": {
          "type": "string",
          "enum": [
            "production",
            "test"
          ],
          "description": "Required workspace traffic selector, frozen into the applied binding. Production and test traffic use the same deployed mappings; this does not deploy unpublished mappings or create another workspace. Runs use the selected AS2/X12 profile and keep transaction lookup, acknowledgments, and evidence in that environment, without falling back to the other profile."
        },
        "actors": {
          "type": "array",
          "description": "Exactly one binding for every definition actor. IDs must be unique; exactly one endpoint must be this workspace and at least one must be a current external partner.",
          "minItems": 1,
          "maxItems": 100,
          "items": {
            "$ref": "#/$defs/actorBinding"
          }
        },
        "steps": {
          "type": "array",
          "description": "Exactly one binding for every definition step. IDs must be unique.",
          "minItems": 1,
          "maxItems": 500,
          "items": {
            "$ref": "#/$defs/stepBinding"
          }
        }
      }
    },
    "regressionCases": {
      "title": "Saved conversation tests",
      "description": "Optional offline tests for this binding. Each observation references a saved case on the step's bound runtime mapping. Tests execute proposed maps and the existing graph interpreter without sending EDI or creating live run evidence. Case order is canonicalized by ID; observation order is preserved. Up to 10 cases, 20 observations each, and 64 KiB total.",
      "type": "array",
      "maxItems": 10,
      "items": {
        "$ref": "#/$defs/regressionCase"
      }
    },
    "regressionCase": {
      "title": "Saved conversation case",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "parameters",
        "observations",
        "evaluatedAfterSeconds",
        "closeSteps",
        "expected"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,63}$"
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "pattern": "\\S"
        },
        "parameters": {
          "type": "object",
          "additionalProperties": false,
          "patternProperties": {
            "^[A-Za-z][A-Za-z0-9_-]{0,127}$": {}
          },
          "maxProperties": 100,
          "description": "Ordinary run parameters, resolved and type-checked against this exact definition."
        },
        "observations": {
          "type": "array",
          "maxItems": 20,
          "items": {
            "$ref": "#/$defs/regressionObservation"
          },
          "description": "Listed attachment order. Repeated step IDs are assigned occurrence 1, 2, and so on. No sorting by business event time occurs."
        },
        "evaluatedAfterSeconds": {
          "$ref": "#/$defs/testSeconds",
          "description": "Evaluation time relative to a fixed synthetic UTC start. Must not precede any observation."
        },
        "closeSteps": {
          "type": "array",
          "maxItems": 100,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/token"
          },
          "description": "Explicit-completion steps to close at the evaluation time. Closing never waives their checks."
        },
        "expected": {
          "$ref": "#/$defs/regressionExpected"
        }
      }
    },
    "testSeconds": {
      "type": "integer",
      "minimum": 0,
      "maximum": 31536000,
      "description": "Whole seconds after the synthetic start (at most 365 days)."
    },
    "regressionObservation": {
      "title": "Saved mapping-case observation",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "stepId",
        "mappingCaseId",
        "observedAfterSeconds"
      ],
      "properties": {
        "stepId": {
          "$ref": "#/$defs/token"
        },
        "mappingCaseId": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "\\S",
          "description": "ID of a saved case belonging to this step's bound runtime mapping. Incoming observations use that case's selected X12 transaction; outgoing observations use the actual generated output, never expectedOutput."
        },
        "observedAfterSeconds": {
          "$ref": "#/$defs/testSeconds"
        }
      }
    },
    "regressionOutcome": {
      "type": "string",
      "enum": [
        "PASSED",
        "FAILED",
        "PENDING",
        "INCONCLUSIVE"
      ]
    },
    "regressionExpected": {
      "title": "Expected conversation result",
      "description": "A FAILED expectation must include at least one specific FAILED check, preventing an unrelated failure from satisfying a negative test.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "outcome",
        "checks"
      ],
      "properties": {
        "outcome": {
          "$ref": "#/$defs/regressionOutcome"
        },
        "checks": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "outcome",
              "code"
            ],
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 1024,
                "pattern": "\\S"
              },
              "outcome": {
                "$ref": "#/$defs/regressionOutcome"
              },
              "code": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "\\S"
              }
            }
          }
        }
      }
    },
    "definitionReference": {
      "title": "Published definition reference",
      "description": "Exact identity and canonical content hash returned by Publish or the scenario catalog. Apply fails closed if any part differs.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "namespace",
        "key",
        "version",
        "contentSha256"
      ],
      "properties": {
        "namespace": {
          "$ref": "#/$defs/namespace"
        },
        "key": {
          "$ref": "#/$defs/token"
        },
        "version": {
          "$ref": "#/$defs/artifactVersion"
        },
        "contentSha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$",
          "description": "Lowercase SHA-256 digest of the canonical published definition."
        }
      }
    },
    "actorBinding": {
      "title": "Actor binding",
      "description": "Connects one definition actor to this workspace or a current trading partner.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "actorId",
        "endpoint"
      ],
      "properties": {
        "actorId": {
          "$ref": "#/$defs/token",
          "description": "Actor ID from the referenced ScenarioDefinition."
        },
        "endpoint": {
          "$ref": "#/$defs/endpoint"
        }
      }
    },
    "endpoint": {
      "title": "Actor endpoint",
      "description": "OneOf discriminator: workspace accepts only kind; partner also requires a positive tenant partner ID.",
      "oneOf": [
        {
          "title": "Workspace endpoint",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "kind"
          ],
          "properties": {
            "kind": {
              "const": "workspace",
              "description": "The current tenant workspace."
            }
          }
        },
        {
          "title": "Partner endpoint",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "kind",
            "partnerKey"
          ],
          "properties": {
            "kind": {
              "const": "partner"
            },
            "partnerKey": {
              "type": "string",
              "format": "uuid",
              "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
              "description": "Stable Partner resource key in this configuration bundle; resolved to a workspace ID during apply."
            }
          }
        }
      ]
    },
    "stepBinding": {
      "title": "Step binding",
      "description": "Connects one definition step to an executable or observation target and binds every declared semantic fact to an implementation-guide source. syntaxTree is optional while authoring; Apply resolves an exact authoritative syntax-tree artifact before runtime execution.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "stepId",
        "target"
      ],
      "properties": {
        "stepId": {
          "$ref": "#/$defs/token",
          "description": "Step ID from the referenced ScenarioDefinition."
        },
        "syntaxTree": {
          "$ref": "#/$defs/syntaxTreeReference",
          "description": "Optional explicit implementation-guide identity. Omit it to let Apply select the exact current syntax tree from the authoritative mapping or adapter; an applied runtime binding is always resolved and immutable."
        },
        "target": {
          "$ref": "#/$defs/target"
        },
        "factSources": {
          "type": "array",
          "maxItems": 200,
          "description": "One source for every fact declared by this ScenarioDefinition step. Omission is a deliberate authoring shorthand only when the step declares no facts; canonical Apply materializes an empty array. Server validation otherwise requires exact fact-name coverage. Collection shape, element type, cardinality, normalization, and sensitivity come only from the declaration and are intentionally not duplicated here.",
          "items": {
            "$ref": "#/$defs/factSourceBinding"
          }
        }
      }
    },
    "factSourceBinding": {
      "title": "Fact source binding",
      "description": "Binds one named fact declaration to one validated X12 Mapper expression. Fact names must be unique within a step.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "fact",
        "source"
      ],
      "properties": {
        "fact": {
          "$ref": "#/$defs/token",
          "description": "Fact name declared by this exact ScenarioDefinition step."
        },
        "source": {
          "$ref": "#/$defs/factSource"
        }
      }
    },
    "factSource": {
      "title": "Fact source",
      "description": "One ordinary X12 Mapper expression fact source. It uses the same language and execution engine as an incoming map's primaryKeyExtractor; v1 does not define a second scenario-specific selector language. The expression must read the bound X12 document so its value has concrete provenance.",
      "oneOf": [
        {
          "$ref": "#/$defs/x12MapperExpressionSource"
        }
      ]
    },
    "x12MapperExpressionSource": {
      "title": "X12 Mapper expression fact source",
      "description": "Evaluates one ordinary X12 Mapper expression against the step transaction. Validate and Apply use the same parser, semantic validator, function registry, budget mechanism, and syntax-tree-aware engine as runtime mappings and primaryKeyExtractor, with scenario-specific bounded limits. Validate and Apply reject an expression that is statically incapable of reading an X12 segment or element, and runtime fails closed when the executed path produces no concrete X12 read. Scenario evidence uses that engine's element-read tracing facility; this does not imply that existing primary-key persistence emits the same trace. The ScenarioDefinition remains authoritative for value type, cardinality, normalization, and sensitivity.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "kind",
        "expression"
      ],
      "properties": {
        "kind": {
          "const": "x12-mapper-expression",
          "description": "Uses ModernEDI's existing X12 Mapper expression language and engine, shared with primaryKeyExtractor."
        },
        "expression": {
          "type": "string",
          "minLength": 1,
          "maxLength": 16384,
          "pattern": "\\S",
          "examples": [
            "ST->BEG(03)",
            "forEach(line in ST->PO1+ => line(02))"
          ],
          "description": "One value-returning X12 Mapper expression that reads at least one X12 segment or element. A constant-only expression cannot provide authoritative document evidence. Scalars, lists, and sets are interpreted according to the matching fact declaration; secrets and credentials must never appear here."
        }
      }
    },
    "syntaxTreeReference": {
      "title": "Syntax-tree reference",
      "description": "Optional authoring-time pin to an exact X12 release and transaction set. Both fields are required when this object is present.",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "x12Version",
        "transactionSet"
      ],
      "properties": {
        "x12Version": {
          "type": "string",
          "pattern": "^[Vv]?(?:00)?[1-9][0-9]{3}$",
          "examples": [
            "004010",
            "005010"
          ],
          "description": "X12 release token. Apply canonicalizes accepted forms to a six-digit release such as 004010."
        },
        "transactionSet": {
          "type": "string",
          "pattern": "^[0-9]{3}$",
          "description": "Known three-digit X12 transaction set. The code must match the referenced definition step."
        }
      }
    },
    "target": {
      "title": "Step target",
      "description": "OneOf discriminator selecting an evidence-constrained runtime mapping, a registered active executor adapter, or observation-only evidence.",
      "oneOf": [
        {
          "title": "Runtime mapping target",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "kind",
            "mappingKey"
          ],
          "properties": {
            "kind": {
              "const": "runtime-mapping"
            },
            "mappingKey": {
              "type": "string",
              "format": "uuid",
              "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
              "description": "Stable Mapping resource key in this configuration bundle; resolved to a workspace ID during apply."
            }
          }
        },
        {
          "title": "Adapter target",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "kind",
            "adapterId"
          ],
          "properties": {
            "kind": {
              "const": "adapter"
            },
            "adapterId": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256,
              "pattern": "\\S",
              "description": "Registered active executor adapter ID. Apply verifies it implements this definition and step."
            },
            "mappingKey": {
              "type": "string",
              "format": "uuid",
              "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
              "description": "Stable Mapping resource key in this configuration bundle; resolved to a workspace ID during apply."
            }
          }
        },
        {
          "title": "Observation-only target",
          "type": "object",
          "additionalProperties": false,
          "required": [
            "kind"
          ],
          "properties": {
            "kind": {
              "const": "observation-only",
              "description": "ModernEDI observes and evaluates this step but does not initiate it."
            }
          }
        }
      ]
    }
  }
}
