<!-- task=m-openai-responses-input-surface/02+01_responses_smoke plan=0 tag=TEST -->

# Plan - TEST

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

`CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 단계다. 구현 후 검증 명령의 실제 stdout/stderr를 붙이고 active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 사용자 전용 결정, 외부 환경 준비, scope 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 중단한다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

Responses API 구현만으로는 Milestone smoke 기준이 닫히지 않는다. 현재 `iop-edge smoke openai`는 `/v1/chat/completions`만 확인하므로 `/v1/responses` listener path가 실제 smoke 흐름에서 검증되지 않는다. 이 작업은 01번 API surface 완료 후 CLI smoke와 local test profile을 Responses 기준으로 갱신한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-cloud-G06.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 프롬프트는 금지되며, code-review가 요청 타당성을 검증하고 필요할 때만 `USER_REVIEW.md`를 작성한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-responses-input-surface.md`
- Task ids:
  - `responses-smoke`: `iop-edge smoke openai` 또는 동등 smoke가 `/healthz`, `/v1/models`, `POST /v1/responses`를 확인한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `AGENTS.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-responses-input-surface.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `apps/edge/internal/edgecmd/smoke_openai.go`
- `apps/edge/internal/edgecmd/root.go`
- `apps/edge/internal/edgecmd/edgecmd_test.go`
- `apps/edge/cmd/edge/main.go`
- `apps/edge/cmd/edge/main_test.go`
- `apps/edge/internal/openai/types.go`
- `go.mod`

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`는 존재하며 읽었다.
- 적용 profile은 `agent-test/local/edge-smoke.md`다.
- profile 명령: 변경한 edge 패키지 또는 `go test ./apps/edge/...`; OpenAI-compatible 경계는 `iop-edge smoke openai` 또는 동등한 `/healthz`, `/v1/models`, `/v1/chat/completions` 확인.
- 이 plan은 profile 자체의 OpenAI-compatible smoke endpoint 기준을 `/v1/responses`로 갱신한다.
- 실제 listener smoke 명령은 user/runner가 Edge server와 node adapter를 준비해야 통과한다. 준비되지 않으면 구현 에이전트는 review stub의 `사용자 리뷰 요청`에 실행 명령, 실패 근거, 재개 조건을 남긴다.
- 구조적 blank, `<확인 필요>` 값, missing profile은 없다.

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

- `TestSmokeOpenAICommandSuccess`는 현재 `/v1/chat/completions`만 mock/assert한다.
- `TestSmokeOpenAICommandFailure`는 chat completions non-200/empty content만 실패 path로 본다.
- `smoke_openai.go`는 Responses response shape에서 text를 추출하는 parser가 없다.
- `agent-test/local/edge-smoke.md` 판정 기준은 아직 `/v1/chat/completions`를 OpenAI-compatible smoke 기준으로 적고 있다.

### 심볼 참조

- renamed/removed symbol: none.
- `smokeOpenAICmd`, `chatCompletionResponse`, `/v1/chat/completions` 참조는 `rg --sort path`로 확인했다.

### 분할 판단

- split decision policy를 plan 파일 선택 전에 평가했다.
- shared task group: `agent-task/m-openai-responses-input-surface/`
- sibling `01_responses_api_surface`: `/v1/responses` API 구현. predecessor 없음.
- this subtask `02+01_responses_smoke`: predecessor index `01`.
- predecessor state: active `agent-task/m-openai-responses-input-surface/01_responses_api_surface/complete.log` 없음. archive 후보는 현재 새 task group이므로 없음. 구현은 01번 PASS archive 또는 active complete.log 확인 뒤 시작한다.

### 범위 결정 근거

- `/v1/responses` API 구현 자체는 01번 plan 범위다.
- chat completions endpoint 제거는 하지 않는다. 이 작업은 `smoke openai`의 inference check를 Responses로 전환하는 데 집중한다.
- Docker compose, code-server recreate, toki-labs.com port open 작업은 제외한다. 실제 listener가 필요하면 user-owned environment blocker로 기록한다.
- NomadCode Core 통합 smoke는 `../nomadcode` External Integration Milestone 범위라 제외한다.

### 빌드 등급

- build lane: `cloud-G07`; review lane: `cloud-G06`.
- 근거: CLI smoke command, HTTP response parsing, stdout/error contract가 중심이고 실제 listener 검증은 외부 runtime 준비 상태에 영향을 받는다.

## 구현 체크리스트

- [ ] `iop-edge smoke openai`의 inference check를 `POST /v1/responses` 기준으로 갱신하고 response text extractor를 추가한다. 검증: `go test -count=1 ./apps/edge/cmd/edge ./apps/edge/internal/edgecmd`.
- [ ] smoke command 성공/실패 tests를 Responses shape와 error message 기준으로 갱신한다. 검증: `go test -count=1 ./apps/edge/cmd/edge`.
- [ ] `agent-test/local/edge-smoke.md`의 OpenAI-compatible smoke 기준을 `/healthz`, `/v1/models`, `/v1/responses`로 갱신한다.
- [ ] 전체 edge regression과 실제 listener smoke를 실행한다. 검증: `go test -count=1 ./apps/edge/...` 및 `go run ./apps/edge/cmd/edge smoke openai --model test-model --base-url http://127.0.0.1:18081 --prompt ping --timeout 15s`.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

`02+01_responses_smoke`는 directory name 기준으로 predecessor `01`에 의존한다. 구현 시작 전 `agent-task/m-openai-responses-input-surface/01_responses_api_surface/complete.log` 또는 matching archive complete.log를 확인한다. 01이 완료되지 않았으면 이 plan은 대기한다.

### [TEST-1] Smoke Command Uses Responses

#### 문제

`smoke_openai.go` help text와 Step 3은 chat completions를 기준으로 한다.

```go
// apps/edge/internal/edgecmd/smoke_openai.go:105
Long: `Validate the IOP Edge OpenAI-compatible HTTP API surface.
It runs a three-step diagnostics check:
  1. Checks GET /healthz (server status).
  2. Checks GET /v1/models (advertised model compatibility).
  3. Checks POST /v1/chat/completions (inference request processing).
```

```go
// apps/edge/internal/edgecmd/smoke_openai.go:190
fmt.Fprintf(cmd.OutOrStdout(), "Step 3: Checking /v1/chat/completions ... ")
```

#### 해결 방법

- Step 3 endpoint를 `/v1/responses`로 바꾼다.
- request payload는 최소 shape로 보낸다: `model`, `input`, `stream:false`.
- response parser는 `output_text`를 우선 읽고, 없으면 `output[].content[].text`에서 첫 non-empty text를 읽는다.
- empty response text는 `POST ... returned empty response text`로 실패 처리한다.
- 기존 model warning과 `/healthz`, `/v1/models` 검증은 유지한다.

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

- [ ] `apps/edge/internal/edgecmd/smoke_openai.go`: help text, Step 3 label, payload, response DTO, text extractor 갱신.
- [ ] `apps/edge/cmd/edge/main_test.go`: success/failure expectations 갱신.

#### 테스트 작성

- `TestSmokeOpenAICommandSuccess`: mock server가 `/v1/responses`를 받고 `output_text` 또는 nested output content를 반환하도록 변경한다.
- `TestSmokeOpenAICommandFailure`: `/v1/responses` non-200와 empty response text 실패를 검증한다.

#### 중간 검증

```bash
go test -count=1 ./apps/edge/cmd/edge ./apps/edge/internal/edgecmd
```

Expected: both packages `ok`.

### [TEST-2] Smoke Contract Documentation Tracks Responses

#### 문제

`agent-test/local/edge-smoke.md`의 필수 검증과 판정 기준은 아직 chat completions를 OpenAI-compatible smoke 기준으로 둔다.

```md
<!-- agent-test/local/edge-smoke.md:48 -->
- OpenAI-compatible 경계를 바꾼 경우 `iop-edge smoke openai` 또는 동등한 `/healthz`, `/v1/models`, `/v1/chat/completions` 확인으로 edge service와 node adapter 경로 수렴을 확인한다.
```

```md
<!-- agent-test/local/edge-smoke.md:63 -->
- OpenAI-compatible smoke에서 `/healthz`, `/v1/models`, `/v1/chat/completions`가 기대 상태로 응답한다.
```

#### 해결 방법

- OpenAI-compatible smoke 기준을 `/healthz`, `/v1/models`, `/v1/responses`로 변경한다.
- chat completions baseline은 기존 unit tests가 유지한다는 범위 설명을 덧붙인다.

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

- [ ] `agent-test/local/edge-smoke.md`: 필수 검증과 판정 기준의 endpoint를 Responses 기준으로 갱신.

#### 테스트 작성

- 문서/규칙 변경이라 별도 test file은 없다. `rg --sort path -n "/v1/chat/completions|/v1/responses" agent-test/local/edge-smoke.md apps/edge/internal/edgecmd/smoke_openai.go apps/edge/cmd/edge/main_test.go`로 기준 drift를 확인한다.

#### 중간 검증

```bash
rg --sort path -n "/v1/chat/completions|/v1/responses" agent-test/local/edge-smoke.md apps/edge/internal/edgecmd/smoke_openai.go apps/edge/cmd/edge/main_test.go
```

Expected: smoke command/test/profile의 inference smoke 기준이 `/v1/responses`를 가리키며, chat completions 참조가 남아 있다면 legacy unit test나 기존 API 문맥으로만 남아야 한다.

### [TEST-3] Actual Listener Smoke Evidence

#### 문제

Milestone task는 remote/local edge smoke에서 non-streaming Responses path가 실제 listener에 대해 PASS해야 한다. unit `httptest`만으로는 실제 listener와 node adapter 경로 수렴을 증명하지 못한다.

```go
// apps/edge/cmd/edge/main_test.go:1043
func TestSmokeOpenAICommandSuccess(t *testing.T) {
	// Start an httptest server to mock the OpenAI-compatible endpoint
```

#### 해결 방법

- 구현 후 local 또는 remote runner에서 실제 Edge OpenAI-compatible listener를 준비하고 smoke command를 실행한다.
- 현재 agent가 listener/node 준비 권한이나 port open 권한이 없으면 `사용자 리뷰 요청`에 blocker를 남긴다.
- user가 code-server/Edge runtime을 준비한 뒤 같은 plan을 재개할 수 있도록 exact command와 실패 근거를 기록한다.

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

- [ ] `CODE_REVIEW-cloud-G06.md`: 실제 listener smoke 결과 또는 user-owned blocker 기록.

#### 테스트 작성

- 별도 test file은 없다. 실제 listener command output이 evidence다.

#### 중간 검증

```bash
go run ./apps/edge/cmd/edge smoke openai --model test-model --base-url http://127.0.0.1:18081 --prompt ping --timeout 15s
```

Expected: command exits 0 and output includes `Step 3: Checking /v1/responses ... [OK]`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/edgecmd/smoke_openai.go` | TEST-1 |
| `apps/edge/cmd/edge/main_test.go` | TEST-1 |
| `agent-test/local/edge-smoke.md` | TEST-2 |
| `CODE_REVIEW-cloud-G06.md` | TEST-3 |

## 최종 검증

```bash
go test -count=1 ./apps/edge/cmd/edge ./apps/edge/internal/edgecmd
go test -count=1 ./apps/edge/...
rg --sort path -n "/v1/chat/completions|/v1/responses" agent-test/local/edge-smoke.md apps/edge/internal/edgecmd/smoke_openai.go apps/edge/cmd/edge/main_test.go
go run ./apps/edge/cmd/edge smoke openai --model test-model --base-url http://127.0.0.1:18081 --prompt ping --timeout 15s
```

Expected: Go tests return `ok`; rg output shows `/v1/responses` as smoke inference target; actual listener smoke exits 0 with `Step 3: Checking /v1/responses ... [OK]`. Test cache output is not acceptable.

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