{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://agenticobservability.app/crew-record/crew-record.v0.schema.json",
  "title": "Crew Record v0",
  "$comment": "Crew Record v0, (c) 2026 RAIVEN (raiven.eu). Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0): https://creativecommons.org/licenses/by/4.0/. Credit: Crew Record by RAIVEN, https://agenticobservability.app/crew-record/.",
  "description": "The open data format Crew Lens reads: one JSON document describing a crew of AI coding agents over a stretch of time (its agents, work items, reviews, sprints, the metadata of the messages between agents, and the moments worth finding again). Timestamps are ISO 8601 in UTC. Agent ids, work ids and sprint ids are the ids of the source harness. A collection or field that is absent means 'not recorded', not 'none happened', and a reader must not count it as zero. Fields not named here are allowed and ignored by readers that do not know them. Message bodies, prompts and code are not part of the record. Made by RAIVEN (raiven.eu) and licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/); see https://agenticobservability.app/crew-record/.",
  "type": "object",
  "required": ["schema_version", "generated_at", "agents", "work", "events"],
  "properties": {
    "schema_version": {
      "description": "The format and its version. Files written by the current Crew Lens builds may still carry the earlier identifier raiven.crew_lens_fixture.v1, which denotes the same format.",
      "enum": ["crew-record.v0", "raiven.crew_lens_fixture.v1"]
    },
    "generated_at": { "$ref": "#/$defs/timestamp", "description": "When the record was written." },
    "now": { "$ref": "#/$defs/timestamp", "description": "The record's present: the moment a live view shows; a replay runs from the first event up to here." },
    "source": { "type": "string", "description": "Plain words on where the record came from and what was left out, for example which harness wrote it." },
    "pm": { "$ref": "#/$defs/id", "description": "The agent that manages the crew's projects (the project manager), if there is one." },
    "projects": {
      "type": "array",
      "description": "The projects the crew works on.",
      "items": {
        "type": "object",
        "required": ["id"],
        "properties": {
          "id": { "$ref": "#/$defs/id" },
          "title": { "type": "string" }
        }
      }
    },
    "agents": {
      "type": "array",
      "description": "The crew: each coding agent (one seat or session identity) and the human operator.",
      "items": {
        "type": "object",
        "required": ["id", "family"],
        "properties": {
          "id": { "$ref": "#/$defs/id", "description": "The agent's id in the source harness, for example c1 or x4; the human operator has one too." },
          "family": { "type": "string", "description": "Who built the model: claude, codex, human, or another vendor's name." },
          "model": { "type": ["string", "null"], "description": "The model the agent is configured for." },
          "effort": { "type": ["string", "null"], "description": "The reasoning effort it is configured for, for example high or max." },
          "roles": { "type": "array", "items": { "type": "string" }, "description": "For example producer, reviewer, sprint_master, project_manager." },
          "profile": { "type": ["string", "null"], "description": "The harness's profile or job description for the agent." },
          "since": { "$ref": "#/$defs/timestamp", "description": "When the agent first appears in service (drives the service stripes)." },
          "since_source": { "type": "string" },
          "project": { "$ref": "#/$defs/id", "description": "The agent's home project, one of projects[].id." },
          "projects": { "type": "array", "items": { "$ref": "#/$defs/id" }, "description": "Every project the agent works on." },
          "project_source": { "type": "string" }
        }
      }
    },
    "sprints": {
      "type": "array",
      "description": "Sets of work items with one goal.",
      "items": {
        "type": "object",
        "required": ["id", "title", "state"],
        "properties": {
          "id": { "$ref": "#/$defs/id" },
          "project": { "$ref": "#/$defs/id" },
          "title": { "type": "string" },
          "state": { "type": "string", "description": "For example active or done." },
          "sprint_master": { "$ref": "#/$defs/id", "description": "The agent that keeps the sprint on track." },
          "product_owner": { "$ref": "#/$defs/id" },
          "members": { "type": "array", "items": { "$ref": "#/$defs/id" }, "description": "The work items in the sprint (work[].id)." },
          "opened_at": { "$ref": "#/$defs/timestamp" },
          "updated_at": { "$ref": "#/$defs/timestamp" },
          "closed_at": { "$ref": "#/$defs/nullable_timestamp" },
          "scope_paths": { "type": "integer", "minimum": 0, "description": "Paths under the sprint's write scope." },
          "scope_loc": { "type": "integer", "minimum": 0, "description": "Lines of code under the write scope (sizes the hangar)." }
        }
      }
    },
    "work": {
      "type": "array",
      "description": "Work items: each produced by one agent and reviewed by another.",
      "items": {
        "type": "object",
        "required": ["id", "producer", "created_at", "work_state", "review_state"],
        "properties": {
          "id": { "$ref": "#/$defs/id" },
          "title": { "type": "string" },
          "producer": { "$ref": "#/$defs/id" },
          "reviewer": { "$ref": "#/$defs/id" },
          "commissioned_by": { "$ref": "#/$defs/id" },
          "created_at": { "$ref": "#/$defs/timestamp" },
          "updated_at": { "$ref": "#/$defs/timestamp" },
          "work_state": { "type": "string", "description": "For example open, completed or cancelled." },
          "review_state": { "type": "string", "description": "For example pending, converged (producer and reviewer agree: the diamond is cut free) or terminated (scrapped: a black diamond)." },
          "target_version": { "type": "integer", "minimum": 0 },
          "round": { "type": "integer", "minimum": 0, "description": "Review rounds so far." },
          "scope_paths": { "type": "integer", "minimum": 0 },
          "scope_loc": { "type": "integer", "minimum": 0 },
          "reports": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["kind", "at"],
              "properties": {
                "kind": { "type": "string", "description": "For example assessment." },
                "at": { "$ref": "#/$defs/timestamp" },
                "round": { "type": "integer", "minimum": 0 },
                "commit": { "type": "string" }
              }
            }
          },
          "git": {
            "type": ["object", "null"],
            "description": "What the item changed, between its base and target revisions.",
            "properties": {
              "base_ref": { "type": "string" },
              "target_ref": { "type": "string" },
              "files": { "type": "integer", "minimum": 0 },
              "insertions": { "type": "integer", "minimum": 0 },
              "deletions": { "type": "integer", "minimum": 0 },
              "commit_count": { "type": "integer", "minimum": 0 },
              "commits": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": { "sha": { "type": "string" }, "subject": { "type": "string" }, "at": { "$ref": "#/$defs/timestamp" } }
                }
              },
              "source": { "type": "string" }
            }
          },
          "progress": {
            "type": "object",
            "properties": {
              "phase": { "type": "string" },
              "progress_pct": { "type": "number", "minimum": 0, "maximum": 100 },
              "message": { "type": "string", "description": "The producer's last progress note (optional; leave it out to keep work details private)." }
            }
          },
          "effort": {
            "type": "object",
            "properties": {
              "estimate_llm_hours": {
                "type": "object",
                "properties": { "low": { "type": "number" }, "base": { "type": "number" }, "high": { "type": "number" } }
              },
              "estimator": { "type": "string" },
              "actual_llm_hours": { "type": ["number", "null"] },
              "unit": { "type": "string" }
            }
          },
          "objective": { "type": "string", "description": "Optional commission text; leave it out to keep work details private." },
          "acceptance": { "type": "array", "items": { "type": "string" } },
          "constraints": { "type": "array", "items": { "type": "string" } }
        }
      }
    },
    "deliveries": {
      "type": "array",
      "description": "Messages between agents and with the operator: metadata only, not the body.",
      "items": {
        "type": "object",
        "required": ["sent_at", "sender", "recipient", "kind"],
        "properties": {
          "sequence": { "type": "integer", "description": "Order in the source harness." },
          "sent_at": { "$ref": "#/$defs/timestamp" },
          "sender": { "$ref": "#/$defs/id" },
          "recipient": { "$ref": "#/$defs/id" },
          "kind": { "type": "string", "description": "For example direct message, review request, coding request, review disposition." },
          "state": { "type": "string", "description": "For example sent or delivered." },
          "size": { "type": "integer", "minimum": 0, "description": "Size of the body in bytes (the body itself is not in the record)." },
          "work": { "type": ["string", "null"], "description": "The work item, or the message thread, the message concerns." },
          "seen_at": { "$ref": "#/$defs/nullable_timestamp" },
          "reply_at": { "$ref": "#/$defs/nullable_timestamp" },
          "subject": { "type": "string", "description": "Optional short subject; leave it out to keep message content private." },
          "synthetic": { "type": "boolean", "description": "True when the row is synthetic, or when only its time is: a real message moved in time keeps its recorded time in retimed_from." },
          "retimed_from": { "$ref": "#/$defs/timestamp", "description": "The recorded time of a real message whose sent_at was moved (for example into its work window in a demo)." }
        }
      }
    },
    "events": {
      "type": "array",
      "description": "The replay stream, in time order: deliveries (type delivery) and work-item transitions.",
      "items": {
        "type": "object",
        "required": ["at", "type"],
        "properties": {
          "at": { "$ref": "#/$defs/timestamp" },
          "type": { "type": "string", "description": "For example delivery, commissioned, converged, terminated." },
          "kind": { "type": "string" },
          "from": { "$ref": "#/$defs/id" },
          "to": { "$ref": "#/$defs/id" },
          "sequence": { "type": "integer" },
          "size": { "type": "integer", "minimum": 0 },
          "work": { "type": ["string", "null"] },
          "reply_at": { "$ref": "#/$defs/nullable_timestamp" },
          "synthetic": { "type": "boolean", "description": "As on deliveries: the row, or its time, is synthetic." },
          "retimed_from": { "$ref": "#/$defs/timestamp" }
        }
      }
    },
    "nudges": {
      "type": "object",
      "description": "Summary of the operator's nudges: messages whose sender is the human operator (commissions count as coding requests, not nudges).",
      "properties": {
        "operator": { "$ref": "#/$defs/id" },
        "definition": { "type": "string" },
        "by_recipient": { "type": "object", "additionalProperties": { "type": "integer", "minimum": 0 } },
        "total": { "type": "integer", "minimum": 0 }
      }
    },
    "fanouts": { "type": "array", "description": "Sub-agent fan-outs of a working agent.", "items": { "$ref": "#/$defs/fanout" } },
    "stalls": { "type": "array", "description": "Stretches in which an agent holding a task stopped making progress.", "items": { "$ref": "#/$defs/interval" } },
    "findings": {
      "type": "array",
      "description": "Watchdog (Sentinel) findings of an agent drifting from its brief or protocol.",
      "items": {
        "allOf": [{ "$ref": "#/$defs/interval" }],
        "type": "object",
        "required": ["kind"],
        "properties": {
          "kind": { "type": "string", "description": "For example scope_breach, protocol_drift, unauthorized_reroute." },
          "severity": { "enum": ["warning", "critical"] }
        }
      }
    },
    "cyber": {
      "type": "array",
      "description": "Events of the model vendor's cyber classifier on an agent's work.",
      "items": {
        "allOf": [{ "$ref": "#/$defs/interval" }],
        "type": "object",
        "required": ["action"],
        "properties": {
          "action": { "enum": ["warning", "hold"], "description": "A hold stops the agent until the operator has looked; the work it stopped did not finish." },
          "classifier": { "type": "string", "description": "Which classifier acted, for example the model vendor's cyber classifier." },
          "reason": { "type": ["string", "null"], "description": "The vendor's stated reason, if it gives one. Leave it out otherwise; a reader must not infer one." }
        }
      }
    },
    "models": {
      "type": "array",
      "description": "Samples of the model and reasoning effort an agent asked for and was served; a difference is a timeline mark.",
      "items": {
        "type": "object",
        "required": ["agent", "at", "requested_model", "served_model"],
        "properties": {
          "agent": { "$ref": "#/$defs/id" },
          "at": { "$ref": "#/$defs/timestamp" },
          "requested_model": { "type": "string" },
          "requested_effort": { "type": ["string", "null"] },
          "served_model": { "type": "string" },
          "served_effort": { "type": ["string", "null"] },
          "source": { "$ref": "#/$defs/source" }
        }
      }
    },
    "glow": {
      "type": "object",
      "description": "Per-agent activity, keyed by agent id: a series of normalised output-token activity in buckets of glow_window.step_minutes, and the agent's current state. v1 replaces this with agents[].activity and agents[].context.",
      "additionalProperties": {
        "type": "object",
        "properties": {
          "tokens_norm_5min": { "type": "array", "items": { "type": ["number", "null"] } },
          "context_pct": { "type": ["number", "null"], "minimum": 0, "maximum": 100 },
          "state": { "type": "string" }
        }
      }
    },
    "glow_window": {
      "type": "object",
      "properties": { "start": { "$ref": "#/$defs/timestamp" }, "step_minutes": { "type": "integer", "minimum": 1 } }
    }
  },
  "$defs": {
    "timestamp": { "type": "string", "format": "date-time" },
    "nullable_timestamp": { "type": ["string", "null"], "format": "date-time" },
    "id": { "type": "string", "minLength": 1 },
    "source": {
      "type": "string",
      "description": "Where the row came from. A row that is not read from a primary record must say so: derived (computed from a primary record) or synthetic (generated, for example in a demo). v1 turns this into a required provenance field with the values real, derived and synthetic."
    },
    "interval": {
      "type": "object",
      "required": ["agent", "started_at"],
      "properties": {
        "agent": { "$ref": "#/$defs/id" },
        "work": { "type": ["string", "null"], "description": "The work item the agent held at the time." },
        "started_at": { "$ref": "#/$defs/timestamp" },
        "ended_at": { "$ref": "#/$defs/nullable_timestamp", "description": "null while still open." },
        "source": { "$ref": "#/$defs/source" }
      }
    },
    "fanout": {
      "allOf": [{ "$ref": "#/$defs/interval" }],
      "type": "object",
      "properties": {
        "children": { "type": "integer", "minimum": 1, "description": "Sub-agents running at once." }
      }
    }
  }
}
