<!-- task=m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough plan=1 tag=REVIEW_SEULGI_RESPONSES -->

# Implementation Plan - REVIEW_SEULGI_RESPONSES

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수 완료 조건이다. 구현 후 검증을 실행하고, 실제 stdout/stderr를 붙이고, active 파일은 그대로 둔 뒤 review ready로 보고한다. selected Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막는 경우에만 review stub의 `사용자 리뷰 요청`에 정확한 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 외부 secret/service 준비, 검증 증거 공백, 일반 범위 조정은 사용자 리뷰 요청이 아니라 후속 plan 또는 검증 기록으로 처리한다. finalization은 code-review-skill 전용이다.

## 배경

이전 loop는 `/v1/responses` provider raw tunnel의 핵심 dispatch/body/auth/stream tests를 구현했지만, 성공 metering이 shared Chat tunnel labels로 기록되고 OpenAI-compatible 계약/spec 문서가 이전 reject behavior를 유지했다. 이 follow-up은 코드 동작, usage metric labels, 계약 문서를 같은 상태로 맞춰 `responses-passthrough` Roadmap Task를 PASS 가능하게 만든다.

## 사용자 리뷰 요청 흐름

선택된 Milestone lock decision이 실구현을 차단할 때만 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 직접 사용자에게 질문하지 않고, code-review가 그 요청을 검증해 `USER_REVIEW.md` 작성 여부를 결정한다.

## Archive Evidence Snapshot

- Current task path: `agent-task/m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough`
- Archived loop logs:
  - Plan: `agent-task/m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough/plan_cloud_G07_0.log`
  - Review: `agent-task/m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough/code_review_cloud_G07_0.log`
- Verdict: FAIL
- Required summary:
  - `apps/edge/internal/openai/responses_handler.go:303` calls `writeProviderTunnelResponse(..., "")`; `apps/edge/internal/openai/stream.go:471` then records success metrics as endpoint=`chat.completions`, model_group=empty instead of endpoint=`responses`, model_group=request alias.
  - `agent-contract/outer/openai-compatible-api.md:129`, `:209`, `:232` and `agent-spec/input/openai-compatible-surface.md:135` still describe provider `/v1/responses` as unsupported or non-streaming-only.
- Verification evidence from prior loop:
  - `GOCACHE=/tmp/iop-gocache go test ./apps/edge/internal/openai -count=1` PASS.
  - `GOCACHE=/tmp/iop-gocache go test ./apps/edge/...` PASS.
- Roadmap carryover: keep `responses-passthrough` as the only completion target.
- Narrow reread allowed when needed: only the two archived loop logs listed above and the predecessor complete logs cited in `분할 판단`; do not search `agent-task/archive/**` broadly.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/routing-policy-model-orchestration/milestones/seulgivibe-openai-compatible-provider.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/routing-policy-model-orchestration/milestones/seulgivibe-openai-compatible-provider.md)
- Task ids:
  - `responses-passthrough`: OpenAI-compatible provider route에서 `/v1/responses` raw passthrough가 동작
- 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/edge/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-contract/index.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-spec/index.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-roadmap/phase/routing-policy-model-orchestration/milestones/seulgivibe-openai-compatible-provider.md`
- `agent-roadmap/sdd/routing-policy-model-orchestration/seulgivibe-openai-compatible-provider/SDD.md`
- `apps/edge/internal/openai/responses_handler.go`
- `apps/edge/internal/openai/stream.go`
- `apps/edge/internal/openai/types.go`
- `apps/edge/internal/openai/server_test.go`
- `apps/edge/internal/openai/usage_metrics_test.go`
- `apps/edge/internal/service/run_dispatch.go`
- `apps/node/internal/adapters/openai_compat/openai_compat.go`
- `apps/node/internal/runtime/types.go`
- `proto/iop/runtime.proto`
- `agent-task/archive/2026/07/m-seulgivibe-openai-compatible-provider/01_config_auth_catalog/complete.log`
- `agent-task/archive/2026/07/m-seulgivibe-openai-compatible-provider/02+01_provider_tunnel_auth/complete.log`
- `agent-task/m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough/plan_cloud_G07_0.log`
- `agent-task/m-seulgivibe-openai-compatible-provider/03+01,02_responses_passthrough/code_review_cloud_G07_0.log`

### SDD 기준

- SDD: `agent-roadmap/sdd/routing-policy-model-orchestration/seulgivibe-openai-compatible-provider/SDD.md`
- 상태: `[승인됨]`
- SDD 잠금: 해제
- 대상 Acceptance Scenario:
  - S04 -> `responses-passthrough`: Codex-style `/v1/responses` payload를 provider route에서 strict normalized parser 없이 raw tunnel로 전달한다.
- Evidence Map:
  - S04 requires Edge OpenAI Responses tunnel tests and `go test ./apps/edge/internal/openai -count=1` evidence.
- 반영:
  - metric label regression test는 S04의 provider tunnel success path가 OpenAI-compatible surface 관측에도 올바르게 잡히는지 확인한다.
  - 계약/spec 갱신은 SDD Interface Contract의 Responses provider tunnel output과 OpenAI-compatible contract source of truth를 일치시킨다.

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`와 `agent-test/local/edge-smoke.md`를 읽었다.
- 적용 규칙: 변경 패키지 대상 Go test, Edge OpenAI-compatible 입력 표면 변경이므로 가능한 `go test ./apps/edge/...` 회귀 확인.
- 외부 Seulgivibe/provider live smoke는 endpoint/credential이 필요하므로 이 follow-up의 필수 검증이 아니다. Fake provider tunnel tests로 deterministic 검증한다.
- Go test cache output은 이 작업에서 충분하지 않다. `-count=1`을 사용한다.

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

- 공백: successful `/v1/responses` provider passthrough가 endpoint=`responses`, model_group=request alias, response_mode=`passthrough`로 request/usage metrics를 emit하는 테스트가 없다.
- 공백: contract/spec stale 문구 제거를 검증하는 deterministic search가 없다.
- 기존 커버: dispatch path, raw body model rewrite, unknown fields, provider auth, streaming relay는 `server_test.go`의 Responses provider tunnel tests가 커버한다.

### 심볼 참조

- renamed/removed symbol 없음.
- 변경 예정 call site:
  - `apps/edge/internal/openai/stream.go`: `tunnelChatCompletionPassthrough` -> `writeProviderTunnelResponse` call.
  - `apps/edge/internal/openai/responses_handler.go`: `tunnelResponsesPassthrough` -> `writeProviderTunnelResponse` call.

### 분할 판단

- 이 follow-up은 같은 split subtask `03+01,02_responses_passthrough` 안에서 prior FAIL을 수습한다. 새 sibling subtask를 만들지 않는다.
- predecessor `01`: satisfied by `agent-task/archive/2026/07/m-seulgivibe-openai-compatible-provider/01_config_auth_catalog/complete.log`.
- predecessor `02`: satisfied by `agent-task/archive/2026/07/m-seulgivibe-openai-compatible-provider/02+01_provider_tunnel_auth/complete.log`.
- 두 Required는 같은 `/v1/responses` provider passthrough completion evidence를 닫기 위한 코드+계약 보완이며, 별도 split은 같은 PASS evidence를 불필요하게 나눈다.

### 범위 결정 근거

- 포함: `apps/edge/internal/openai` successful provider tunnel metric labels, targeted usage metric test, `agent-contract/outer/openai-compatible-api.md`, `agent-spec/input/openai-compatible-surface.md`.
- 제외: provider response model echo rewrite, Responses sideband mode, Node/proto contract 변경, live provider smoke, pricing/Grafana 문서 갱신.
- 기존 Chat Completions passthrough metric behavior는 유지해야 한다.

### 빌드 등급

`cloud-G07`. Follow-up은 bounded하지만 API contract, living spec, OpenAI usage metrics, shared tunnel writer call sites를 함께 맞춰야 하며 prior review evidence를 보존해야 한다.

## 구현 체크리스트

- [x] [REVIEW_SEULGI_RESPONSES-1] `/v1/responses` provider passthrough success metrics가 endpoint=`responses`, model_group=request alias, response_mode=`passthrough`로 기록되게 고치고 regression test를 추가한다.
- [x] [REVIEW_SEULGI_RESPONSES-2] OpenAI-compatible contract와 matching agent-spec을 Responses provider raw passthrough behavior에 맞게 갱신하고 stale unsupported 문구 검색을 통과시킨다.
- [x] `GOCACHE=/tmp/iop-gocache go test ./apps/edge/internal/openai -count=1`를 실행한다.
- [x] `GOCACHE=/tmp/iop-gocache go test ./apps/edge/...`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_SEULGI_RESPONSES-1] Responses Passthrough Usage Labels

#### 문제

`apps/edge/internal/openai/responses_handler.go:279`는 Responses용 labels를 만들지만, success relay는 `responses_handler.go:303`에서 `requestModel=""`로 shared writer를 호출한다. `apps/edge/internal/openai/stream.go:471`은 writer 안에서 endpoint=`chat.completions`, model_group=`strings.TrimSpace(requestModel)`로 labels를 다시 만들기 때문에 Responses success metrics가 잘못 분류된다.

Before:

```go
// apps/edge/internal/openai/responses_handler.go:279
metricLabels := s.usageLabelsFor(r.Context(), strings.TrimSpace(env.Model), usageEndpointResponses, responseModePassthrough)
handle, err := s.service.SubmitProviderTunnel(r.Context(), tunnelReq)
// ...
s.writeProviderTunnelResponse(w, r, handle, env.Stream, "")
```

```go
// apps/edge/internal/openai/stream.go:471
metricLabels := s.usageLabelsFor(r.Context(), strings.TrimSpace(requestModel), usageEndpointChatCompletions, responseModePassthrough)
```

#### 해결 방법

`writeProviderTunnelResponse`가 caller가 계산한 `usageLabels`를 받게 하거나 equivalent options struct를 도입한다. Chat path는 기존 chat labels를 넘기고, Responses path는 이미 만든 responses labels를 넘긴다. `requestModel`은 model echo rewrite 용도로만 남겨서 Responses body rewrite는 계속 꺼둔다.

After:

```go
// apps/edge/internal/openai/stream.go
func (s *Server) writeProviderTunnelResponse(w http.ResponseWriter, r *http.Request, handle edgeservice.ProviderTunnelResult, reqStream bool, requestModel string, metricLabels usageLabels) {
    // no local usageEndpointChatCompletions recompute here
}
```

```go
// apps/edge/internal/openai/responses_handler.go
metricLabels := s.usageLabelsFor(r.Context(), strings.TrimSpace(env.Model), usageEndpointResponses, responseModePassthrough)
handle, err := s.service.SubmitProviderTunnel(r.Context(), tunnelReq)
// ...
s.writeProviderTunnelResponse(w, r, handle, env.Stream, "", metricLabels)
```

```go
// apps/edge/internal/openai/stream.go
metricLabels := s.usageLabelsFor(r.Context(), strings.TrimSpace(req.Model), usageEndpointChatCompletions, responseModePassthrough)
s.writeProviderTunnelResponse(w, r, handle, req.Stream, req.Model, metricLabels)
```

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

- [x] `apps/edge/internal/openai/stream.go`: writer signature/call site update, Chat passthrough labels preserved.
- [x] `apps/edge/internal/openai/responses_handler.go`: Responses passthrough passes responses labels to writer.
- [x] `apps/edge/internal/openai/usage_metrics_test.go`: Responses passthrough success metric regression test 추가.

#### 테스트 작성

- Add `TestResponsesProviderTunnelPassthroughObservesUsageMetrics` in `apps/edge/internal/openai/usage_metrics_test.go`.
- Fixture: provider-pool model `pool-model`, principal token auth or default anonymous labels matching existing helpers, `staticProviderTunnelFrames` with a Responses-like body.
- Assertions:
  - HTTP body remains provider-original.
  - `openAIRequestsTotal` delta is `1` for labels endpoint=`responses`, response_mode=`passthrough`, status=`success`, model_group=`pool-model`.
  - The old wrong labels endpoint=`chat.completions`, model_group="" do not increment for the same call.

#### 중간 검증

```bash
GOCACHE=/tmp/iop-gocache go test ./apps/edge/internal/openai -run 'TestResponsesProviderTunnelPassthroughObservesUsageMetrics|TestProviderTunnelPassthroughPreservesBodyAndObservesUsage' -count=1
```

Expected: both tests pass; Chat passthrough usage regression remains green.

### [REVIEW_SEULGI_RESPONSES-2] Contract And Spec Sync

#### 문제

Implementation now allows provider `/v1/responses` passthrough, but the outer contract still says the path is unsupported:

```markdown
<!-- agent-contract/outer/openai-compatible-api.md:129 -->
- OpenAI-compatible provider model group route(provider pool, `openai_compat`, `vllm`)의 `/v1/responses` 호출은 raw passthrough parity가 구현되기 전까지 `400 invalid_request_error`로 거부한다.
```

`agent-contract/outer/openai-compatible-api.md:209` and `:232` repeat the same pre-parity state. `agent-spec/input/openai-compatible-surface.md:135` also says `/v1/responses` is non-streaming string input only, which is no longer true for provider raw passthrough routes.

#### 해결 방법

Update the OpenAI-compatible contract and matching living spec to describe the new split behavior:

- normalized non-provider `/v1/responses`: keep strict field validation and non-streaming string input limit.
- provider route (`provider pool`, `openai_compat`, `vllm`): raw passthrough to `POST /v1/responses`, model field rewrite only, unknown/Codex fields preserved, `stream:true` relayed as raw SSE, provider auth forwarding applied, no response model echo rewrite or sideband injection.
- usage metric labels for Responses passthrough use endpoint=`responses`, response_mode=`passthrough`, model_group=request alias.
- Remove stale “raw parity before implementation” unsupported statements.

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

- [x] `agent-contract/outer/openai-compatible-api.md`: Responses API and provider route sections updated.
- [x] `agent-spec/input/openai-compatible-surface.md`: feature list, settings/flow, limitations, verification/change history updated.

#### 테스트 작성

- No Go test for docs-only text.
- Add deterministic stale-text verification command below.

#### 중간 검증

```bash
if rg --sort path -n 'raw passthrough parity가 구현되기 전까지|raw passthrough parity 전까지 지원하지 않는다|provider model group raw passthrough parity 전까지' agent-contract/outer/openai-compatible-api.md agent-spec/input/openai-compatible-surface.md; then exit 1; fi
```

Expected: no matches and exit code 0.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/openai/stream.go` | REVIEW_SEULGI_RESPONSES-1 |
| `apps/edge/internal/openai/responses_handler.go` | REVIEW_SEULGI_RESPONSES-1 |
| `apps/edge/internal/openai/usage_metrics_test.go` | REVIEW_SEULGI_RESPONSES-1 |
| `agent-contract/outer/openai-compatible-api.md` | REVIEW_SEULGI_RESPONSES-2 |
| `agent-spec/input/openai-compatible-surface.md` | REVIEW_SEULGI_RESPONSES-2 |

## 최종 검증

```bash
GOCACHE=/tmp/iop-gocache go test ./apps/edge/internal/openai -count=1
GOCACHE=/tmp/iop-gocache go test ./apps/edge/... 
if rg --sort path -n 'raw passthrough parity가 구현되기 전까지|raw passthrough parity 전까지 지원하지 않는다|provider model group raw passthrough parity 전까지' agent-contract/outer/openai-compatible-api.md agent-spec/input/openai-compatible-surface.md; then exit 1; fi
```

Expected: both Go test commands pass with fresh execution; stale unsupported-contract search prints no matches and exits 0.

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