{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "anu.watcher_contract.v1",
  "title": "ANU watcher_contract_v1",
  "description": "task-2643 Track B — PR open 이후 CI/Gemini terminal state 까지 책임지는 watcher 의 contract. ANU 본체 위장 폴링 차단 (owner/collector_role validation 포함).",
  "type": "object",
  "required": [
    "schema",
    "task_id",
    "pr_number",
    "head_sha",
    "terminal_states",
    "ttl_seconds",
    "callback_target",
    "duplicate_policy",
    "owner",
    "collector_role"
  ],
  "properties": {
    "schema": {
      "type": "string",
      "const": "anu.watcher_contract.v1"
    },
    "task_id": {
      "type": "string",
      "minLength": 1,
      "description": "발사 주체 task id (예: task-2643 / cron-watcher-2643)"
    },
    "pr_number": {
      "type": "integer",
      "minimum": 1
    },
    "head_sha": {
      "type": "string",
      "pattern": "^[0-9a-f]{40}$",
      "description": "stale 판정 기준점 (force-push 검출)"
    },
    "terminal_states": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "MERGE_READY",
          "CHAIR_REQUIRED",
          "GEMINI_EXTERNAL_TRIGGER_STALE",
          "CI_FAILED_NON_REMEDIABLE",
          "LOOP_BOUNDARY"
        ]
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "ttl_seconds": {
      "type": "integer",
      "minimum": 1,
      "maximum": 7200,
      "description": "hard timeout. TTL 만료 → dead-letter (ANU 직접 polling 복귀 금지)"
    },
    "callback_target": {
      "type": "object",
      "required": ["type", "owner_key", "envelope_axes"],
      "properties": {
        "type": {
          "type": "string",
          "const": "ANU_NORMAL_CALLBACK"
        },
        "owner_key": {
          "type": "string",
          "const": "c119085addb0f8b7",
          "description": "ANU collector key 고정 (SELF_COLLECTOR 차단)"
        },
        "envelope_axes": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "delivery_outcome",
              "miss_cause",
              "root_cause_tags",
              "canonical_root",
              "registration_status"
            ]
          },
          "minItems": 5,
          "uniqueItems": true
        }
      }
    },
    "duplicate_policy": {
      "type": "string",
      "enum": ["DEDUPE_ON_PR_HEAD_SHA"],
      "description": "동일 (pr, head_sha) 에 watcher 1개만 허용"
    },
    "owner": {
      "type": "string",
      "minLength": 1,
      "description": "dev bot name 또는 cron-watcher-* . 'anu' / 'ANU' / 'anu_main' 등 ANU 본체 식별자 금지",
      "not": {
        "pattern": "^(anu|ANU|anu_main|anu_session|ANU_MAIN|ANU_SESSION)$"
      }
    },
    "collector_role": {
      "type": "string",
      "minLength": 1,
      "description": "callback collector role. 'ANU' 만 허용. 'anu_watcher' / 'anu_polling' 같은 위장 폴링 식별자 금지",
      "enum": ["ANU"]
    },
    "callback_only_reporting": {
      "type": "boolean",
      "const": true,
      "description": "watcher 는 result 를 callback envelope 으로만 전달. 본 필드 누락/false 시 session-bound polling 분류"
    },
    "watcher_owner_kind": {
      "type": "string",
      "enum": ["dev_bot", "cron_watcher"],
      "description": "watcher 발사 형태 분류 (선택)"
    }
  },
  "additionalProperties": true,
  "examples": [
    {
      "schema": "anu.watcher_contract.v1",
      "task_id": "task-2643",
      "pr_number": 146,
      "head_sha": "0e172435a2d7d3664ae63320c2f96c12882df0e3",
      "terminal_states": [
        "MERGE_READY",
        "CHAIR_REQUIRED",
        "GEMINI_EXTERNAL_TRIGGER_STALE",
        "CI_FAILED_NON_REMEDIABLE",
        "LOOP_BOUNDARY"
      ],
      "ttl_seconds": 7200,
      "callback_target": {
        "type": "ANU_NORMAL_CALLBACK",
        "owner_key": "c119085addb0f8b7",
        "envelope_axes": [
          "delivery_outcome",
          "miss_cause",
          "root_cause_tags",
          "canonical_root",
          "registration_status"
        ]
      },
      "duplicate_policy": "DEDUPE_ON_PR_HEAD_SHA",
      "owner": "dev6-perun",
      "collector_role": "ANU",
      "callback_only_reporting": true,
      "watcher_owner_kind": "dev_bot"
    }
  ]
}
