{
  "_meta": {
    "fixture": "foundationmodels-respond",
    "purpose": "S2-3a — Apple FoundationModels live capture (OnDeviceProvider 결선 + 테스트 동결 기준)",
    "captured_on": "M5 Max MacBook Pro (128GB, Apple Intelligence)",
    "captured_date": "2026-06-04",
    "sdk": "macOS 26.5 SDK / FoundationModels.framework",
    "note": "SDK 가 marshaling 하므로 raw request_body 는 없음. 이 파일은 응답 모양 + 에러 타입 + 취소 동작의 동결 기준."
  },

  "availability": {
    "observed": "available",
    "is_available_convenience": true,
    "read_is_synchronous": true,
    "api": "SystemLanguageModel.default.availability",
    "enum": {
      "available": "case available",
      "unavailable_reasons": ["deviceNotEligible", "appleIntelligenceNotEnabled", "modelNotReady"]
    },
    "supports_korean": true,
    "supported_language_count": 23
  },

  "happy_path": {
    "api": "session.respond(to: String, options: GenerationOptions) async throws -> Response<String>",
    "content_accessor": "response.content   (Response<String>.content : String)",
    "observed_content": "압력용기의 충격시험(Charpy) 면제 판정은, 용기의 압력 등급이 10MPa 이하인 경우, 충격 시험을 면제할 수 있으며, 이는 용기의 안전성을 보장하기 위한 중요한 기준입니다.",
    "observed_latency_ms": 1291.3,
    "transcript_entries_count": 1,
    "is_responding_after": false,
    "quality_note": "내용은 부정확(온디바이스 ~3B/2-bit QAT). corpusAsk 부적합·quickSummarize/classify 적합 라우팅 정합 — 사실성은 RemoteDS 코퍼스가 담당."
  },

  "session_init": {
    "api": "LanguageModelSession(model: .default, tools: [], instructions: String?)",
    "instructions_timing": "init (per-call 아님)",
    "instructions_nil_handling": "systemPrompt == nil 이면 instructions 인자 생략 (빈 문자열 금지)",
    "prewarm": "session.prewarm() — 동기 반환(관찰 ~1.3ms), 백그라운드 워밍",
    "stateless_per_request": "호출마다 새 세션 생성 → instructions(init-time) + rateLimited/concurrentRequests(세션 상태) 둘 다 우회"
  },

  "generation_options": {
    "api": "GenerationOptions(sampling: SamplingMode? = nil, temperature: Double? = nil, maximumResponseTokens: Int? = nil)",
    "mapping": "AICompletionRequest.maxTokens -> maximumResponseTokens",
    "temperature_note": "AICompletionRequest 에 temperature 필드 없음(동결) → 미설정(모델 기본). LocalMLX 와 동일 정책(둘 다 미설정)."
  },

  "generation_error": {
    "_source": "Xcode jump-to-def / swiftinterface (LanguageModelSession.GenerationError) — authoritative, version-accurate",
    "type": "LanguageModelSession.GenerationError : Error, LocalizedError",
    "associated_value": "각 case 는 GenerationError.Context (refusal 은 (Refusal, Context))",
    "cases": [
      "exceededContextWindowSize(Context)",
      "assetsUnavailable(Context)",
      "guardrailViolation(Context)",
      "unsupportedGuide(Context)",
      "unsupportedLanguageOrLocale(Context)",
      "decodingFailure(Context)",
      "rateLimited(Context)",
      "concurrentRequests(Context)",
      "refusal(Refusal, Context)"
    ],
    "plan_corrections": [
      "plan 가정 'refusal 케이스명 없음' = 틀림 → refusal 은 별도 case 로 존재(guardrailViolation 과 구분).",
      "plan 에 없던 concurrentRequests case 존재 — rateLimited 와 함께 stateless 세션에서 뜨면 세션 공유 버그 신호.",
      "assetsUnavailable 정확명 확정(모델 자산 미가용)."
    ],
    "reproduced_live": {
      "exceededContextWindowSize": {
        "trigger": "의도적 컨텍스트 오버플로(긴 프롬프트)",
        "errorDescription": "Exceeded model context window size"
      }
    },
    "finish_reason_mapping": {
      "guardrailViolation": ".refused",
      "refusal": ".refused",
      "exceededContextWindowSize": ".unavailable",
      "rateLimited": ".unavailable + loud log (stateless 인데 발생 = 세션 재사용 버그 신호)",
      "concurrentRequests": ".unavailable + loud log (동일 — stateless 위반 신호)",
      "unsupportedLanguageOrLocale": ".unavailable (+ supportedLocale 사전체크로 회피)",
      "unsupportedGuide": ".unavailable",
      "decodingFailure": ".unavailable",
      "assetsUnavailable": ".unavailable",
      "@unknown default": ".unavailable + loud log"
    }
  },

  "cancellation": {
    "_finding": "S2-Fe 전제 확정 — COOPERATIVE",
    "cancel_requested_at_ms": 500,
    "threw": "CancellationError",
    "elapsed_ms": 533.6,
    "interpretation": "respond() 는 mid-flight Task 취소를 협조적으로 honor(요청 33ms 후 CancellationError throw).",
    "implication": "OnDevice complete() 에 surrounding Task.checkCancellation() 은 belt-and-suspenders(실제 중단은 respond() 내부). streamResponse 토큰단위 취소 폴백 불필요(선전환 금지)."
  }
}
