<!-- task=m-agent-readable-repository-refactor/01_openai_modules plan=0 tag=REFACTOR_OPENAI -->

# OpenAI 호환 surface 책임 중심 파일 분해 계획

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

`CODE_REVIEW-*-G??.md` 작성은 구현의 필수 마지막 단계다. 아래 명령을 실제 실행하고 구현 소유 섹션에 출력과 변경 내용을 채운 뒤 active 파일을 유지한 채 리뷰 준비 완료를 보고한다. 최종 판정, archive, `complete.log`는 code-review만 수행한다.

선택 Milestone의 `구현 잠금 > 결정 필요`가 막을 때만 review stub의 `사용자 리뷰 요청`을 채운다. 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`를 만들지 않는다. 환경·서비스·evidence 문제는 일반 follow-up으로 기록한다.

## 배경

`apps/edge/internal/openai`의 네 파일은 합계 5,099줄이며 DTO, text-tool parsing, schema/command normalization, normalized SSE, provider tunnel/rewrite, route/policy가 섞여 있다. package와 식별자, HTTP/SSE 계약은 유지하고 같은 package 안에서 변경 이유별 파일로 재배치한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md)
- Task ids:
  - `openai-modules`: OpenAI chat/Responses, text-tool, normalized SSE, provider tunnel/rewrite, route/policy 책임 분해
- Completion mode: check-on-pass

## 분석 결과

- 읽은 파일: `types.go`, `stream.go`, `chat_handler.go`, `responses_handler.go`, package test 목록, `agent-contract/outer/openai-compatible-api.md`, `agent-contract/inner/edge-node-runtime-wire.md`, `agent-spec/input/openai-compatible-surface.md`, `agent-spec/runtime/edge-node-execution.md`.
- SDD 기준: Milestone은 `SDD: 불필요`; 동작·wire·외부 API를 바꾸지 않는 same-package 재배치다.
- 테스트 환경 규칙: `test_env=local`; `agent-test/local/rules.md`, `edge-smoke.md`, `testing-smoke.md`를 적용한다. 기본 cache 권한 문제 때문에 `GOCACHE=/tmp/iop-go-cache`를 사용하고 fresh test를 위해 `-count=1`을 고정한다.
- 기준선: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai`가 통과했다.
- 분할 판단: 이 plan은 OpenAI internal package의 선언 재배치만 맡는다. 함수 내부 상태 구조화와 dispatch context는 선행 완료 후 `04+01,03_openai_complexity`가 맡아 서로 다른 회귀 원인을 분리한다.
- 제외: JSON field, status/error text, SSE event 순서, provider raw-byte passthrough, logging/usage 의미, exported service API, 새 package·`utils` 파일.
- 빌드 등급: `cloud-G07`; 5천 줄과 외부 호환 계약을 함께 대조해야 하나 변경 경계는 단일 package다.

## 구현 체크리스트

- [x] chat/Responses DTO와 요청 decode·응답 encode 타입을 surface별 파일로 이동하고 JSON tag를 보존한다.
- [x] text-tool parser와 schema/command normalization을 책임 파일로 이동하고 parser/validation 결과를 보존한다.
- [x] normalized SSE와 provider tunnel/rewrite/observation을 파일별로 분리하고 raw passthrough를 보존한다.
- [x] chat/Responses route, policy, completion 책임을 분리하고 package·식별자·동작 계약을 유지한다.
- [x] OpenAI package와 Edge 회귀, readability ratchet을 통과시킨다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR_OPENAI-1] DTO와 text-tool 책임 분리

문제: `types.go:12-1976`이 chat/Responses DTO와 1,600줄 이상의 parser·schema·command normalization을 함께 가진다.

해결 방법: `chat_types.go`, `responses_types.go`, `common_types.go`, `text_tool_parser.go`, `tool_argument_normalization.go`로 선언을 이동한다. 기존 이름·visibility·JSON tag와 parse/normalize 순서는 유지한다.

- [x] DTO와 parser 계열의 import를 새 파일별 최소 집합으로 정리한다.
- [x] `types.go`가 비면 삭제하고 중복 선언이 없게 한다.
- 테스트 작성: skip. `chat_handler_test.go`, `chat_tool_synthesis_test.go`, `responses_handler_test.go`가 직접 계약을 검증한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai -run 'Tool|Schema|Responses|ChatCompletions'`

### [REFACTOR_OPENAI-2] normalized SSE와 provider tunnel 분리

문제: `stream.go:27-1282`가 normalized run stream, provider tunnel relay, model rewrite, response observation, buffered strict SSE를 함께 가진다.

해결 방법: `normalized_sse.go`, `buffered_sse.go`, `provider_tunnel.go`, `provider_model_rewrite.go`, `provider_observation.go`, `sse_writer.go`로 이동한다. `streamChatCompletion` 내부 구조는 이 plan에서 바꾸지 않는다.

- [x] provider response header filtering과 byte rewrite/observation 경계를 유지한다.
- [x] terminal `[DONE]`, error event, flush 순서를 유지한다.
- 테스트 작성: skip. stream reasoning, provider tunnel/observability/usage tests가 존재한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai -run 'Stream|Tunnel|Passthrough|Usage|Observ'`

### [REFACTOR_OPENAI-3] route/policy/completion 분리

문제: `chat_handler.go`와 `responses_handler.go`가 request ingress, provider-pool dispatch, policy, route resolution, completion collection을 각각 한 파일에 둔다.

해결 방법: surface handler는 얇게 유지하고 `chat_decode.go`, `chat_policy.go`, `chat_completion.go`, `responses_decode.go`, `responses_completion.go`, `route_resolution.go`로 이동한다. 함수 signature와 dispatch 흐름은 유지한다.

- [x] strict field decode와 lenient envelope decode의 사용 위치를 바꾸지 않는다.
- [x] model catalog generation/thinking policy와 workspace validation 순서를 유지한다.
- 테스트 작성: skip. handler, policy, selection, cancellation tests가 정상·오류 경로를 검증한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai -run 'Policy|Selection|Route|Cancel|Completion'`

### [REFACTOR_OPENAI-4] 패키지·소비자 회귀

문제: same-package 이동도 Edge route 등록과 service interface 소비를 깨뜨릴 수 있다.

해결 방법: OpenAI/Edge 전체 회귀와 readability ratchet을 실행하고 네 원본 파일의 책임 혼합 위반이 제거됐는지 확인한다.

- [x] stale duplicate와 범용 `utils` 파일이 없는지 확인한다.
- 테스트 작성: skip. 최종 회귀 항목이다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/...`

## 수정 파일 요약

- 기존: `apps/edge/internal/openai/{types.go,stream.go,chat_handler.go,responses_handler.go}`
- 신규 책임 파일: 위 항목에 명시한 same-package Go 파일
- 테스트 파일은 assertion 보강이 실제 공백을 발견한 경우에만 수정한다.

## 최종 검증

```bash
gofmt -d apps/edge/internal/openai/*.go
GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai
GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/...
make readability-audit
git diff --check
```

모든 명령은 종료 코드 0이어야 하고 새/증가 readability 위반이 없어야 한다.

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