{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "StoryNode",
  "description": "An immutable branch event with its state delta and player-facing presentation.",
  "type": "object",
  "properties": {
    "action_id": {
      "type": "string"
    },
    "beats": {
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/PresentationBeat"
      }
    },
    "branch_id": {
      "type": "string"
    },
    "can_continue": {
      "type": "boolean",
      "default": true
    },
    "character": {
      "$ref": "#/$defs/PresentationCharacter",
      "default": {
        "expression": null,
        "id": "",
        "name": "",
        "pose": null
      }
    },
    "delta": {
      "$ref": "#/$defs/StateDelta"
    },
    "id": {
      "type": "string"
    },
    "parent_id": {
      "type": [
        "string",
        "null"
      ]
    },
    "scene": {
      "$ref": "#/$defs/PresentationScene",
      "default": {
        "id": "",
        "title": ""
      }
    },
    "state_hash": {
      "type": "string"
    },
    "story_id": {
      "type": "string"
    },
    "suggestions": {
      "type": "array",
      "default": [],
      "items": {
        "$ref": "#/$defs/ActionSuggestion"
      }
    },
    "user_input": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "story_id",
    "branch_id",
    "action_id",
    "user_input",
    "delta",
    "state_hash"
  ],
  "$defs": {
    "AcquisitionMode": {
      "type": "string",
      "enum": [
        "initial",
        "found",
        "given",
        "borrowed",
        "stolen",
        "returned"
      ]
    },
    "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"
      ]
    },
    "CheckDifficulty": {
      "type": "string",
      "enum": [
        "regular",
        "hard",
        "extreme"
      ]
    },
    "CheckRecord": {
      "type": "object",
      "properties": {
        "action_id": {
          "type": "string"
        },
        "actor": {
          "type": "string"
        },
        "bonus_dice": {
          "type": "integer",
          "format": "int8",
          "maximum": 127,
          "minimum": -128
        },
        "difficulty": {
          "$ref": "#/$defs/CheckDifficulty"
        },
        "id": {
          "type": "string"
        },
        "node_id": {
          "type": "string"
        },
        "pushed_from": {
          "type": [
            "string",
            "null"
          ]
        },
        "result": {
          "$ref": "#/$defs/CheckResult"
        },
        "roll": {
          "type": "integer",
          "format": "uint8",
          "maximum": 255,
          "minimum": 0
        },
        "skill": {
          "type": "string"
        },
        "target": {
          "type": "integer",
          "format": "uint8",
          "maximum": 255,
          "minimum": 0
        }
      },
      "required": [
        "id",
        "action_id",
        "actor",
        "skill",
        "target",
        "difficulty",
        "bonus_dice",
        "roll",
        "result",
        "node_id"
      ]
    },
    "CheckResult": {
      "type": "string",
      "enum": [
        "critical_success",
        "extreme_success",
        "hard_success",
        "success",
        "failure",
        "fumble"
      ]
    },
    "ItemAcquisition": {
      "type": "object",
      "properties": {
        "at_node": {
          "type": "string"
        },
        "from": {
          "type": [
            "string",
            "null"
          ]
        },
        "mode": {
          "$ref": "#/$defs/AcquisitionMode"
        }
      },
      "required": [
        "mode",
        "at_node"
      ]
    },
    "ItemInstance": {
      "type": "object",
      "properties": {
        "acquisition": {
          "$ref": "#/$defs/ItemAcquisition"
        },
        "condition": {
          "type": "string"
        },
        "holder": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "owner": {
          "type": "string"
        },
        "placement": {
          "$ref": "#/$defs/ItemPlacement"
        },
        "quantity": {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        },
        "spec_ref": {
          "type": "string"
        },
        "state_tags": {
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "id",
        "spec_ref",
        "owner",
        "holder",
        "placement",
        "quantity",
        "condition",
        "acquisition"
      ]
    },
    "ItemPlacement": {
      "type": "string",
      "enum": [
        "bag",
        "worn",
        "hand",
        "scene",
        "hidden"
      ]
    },
    "KnowledgeCertainty": {
      "type": "string",
      "enum": [
        "suspected",
        "reported",
        "confirmed"
      ]
    },
    "KnowledgeRecord": {
      "type": "object",
      "properties": {
        "certainty": {
          "$ref": "#/$defs/KnowledgeCertainty"
        },
        "fact": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "last_verified_at": {
          "type": [
            "string",
            "null"
          ]
        },
        "learned_at": {
          "type": "string"
        },
        "observer": {
          "type": "string"
        },
        "source": {
          "type": "string"
        },
        "subject": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "id",
        "observer",
        "fact",
        "certainty",
        "source",
        "learned_at"
      ]
    },
    "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"
      ]
    },
    "Promise": {
      "type": "object",
      "properties": {
        "accepted_at": {
          "type": [
            "string",
            "null"
          ]
        },
        "content": {
          "type": "string"
        },
        "created_at": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "promisee": {
          "type": "string"
        },
        "promiser": {
          "type": "string"
        },
        "resolved_at": {
          "type": [
            "string",
            "null"
          ]
        },
        "status": {
          "$ref": "#/$defs/PromiseStatus"
        },
        "weight": {
          "$ref": "#/$defs/PromiseWeight"
        }
      },
      "required": [
        "id",
        "promiser",
        "promisee",
        "content",
        "status",
        "weight",
        "created_at"
      ]
    },
    "PromiseStatus": {
      "type": "string",
      "enum": [
        "proposed",
        "accepted",
        "fulfilled",
        "broken",
        "released",
        "impossible"
      ]
    },
    "PromiseWeight": {
      "type": "string",
      "enum": [
        "minor",
        "major"
      ]
    },
    "RelationshipAdjustment": {
      "type": "object",
      "properties": {
        "cause": {
          "type": "string"
        },
        "delta": {
          "type": "integer",
          "format": "int16",
          "maximum": 32767,
          "minimum": -32768
        },
        "dimension": {
          "$ref": "#/$defs/RelationshipDimension"
        },
        "judgment_rule": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "dimension",
        "delta",
        "cause"
      ]
    },
    "RelationshipDimension": {
      "type": "string",
      "enum": [
        "affinity",
        "trust",
        "hope",
        "respect",
        "intimacy",
        "attachment"
      ]
    },
    "StateDelta": {
      "type": "object",
      "properties": {
        "ops": {
          "type": "array",
          "default": [],
          "items": {
            "$ref": "#/$defs/StateOp"
          }
        }
      }
    },
    "StateOp": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "key": {
              "type": "string"
            },
            "op": {
              "type": "string",
              "const": "set_world_flag"
            },
            "value": {
              "type": "boolean"
            }
          },
          "required": [
            "op",
            "key",
            "value"
          ]
        },
        {
          "type": "object",
          "properties": {
            "adjustment": {
              "$ref": "#/$defs/RelationshipAdjustment"
            },
            "from": {
              "type": "string"
            },
            "op": {
              "type": "string",
              "const": "adjust_relationship"
            },
            "to": {
              "type": "string"
            }
          },
          "required": [
            "op",
            "from",
            "to",
            "adjustment"
          ]
        },
        {
          "type": "object",
          "properties": {
            "op": {
              "type": "string",
              "const": "create_promise"
            },
            "promise": {
              "$ref": "#/$defs/Promise"
            }
          },
          "required": [
            "op",
            "promise"
          ]
        },
        {
          "type": "object",
          "properties": {
            "op": {
              "type": "string",
              "const": "update_promise"
            },
            "promise_id": {
              "type": "string"
            },
            "resolved_at": {
              "type": [
                "string",
                "null"
              ]
            },
            "status": {
              "$ref": "#/$defs/PromiseStatus"
            }
          },
          "required": [
            "op",
            "promise_id",
            "status"
          ]
        },
        {
          "type": "object",
          "properties": {
            "op": {
              "type": "string",
              "const": "add_knowledge"
            },
            "record": {
              "$ref": "#/$defs/KnowledgeRecord"
            }
          },
          "required": [
            "op",
            "record"
          ]
        },
        {
          "type": "object",
          "properties": {
            "item": {
              "$ref": "#/$defs/ItemInstance"
            },
            "op": {
              "type": "string",
              "const": "add_item"
            }
          },
          "required": [
            "op",
            "item"
          ]
        },
        {
          "type": "object",
          "properties": {
            "from": {
              "type": "string"
            },
            "item_id": {
              "type": "string"
            },
            "mode": {
              "$ref": "#/$defs/AcquisitionMode"
            },
            "op": {
              "type": "string",
              "const": "transfer_item"
            },
            "placement": {
              "$ref": "#/$defs/ItemPlacement"
            },
            "to": {
              "type": "string"
            }
          },
          "required": [
            "op",
            "item_id",
            "from",
            "to",
            "placement",
            "mode"
          ]
        },
        {
          "type": "object",
          "properties": {
            "check": {
              "$ref": "#/$defs/CheckRecord"
            },
            "op": {
              "type": "string",
              "const": "record_check"
            }
          },
          "required": [
            "op",
            "check"
          ]
        },
        {
          "type": "object",
          "properties": {
            "clock_id": {
              "type": "string"
            },
            "delta": {
              "type": "integer",
              "format": "uint16",
              "maximum": 65535,
              "minimum": 0
            },
            "op": {
              "type": "string",
              "const": "advance_clock"
            }
          },
          "required": [
            "op",
            "clock_id",
            "delta"
          ]
        }
      ]
    },
    "VisualDirective": {
      "type": "object",
      "properties": {
        "character": {
          "type": [
            "string",
            "null"
          ]
        },
        "expression": {
          "type": [
            "string",
            "null"
          ]
        },
        "pose": {
          "type": [
            "string",
            "null"
          ]
        },
        "scene": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    }
  }
}
