<!-- task=m-lemonade-provider-serving-validation/03+02_field_smoke plan=1 tag=REVIEW_TEST -->

# Plan - REVIEW_TEST

## 이 파일을 읽는 구현 에이전트에게

구현 완료 전 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 반드시 채운다. 구현 중 사용자 결정, 사용자 소유 외부 환경, secret, 서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 남기고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다. 최종 판정, archive, `complete.log` 작성은 code-review-skill 전용이다.

## 배경

이전 리뷰는 구현 파일 변경이 없고 review stub의 구현 소유 섹션과 검증 출력이 비어 있어 `FAIL`로 종료됐다. 선행 OpenAI-compatible adapter subtask는 archive의 `complete.log`로 완료됐으므로, 이번 후속 작업은 Lemonade field smoke script, Makefile target, 실제 검증 증거를 완성하는 데 집중한다.

## 사용자 리뷰 요청 흐름

구현 중 차단이 생기면 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 에이전트가 채팅으로 직접 사용자에게 묻는 것은 금지되며, code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. 실제 Lemonade endpoint/model/auth 값이 준비되지 않아 real mode smoke를 실행할 수 없으면 이 섹션에 실행한 명령과 출력, 미실행 사유, 재개 조건을 기록하고 PASS를 주장하지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
- Task ids:
  - `models-chat`: Edge OpenAI-compatible `/v1/models`와 non-streaming `/v1/chat/completions`가 Lemonade provider로 수렴하는 기준이 검증되어 있다.
  - `streaming`: streaming `/v1/chat/completions`에서 SSE chunk, finish reason, 종료 신호가 Lemonade provider 경로로 안정적으로 전달되는지 검증되어 있다.
  - `field-smoke`: split-host field smoke에서 Lemonade target/model을 선택해 모델 조회와 chat 왕복이 검증되어 있다.
  - `follow-up-scope`: 코드 수정이 필요한 항목은 이 Milestone의 추가 Task 또는 같은 Milestone task group의 후속 plan으로 정리되어 있다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-roadmap/current.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-roadmap/phase/inference-provider-extension/milestones/lemonade-provider-serving-validation.md`
- `agent-task/archive/2026/06/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/complete.log`
- `agent-task/m-lemonade-provider-serving-validation/03+02_field_smoke/plan_cloud_G07_0.log`
- `agent-task/m-lemonade-provider-serving-validation/03+02_field_smoke/code_review_cloud_G07_0.log`
- `Makefile`
- `scripts/e2e-openai-lemonade.sh` 존재 확인: missing

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`와 matched profile `agent-test/local/testing-smoke.md`를 읽었다.
- 테스트 도구 자체를 바꾸는 작업이므로 해당 도구를 직접 실행해야 한다.
- 적용 명령: `bash -n scripts/e2e-openai-lemonade.sh`, `./scripts/e2e-openai-lemonade.sh`, `make test-openai-lemonade`, real mode smoke, 관련 Go package tests, `git diff --check`.
- field/runtime 외부 endpoint evidence가 필요하면 사용자 소유 host 또는 원격 runner가 필요할 수 있다. real mode 환경값이 없으면 review stub의 사용자 리뷰 요청으로 멈춘다.
- secret, token, 개인 endpoint 원문은 tracked 파일과 최종 보고에 기록하지 않는다.

### 테스트 커버리지 공백

- `scripts/e2e-openai-lemonade.sh`가 없어서 Lemonade/OpenAI-compatible adapter route fake/real smoke가 없다.
- `Makefile`에 `test-openai-lemonade` target이 없어 명시 실행 계약이 없다.
- 이전 review stub은 실제 stdout/stderr를 붙이지 않아 fake mode, real mode, Go package tests, whitespace 검증 신뢰성이 없다.

### 심볼 참조

- rename/remove 없음.
- 새 script target `test-openai-lemonade` call site는 `Makefile`에 한정한다.

### 분할 판단

- 이 후속 plan은 기존 split subtask `03+02_field_smoke` 안에서 이전 `FAIL` findings를 해소한다.
- predecessor `02`는 `agent-task/archive/2026/06/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/complete.log`로 충족됐다.
- 새 split은 만들지 않는다. 남은 문제는 script, Makefile target, 검증 증거가 같은 field smoke 계약 안에서 함께 판정되어야 한다.

### 범위 결정 근거

- Lemonade 서버 설치, 모델 다운로드, 운영 제품화는 제외한다.
- `make test-e2e` 기본 흐름에는 real Lemonade dependency를 추가하지 않는다.
- secret/header 값은 tracked script, config, docs, roadmap에 쓰지 않는다.
- smoke 중 발견된 provider 또는 OpenAI-compatible runtime 이슈가 script/config 소규모 보정으로 해결되지 않으면, 범위를 확장해 고치지 말고 review stub에 증거를 남겨 후속 plan으로 넘긴다.

### 빌드 등급

- `cloud-G07`: shell smoke orchestration, process control, stdout/stderr evidence, external real mode gate가 핵심이다.

## 구현 체크리스트

- [ ] `scripts/e2e-openai-lemonade.sh`를 추가해 fake mode와 real mode에서 Edge/Node/OpenAI-compatible route를 검증한다.
- [ ] `Makefile`에 명시 실행 target `test-openai-lemonade`를 추가하되 `test-e2e` 기본 dependency에는 넣지 않는다.
- [ ] fake mode smoke, real mode smoke, 관련 Go package tests, `git diff --check`를 실행한다. real mode 환경값이 없으면 review stub의 `사용자 리뷰 요청`에 기록하고 PASS를 주장하지 않는다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- `03+02_field_smoke`의 predecessor `02`는 `agent-task/archive/2026/06/m-lemonade-provider-serving-validation/02+01_openai_compatible_adapter/complete.log`로 충족됐다.
- 먼저 `scripts/e2e-openai-lemonade.sh`를 구현하고 fake mode를 직접 통과시킨다.
- 그 다음 `Makefile` target을 추가하고 `make test-openai-lemonade`가 같은 script를 실행하는지 확인한다.
- 마지막으로 real mode와 Go package tests, `git diff --check` 결과를 review stub에 실제 출력으로 붙인다.

## REVIEW_TEST-1 Lemonade smoke script

### 문제

`scripts/e2e-openai-lemonade.sh`가 없고, 이전 review의 `code_review_cloud_G07_0.log`는 `CODE_REVIEW-cloud-G07.md:48`에서 모든 구현 항목이 미완료였다. Lemonade/OpenAI-compatible route를 fake/real mode로 검증하는 산출물이 없다.

### 해결 방법

- 새 `scripts/e2e-openai-lemonade.sh`를 추가한다.
- 기본 `IOP_LEMONADE_MODE`는 `fake`다. fake mode는 임시 Go server가 `/v1/models`와 `/v1/chat/completions` non-streaming/streaming SSE를 제공하게 한다.
- real mode는 `IOP_LEMONADE_MODE=real`일 때 외부 Lemonade server를 사용한다. required env는 `IOP_LEMONADE_ENDPOINT`, `IOP_LEMONADE_MODEL`이다. endpoint는 root 또는 trailing `/v1` 둘 다 허용하되 Node adapter config에는 root 형태로 넣는다.
- optional auth는 `IOP_LEMONADE_AUTH_HEADER_NAME`과 `IOP_LEMONADE_AUTH_HEADER_VALUE`가 모두 있을 때 generated temp YAML `headers`에만 쓴다. script stdout/stderr에는 header value를 출력하지 않는다.
- generated Edge config는 `openai.model_routes`에 client-facing model `client-request-model`, `adapter: "openai_compat"`, `target: "$MODEL"`, `node: "test-node"`를 둔다.
- generated node adapter는 `openai_compat_instances` name `lemonade-local`, provider `lemonade`, endpoint, capacity/queue timeout을 사용한다.
- 검증 단계는 Edge `/healthz`, Edge `/v1/models`, Edge `/v1/chat/completions` non-streaming sentinel, streaming SSE content chunk와 `data: [DONE]`, `go run ./apps/edge/cmd/edge smoke openai --model client-request-model ...` 순서로 둔다.
- fake mode sentinel은 `IOP_OPENAI_LEMONADE_OK`로 고정한다. real mode는 empty response가 아니고 model route가 성공하면 통과하되, streaming `[DONE]`은 반드시 확인한다.

### 수정 파일 및 체크리스트

- [ ] `scripts/e2e-openai-lemonade.sh`: 새 smoke script 추가.
- [ ] script 내부 temp config에는 secret value를 출력하지 않는다.
- [ ] script cleanup은 Edge/Node/fake server process와 temp dir를 정리한다.

### 테스트 작성

- shell script 자체는 Go unit test 대신 `bash -n`, fake mode 실행, Makefile target 실행으로 검증한다.

### 중간 검증

```bash
bash -n scripts/e2e-openai-lemonade.sh
./scripts/e2e-openai-lemonade.sh
```

기대 결과: syntax check PASS, fake mode smoke PASS.

## REVIEW_TEST-2 Makefile target

### 문제

`Makefile:1`의 `.PHONY`와 `Makefile:88` 주변 target 목록에 `test-openai-lemonade`가 없다.

```makefile
1	.PHONY: all build build-local build-edge build-edge-host build-node build-node-target build-node-targets pack-node-target pack-edge archive-edge tidy test test-e2e test-control-plane-edge-wire test-openai-ollama proto proto-dart client-test client-build-web clean
...
88	test-openai-ollama:
89		./scripts/e2e-openai-ollama.sh
```

### 해결 방법

- `.PHONY`에 `test-openai-lemonade`를 추가한다.
- `test-openai-lemonade` target은 `./scripts/e2e-openai-lemonade.sh`를 실행한다.
- `test-e2e`에는 추가하지 않는다.

```makefile
.PHONY: ... test-openai-ollama test-openai-lemonade ...

test-openai-ollama:
	./scripts/e2e-openai-ollama.sh

test-openai-lemonade:
	./scripts/e2e-openai-lemonade.sh
```

### 수정 파일 및 체크리스트

- [ ] `Makefile`: `.PHONY`와 `test-openai-lemonade` target 추가.

### 테스트 작성

- Makefile target은 별도 unit test 없이 `make test-openai-lemonade` 실행으로 검증한다.

### 중간 검증

```bash
make test-openai-lemonade
```

기대 결과: fake mode smoke PASS.

## REVIEW_TEST-3 Verification and review artifact completion

### 문제

이전 review의 `code_review_cloud_G07_0.log`는 `CODE_REVIEW-cloud-G07.md:117`부터 `CODE_REVIEW-cloud-G07.md:150`까지 검증 출력이 `(output)` placeholder였고, `계획 대비 변경 사항`과 `주요 설계 결정`도 placeholder였다. 구현 완료 여부를 판단할 실제 stdout/stderr와 구현 설명이 없다.

### 해결 방법

- 계획된 검증 명령을 실제 실행하고 `CODE_REVIEW-cloud-G07.md`의 `검증 결과`에 실제 stdout/stderr를 붙인다.
- `계획 대비 변경 사항`은 변경이 없으면 `없음`으로, 변경이 있으면 이유와 대체 명령까지 적는다.
- `주요 설계 결정`에는 fake/real mode 구성, endpoint normalization, auth redaction, cleanup 처리 결정을 짧게 기록한다.
- real mode 환경값이 없거나 endpoint/model/auth 준비가 안 되어 있으면 `사용자 리뷰 요청`에 실제 명령과 출력, 자동 후속 불가 이유, 재개 조건을 기록하고 구현 완료를 주장하지 않는다.

### 수정 파일 및 체크리스트

- [ ] `CODE_REVIEW-cloud-G07.md`: 구현 항목별 완료 여부, 구현 체크리스트, 계획 대비 변경 사항, 주요 설계 결정, 사용자 리뷰 요청, 검증 결과를 실제 내용으로 채운다.

### 테스트 작성

- 테스트 작성 없음. 이 항목은 검증 evidence와 review artifact 완성 항목이다.

### 중간 검증

```bash
IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/edgecmd ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters
git diff --check
```

기대 결과: real mode 환경값이 있으면 real Lemonade smoke PASS. 없으면 사용자 리뷰 요청에 blocker evidence를 남기고 PASS 금지. Go package tests와 whitespace check는 PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `scripts/e2e-openai-lemonade.sh` | REVIEW_TEST-1, REVIEW_TEST-3 |
| `Makefile` | REVIEW_TEST-2 |
| `CODE_REVIEW-cloud-G07.md` | REVIEW_TEST-3 |

## 최종 검증

```bash
bash -n scripts/e2e-openai-lemonade.sh
./scripts/e2e-openai-lemonade.sh
make test-openai-lemonade
IOP_LEMONADE_MODE=real ./scripts/e2e-openai-lemonade.sh
go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/edgecmd ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters
git diff --check
```

기대 결과: 모든 명령이 exit 0. real mode 환경값이 없거나 endpoint/model/auth 준비가 안 되어 있으면 PASS 금지, `사용자 리뷰 요청` 작성.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
