<!-- task=m-openai-compatible-provider-passthrough-contract-sync plan=0 tag=TEST -->

# Code Review Reference - TEST

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-14
task=m-openai-compatible-provider-passthrough-contract-sync, plan=0, tag=TEST

## Roadmap Targets

- Milestone: `agent-roadmap/phase/routing-policy-model-orchestration/milestones/openai-compatible-provider-passthrough-contract-sync.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/routing-policy-model-orchestration/milestones/openai-compatible-provider-passthrough-contract-sync.md)
- Task ids:
  - `devcorp-smoke`: dev-corp IOP 경유 Ornith Spark 요청에서 `chat_template_kwargs.enable_thinking=false`가 provider까지 전달된다.
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-openai-compatible-provider-passthrough-contract-sync/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [TEST-1] Dev-Corp Preflight | [x] |
| [TEST-2] Think-Off Field Smoke | [ ] (실행·기록 완료 / direct Spark 2건 PASS, IOP-via-Edge case는 배포 stale로 400 → gate 미충족) |

## 구현 체크리스트

- [x] TEST-1 dev-corp runner, provider, public Edge preflight를 실행하고 blocker 여부를 `CODE_REVIEW-cloud-G07.md`에 기록한다.
- [x] TEST-2 direct Spark와 IOP 경유 `chat_template_kwargs.enable_thinking=false` 단일 smoke를 실행하고 first content latency, reasoning delta 관측, content summary를 `CODE_REVIEW-cloud-G07.md`에 기록한다. (실행/기록 완료. direct Spark01/02는 `ok:true`, IOP 경유는 HTTP 400 → 세 case 모두 `ok:true` 게이트는 미충족. 근본 원인은 배포 stale.)
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-openai-compatible-provider-passthrough-contract-sync/`를 `agent-task/archive/YYYY/MM/m-openai-compatible-provider-passthrough-contract-sync/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

- 계획된 검증 명령(TEST-1 preflight 2개, TEST-2 Python smoke)을 **대체하지 않았다**. 명령 자체는 고정 계약 그대로 실행했다.
- 이전 세션(2026-07-13)은 dev-corp LAN 밖이라 runner(`ssh fe@172.24.63.178`) 미도달로 **BLOCKED**였으나, 이번 세션(2026-07-14)은 runner에 도달 가능하여(`SSH_OK`, exit 0) 계약 명령 3개를 **실제로 실행하고 실측 evidence를 수집**했다.
- **배포 drift 발견 (실구현 blocker, 코드 결함 아님):** runner 배포 HEAD=`10ede93`은 provider passthrough 구현(`5cd7aea fix(openai): provider native thinking을 보존한다`, `33f8f5e feat(edge): OpenAI compatible provider pass-through & test coverage`) **이전** 바이너리다. 그 결과 IOP 경유 case가 `chat_template_kwargs`를 인식하지 못하고 HTTP 400(`chat_template_kwargs is not supported for /v1/chat/completions`, `chat_handler.go:334`의 generic unsupported-key 경로)을 반환한다.
- 이 plan은 `범위 결정 근거`에서 runtime rebuild/config refresh/redeploy를 **명시적으로 제외**한다. 따라서 발견한 배포 drift를 이 plan 안에서 임의 수정하지 않고, 후속 dev-corp deploy(clean sync → rebuild → redeploy → TEST-2 재실행, `agent-ops/skills/project/dev-corp-runtime-deploy/SKILL.md`)로 해소하도록 blocker/follow-up으로 기록한다.
- 이 blocker는 배포 stale(외부 서비스 준비/배포 drift)이므로 stub 규칙에 따라 **user-review 사유가 아니라 검증 blocker/follow-up**으로 분류한다. `사용자 리뷰 요청`은 `없음`으로 유지한다.

## 주요 설계 결정

- repo source 파일 수정 없음. 이 plan은 evidence 수집 전용(TEST 등급)이며 코드/config/build/배포 변경을 수행하지 않는다.
- 출력 위조 없음. `검증 결과`의 세 case JSON summary는 runner에서 실행한 실제 stdout이다.
- IOP 경유 400의 근본 원인을 로컬 소스와 배포 ref 대조로 특정했다:
  - `git merge-base --is-ancestor 10ede93 HEAD` → true (배포본이 로컬 HEAD의 조상).
  - `git show 10ede93:apps/edge/internal/openai/chat_handler.go | grep chat_template_kwargs` → 없음 (배포 바이너리 소스에 passthrough 처리 부재).
  - 로컬 HEAD는 `chat_handler.go:519`에서 `chat_template_kwargs`를 처리하고, `server_test.go:5681-5713` 단위 테스트가 `chat_template_kwargs":{"enable_thinking":false}`가 provider tunnel body로 보존됨을 증명한다.
  - 결론: 현재 소스는 passthrough를 지원하며, IOP 경유 400은 **배포 stale**(현재 소스가 dev-corp에 배포되지 않음)의 결과다.
- 범위 준수: rebuild/redeploy는 이 plan 범위 밖이므로 수행하지 않았고 후속 dev-corp deploy plan을 권고한다. direct Spark01/02 evidence는 provider-native think-off가 provider 레벨에서 동작함을 이미 증명한다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- `Roadmap Targets`가 `devcorp-smoke` 하나만 가리키는지 확인한다.
- TEST-1 preflight가 secret 원문 없이 runner/provider/public Edge 상태를 증명하는지 확인한다.
- TEST-2 결과에서 direct Spark01/Spark02와 IOP public case가 모두 `ok: true`, `reasoning_seen: false`, `first_content_sec` 값을 갖는지 확인한다.
- 실패 또는 blocker가 user-review가 아니라 검증 blocker/follow-up으로 기록됐는지 확인한다.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.

> **실행 세션 (2026-07-14):** 이 세션은 dev-corp runner(`ssh fe@172.24.63.178`)에 도달 가능하여 계약 명령(TEST-1 preflight 2개, TEST-2 Python smoke)을 **실제로 실행**했다. 아래 출력은 runner 실행 실제 stdout이며 위조가 아니다. secret 원문은 출력하지 않았다. (이전 2026-07-13 세션은 LAN 밖 미도달 BLOCKED였으나 이번 세션에서 해소.)

### 도달성 사전 probe (capture 2026-07-14)
```text
$ ssh -o BatchMode=yes -o ConnectTimeout=15 fe@172.24.63.178 'echo SSH_OK'
SSH_OK
exit=0

$ curl -sS -m 10 -w "\nhttp=%{http_code} time_total=%{time_total}s\n" https://digitalplatform.iop.ai.kr/v1/models
{"error":{"type":"unauthorized","message":"unauthorized"}}
http=401 time_total=0.050568s        # 무인증 도달 확인(인증 필요=정상)
```

### TEST-1 중간 검증
계약 명령 1 (실행함):
```text
$ ssh fe@172.24.63.178 'cd /Users/fe/agent-work/iop-dev-corp && git status --short --branch && git log --oneline -1 && command -v python3 && command -v curl && test -r build/dev-corp-runtime/.secrets/openai_api_key'
## main...origin/main
10ede93 chore(dev-corp): Control Plane compose 배포 상태를 반영한다
/usr/bin/python3
/usr/bin/curl
exit=0
```
=> runner checkout clean(dirty 없음), 배포 HEAD=`10ede93`, python3/curl 존재, secret 파일 가독(`test -r` 성공, 원문 미출력).

계약 명령 2 (실행함):
```text
$ ssh fe@172.24.63.178 'set -e
curl -fsS http://192.168.2.2:8003/health
curl -fsS http://192.168.2.2:8003/v1/models
curl -fsS http://192.168.2.4:8005/health
curl -fsS http://192.168.2.4:8005/v1/models
cd /Users/fe/agent-work/iop-dev-corp
OPENAI_API_KEY="$(cat build/dev-corp-runtime/.secrets/openai_api_key)"
curl -fsS -H "Authorization: Bearer ${OPENAI_API_KEY}" https://digitalplatform.iop.ai.kr/v1/models
'
== spark01 /health ==                # http 200, empty body
== spark01 /v1/models ==
{"object":"list","data":[{"id":"ornith:35b","object":"model","created":1783981560,"owned_by":"vllm","root":"deepreinforce-ai/Ornith-1.0-35B-FP8",...}]}
== spark02 /health ==                # http 200, empty body
== spark02 /v1/models ==
{"object":"list","data":[{"id":"ornith:35b","object":"model","created":1783981560,"owned_by":"vllm","root":"deepreinforce-ai/Ornith-1.0-35B-FP8",...}]}
== public edge /v1/models ==
{"object":"list","data":[{"id":"gemma4:26b","object":"model","created":1783981560,"owned_by":"iop"},{"id":"ornith:35b","object":"model","created":1783981560,"owned_by":"iop"}]}
exit=0
```
판정: TEST-1 preflight **PASS** — 두 계약 명령 모두 exit 0. Spark01/02 direct `/health`·`/v1/models`에서 `ornith:35b` 정상, public Edge `/v1/models` auth 성공(`gemma4:26b`, `ornith:35b`). blocker 없음. (참고: 배포 HEAD `10ede93`은 로컬 HEAD의 조상 = 배포 stale, TEST-2 판정에서 상술.)

### TEST-2 중간 검증
```text
$ ssh fe@172.24.63.178 'cd /Users/fe/agent-work/iop-dev-corp && OPENAI_API_KEY="$(cat build/dev-corp-runtime/.secrets/openai_api_key)" python3 - <<'"'"'PY'"'"'
import json
import os
import time
import urllib.error
import urllib.request

payload = {
    "model": "ornith:35b",
    "messages": [{"role": "user", "content": "Reply with one short sentence and do not include reasoning."}],
    "stream": True,
    "temperature": 0,
    "max_tokens": 64,
    "chat_template_kwargs": {"enable_thinking": False},
}

cases = [
    ("direct_spark01", "http://192.168.2.2:8003/v1/chat/completions", {}),
    ("direct_spark02", "http://192.168.2.4:8005/v1/chat/completions", {}),
    ("iop_public_ornith", "https://digitalplatform.iop.ai.kr/v1/chat/completions", {"Authorization": "Bearer " + os.environ["OPENAI_API_KEY"]}),
]

def run_case(name, url, headers):
    req_headers = {"Content-Type": "application/json"}
    req_headers.update(headers)
    req = urllib.request.Request(url, data=json.dumps(payload).encode("utf-8"), headers=req_headers, method="POST")
    started = time.monotonic()
    content = []
    reasoning_seen = False
    first_content_sec = None
    status = None
    try:
        with urllib.request.urlopen(req, timeout=180) as resp:
            status = resp.status
            for raw in resp:
                line = raw.decode("utf-8", "replace").strip()
                if not line.startswith("data: "):
                    continue
                data = line[6:]
                if data == "[DONE]":
                    break
                obj = json.loads(data)
                for choice in obj.get("choices", []):
                    delta = choice.get("delta") or {}
                    if delta.get("reasoning_content") or delta.get("reasoning") or delta.get("reasoning_text"):
                        reasoning_seen = True
                    piece = delta.get("content")
                    if piece:
                        if first_content_sec is None:
                            first_content_sec = round(time.monotonic() - started, 3)
                        content.append(piece)
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", "replace")
        return {"name": name, "ok": False, "status": exc.code, "error_body": body[:500]}
    except Exception as exc:
        return {"name": name, "ok": False, "error": repr(exc)}
    text = "".join(content).strip()
    return {
        "name": name,
        "ok": status == 200 and first_content_sec is not None and not reasoning_seen,
        "status": status,
        "first_content_sec": first_content_sec,
        "reasoning_seen": reasoning_seen,
        "content_preview": text[:200],
    }

results = [run_case(*case) for case in cases]
print(json.dumps({"payload": {"model": payload["model"], "chat_template_kwargs": payload["chat_template_kwargs"]}, "results": results}, ensure_ascii=False, indent=2))
if not all(item.get("ok") for item in results):
    raise SystemExit(1)
PY'
{
  "payload": {
    "model": "ornith:35b",
    "chat_template_kwargs": {
      "enable_thinking": false
    }
  },
  "results": [
    {
      "name": "direct_spark01",
      "ok": true,
      "status": 200,
      "first_content_sec": 0.094,
      "reasoning_seen": false,
      "content_preview": "Understood."
    },
    {
      "name": "direct_spark02",
      "ok": true,
      "status": 200,
      "first_content_sec": 0.118,
      "reasoning_seen": false,
      "content_preview": "Understood."
    },
    {
      "name": "iop_public_ornith",
      "ok": false,
      "status": 400,
      "error_body": "{\"error\":{\"type\":\"invalid_request_error\",\"message\":\"chat_template_kwargs is not supported for /v1/chat/completions\"}}\n"
    }
  ]
}
exit=1
```
판정: TEST-2 think-off field smoke **PARTIAL / gate 미충족(exit 1)**.
- `direct_spark01`, `direct_spark02`: `ok:true` (status 200, `reasoning_seen:false`, `first_content_sec` 0.094s / 0.118s, content `"Understood."`) → provider-native think-off가 Spark provider 레벨에서 동작함을 증명.
- `iop_public_ornith`: `ok:false`, HTTP **400** `chat_template_kwargs is not supported for /v1/chat/completions` → IOP-via-Edge case 실패. 세 case 모두 `ok:true` 게이트 미충족.
- 근본 원인: 배포 HEAD `10ede93`이 passthrough 구현(`5cd7aea`/`33f8f5e`) 이전 바이너리라 `chat_template_kwargs`를 unsupported-key(`chat_handler.go:334`)로 거부. 현재 소스(HEAD)는 `chat_handler.go:519`에서 처리하고 `server_test.go:5681-5713` 단위 테스트로 provider tunnel body 보존을 증명(코드 결함 아님, 배포 stale). `계획 대비 변경 사항`·`주요 설계 결정` 참조.
- 후속: dev-corp runtime을 passthrough 포함 ref로 clean sync → rebuild → redeploy(`agent-ops/skills/project/dev-corp-runtime-deploy/SKILL.md`) 후 TEST-2 재실행하면 S10의 IOP-via-Edge evidence가 채워진다. rebuild/redeploy는 이 plan 범위 밖이다.

### 최종 검증
```text
$ TEST-1 preflight commands (2) + TEST-2 smoke command  — 모두 runner에서 실행함
- TEST-1 preflight cmd1: exit 0 (PASS)
- TEST-1 preflight cmd2: exit 0 (PASS)  — Spark01/02 ornith:35b + public Edge auth 정상
- TEST-2 smoke: exit 1  — direct_spark01/02 ok:true, iop_public_ornith HTTP 400

최종 판정: 부분 통과 / 배포 stale blocker.
- TEST-1 preflight: PASS (계약 2개 명령 모두 exit 0).
- TEST-2 최종 게이트(세 case 모두 ok:true, exit 0): 미충족(exit 1). direct Spark 2건은 PASS이나
  IOP-via-Edge case가 배포 stale(HEAD 10ede93 < passthrough 커밋 5cd7aea/33f8f5e)로 400.
- devcorp-smoke(S10)의 IOP-via-Edge runtime evidence는 현재 배포본에서 미충족.
후속 조치: dev-corp runtime을 passthrough 포함 ref로 clean sync + rebuild + redeploy
  (agent-ops/skills/project/dev-corp-runtime-deploy/SKILL.md) 후 TEST-2를 재실행하면 S10 evidence가 채워진다.
  이 plan은 rebuild/redeploy를 범위에서 제외하므로 별도 dev-corp deploy follow-up으로 처리한다.
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

Sections and their ownership:

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
| Roadmap Targets | Fixed at stub creation from plan | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| 구현 항목별 완료 여부 | Implementing agent | Check completed items only |
| 구현 체크리스트 | Implementing agent | Check completed items only; final checkbox is mandatory |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone lock decision blocks implementation |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 | Implementing agent | Fill actual command output |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Pass
  - Completeness: Fail
  - Test coverage: Fail
  - API contract: Fail
  - Code quality: Pass
  - Implementation deviation: Pass
  - Verification trust: Pass
  - Spec conformance: Fail
- 발견된 문제:
  - Required: `agent-task/m-openai-compatible-provider-passthrough-contract-sync/CODE_REVIEW-cloud-G07.md:269`의 `iop_public_ornith` case가 `ok:false`, HTTP 400 `chat_template_kwargs is not supported for /v1/chat/completions`로 실패했다. `PLAN-cloud-G07.md:226`의 TEST-2 성공 조건은 세 case 모두 `ok:true`이고, SDD S10 및 Milestone `devcorp-smoke`는 IOP 경유 Ornith Spark 요청에서 `chat_template_kwargs.enable_thinking=false`가 provider까지 전달되는 evidence를 요구한다. direct Spark 2건은 통과했지만 IOP 경유 smoke가 실패했으므로 Roadmap Target 완료 evidence가 부족하다. 수정: dev-corp runtime을 passthrough 포함 ref로 clean sync, rebuild, redeploy한 뒤 동일 TEST-2 smoke를 재실행해 `iop_public_ornith`까지 `ok:true`와 first content/reasoning observation을 기록한다.
- 다음 단계: FAIL follow-up으로 같은 task group에 `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 새로 작성한다.
