<!-- task=m-agent-readable-repository-refactor/04+01,03_openai_complexity plan=0 tag=REFACTOR_DISPATCH -->

# OpenAI stream session과 dispatch context 구조화 계획

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

이 plan은 `01_openai_modules`와 `03_service_node_modules`가 PASS되어 active sibling 또는 matching archive에 각각 하나의 `complete.log`가 존재할 때만 시작한다. 없거나 둘 이상이면 구현하지 말고 선행 상태를 검증 결과에 기록한다. 구현 뒤 review stub을 실제 출력으로 채우고 active 파일을 유지한다. Milestone 결정 차단 외 직접 질문과 user-review 파일 작성은 금지한다.

## 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:
  - `stream-session`: normalized SSE 상태와 terminal 처리를 명명된 session으로 구조화
  - `dispatch-context`: Chat/Responses/provider-pool 인자와 reservation/release lifecycle 구조화
- Completion mode: check-on-pass

## 분석 결과

- 선행 완료 근거: 계획 작성 시점에는 없음. active sibling plan 01/03이므로 runtime은 이 plan 구현을 차단해야 한다. 완료 시 active 또는 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/<subtask>/complete.log` 중 정확히 하나를 선택한다.
- 현재 근거: `stream.go:27`의 `streamChatCompletion` 299줄, `chat_handler.go:22/185`, `responses_handler.go:19/223`, `run_dispatch.go:959`과 queue release 경로.
- 계약: OpenAI outer API, edge-node runtime wire, provider pool refresh spec를 보존한다.
- 테스트 환경: local edge/node/testing profiles, fresh Go tests, full-cycle. 기준선 OpenAI/service/Node tests 통과.
- 분할 판단: 상태 객체와 dispatch context는 module split 뒤 실제 경로에 적용해야 하며 서로 같은 request lifecycle을 공유하므로 한 후속 plan에서 검증한다.
- 제외: event schema, retry count, timeout/cancel policy, queue scheduling algorithm, public interface.
- 빌드 등급: `cloud-G08`.

## 구현 체크리스트

- [ ] 선행 01/03의 `complete.log`와 최종 source path를 확인한다.
- [ ] content/reasoning/tool/usage/trace/terminal 상태를 `chatStreamSession`과 event 단계로 구조화한다.
- [ ] Chat/Responses/provider-pool 호출의 다인자를 명명된 immutable dispatch context로 바꾼다.
- [ ] queue reservation/release를 소유하는 context를 두고 모든 성공·오류·cancel 경로의 exactly-once release를 검증한다.
- [ ] 관련 회귀, full-cycle, readability ratchet을 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR_DISPATCH-1] Normalized stream session

문제: 현재 `streamChatCompletion` local state가 content/reasoning/tool/usage/trace와 terminal write를 한 함수에서 갱신한다.

해결 방법: request 고정값과 누적 상태를 분리한 `chatStreamSession`을 만들고 `consumeEvent`, `emitDelta`, `finish`, `fail` 단계로 위임한다. terminal emission과 cancel은 한 경로에서만 소유한다.

- [ ] live SSE와 strict buffered path를 혼동하지 않는다.
- [ ] native/text tool, reasoning visibility, sentinel filtering, usage를 보존한다.
- 테스트 작성: session 단위 table test를 추가해 complete/error/closed/cancel에서 terminal 1회와 state 결과를 검증한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai -run 'Stream|Reasoning|Tool|Cancel|Timeout|Usage'`

### [REFACTOR_DISPATCH-2] OpenAI request/dispatch context

문제: Chat/Responses handler와 provider-pool helper가 request, raw body, route, metadata, estimate, classification, output policy 등 10개 이상 값을 전달한다.

해결 방법: surface별 decoded request와 공통 routing/identity/metrics를 담는 context를 도입한다. 생성 지점은 ingress 하나이며 downstream은 필요한 context만 받는다.

- [ ] mutable request policy 적용 전/후 값을 명확히 분리한다.
- [ ] raw provider body와 rewritten body를 별도 필드로 둔다.
- 테스트 작성: 기존 dispatch fake가 받은 request/metadata assertion을 유지하고 context constructor 경계 test만 보강한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai -run 'ProviderPool|Metadata|Policy|Dispatch|Responses'`

### [REFACTOR_DISPATCH-3] Reservation/release context

문제: Edge service provider-pool와 queue 경로에서 reservation 정보와 release closure가 여러 반환형/오류 경로로 전달된다.

해결 방법: node/provider/long-slot/queue reason과 idempotent release를 소유하는 내부 context를 두고 handoff 지점을 명명한다.

- [ ] success, prepare error, send error, terminal frame, close, context cancel을 모두 exactly-once로 닫는다.
- 테스트 작성: 기존 release exactly-once tests를 context 경계별 table로 보강한다.
- 중간 검증: `GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/service -run 'ProviderPool|ProviderTunnel|Release|Admission|Cancel'`

## 최종 검증

```bash
for d in 01_openai_modules 03_service_node_modules; do mapfile -t logs < <(find "agent-task/m-agent-readable-repository-refactor/$d" agent-task/archive -type f -path "*/m-agent-readable-repository-refactor/$d/complete.log" 2>/dev/null | sort); test "${#logs[@]}" -eq 1; rg -q 'PASS' "${logs[0]}"; done
gofmt -d apps/edge/internal/openai/*.go apps/edge/internal/service/*.go
GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/node/internal/node
GOCACHE=/tmp/iop-go-cache go test -count=1 ./apps/edge/... ./apps/node/...
make readability-audit
git diff --check
```

`e2e-smoke` `mode=full-cycle`, `scope=OpenAI stream and provider-pool dispatch context`로 stream 2회, cancel/timeout, command 결과를 기록한다.

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