<!-- task=m-openai-compatible-raw-tunnel-sideband-passthrough/01_node_relay plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

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

`CODE_REVIEW-local-G07.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 단계다. 검증 명령은 실제로 실행하고 stdout/stderr를 붙인다. 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청`을 채우고, 사용자에게 직접 질문하지 않는다. 환경/secret/service blocker나 evidence gap은 사용자 리뷰가 아니라 검증 결과 또는 follow-up으로 기록한다.

## 배경

이 follow-up은 `node-relay` 구현의 FAIL 리뷰를 닫기 위한 좁은 보정 작업이다. 성공 body passthrough fixture는 통과했지만, tunnel error/cancel path의 ordered terminal frame semantics와 계획 필수 테스트가 부족하다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 Milestone lock decision이 실구현을 차단할 때만 active review stub에 기록한다. 직접 채팅 질문, 선택지 제시, `request_user_input`, `USER_REVIEW.md` 작성, archive/complete.log 작성은 금지한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/routing-policy-model-orchestration/milestones/openai-compatible-raw-tunnel-sideband-passthrough.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/routing-policy-model-orchestration/milestones/openai-compatible-raw-tunnel-sideband-passthrough.md)
- Task ids:
  - `node-relay`: Node는 OpenAI-compatible provider HTTP/SSE response를 열고 provider가 보낸 chunk를 tunnel frame으로 relay한다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-openai-compatible-raw-tunnel-sideband-passthrough/01_node_relay/plan_local_G07_0.log`
- Archived review: `agent-task/m-openai-compatible-raw-tunnel-sideband-passthrough/01_node_relay/code_review_local_G07_0.log`
- Verdict: FAIL
- Required summary:
  - R1: adapter read/cancel error frames do not carry the next ordered `sequence`, and Node can emit a duplicate ERROR frame after adapter-owned errors.
  - R2: required handler nil/error, actual tunnel frame send, upstream context cancellation/read-error tests are missing.
- Affected files:
  - `apps/node/internal/adapters/openai_compat/openai_compat.go`
  - `apps/node/internal/adapters/openai_compat/openai_compat_test.go`
  - `apps/node/internal/adapters/vllm/vllm.go`
  - `apps/node/internal/adapters/vllm/vllm_test.go`
  - `apps/node/internal/node/node.go`
  - `apps/node/internal/node/node_test.go`
  - `apps/node/internal/transport/session_test.go`
- Verification evidence from review:
  - `go test -count=1 ./apps/node/internal/transport ./apps/node/internal/node ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm` PASS.
  - `go test -count=1 ./apps/edge/internal/transport` PASS.
  - `gofmt -l` on planned Go files was empty after review-agent formatting repair.
- Roadmap carryover: `node-relay` remains incomplete; S02/S03 success byte fixtures exist, but S09 cancel/error evidence is insufficient.
- Narrow archive reread allowed if needed: the two archived files listed above only.

## 범위 결정 근거

- 포함: tunnel adapter error/cancel terminal frame ownership, sequence assignment, duplicate error prevention, missing targeted tests.
- 제외: Edge OpenAI HTTP passthrough integration, response mode parsing, sideband/transformed mode, assembled log persistence, external provider field smoke.
- `USER_REVIEW.md`는 만들지 않는다. 모든 문제는 코드와 deterministic tests로 해소 가능하다.

## 구현 체크리스트

- [ ] `openai_compat`와 `vllm` tunnel path에서 provider/body read error와 context cancellation이 정확히 하나의 terminal ERROR frame을 현재 sequence로 emit하도록 정리한다.
- [ ] `node.OnProviderTunnelRequest`가 adapter 실행 전 실패만 직접 ERROR frame으로 보내고, adapter가 이미 소유한 tunnel error를 중복 전송하지 않게 한다.
- [ ] transport/node tests에 handler nil/error path, unsupported adapter/lookup failure, 실제 `ProviderTunnelFrame` send와 sequence 검증을 추가한다.
- [ ] adapter tests에 upstream context cancellation 또는 body read error가 provider request를 중단하고 ordered ERROR frame으로 끝나는 검증을 추가한다.
- [ ] `go test -count=1 ./apps/node/internal/transport ./apps/node/internal/node ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_API-1] Tunnel Error Frame Semantics

문제: adapter body read error 또는 context cancellation이 body frames 이후 발생하면 ERROR frame이 ordered sequence를 잃거나 Node가 같은 오류를 다시 전송할 수 있다.

해결 방법:

- adapter tunnel implementation이 `seq`를 소유하고 RESPONSE_START/BODY/ERROR/END 모두 단조 증가 sequence를 사용하게 한다.
- provider request build/do 실패처럼 response start 전 실패는 sequence `0` ERROR frame 하나로 끝낸다.
- response start 이후 read error 또는 context cancellation은 다음 sequence의 ERROR frame 하나로 끝낸다.
- `TunnelProvider`가 ERROR frame을 이미 emit한 오류에 대해서는 Node가 추가 ERROR frame을 보내지 않게 한다. Node direct ERROR frame은 adapter lookup 실패, unsupported adapter처럼 adapter 실행 전 실패에 한정한다.

수정 파일 및 체크리스트:

- [ ] `apps/node/internal/adapters/openai_compat/openai_compat.go`
- [ ] `apps/node/internal/adapters/vllm/vllm.go`
- [ ] `apps/node/internal/node/node.go`

중간 검증:

```bash
go test -count=1 ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm -run 'Test(OpenAICompat|Vllm)TunnelProvider'
```

### [REVIEW_API-2] Failure/Cancel Coverage

문제: 첫 루프 테스트는 success passthrough만 확인하고 계획의 nil/error/cancel 검증을 닫지 못했다.

해결 방법:

- `transport.Session` test에서 `ProviderTunnelRequest` no handler/handler error path가 panic 없이 처리되는지 확인한다.
- `node.Node` test에서 real or fake proto sender를 통해 unsupported/lookup failure와 adapter error path가 정확히 하나의 ERROR frame을 보내고 sequence/tunnel id/node id를 보존하는지 확인한다.
- adapter tests에서 `httptest` handler가 request context cancellation을 관측하고, sink가 ordered ERROR frame으로 종료되는지 확인한다.

수정 파일 및 체크리스트:

- [ ] `apps/node/internal/transport/session_test.go`
- [ ] `apps/node/internal/node/node_test.go`
- [ ] `apps/node/internal/adapters/openai_compat/openai_compat_test.go`
- [ ] `apps/node/internal/adapters/vllm/vllm_test.go`

중간 검증:

```bash
go test -count=1 ./apps/node/internal/transport ./apps/node/internal/node -run 'Test(SessionProviderTunnel|NodeOnProviderTunnel)'
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/openai_compat/openai_compat.go` | REVIEW_API-1 |
| `apps/node/internal/adapters/openai_compat/openai_compat_test.go` | REVIEW_API-2 |
| `apps/node/internal/adapters/vllm/vllm.go` | REVIEW_API-1 |
| `apps/node/internal/adapters/vllm/vllm_test.go` | REVIEW_API-2 |
| `apps/node/internal/node/node.go` | REVIEW_API-1 |
| `apps/node/internal/node/node_test.go` | REVIEW_API-2 |
| `apps/node/internal/transport/session_test.go` | REVIEW_API-2 |

## 최종 검증

```bash
go test -count=1 ./apps/node/internal/transport ./apps/node/internal/node ./apps/node/internal/adapters/openai_compat ./apps/node/internal/adapters/vllm
```

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