{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://naxodev.com/apnea/state.schema.json",
  "title": "ApneaRunState",
  "type": "object",
  "additionalProperties": false,
  "required": ["version", "slug", "step", "phase_index", "rounds", "vcs"],
  "allOf": [
    {
      "if": {
        "properties": { "version": { "const": 2 } },
        "required": ["version"]
      },
      "then": { "required": ["pending_commit"] },
      "description": "Runtime enforcement is authoritative: decodeRunState applies the same rule plus consistency checks the JSON Schema cannot express."
    }
  ],
  "properties": {
    "run_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
      "description": "New runs use unique namespaces. Absent on legacy active runs."
    },
    "acquired_panes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["pane_id", "label"],
        "properties": {
          "pane_id": { "type": "string" },
          "label": { "type": "string" }
        }
      }
    },
    "version": {
      "enum": [1, 2],
      "description": "Version 1 is accepted INPUT for migration (decodes with pending_commit=null); version 2 is the OUTPUT every save writes and must carry pending_commit explicitly."
    },
    "slug": { "type": "string", "minLength": 1 },
    "step": {
      "type": "string",
      "enum": [
        "planning",
        "plan_review",
        "phase_packaging",
        "coding",
        "code_review",
        "committing",
        "finishing",
        "done"
      ]
    },
    "phase_index": {
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "phase_count_hint": {
      "type": ["integer", "null"],
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "rounds": {
      "type": "object",
      "description": "Keys like phase-01/plan_review or phase-01/code_review",
      "additionalProperties": {
        "type": "integer",
        "minimum": 1,
        "maximum": 9007199254740991
      }
    },
    "vcs": { "type": "string", "enum": ["jj", "git"] },
    "allow_dirty": { "type": "boolean" },
    "goal": { "type": "string" },
    "last_error": { "type": ["string", "null"] },
    "pending_artifact": {
      "type": ["string", "null"],
      "pattern": "^\\.apnea\\/(?!.*\\u0000)(?!\\.(?:/|$)|\\.\\.(?:/|$))(?!.*\\/\\.(?:\\/|$))(?!.*\\/\\.\\.(?:\\/|$))(?!.*\\\\)[^/]+(?:/[^/]+)*$"
    },
    "pending_role": { "type": ["string", "null"] },
    "pending_delivery": {
      "type": ["string", "null"],
      "enum": ["manual", "interactive", null],
      "description": "Known delivery boundary for pending ownership; null is idle or legacy-ambiguous"
    },
    "pending_floating_exit": {
      "type": "null",
      "deprecated": true,
      "description": "Deprecated migration-only field. Missing or null values migrate; active non-null legacy floating runs must be abandoned."
    },
    "pending_pane_id": { "type": ["string", "null"] },
    "pending_pane_label": { "type": ["string", "null"] },
    "pending_started_at": {
      "type": ["integer", "null"],
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "pending_deadline_ms": {
      "type": ["integer", "null"],
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "pending_nudged_at": {
      "type": ["integer", "null"],
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "pending_final_grace": { "type": "boolean" },
    "pending_extended": { "type": "boolean" },
    "role_panes": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["pane_id", "label"],
        "properties": {
          "pane_id": { "type": "string" },
          "label": { "type": "string" },
          "profile_fingerprint": { "type": ["string", "null"] }
        }
      }
    },
    "package_root": { "type": "string" },
    "reviewer_tree_fingerprint": { "type": ["string", "null"] },
    "current_phase_package": {
      "type": ["string", "null"],
      "pattern": "^\\.apnea\\/(?!.*\\u0000)(?!\\.(?:/|$)|\\.\\.(?:/|$))(?!.*\\/\\.(?:\\/|$))(?!.*\\/\\.\\.(?:\\/|$))(?!.*\\\\)[^/]+(?:/[^/]+)*$"
    },
    "current_code_review": {
      "type": ["string", "null"],
      "pattern": "^\\.apnea\\/(?!.*\\u0000)(?!\\.(?:/|$)|\\.\\.(?:/|$))(?!.*\\/\\.(?:\\/|$))(?!.*\\/\\.\\.(?:\\/|$))(?!.*\\\\)[^/]+(?:/[^/]+)*$"
    },
    "required_rework": {
      "type": ["string", "null"],
      "enum": ["plan", "code", "phase_package", null],
      "description": "Exact dispatch target that owns the next review round"
    },
    "pending_commit": {
      "description": "Durable record of an in-flight commit transaction (crash-recoverable commit completion). Requires step=committing and phase_index equal to the run's phase_index; non-committing states must carry null. Version-1 files must not contain this key.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "backend",
            "id",
            "phase_index",
            "message",
            "no_remaining_phases",
            "verify_log",
            "branch",
            "parent_commit",
            "tree_id"
          ],
          "properties": {
            "backend": { "const": "git" },
            "id": {
              "type": "string",
              "minLength": 1,
              "description": "Transaction uuid; appears in the commit message as Apnea-Transaction: <id>"
            },
            "phase_index": { "type": "integer", "minimum": 1 },
            "message": {
              "type": "string",
              "minLength": 1,
              "description": "Full commit message including the Apnea-Transaction trailer line"
            },
            "no_remaining_phases": { "type": "boolean" },
            "verify_log": { "type": "string", "minLength": 1 },
            "branch": {
              "type": "string",
              "minLength": 1,
              "description": "Full ref the commit must land on, e.g. refs/heads/apnea/<slug>"
            },
            "parent_commit": {
              "type": "string",
              "minLength": 1,
              "description": "Expected HEAD at completion; also the created commit's parent"
            },
            "tree_id": {
              "type": "string",
              "minLength": 1,
              "description": "Prepared tree id staged with .apnea excluded"
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "backend",
            "id",
            "phase_index",
            "message",
            "no_remaining_phases",
            "verify_log",
            "change_id",
            "content_fingerprint"
          ],
          "properties": {
            "backend": { "const": "jj" },
            "id": {
              "type": "string",
              "minLength": 1,
              "description": "Transaction uuid; appears in the change description as Apnea-Transaction: <id>"
            },
            "phase_index": { "type": "integer", "minimum": 1 },
            "message": {
              "type": "string",
              "minLength": 1,
              "description": "Full description including the Apnea-Transaction trailer line"
            },
            "no_remaining_phases": { "type": "boolean" },
            "verify_log": { "type": "string", "minLength": 1 },
            "change_id": {
              "type": "string",
              "minLength": 1,
              "description": "Change id of the described working-copy change"
            },
            "content_fingerprint": {
              "type": "string",
              "minLength": 1,
              "description": "Fingerprint of the change's non-.apnea diff at preparation time"
            }
          }
        }
      ]
    },
    "phase_package_rework": {
      "type": "boolean",
      "deprecated": true,
      "description": "Deprecated migration-only field. True migrates to required_rework=phase_package."
    }
  }
}
