{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://kindlyrobotics.com/clinical/kindly-episode.schema-v0.1.json",
  "title": "Kindly Robotics clinical episode (v0.1)",
  "description": "Machine-readable JSON Schema for a single Kindly Robotics clinical-workflow annotation episode export, v0.1. Describes the four-layer label sidecar (L1 phase/step, L2 action triplets, L3 instrument/object state, L4 workflow-context events) plus a derived RLDS excerpt. This is an interop schema, not a lockdown: object nodes allow additional properties so a partner's superset still validates, and only the core fields that appear across the worked sample episode are required. Synthetic-friendly — the schema makes no claim that any episode contains real clinical capture. v0.1 / in development; will evolve with first-cohort design partners.",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "export_format",
    "schema_version",
    "episode_envelope",
    "labels",
    "rlds_excerpt"
  ],
  "properties": {
    "export_format": {
      "type": "string",
      "description": "Export-container format identifier, e.g. 'kindly-labeling-export/1.0'."
    },
    "schema_version": {
      "type": "string",
      "description": "SemVer of the annotation schema this episode targets, e.g. '0.1.0'."
    },
    "vocab_ref": {
      "type": "string",
      "description": "Reference to the versioned closed vocabulary for this workflow family."
    },
    "synthetic": {
      "type": "boolean",
      "description": "True when every value in the episode is synthetic and no clinical capture occurred."
    },
    "_readme": {
      "type": "object",
      "description": "Human-readable orientation block. Free-form; not load-bearing for validation.",
      "additionalProperties": true
    },
    "episode_envelope": {
      "$ref": "#/$defs/episodeEnvelope"
    },
    "labels": {
      "type": "object",
      "additionalProperties": true,
      "required": ["L1", "L2", "L3", "L4"],
      "description": "The four independent label tracks over the episode timeline. Each is an array; a buyer may consume any subset.",
      "properties": {
        "L1": {
          "type": "array",
          "description": "Workflow phase/step segments.",
          "items": { "$ref": "#/$defs/l1Segment" }
        },
        "L2": {
          "type": "array",
          "description": "Action triplets <actor-role, verb, object> as timestamped intervals.",
          "items": { "$ref": "#/$defs/l2Triplet" }
        },
        "L3": {
          "type": "array",
          "description": "Per-object instrument/object state transitions.",
          "items": { "$ref": "#/$defs/l3StateTrack" }
        },
        "L4": {
          "type": "array",
          "description": "Workflow-context events: interruptions, rework, deviations, handoffs.",
          "items": { "$ref": "#/$defs/l4Event" }
        }
      }
    },
    "rlds_excerpt": {
      "$ref": "#/$defs/rldsExcerpt"
    }
  },
  "$defs": {
    "confidence": {
      "type": "string",
      "description": "Annotator confidence band. Closed in spirit but kept open in v0.1 for interop.",
      "examples": ["high", "medium", "low"]
    },
    "objectState": {
      "type": "object",
      "additionalProperties": true,
      "description": "An object's state at a point in time.",
      "required": ["zone", "sterility", "in_count"],
      "properties": {
        "zone": {
          "type": "string",
          "description": "Spatial zone, e.g. 'staging_mat', 'tray_well_3', 'absent'."
        },
        "sterility": {
          "type": "string",
          "description": "Sterility state, e.g. 'decontaminated', 'unknown'."
        },
        "in_count": {
          "type": "boolean",
          "description": "Whether the object is currently included in the tray count."
        }
      }
    },
    "episodeEnvelope": {
      "type": "object",
      "additionalProperties": true,
      "description": "Per-episode metadata: identity, capture, provenance, de-identification, consent, and the expected-contents manifest.",
      "required": ["episode_id", "schema_version", "synthetic", "workflow_family"],
      "properties": {
        "episode_id": {
          "type": "string",
          "description": "Stable identifier for this episode."
        },
        "schema_version": {
          "type": "string",
          "description": "SemVer of the schema this episode targets."
        },
        "synthetic": {
          "type": "boolean",
          "description": "True for synthetic/paper-example episodes with no real capture."
        },
        "data_provenance": {
          "type": "string",
          "description": "How the episode data was produced, e.g. 'synthetic_paper_example_no_capture'."
        },
        "site_pseudonym": {
          "type": "string",
          "description": "Pseudonymous site identifier; never a real facility name."
        },
        "workflow_family": {
          "type": "string",
          "description": "Workflow family identifier, e.g. 'SPD.tray_assembly'."
        },
        "boundary_def": {
          "type": "string",
          "description": "Versioned definition of the episode's start/end boundary."
        },
        "vocab_ref": {
          "type": "string",
          "description": "Reference to the versioned closed vocabulary for this workflow family."
        },
        "tray_map_ref": {
          "type": "string",
          "description": "Reference to the tray-map / layout artifact."
        },
        "program_phase": {
          "type": "string",
          "description": "Capture program phase, e.g. 'pilot'."
        },
        "capture_rig_id": {
          "type": "string",
          "description": "Pseudonymous identifier of the capture rig."
        },
        "operator_pseudonyms": {
          "type": "array",
          "description": "Pseudonymous operator identifiers.",
          "items": { "type": "string" }
        },
        "session_log": {
          "type": "object",
          "additionalProperties": true,
          "description": "Free-form session-level metadata (task type, anomaly flags, notes)."
        },
        "expected_contents": {
          "type": "object",
          "additionalProperties": true,
          "description": "Manifest of items the episode is expected to contain (drives expected-but-absent detection).",
          "required": ["items"],
          "properties": {
            "count_sheet_ref": {
              "type": "string",
              "description": "Reference to the authoritative count sheet."
            },
            "items": {
              "type": "array",
              "description": "Expected items with object class and quantity.",
              "items": {
                "type": "object",
                "additionalProperties": true,
                "required": ["object_class", "qty"],
                "properties": {
                  "object_class": { "type": "string" },
                  "qty": { "type": "integer", "minimum": 0 }
                }
              }
            }
          }
        },
        "capture": {
          "type": "object",
          "additionalProperties": true,
          "description": "Capture parameters for the episode footage.",
          "required": ["fps", "duration_s", "modalities", "frame_count"],
          "properties": {
            "fps": {
              "type": "number",
              "exclusiveMinimum": 0,
              "description": "Frames per second."
            },
            "duration_s": {
              "type": "number",
              "minimum": 0,
              "description": "Episode duration in seconds."
            },
            "modalities": {
              "type": "array",
              "description": "Capture modalities, e.g. 'rgb_egocentric', 'rgb_overhead'.",
              "items": { "type": "string" }
            },
            "frame_count": {
              "type": "integer",
              "minimum": 0,
              "description": "Total frame count."
            }
          }
        },
        "deid": {
          "type": "object",
          "additionalProperties": true,
          "description": "De-identification status and method for the episode.",
          "required": ["status"],
          "properties": {
            "status": {
              "type": "string",
              "description": "De-identification status, e.g. 'verified', 'pending'."
            },
            "method": {
              "type": "string",
              "description": "De-identification method(s) applied."
            },
            "reviewed_by": { "type": "string" },
            "reviewed_at": {
              "type": "string",
              "description": "ISO-8601 timestamp of de-id review.",
              "format": "date-time"
            },
            "phi_residual_risk": {
              "type": "string",
              "description": "Residual PHI risk assessment, e.g. 'low'."
            }
          }
        },
        "consent": {
          "type": "object",
          "additionalProperties": true,
          "description": "Consent and data-processing references for the episode.",
          "properties": {
            "staff_consent_ref": { "type": "string" },
            "facility_dpa_ref": { "type": "string" },
            "patient_in_frame": {
              "type": "boolean",
              "description": "Whether any patient appears in frame. For released episodes this should be false."
            }
          }
        },
        "task_summary": {
          "type": "string",
          "description": "Short prose summary of the task performed in the episode."
        },
        "annotation_provenance": {
          "type": "object",
          "additionalProperties": true,
          "description": "Who annotated and reviewed the episode and by what consensus method.",
          "properties": {
            "annotators": {
              "type": "array",
              "items": { "type": "string" }
            },
            "clinical_reviewer": { "type": "string" },
            "consensus_method": { "type": "string" }
          }
        }
      }
    },
    "l1Segment": {
      "type": "object",
      "additionalProperties": true,
      "description": "An L1 phase/step segment over [t_start_s, t_end_s].",
      "required": ["layer", "t_start_s", "t_end_s", "phase", "step"],
      "properties": {
        "layer": { "const": "L1" },
        "t_start_s": { "type": "number", "minimum": 0 },
        "t_end_s": { "type": "number", "minimum": 0 },
        "phase": {
          "type": "string",
          "description": "Workflow phase, e.g. 'assemble', 'wrap', 'sterilize'."
        },
        "step": {
          "type": "string",
          "description": "Ordered step within the phase."
        },
        "confidence": { "$ref": "#/$defs/confidence" },
        "out_of_episode": {
          "type": "boolean",
          "description": "True when the segment falls past the episode boundary definition."
        },
        "note": { "type": "string" }
      }
    },
    "l2Triplet": {
      "type": "object",
      "additionalProperties": true,
      "description": "An L2 action triplet <actor_role, verb, object> as a timestamped interval.",
      "required": ["layer", "actor_role", "verb", "object", "t_start_s", "t_end_s"],
      "properties": {
        "layer": { "const": "L2" },
        "actor_role": {
          "type": "string",
          "description": "Who acts, e.g. 'tech', 'tech2', 'nurse', 'assist', 'robot', 'surgeon'."
        },
        "verb": {
          "type": "string",
          "description": "Action verb from the workflow vocabulary, e.g. 'place', 'grasp', 'inspect'."
        },
        "object": {
          "type": "string",
          "description": "Object the action targets."
        },
        "t_start_s": { "type": "number", "minimum": 0 },
        "t_end_s": { "type": "number", "minimum": 0 },
        "target_zone": {
          "type": "string",
          "description": "Destination/target zone for the action."
        },
        "count": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of like objects acted on, when the triplet covers a group."
        },
        "counterparty_role": {
          "type": "string",
          "description": "Receiving role for handoff-style verbs (giver = actor_role)."
        },
        "evidence": {
          "type": "string",
          "description": "Evidence qualifier, e.g. 'masked' when the on-screen evidence is hard-masked."
        },
        "confidence": { "$ref": "#/$defs/confidence" },
        "note": { "type": "string" }
      }
    },
    "l3StateTrack": {
      "type": "object",
      "additionalProperties": true,
      "description": "An L3 per-object state transition emitted at time t_s.",
      "required": ["layer", "object_id", "object_class", "state", "t_s"],
      "properties": {
        "layer": { "const": "L3" },
        "object_id": {
          "type": "string",
          "description": "Stable per-object instance id, e.g. 'obj_needle_driver_01'."
        },
        "object_class": {
          "type": "string",
          "description": "Object class from the vocabulary, e.g. 'needle_driver'."
        },
        "state": { "$ref": "#/$defs/objectState" },
        "transition_from": {
          "description": "Prior state, or null for first observation / expected-but-absent objects.",
          "oneOf": [
            { "$ref": "#/$defs/objectState" },
            { "type": "null" }
          ]
        },
        "t_s": {
          "type": "number",
          "minimum": 0,
          "description": "Timestamp of the transition in seconds."
        },
        "count_event": {
          "description": "Count-relevant event, e.g. 'added_to_tray', 'recount', 'discrepancy_flagged', 'substituted_in', 'substituted_out', 'relocated_within_tray', or null.",
          "type": ["string", "null"]
        },
        "confidence": { "$ref": "#/$defs/confidence" },
        "note": { "type": "string" }
      }
    },
    "l4Event": {
      "type": "object",
      "additionalProperties": true,
      "description": "An L4 workflow-context event over [t_start_s, t_end_s].",
      "required": ["layer", "label_id", "event_type", "t_start_s", "t_end_s"],
      "properties": {
        "layer": { "const": "L4" },
        "label_id": {
          "type": "string",
          "description": "Stable id for cross-referencing (e.g. via caused_by)."
        },
        "event_type": {
          "type": "string",
          "description": "Event type, e.g. 'exception', 'handoff', 'rework'."
        },
        "subtype": {
          "type": "string",
          "description": "Finer-grained subtype, e.g. 'workflow_blocked_missing_item', 'sanctioned_substitution'."
        },
        "t_start_s": { "type": "number", "minimum": 0 },
        "t_end_s": { "type": "number", "minimum": 0 },
        "triggered_by": {
          "type": "string",
          "description": "Step or condition that triggered the event."
        },
        "resolution": {
          "type": "string",
          "description": "How the event resolved, e.g. 'resolved_substitution', 'completed'."
        },
        "severity": {
          "type": "string",
          "description": "Severity band, e.g. 'minor', 'major'."
        },
        "actor_role": {
          "type": "string",
          "description": "Primary actor role for the event."
        },
        "counterparty_role": {
          "type": "string",
          "description": "Counterparty role (e.g. receiver of a handoff)."
        },
        "deviation_from_protocol_ref": {
          "description": "Reference to a violated protocol, or null when the action was protocol-correct.",
          "type": ["string", "null"]
        },
        "protocol_followed_ref": {
          "description": "Reference to the authorizing protocol when the action was sanctioned, or null.",
          "type": ["string", "null"]
        },
        "caused_by": {
          "type": "array",
          "description": "label_id values of upstream events that caused this one.",
          "items": { "type": "string" }
        },
        "confidence": { "$ref": "#/$defs/confidence" },
        "note": { "type": "string" }
      }
    },
    "rldsExcerpt": {
      "type": "object",
      "additionalProperties": true,
      "description": "A derived RLDS slice over the episode. The L1-L4 sidecar remains the source of truth; this is a regenerable flattening.",
      "required": ["slice", "rlds_steps"],
      "properties": {
        "_note": { "type": "string" },
        "episode_id": { "type": "string" },
        "synthetic": { "type": "boolean" },
        "slice": {
          "type": "object",
          "additionalProperties": true,
          "description": "The time window and decimation of the excerpt.",
          "required": ["t_start_s", "t_end_s"],
          "properties": {
            "t_start_s": { "type": "number", "minimum": 0 },
            "t_end_s": { "type": "number", "minimum": 0 },
            "decimation_hz": {
              "type": "number",
              "exclusiveMinimum": 0,
              "description": "Decimation rate of the derived view in Hz."
            }
          }
        },
        "rlds_steps": {
          "type": "array",
          "description": "RLDS step records in the OXE step/episode model.",
          "items": { "$ref": "#/$defs/rldsStep" }
        }
      }
    },
    "rldsStep": {
      "type": "object",
      "additionalProperties": true,
      "description": "A single RLDS step record.",
      "required": ["t_s", "observation", "action", "reward"],
      "properties": {
        "t_s": { "type": "number", "minimum": 0 },
        "is_first": { "type": "boolean" },
        "is_last": { "type": "boolean" },
        "is_terminal": { "type": "boolean" },
        "observation": {
          "type": "object",
          "additionalProperties": true,
          "description": "Observation at this step: phase/step, active triplet, object states, active context events.",
          "properties": {
            "phase": { "type": "string" },
            "step": { "type": "string" },
            "active_triplet": {
              "type": "array",
              "description": "[actor_role, verb, object] currently active.",
              "items": { "type": "string" }
            },
            "object_states": {
              "type": "object",
              "description": "Map of object_id -> object state.",
              "additionalProperties": { "$ref": "#/$defs/objectState" }
            },
            "context_events": {
              "type": "array",
              "description": "L4 context events active at this step.",
              "items": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "action": {
          "type": "object",
          "additionalProperties": true,
          "description": "Action taken at this step.",
          "properties": {
            "actor_role": { "type": "string" },
            "verb": { "type": "string" },
            "target_zone": { "type": "string" }
          }
        },
        "reward": {
          "type": "number",
          "description": "Reward for the step. Kindly ships events, not reward shaping; reward is 0.0 by default."
        },
        "discount": {
          "type": "number",
          "description": "RLDS discount factor for the step."
        }
      }
    }
  }
}
