{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://vitals.build/schema/0.1-draft.5/stream.schema.json",
  "title": "Vitals capsule (one NDJSON line)",
  "description": "Validates a single capsule of a vitals/v0.1 stream at spec_revision 0.1-draft.5. A stream is a sequence of such capsules, one per line. Cross-record rules (stream_seq continuity, eid pairing, parent_eid matrix, Pulse/BuildFinished agreement) are the semantic validator's job (spec §11.6), not this schema's.",
  "oneOf": [
    { "$ref": "#/$defs/StreamHeader" },
    { "$ref": "#/$defs/BuildConfig" },
    { "$ref": "#/$defs/BuildStarted" },
    { "$ref": "#/$defs/BuildFinished" },
    { "$ref": "#/$defs/UnitStarted" },
    { "$ref": "#/$defs/UnitFinished" },
    { "$ref": "#/$defs/StepStarted" },
    { "$ref": "#/$defs/StepFinished" },
    { "$ref": "#/$defs/Diagnostic" },
    { "$ref": "#/$defs/TestSummary" },
    { "$ref": "#/$defs/TestCaseFailed" },
    { "$ref": "#/$defs/Output" },
    { "$ref": "#/$defs/Pulse" },
    { "$ref": "#/$defs/Meta" }
  ],
  "$defs": {
    "safeInt": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "posInt": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },
    "ts": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$",
      "description": "RFC 3339 UTC, exactly millisecond precision, literal Z."
    },
    "severity": { "enum": ["error", "warning", "info", "ignore"] },
    "terminalStatus": { "enum": ["success", "failure", "cancelled", "skipped"] },
    "buildStatus": { "enum": ["success", "failure", "cancelled"] },
    "work": { "enum": ["executed", "up-to-date", "cache", "no-source"] },
    "impact": { "enum": ["none", "diagnostics-incomplete", "tests-incomplete", "output-dropped", "stream-incomplete"] },
    "kebab": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
    "sha256hex": { "type": "string", "pattern": "^[0-9a-f]{64}$" },

    "ext": {
      "type": "object",
      "description": "Extension container: reverse-DNS namespace keys; children are permissive. Keys beginning 'vitals.' are reserved.",
      "propertyNames": { "pattern": "^[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)+$" },
      "additionalProperties": { "type": "object" },
      "maxProperties": 16
    },

    "exception": {
      "type": "object",
      "required": ["type", "msg"],
      "additionalProperties": false,
      "properties": {
        "type": { "type": "string", "maxLength": 512 },
        "msg": { "type": "string", "maxLength": 2000 },
        "msg_truncated": { "type": "boolean" },
        "stack": { "type": "array", "items": { "type": "string", "maxLength": 1000 }, "maxItems": 20 },
        "stack_truncated": { "type": "boolean" },
        "causes": {
          "type": "array",
          "maxItems": 8,
          "items": {
            "type": "object",
            "required": ["type", "msg"],
            "additionalProperties": false,
            "properties": {
              "type": { "type": "string", "maxLength": 512 },
              "msg": { "type": "string", "maxLength": 2000 }
            }
          }
        }
      }
    },

    "loc": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "path": { "type": "string" },
        "path_base": { "enum": ["unit", "root"] },
        "abs_path": { "type": "string" },
        "line": { "$ref": "#/$defs/posInt" },
        "col": { "$ref": "#/$defs/posInt" },
        "end_line": { "$ref": "#/$defs/posInt" },
        "end_col": { "$ref": "#/$defs/posInt" }
      },
      "anyOf": [ { "required": ["path"] }, { "required": ["abs_path"] } ],
      "dependentRequired": { "path": ["path_base"] }
    },

    "envelope": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts"],
      "properties": {
        "event": { "type": "string" },
        "kind": { "type": "string" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" }
      }
    },

    "StreamHeader": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "schema", "spec_revision", "build_id", "producer", "style"],
      "additionalProperties": false,
      "properties": {
        "event": { "const": "StreamHeader" },
        "kind": { "const": "StreamHeader" },
        "stream_seq": { "const": 0 },
        "ts": { "$ref": "#/$defs/ts" },
        "schema": { "type": "string", "pattern": "^vitals/v[0-9]+\\.[0-9]+$" },
        "spec_revision": { "type": "string" },
        "schema_uri": { "type": "string", "format": "uri" },
        "schema_sha256": { "$ref": "#/$defs/sha256hex" },
        "build_id": { "type": "string" },
        "producer": {
          "type": "object",
          "required": ["tool", "tool_version", "impl"],
          "additionalProperties": false,
          "properties": {
            "tool": { "type": "string" },
            "tool_version": { "type": "string" },
            "impl": { "type": "string" }
          }
        },
        "capabilities": { "type": "array", "items": { "type": "string" } },
        "root": { "type": "string" },
        "privacy": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "mode": { "enum": ["local", "portable"] },
            "redacted": { "type": "boolean" }
          }
        },
        "style": { "enum": ["contextual", "verbose"] },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "BuildConfig": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "targets"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "BuildConfig" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "targets": { "type": "array", "items": { "type": "string" } },
        "cwd": { "type": "string" },
        "command_display": { "type": "string" },
        "args": { "type": "array", "items": { "type": "string" } },
        "args_redacted": { "type": "boolean" },
        "parallelism": { "type": "string" },
        "offline": { "type": "boolean" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "BuildStarted": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "BuildStarted" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "BuildFinished": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "status"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "BuildFinished" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "status": { "$ref": "#/$defs/buildStatus" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "exception": { "$ref": "#/$defs/exception" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "UnitStarted": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "dir", "eid"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "UnitStarted" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "unit_identity": { "$ref": "#/$defs/unitIdentity" },
        "dir": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "parent_eid": { "$ref": "#/$defs/safeInt" },
        "retry_of_eid": { "$ref": "#/$defs/safeInt" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "UnitFinished": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "status"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "UnitFinished" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "status": { "$ref": "#/$defs/terminalStatus" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "work": { "$ref": "#/$defs/work" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "parent_eid": { "$ref": "#/$defs/safeInt" },
        "retry_of_eid": { "$ref": "#/$defs/safeInt" },
        "exception": { "$ref": "#/$defs/exception" },
        "reason": { "$ref": "#/$defs/kebab" },
        "caused_by": { "type": "string" },
        "ext": { "$ref": "#/$defs/ext" }
      },
      "if": { "properties": { "status": { "const": "skipped" } } },
      "then": { "not": { "anyOf": [ { "required": ["eid"] }, { "required": ["duration_ms"] } ] } },
      "else": { "required": ["eid"] }
    },

    "StepStarted": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "step", "eid"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "StepStarted" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "step_detail": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "parent_eid": { "$ref": "#/$defs/safeInt" },
        "retry_of_eid": { "$ref": "#/$defs/safeInt" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "StepFinished": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "step", "status"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "StepFinished" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "status": { "$ref": "#/$defs/terminalStatus" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "work": { "$ref": "#/$defs/work" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "parent_eid": { "$ref": "#/$defs/safeInt" },
        "retry_of_eid": { "$ref": "#/$defs/safeInt" },
        "exception": { "$ref": "#/$defs/exception" },
        "exit_code": { "$ref": "#/$defs/safeInt" },
        "reason": { "$ref": "#/$defs/kebab" },
        "caused_by": { "type": "string" },
        "ext": { "$ref": "#/$defs/ext" }
      },
      "if": { "properties": { "status": { "const": "skipped" } } },
      "then": { "not": { "anyOf": [ { "required": ["eid"] }, { "required": ["duration_ms"] } ] } },
      "else": { "required": ["eid"] }
    },

    "Diagnostic": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "tool", "rule_id", "severity", "msg", "fp"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "Diagnostic" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "unit_identity": { "$ref": "#/$defs/unitIdentity" },
        "step": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "tool": { "type": "string" },
        "rule_id": { "type": "string" },
        "rule_source": { "type": "string" },
        "severity": { "$ref": "#/$defs/severity" },
        "msg": { "type": "string", "maxLength": 2000 },
        "msg_truncated": { "type": "boolean" },
        "detail": { "type": "string", "maxLength": 8000 },
        "detail_truncated": { "type": "boolean" },
        "loc": { "$ref": "#/$defs/loc" },
        "fp": { "type": "string" },
        "fp_scheme": { "type": "string" },
        "stable_fp": { "$ref": "#/$defs/sha256hex" },
        "stable_fp_scheme": { "type": "string" },
        "scope": { "$ref": "#/$defs/kebab" },
        "ext": { "$ref": "#/$defs/ext" }
      },
      "dependentRequired": { "stable_fp": ["stable_fp_scheme"] },
      "if": { "properties": { "loc": { "properties": { "path_base": { "const": "unit" } }, "required": ["path_base"] } }, "required": ["loc"] },
      "then": { "required": ["unit"] }
    },

    "TestSummary": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "step", "eid", "totals"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "TestSummary" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "retry_of_eid": { "$ref": "#/$defs/safeInt" },
        "totals": { "$ref": "#/$defs/testTotals" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "TestCaseFailed": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "unit", "step", "eid", "suite", "test", "fp"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "TestCaseFailed" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "suite": { "type": "string" },
        "test": { "type": "string" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "exception": { "$ref": "#/$defs/exception" },
        "output": { "type": "string", "maxLength": 4000 },
        "output_truncated": { "type": "boolean" },
        "attempts": { "$ref": "#/$defs/posInt" },
        "loc": { "$ref": "#/$defs/loc" },
        "fp": { "type": "string" },
        "fp_scheme": { "type": "string" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "Output": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "eid", "channel", "text", "seq"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "Output" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "channel": { "enum": ["stdout", "stderr"] },
        "text": { "type": "string", "maxLength": 8000 },
        "seq": { "$ref": "#/$defs/safeInt" },
        "continued": { "type": "boolean" },
        "split": { "type": "boolean" },
        "dropped_before_bytes": { "$ref": "#/$defs/safeInt" },
        "dropped_after_bytes": { "$ref": "#/$defs/safeInt" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "Pulse": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "outcome", "totals", "units"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "Pulse" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "outcome": { "$ref": "#/$defs/buildStatus" },
        "duration_ms": { "$ref": "#/$defs/safeInt" },
        "totals": {
          "type": "object",
          "required": ["units", "succeeded", "failed", "cancelled", "skipped"],
          "additionalProperties": false,
          "properties": {
            "units": { "$ref": "#/$defs/safeInt" },
            "succeeded": { "$ref": "#/$defs/safeInt" },
            "failed": { "$ref": "#/$defs/safeInt" },
            "cancelled": { "$ref": "#/$defs/safeInt" },
            "skipped": { "$ref": "#/$defs/safeInt" },
            "diagnostics": {
              "type": "object",
              "required": ["error", "warning", "info", "ignore"],
              "additionalProperties": false,
              "properties": {
                "error": { "$ref": "#/$defs/safeInt" },
                "warning": { "$ref": "#/$defs/safeInt" },
                "info": { "$ref": "#/$defs/safeInt" },
                "ignore": { "$ref": "#/$defs/safeInt" }
              }
            },
            "tests": { "$ref": "#/$defs/testTotals" }
          }
        },
        "units": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["unit", "dir", "status"],
            "additionalProperties": false,
            "properties": {
              "unit": { "type": "string" },
              "dir": { "type": "string" },
              "status": { "$ref": "#/$defs/terminalStatus" },
              "duration_ms": { "$ref": "#/$defs/safeInt" },
              "failed_steps": { "type": "array", "items": { "type": "string" } },
              "exception": { "$ref": "#/$defs/exception" },
              "reason": { "$ref": "#/$defs/kebab" },
              "caused_by": { "type": "string" }
            }
          }
        },
        "exception": { "$ref": "#/$defs/exception" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "Meta": {
      "type": "object",
      "required": ["event", "kind", "stream_seq", "ts", "category", "msg", "impact"],
      "additionalProperties": false,
      "properties": {
        "event": { "type": "string" },
        "kind": { "const": "Meta" },
        "stream_seq": { "$ref": "#/$defs/safeInt" },
        "ts": { "$ref": "#/$defs/ts" },
        "th": { "$ref": "#/$defs/safeInt" },
        "unit": { "type": "string" },
        "step": { "type": "string" },
        "eid": { "$ref": "#/$defs/safeInt" },
        "category": { "type": "string" },
        "msg": { "type": "string", "maxLength": 2000 },
        "impact": { "$ref": "#/$defs/impact" },
        "ext": { "$ref": "#/$defs/ext" }
      }
    },

    "unitIdentity": {
      "type": "object",
      "required": ["scheme", "name"],
      "additionalProperties": false,
      "properties": {
        "scheme": { "type": "string" },
        "name": { "type": "string" },
        "version": { "type": "string" }
      }
    },

    "testTotals": {
      "type": "object",
      "required": ["total", "passed", "failed", "skipped", "flaky"],
      "additionalProperties": false,
      "properties": {
        "total": { "$ref": "#/$defs/safeInt" },
        "passed": { "$ref": "#/$defs/safeInt" },
        "failed": { "$ref": "#/$defs/safeInt" },
        "skipped": { "$ref": "#/$defs/safeInt" },
        "flaky": { "$ref": "#/$defs/safeInt" }
      }
    }
  }
}
