{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "schemas/dispatch_callback_contract.schema.json",
  "title": "anu_v3.dispatch_callback_contract.v1",
  "description": "task-2614 §7b (회장 2026-05-20 야간 필수 보강) — dispatch callback/progress-trigger 계약 record. 모든 executor dispatch 는 normal completion callback(독립 ANU key c119085addb0f8b7) + ANU-key fallback safety-net(미수신 recovery 전용·NON_BLOCKING) 둘 다 등록하는 계약을 가진다. normal callback 수신 시 fallback cancel-on-success. fallback 은 진행 트리거 아님(fixed-time/dead-man 금지). normal·fallback 둘 다 부재 → DISPATCH_CONTRACT_VIOLATION. result 존재 + normal missing + fallback missing 조건에서만 recovery watcher 가 독립 ANU collector 를 idempotent 1회 spawn(중복 0·무조율 dead-man/fixed-time 진행트리거 절대 금지·640665C8 안티패턴 차단). executor self-key(1e41a2324a3ccdd0)는 collector/adjudication/dispatch 에 절대 사용 불가 — 독립 ANU key 전용(+49 정본). forward-reject: 미정의 키 금지.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema",
    "task_id",
    "normal_callback_present",
    "fallback_present",
    "result_present",
    "classification",
    "idempotency_key",
    "collector_key",
    "executor_self_key_forbidden",
    "fallback_cancel_on_success",
    "recovery_required",
    "recovery_is_fixed_time_or_dead_man",
    "reasons"
  ],
  "properties": {
    "schema": { "const": "anu_v3.dispatch_callback_contract.v1" },
    "task_id": { "type": "string", "minLength": 1 },
    "normal_callback_present": {
      "type": "boolean",
      "description": "독립 ANU key normal completion callback 수신 여부 (계약 1)."
    },
    "fallback_present": {
      "type": "boolean",
      "description": "ANU-key fallback safety-net 존재 여부 (계약 2·미수신 recovery 전용·NON_BLOCKING)."
    },
    "result_present": {
      "type": "boolean",
      "description": "executor result 산출 존재 여부. 계약 6 recovery spawn 의 필수 조건."
    },
    "classification": {
      "enum": [
        "CONTRACT_OK",
        "FALLBACK_RECOVERY",
        "DISPATCH_CONTRACT_VIOLATION",
        "RECOVERY_SPAWNED"
      ],
      "description": "CONTRACT_OK=normal 수신. FALLBACK_RECOVERY=normal 미수신·fallback 존재(안전망·진행트리거 아님). DISPATCH_CONTRACT_VIOLATION=normal·fallback 둘 다 부재(계약 5). RECOVERY_SPAWNED=위반+result 존재로 독립 ANU collector 1회 spawn 됨."
    },
    "idempotency_key": {
      "type": "string",
      "minLength": 1,
      "description": "task 단위 1회 보장 키 — recovery watcher 중복 spawn 0."
    },
    "collector_key": {
      "const": "c119085addb0f8b7",
      "description": "collector/adjudication/dispatch 는 독립 ANU key 전용. executor self-key(1e41a2324a3ccdd0) 절대 금지 — fail-closed(+49 정본)."
    },
    "executor_self_key_forbidden": {
      "const": true,
      "description": "executor self-key 사용 금지 불변식 — 항상 true(약화 forward-reject)."
    },
    "fallback_cancel_on_success": {
      "type": "boolean",
      "description": "normal callback durable-success 확정 즉시 fallback cancel-on-success 제거(노이즈 0·계약 3). CONTRACT_OK ∧ fallback 존재 시에만 true."
    },
    "recovery_required": {
      "type": "boolean",
      "description": "result 존재 + normal missing + fallback missing(=계약 6) 일 때만 true. recovery watcher 가 독립 ANU collector 1회 spawn 해야 함."
    },
    "recovery_is_fixed_time_or_dead_man": {
      "const": false,
      "description": "recovery 는 조건 탐지 결과일 뿐 fixed-time/dead-man 진행트리거가 아니다 — 항상 false(640665C8 안티패턴 재발 차단의 schema 명문·약화 forward-reject)."
    },
    "reasons": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1
    }
  },
  "allOf": [
    {
      "if": { "properties": { "classification": { "const": "DISPATCH_CONTRACT_VIOLATION" } } },
      "then": {
        "properties": {
          "normal_callback_present": { "const": false },
          "fallback_present": { "const": false },
          "fallback_cancel_on_success": { "const": false }
        }
      }
    },
    {
      "if": { "properties": { "classification": { "const": "FALLBACK_RECOVERY" } } },
      "then": {
        "properties": {
          "normal_callback_present": { "const": false },
          "fallback_present": { "const": true },
          "recovery_required": { "const": false }
        }
      }
    },
    {
      "if": { "properties": { "recovery_required": { "const": true } } },
      "then": {
        "properties": {
          "result_present": { "const": true },
          "normal_callback_present": { "const": false },
          "fallback_present": { "const": false },
          "classification": {
            "enum": ["DISPATCH_CONTRACT_VIOLATION", "RECOVERY_SPAWNED"]
          }
        }
      }
    }
  ]
}
