{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "obs-receipt/1.3",
  "title": "Observation Receipt — outage-report measurement contract",
  "type": "object",
  "required": ["receipt", "id", "issued_at", "kind", "layer", "claims_about", "source", "window", "geography", "measure"],
  "properties": {
    "receipt": { "enum": ["obs-receipt/1.0", "obs-receipt/1.1", "obs-receipt/1.2", "obs-receipt/1.3"] },
    "id": { "type": "string", "minLength": 1 },
    "issued_at": { "type": "string", "format": "date-time" },
    "kind": { "enum": ["direct", "derived"] },
    "layer": { "enum": ["transport-bgp", "transport-prefix", "application-service", "access-report", "household-access"] },
    "claims_about": { "enum": ["sample", "population"] },
    "source": {
      "type": "object",
      "required": ["name", "hand"],
      "properties": {
        "name": { "type": "string" },
        "ref": { "type": "string" },
        "retrieved_at": { "type": "string", "format": "date-time" },
        "hand": { "enum": ["first", "second"] },
        "channel": { "enum": ["instrument", "upstream-metadata", "self-report", "derived", "unknown"] }
      }
    },
    "window": {
      "type": "object",
      "required": ["start", "end"],
      "properties": {
        "start": { "type": "string", "format": "date-time" },
        "end": { "type": "string", "format": "date-time" }
      }
    },
    "geography": {
      "type": "object",
      "required": ["scope"],
      "properties": {
        "scope": { "enum": ["country", "asn", "province", "city", "household", "global", "unknown"] },
        "code": { "type": "string" }
      }
    },
    "measure": {
      "type": "object",
      "required": ["metric", "numerator", "denominator", "unit"],
      "properties": {
        "metric": { "type": "string" },
        "numerator": { "type": "number" },
        "denominator": { "type": "number" },
        "unit": { "enum": ["measurements", "prefixes", "probes", "sessions", "reports", "users", "events", "unknown"] }
      }
    },
    "confidence": {
      "type": "object",
      "properties": {
        "label": { "enum": ["high", "medium", "low"] },
        "reason": { "type": "string" }
      }
    },
    "valid_until": { "type": "string", "format": "date-time" },
    "coupling": {
      "type": "object",
      "required": ["model", "from_layer", "to_layer", "valid_until"],
      "properties": {
        "model": { "type": "string" },
        "from_layer": { "type": "string" },
        "to_layer": { "type": "string" },
        "valid_until": { "type": "string", "format": "date-time" },
        "divergence_log": { "type": "string", "description": "pointer to the observed predicted-vs-actual series; the conformance instrument" },
        "declared_bounds": {
          "type": "object",
          "description": "how wrong the coupling model is allowed to be, stated up front; required for derived claims under receipt/1.1",
          "properties": {
            "margin_abs": { "type": "number", "minimum": 0, "description": "allowed |actual - predicted| in the to-layer's units" },
            "margin_rel": { "type": "number", "minimum": 0, "description": "allowed |actual - predicted| / |predicted|" },
            "on_metric": { "type": "string" },
            "calibration_window": {
              "type": "object",
              "properties": {
                "start": { "type": "string", "format": "date-time" },
                "end": { "type": "string", "format": "date-time" }
              }
            }
          },
          "anyOf": [ { "required": ["margin_abs"] }, { "required": ["margin_rel"] } ]
        },
        "observed": {
          "type": "array",
          "description": "inline predicted-vs-actual pairs — what declared_bounds is checked against",
          "items": {
            "type": "object",
            "required": ["t", "predicted", "actual"],
            "properties": {
              "t": { "type": "string", "format": "date-time" },
              "predicted": { "type": "number" },
              "actual": { "type": "number" }
            }
          }
        },
        "on_violation": {
          "type": "object",
          "required": ["action", "remediation_window"],
          "description": "declared at issue time: what must happen if the coupling is falsified; required for derived claims under receipt/1.2",
          "properties": {
            "action": { "enum": ["supersede", "recalibrate", "halt"] },
            "remediation_window": {
              "type": "string",
              "pattern": "^P(?=.*\\d)(\\d+(\\.\\d+)?W)?(\\d+(\\.\\d+)?D)?(T(\\d+(\\.\\d+)?H)?(\\d+(\\.\\d+)?M)?(\\d+(\\.\\d+)?S)?)?$",
              "description": "ISO-8601 duration (e.g. PT24H); the remediation deadline runs breach-time + window"
            }
          }
        }
      }
    },
    "supersedes": { "type": "string", "minLength": 1, "description": "id of the receipt this one remediates/replaces" },
    "depends_on": { "type": "array", "items": { "type": "string", "minLength": 1 }, "description": "ids of receipts this claim derives from; a breach upstream propagates downstream" },
    "notes": { "type": "string" }
  }
}
