{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://mosaicstack.dev/schemas/wake-watch-list.schema.json",
  "title": "Mosaic Wake Watch-List",
  "description": "Declarative watch-list for the wake/heartbeat detector (EPIC #892). The SCHEMA is framework-owned; the VALUES are operator-supplied (repos, board files, lane anchors, per-class SLOs). This is the W2 schema contract only — the detector (W4) and digest renderer (W3) consume it. Per CONVERGED-DESIGN §1.4: 'operator repo; schema is framework, values are operator.'",
  "type": "object",
  "required": ["schema_version", "watches"],
  "additionalProperties": false,
  "properties": {
    "schema_version": {
      "type": "integer",
      "minimum": 1,
      "description": "Watch-list schema version. The wake component's manifest.txt declares the supported range (schema_min/schema_max, Gate B); a watch-list outside that range is rejected by the component, not silently coerced."
    },
    "host": {
      "type": "string",
      "description": "Optional operator label for the host this watch-list serves. Per-host single-instance detector (§1.1). Operator-supplied; no semantic meaning to the schema."
    },
    "repos": {
      "type": "array",
      "description": "Git repositories to watch. Source SHAs are descriptors, not the cursor (§2.4).",
      "items": {
        "type": "object",
        "required": ["id"],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Operator-chosen stable identifier for this repo watch."
          },
          "remote": {
            "type": "string",
            "description": "Remote/clone locator (operator-supplied). No credentials inline; secrets are by-name via load_credentials."
          },
          "branches": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Branch refs to track. Empty => default branch."
          },
          "class": { "$ref": "#/$defs/class" },
          "slo": { "$ref": "#/$defs/slo_ref" },
          "aba_sensitive": {
            "type": "boolean",
            "default": false,
            "description": "If true, this source needs an event-stream/webhook rather than poll-only (intra-poll ABA mitigation, §2.4 / gate G5). Poll-only remains a mitigation, not elimination."
          }
        }
      }
    },
    "board_files": {
      "type": "array",
      "description": "Board / decision files whose edits must be caught (repo-section/anchor-scoped hashing, §1.1). Human-decision file edits, not just API-visible state.",
      "items": {
        "type": "object",
        "required": ["id", "path"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string" },
          "repo": {
            "type": "string",
            "description": "Optional reference to a repos[].id this file lives in."
          },
          "path": {
            "type": "string",
            "description": "File path (operator-supplied). Locators are hard: repo/issue#/SHA/file:anchor (§2.1)."
          },
          "class": { "$ref": "#/$defs/class" },
          "slo": { "$ref": "#/$defs/slo_ref" }
        }
      }
    },
    "lane_anchors": {
      "type": "array",
      "description": "In-file anchors (headings/markers) scoping a lane's obligations, so a file edit outside the lane's anchor does not wake it.",
      "items": {
        "type": "object",
        "required": ["id", "anchor"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string" },
          "board_file": {
            "type": "string",
            "description": "Optional reference to a board_files[].id this anchor lives in."
          },
          "anchor": {
            "type": "string",
            "description": "Anchor text/marker delimiting the lane's section within the file."
          },
          "class": { "$ref": "#/$defs/class" },
          "slo": { "$ref": "#/$defs/slo_ref" }
        }
      }
    },
    "slos": {
      "type": "object",
      "description": "Named per-class urgency SLO tiers. SYMBOLIC — the operator sets concrete durations; the schema only fixes the shape and the class ordering intent (§4: security/lease/CI = tight; board = tens of minutes; routine = hours). No numeric parameters are baked into the framework.",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "class": { "$ref": "#/$defs/class" },
          "fallback_bound": {
            "type": "string",
            "description": "Operator-supplied duration (e.g. '5m', '30m', '4h'). Symbolic tier is set by the operator, not the framework."
          },
          "fallback_cadence": {
            "type": "string",
            "description": "OPTIONAL, additive (schema_version 1, backward-compatible — omitting it is valid). The per-class cadence bound for the framework-shipped canon FALLBACK WAKE (F7 replacement-before-retirement, EPIC #892): the low-frequency SAFETY-wake timer (mosaic-wake-fallback.timer) that fires the canon drain INDEPENDENT of the event-driven detector, so a stalled detector cannot silently starve delivery. The A10 installer reads this per-class value and writes it as the fallback timer's OnUnitActiveSec via the blank-reset drop-in (exactly one effective OnUnitActiveUSec). SYMBOLIC — an operator-supplied duration (e.g. '30m', '1h', '4h'); the framework bakes in no numeric. Config, not code. Should be no tighter than this tier's `fallback_bound` (the safety wake is a floor, never the primary mechanism)."
          },
          "quiet_hours_may_suppress": {
            "type": "boolean",
            "default": false,
            "description": "If true, quiet-hours may suppress the cold fallback for this tier. MUST remain false for actionable/critical classes (§3: quiet-hours never gate an actionable/critical class)."
          },
          "measure_to": {
            "type": "string",
            "enum": ["consumed", "qualified-action-or-handoff"],
            "description": "Terminal the SLO is measured to (§4/G8): CONSUMED measures reading; qualified-action-or-handoff measures doing. Actionable/critical classes measure to the action terminal."
          }
        }
      }
    },
    "watches": {
      "type": "array",
      "description": "The declared source-coverage inventory: a lane-by-lane list of every operational source the lane depends on, so an omitted source cannot make the retirement vector pass vacuously (§4/G3 parity inventory). Each entry references a source declared above by kind+id.",
      "items": {
        "type": "object",
        "required": ["lane", "sources"],
        "additionalProperties": false,
        "properties": {
          "lane": {
            "type": "string",
            "description": "Operator lane identifier this watch serves."
          },
          "sources": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": ["kind", "id"],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": ["repo", "board_file", "lane_anchor"],
                  "description": "Which top-level collection the id refers to."
                },
                "id": {
                  "type": "string",
                  "description": "Reference to repos[].id / board_files[].id / lane_anchors[].id."
                }
              }
            }
          }
        }
      }
    }
  },
  "$defs": {
    "class": {
      "type": "string",
      "enum": ["digest", "actionable", "human", "terminal-log", "reaction"],
      "description": "Wake class (§2.3). Only `digest` coalesces (cumulative-state replace); actionable/human APPEND. ALL classes are durable. Absent class => the consumer treats it as `actionable` (fail-safe)."
    },
    "slo_ref": {
      "type": "string",
      "description": "Name of an entry in the top-level `slos` map to apply to this source."
    }
  }
}
