{
  "openapi": "3.1.0",
  "info": {
    "title": "Bastion GM GPT Actions",
    "version": "1.0.0",
    "description": "Four compact, system-neutral campaign operations. The server owns canonical state; the GPT owns presentation."
  },
  "servers": [
    { "url": "https://gm.test.bastionsystems.org" }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "paths": {
    "/api/v1/campaigns/{campaignId}/context": {
      "get": {
        "operationId": "getCampaignContext",
        "summary": "Load an audience-safe campaign checkpoint",
        "description": "Returns the current revision, fictional date and time, character status, visible knowledge and clocks, and the most recent visible presentation. Use the least-privileged perspective appropriate to the user.",
        "parameters": [
          { "$ref": "#/components/parameters/CampaignId" },
          {
            "name": "perspective",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["gm", "player", "npc"], "default": "gm" }
          },
          {
            "name": "entity",
            "in": "query",
            "required": false,
            "description": "Required for player and npc perspectives; omit for gm.",
            "schema": { "$ref": "#/components/schemas/EntityKey" }
          }
        ],
        "responses": {
          "200": {
            "description": "Current audience-safe campaign context.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CampaignContext" } } }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" },
          "504": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/campaigns/{campaignId}/turns": {
      "post": {
        "operationId": "commitTurn",
        "summary": "Commit one canonical turn and its exact presentation",
        "description": "Persists the exact text that will be shown to the user in compressed form together with all state changes and status basis. The requestId is the idempotent operation identity.",
        "parameters": [
          { "$ref": "#/components/parameters/CampaignId" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TurnCommit" } } }
        },
        "responses": {
          "200": {
            "description": "Committed revision, compact presentation receipt, current status basis, and visible clocks.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TurnCommitResult" } } }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" },
          "504": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/campaigns/{campaignId}/rules/search": {
      "post": {
        "operationId": "searchRules",
        "summary": "Find campaign-relevant book and table rules",
        "description": "Searches the campaign system and edition plus its play-group table rules. Results identify the mechanic, source, source kind, heading, and optional URI.",
        "parameters": [
          { "$ref": "#/components/parameters/CampaignId" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleSearch" } } }
        },
        "responses": {
          "200": {
            "description": "Ranked rule hits with book or table provenance.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleSearchResult" } } }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" },
          "504": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/campaigns/{campaignId}/retcons": {
      "post": {
        "operationId": "applyRetcon",
        "summary": "Apply an explicit user-confirmed correction",
        "description": "Replaces exactly one knowledge, entity, clock, campaign-time, or character-status record. The requestId is the idempotent operation identity. Explain the returned alterations to the user.",
        "parameters": [
          { "$ref": "#/components/parameters/CampaignId" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Retcon" } } }
        },
        "responses": {
          "200": {
            "description": "New revision and exact before/after alteration summary.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RetconResult" } } }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" },
          "504": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer" }
    },
    "parameters": {
      "CampaignId": {
        "name": "campaignId",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "Error": {
        "description": "Compact machine-actionable failure. On revision conflict, currentRevision is supplied when known.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["ok", "code"],
        "additionalProperties": false,
        "properties": {
          "ok": { "const": false },
          "code": { "type": "string", "minLength": 1, "maxLength": 128 },
          "message": { "type": "string" },
          "currentRevision": { "type": "integer", "minimum": 0 }
        }
      },
      "OperationId": {
        "type": "string",
        "minLength": 1,
        "maxLength": 128,
        "pattern": "^[A-Za-z0-9._-]+$"
      },
      "EntityKey": {
        "type": "string",
        "minLength": 1,
        "maxLength": 120
      },
      "FictionalTime": {
        "type": "object",
        "required": ["date", "time", "timeZone"],
        "additionalProperties": false,
        "properties": {
          "date": { "type": "string", "format": "date" },
          "time": { "type": "string", "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9](\\.[0-9]{1,7})?)?$" },
          "timeZone": { "type": "string", "minLength": 1, "maxLength": 80 }
        }
      },
      "Entity": {
        "type": "object",
        "required": ["key", "name", "kind"],
        "additionalProperties": false,
        "properties": {
          "key": { "$ref": "#/components/schemas/EntityKey" },
          "name": { "type": "string", "minLength": 1, "maxLength": 200 },
          "kind": { "type": "string", "enum": ["playerCharacter", "nonPlayerCharacter", "faction", "location", "item", "concept"] },
          "summary": { "type": ["string", "null"], "maxLength": 2000 }
        }
      },
      "KnowledgeTag": {
        "type": "object",
        "required": ["entityKey", "role"],
        "additionalProperties": false,
        "properties": {
          "entityKey": { "$ref": "#/components/schemas/EntityKey" },
          "role": { "type": "string", "enum": ["subject", "target", "knower", "source", "related"] }
        }
      },
      "Knowledge": {
        "type": "object",
        "required": ["key", "statement", "visibility"],
        "additionalProperties": false,
        "properties": {
          "key": { "type": "string", "minLength": 1, "maxLength": 160 },
          "statement": { "type": "string", "minLength": 1, "maxLength": 8000 },
          "visibility": { "type": "string", "enum": ["gmOnly", "restricted", "player"] },
          "tags": { "type": "array", "items": { "$ref": "#/components/schemas/KnowledgeTag" } }
        }
      },
      "ClockChange": {
        "type": "object",
        "required": ["key"],
        "additionalProperties": false,
        "properties": {
          "key": { "type": "string", "minLength": 1, "maxLength": 120 },
          "name": { "type": ["string", "null"], "maxLength": 200 },
          "totalSegments": { "type": ["integer", "null"], "minimum": 1 },
          "delta": { "type": "integer", "default": 0 },
          "isPlayerVisible": { "type": ["boolean", "null"] },
          "progressesOffScreen": { "type": ["boolean", "null"] },
          "ownerEntityKey": { "type": ["string", "null"] }
        }
      },
      "CharacterStatusChange": {
        "type": "object",
        "required": ["entityKey", "key", "value"],
        "additionalProperties": false,
        "properties": {
          "entityKey": { "$ref": "#/components/schemas/EntityKey" },
          "key": { "type": "string", "minLength": 1, "maxLength": 80 },
          "value": { "type": "string", "minLength": 1, "maxLength": 400 }
        }
      },
      "TurnCommit": {
        "type": "object",
        "required": ["requestId", "expectedRevision", "presentation"],
        "additionalProperties": false,
        "properties": {
          "requestId": { "$ref": "#/components/schemas/OperationId" },
          "expectedRevision": { "type": "integer", "minimum": 1 },
          "presentation": { "type": "string", "minLength": 1, "maxLength": 1000000, "description": "Exact text to be shown to the user; the server persists a compressed copy." },
          "presentedToEntityKey": { "$ref": "#/components/schemas/EntityKey" },
          "fictionalTime": { "$ref": "#/components/schemas/FictionalTime" },
          "entities": { "type": "array", "items": { "$ref": "#/components/schemas/Entity" } },
          "knowledge": { "type": "array", "items": { "$ref": "#/components/schemas/Knowledge" } },
          "clocks": { "type": "array", "items": { "$ref": "#/components/schemas/ClockChange" } },
          "characterStatuses": { "type": "array", "items": { "$ref": "#/components/schemas/CharacterStatusChange" } }
        }
      },
      "RuleSearch": {
        "type": "object",
        "required": ["text"],
        "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "minLength": 1, "maxLength": 300 },
          "limit": { "type": "integer", "minimum": 1, "maximum": 10, "default": 5 }
        }
      },
      "RetconTarget": {
        "type": "object",
        "required": ["kind"],
        "additionalProperties": false,
        "properties": {
          "kind": { "type": "string", "enum": ["knowledge", "entity", "clock", "campaignTime", "characterStatus"] },
          "key": { "type": ["string", "null"], "maxLength": 160 },
          "entityKey": { "type": ["string", "null"], "maxLength": 120 }
        }
      },
      "KnowledgeReplacement": {
        "type": "object",
        "required": ["statement", "visibility"],
        "additionalProperties": false,
        "properties": {
          "statement": { "type": "string", "minLength": 1, "maxLength": 8000 },
          "visibility": { "type": "string", "enum": ["gmOnly", "restricted", "player"] },
          "tags": { "type": "array", "items": { "$ref": "#/components/schemas/KnowledgeTag" } }
        }
      },
      "EntityReplacement": {
        "type": "object",
        "required": ["name", "kind"],
        "additionalProperties": false,
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 200 },
          "kind": { "type": "string", "enum": ["playerCharacter", "nonPlayerCharacter", "faction", "location", "item", "concept"] },
          "summary": { "type": ["string", "null"], "maxLength": 2000 }
        }
      },
      "ClockReplacement": {
        "type": "object",
        "required": ["name", "current", "total", "playerVisible", "offScreen"],
        "additionalProperties": false,
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 200 },
          "current": { "type": "integer", "minimum": 0 },
          "total": { "type": "integer", "minimum": 1 },
          "playerVisible": { "type": "boolean" },
          "offScreen": { "type": "boolean" },
          "ownerEntityKey": { "type": ["string", "null"] }
        }
      },
      "CharacterStatusReplacement": {
        "type": "object",
        "required": ["value"],
        "additionalProperties": false,
        "properties": {
          "value": { "type": "string", "minLength": 1, "maxLength": 400 }
        }
      },
      "Retcon": {
        "type": "object",
        "required": ["requestId", "expectedRevision", "userConfirmed", "userDirective", "requestedBy", "confirmedBy", "changeSummary", "target"],
        "additionalProperties": false,
        "properties": {
          "requestId": { "$ref": "#/components/schemas/OperationId" },
          "expectedRevision": { "type": "integer", "minimum": 1 },
          "userConfirmed": { "const": true },
          "userDirective": { "type": "string", "minLength": 1, "maxLength": 2000 },
          "requestedBy": { "type": "string", "minLength": 1, "maxLength": 200 },
          "confirmedBy": { "type": "string", "minLength": 1, "maxLength": 200 },
          "changeSummary": { "type": "string", "minLength": 1, "maxLength": 4000 },
          "target": { "$ref": "#/components/schemas/RetconTarget" },
          "knowledge": { "$ref": "#/components/schemas/KnowledgeReplacement" },
          "entity": { "$ref": "#/components/schemas/EntityReplacement" },
          "clock": { "$ref": "#/components/schemas/ClockReplacement" },
          "campaignTime": { "$ref": "#/components/schemas/FictionalTime" },
          "characterStatus": { "$ref": "#/components/schemas/CharacterStatusReplacement" }
        },
        "oneOf": [
          {
            "properties": { "target": { "properties": { "kind": { "const": "knowledge" } }, "required": ["kind"] } },
            "required": ["knowledge"],
            "not": { "anyOf": [ { "required": ["entity"] }, { "required": ["clock"] }, { "required": ["campaignTime"] }, { "required": ["characterStatus"] } ] }
          },
          {
            "properties": { "target": { "properties": { "kind": { "const": "entity" } }, "required": ["kind"] } },
            "required": ["entity"],
            "not": { "anyOf": [ { "required": ["knowledge"] }, { "required": ["clock"] }, { "required": ["campaignTime"] }, { "required": ["characterStatus"] } ] }
          },
          {
            "properties": { "target": { "properties": { "kind": { "const": "clock" } }, "required": ["kind"] } },
            "required": ["clock"],
            "not": { "anyOf": [ { "required": ["knowledge"] }, { "required": ["entity"] }, { "required": ["campaignTime"] }, { "required": ["characterStatus"] } ] }
          },
          {
            "properties": { "target": { "properties": { "kind": { "const": "campaignTime" } }, "required": ["kind"] } },
            "required": ["campaignTime"],
            "not": { "anyOf": [ { "required": ["knowledge"] }, { "required": ["entity"] }, { "required": ["clock"] }, { "required": ["characterStatus"] } ] }
          },
          {
            "properties": { "target": { "properties": { "kind": { "const": "characterStatus" } }, "required": ["kind"] } },
            "required": ["characterStatus"],
            "not": { "anyOf": [ { "required": ["knowledge"] }, { "required": ["entity"] }, { "required": ["clock"] }, { "required": ["campaignTime"] } ] }
          }
        ]
      },
      "Audience": {
        "type": "object",
        "required": ["kind"],
        "additionalProperties": false,
        "properties": {
          "kind": { "type": "string", "enum": ["gm", "playerCharacter", "nonPlayerCharacter"] },
          "entity": { "type": "string" }
        }
      },
      "StatusValue": {
        "type": "object",
        "required": ["key", "value"],
        "additionalProperties": false,
        "properties": {
          "key": { "type": "string" },
          "value": { "type": "string" }
        }
      },
      "CharacterStatus": {
        "type": "object",
        "required": ["entity", "status"],
        "additionalProperties": false,
        "properties": {
          "entity": { "type": "string" },
          "status": { "type": "array", "items": { "$ref": "#/components/schemas/StatusValue" } }
        }
      },
      "CampaignBasis": {
        "type": "object",
        "required": ["date", "time", "timeZone", "characters"],
        "additionalProperties": false,
        "properties": {
          "date": { "type": "string" },
          "time": { "type": "string" },
          "timeZone": { "type": "string" },
          "characters": { "type": "array", "items": { "$ref": "#/components/schemas/CharacterStatus" } }
        }
      },
      "Presentation": {
        "type": "object",
        "required": ["id", "sequence", "text", "digest", "bytes", "compressedBytes"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "sequence": { "type": "integer", "minimum": 1 },
          "text": { "type": "string" },
          "digest": { "type": "string" },
          "bytes": { "type": "integer", "minimum": 0 },
          "compressedBytes": { "type": "integer", "minimum": 0 }
        }
      },
      "CompactPresentation": {
        "type": "object",
        "required": ["id", "sequence", "digest", "bytes", "compressedBytes"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "sequence": { "type": "integer", "minimum": 1 },
          "digest": { "type": "string" },
          "bytes": { "type": "integer", "minimum": 0 },
          "compressedBytes": { "type": "integer", "minimum": 0 }
        }
      },
      "KnowledgeViewTag": {
        "type": "object",
        "required": ["entity", "role"],
        "additionalProperties": false,
        "properties": {
          "entity": { "type": "string" },
          "role": { "type": "string", "enum": ["subject", "target", "knower", "source", "related"] }
        }
      },
      "KnowledgeView": {
        "type": "object",
        "required": ["key", "statement", "visibility", "tags"],
        "additionalProperties": false,
        "properties": {
          "key": { "type": "string" },
          "statement": { "type": "string" },
          "visibility": { "type": "string", "enum": ["gmOnly", "restricted", "player"] },
          "tags": { "type": "array", "items": { "$ref": "#/components/schemas/KnowledgeViewTag" } }
        }
      },
      "ClockView": {
        "type": "object",
        "required": ["key", "name", "current", "total", "playerVisible", "offScreen"],
        "additionalProperties": false,
        "properties": {
          "key": { "type": "string" },
          "name": { "type": "string" },
          "current": { "type": "integer", "minimum": 0 },
          "total": { "type": "integer", "minimum": 1 },
          "owner": { "type": "string" },
          "playerVisible": { "type": "boolean" },
          "offScreen": { "type": "boolean" }
        }
      },
      "CampaignContext": {
        "type": "object",
        "required": ["campaignId", "campaign", "system", "edition", "revision", "audience", "basis", "knowledge", "clocks"],
        "additionalProperties": false,
        "properties": {
          "campaignId": { "type": "string", "format": "uuid" },
          "campaign": { "type": "string" },
          "system": { "type": "string" },
          "edition": { "type": "string" },
          "revision": { "type": "integer", "minimum": 0 },
          "audience": { "$ref": "#/components/schemas/Audience" },
          "basis": { "$ref": "#/components/schemas/CampaignBasis" },
          "presentation": { "$ref": "#/components/schemas/Presentation" },
          "knowledge": { "type": "array", "items": { "$ref": "#/components/schemas/KnowledgeView" } },
          "clocks": { "type": "array", "items": { "$ref": "#/components/schemas/ClockView" } }
        }
      },
      "TurnCommitResult": {
        "type": "object",
        "required": ["revision", "presentation", "basis", "clocks"],
        "additionalProperties": false,
        "properties": {
          "revision": { "type": "integer", "minimum": 1 },
          "presentation": { "$ref": "#/components/schemas/CompactPresentation" },
          "basis": { "$ref": "#/components/schemas/CampaignBasis" },
          "clocks": { "type": "array", "items": { "$ref": "#/components/schemas/ClockView" } }
        }
      },
      "RetconAlteration": {
        "type": "object",
        "required": ["field"],
        "additionalProperties": false,
        "properties": {
          "field": { "type": "string" },
          "before": { "type": "string" },
          "after": { "type": "string" }
        }
      },
      "RetconResult": {
        "type": "object",
        "required": ["auditId", "revision", "summary", "target", "altered"],
        "additionalProperties": false,
        "properties": {
          "auditId": { "type": "string", "format": "uuid" },
          "revision": { "type": "integer", "minimum": 1 },
          "summary": { "type": "string" },
          "target": { "type": "string" },
          "altered": { "type": "array", "items": { "$ref": "#/components/schemas/RetconAlteration" } }
        }
      },
      "RuleSearchHit": {
        "type": "object",
        "required": ["mechanics", "heading", "source", "kind"],
        "additionalProperties": false,
        "properties": {
          "mechanics": { "type": "string" },
          "heading": { "type": "string" },
          "source": { "type": "string" },
          "kind": { "type": "string", "enum": ["book", "table"] },
          "table": { "type": "string" },
          "uri": { "type": "string" }
        }
      },
      "RuleSearchResult": {
        "type": "object",
        "required": ["query", "hits"],
        "additionalProperties": false,
        "properties": {
          "query": { "type": "string" },
          "hits": { "type": "array", "items": { "$ref": "#/components/schemas/RuleSearchHit" } }
        }
      }
    }
  }
}
