{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PresentationSnapshot",
  "description": "Player-authorized presentation output committed with a [`StoryNode`].\n\nThe snapshot deliberately contains no runtime facts. Exact relationships,\nhidden checks, NPC knowledge and private inventory remain in `RuntimeState`\nand must pass through the normal `PlayerView` projection boundary.",
  "type": "object",
  "properties": {
    "beats": {
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/PresentationBeat"
      }
    },
    "can_continue": {
      "type": "boolean",
      "default": true
    },
    "character": {
      "$ref": "#/$defs/PresentationCharacter",
      "default": {
        "expression": null,
        "id": "",
        "name": "",
        "pose": null
      }
    },
    "scene": {
      "$ref": "#/$defs/PresentationScene",
      "default": {
        "id": "",
        "title": ""
      }
    },
    "suggestions": {
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/ActionSuggestion"
      }
    }
  },
  "$defs": {
    "ActionSuggestion": {
      "type": "object",
      "properties": {
        "draft": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "label",
        "draft"
      ]
    },
    "BeatKind": {
      "type": "string",
      "enum": [
        "narration",
        "dialogue",
        "action",
        "system"
      ]
    },
    "PresentationBeat": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/BeatKind"
        },
        "speaker": {
          "type": [
            "string",
            "null"
          ]
        },
        "text": {
          "type": "string"
        },
        "visual": {
          "anyOf": [
            {
              "$ref": "#/$defs/VisualDirective"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "kind",
        "text"
      ]
    },
    "PresentationCharacter": {
      "description": "Resolved public visual state for the primary character at a committed node.\n\n`expression` and `pose` are the final values after the node's beats have\nplayed. Persisting the resolved values means restoring a node does not need\nto replay an earlier branch to reconstruct the current portrait.",
      "type": "object",
      "properties": {
        "expression": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "pose": {
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "required": [
        "id",
        "name"
      ]
    },
    "PresentationScene": {
      "description": "Public scene identity frozen at a committed story node.\n\nThis is presentation data, not authoritative world state. In particular it\nmust never contain unrevealed scene facts or trigger conditions.",
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "title"
      ]
    },
    "VisualDirective": {
      "type": "object",
      "properties": {
        "character": {
          "type": [
            "string",
            "null"
          ]
        },
        "expression": {
          "type": [
            "string",
            "null"
          ]
        },
        "pose": {
          "type": [
            "string",
            "null"
          ]
        },
        "scene": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    }
  }
}
