<!-- task=m-codex-app-server-streaming-migration/02+01_session_events plan=0 tag=REFACTOR -->
# Code Review Reference - REFACTOR

> 구현 에이전트 경고: 이 파일은 구현 마지막 단계에서 반드시 갱신한다. `구현 완료 기록`, `구현 체크리스트`, `검증 출력`을 실제 내용으로 채우기 전에는 작업 완료를 선언하지 않는다.

## 개요
- 날짜: 2026-06-12
- Task: `m-codex-app-server-streaming-migration/02+01_session_events`
- Plan: `PLAN-cloud-G07.md`
- Tag: `REFACTOR`

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/codex-app-server-streaming-migration.md`
- Task ids:
  - `session-thread`: IOP `session_id`별 app-server thread를 재사용하고 `/terminate-session`에서 해당 세션을 정리한다.
  - `event-map`: `item/agentMessage/delta`를 `RuntimeEvent{Type: delta}`로 전달하고, `turn/completed`는 complete, app-server error와 failed turn은 error로 매핑한다.
- Completion mode: check-on-pass

## 리뷰 에이전트 지침
- 선행 `01_appserver_rpc_client/complete.log`가 있는지 확인한다.
- IOP session id와 Codex thread id를 혼동하지 않았는지 확인한다.
- delta/completed/error event가 중복 emit 되지 않는지 우선 리뷰한다.
- archive는 명시 요청 없이 읽지 않는다.

## 구현 완료 기록
| 항목 | 구현 에이전트 기록 |
| --- | --- |
| 변경 요약 | `codex_app_server.go`의 `Execute` stub을 실제 turn 실행으로 교체. `turn/start` 전송, notification drain, `RuntimeEvent` 변환 구현. session list/terminate는 기존 `Sessions()`/`Terminate()` 구현을 그대로 활용. |
| 주요 파일 | `apps/node/internal/adapters/cli/codex_app_server.go`, `apps/node/internal/adapters/cli/codex_app_server_internal_test.go`, `apps/node/internal/adapters/cli/cli_internal_test.go` |
| 범위 외 변경 여부 | 없음. `extractPrompt` 중복 선언 제거(기존 `oneshot.go` 함수 재사용)만 추가됨 |
| 남은 리스크 | 실제 codex app-server 프로세스 smoke 검증은 `04+03_stream_smoke` subtask에서 수행 |

## 구현 항목별 완료 여부
| 항목 | 완료 여부 |
| --- | --- |
| REFACTOR-1 IOP session과 Codex thread 매핑 | 완료 - `resolveCodexAppServerSession`이 `(target, sessionID)` key로 session을 재사용. threadID는 session struct에만 보관 |
| REFACTOR-2 app-server notification을 RuntimeEvent로 변환 | 완료 - `decodeAppServerNotification`: delta/turn-completed/error/turn-failed 매핑. metadata에 source, turn_id 포함 |
| REFACTOR-3 session list와 terminate 연결 | 완료 - 기존 `Sessions()`/`Terminate()`가 reporter chain에 이미 연결되어 있음. 신규 테스트로 검증 |

## 구현 체크리스트
- [x] 선행 `01_appserver_rpc_client/complete.log`를 확인한다. (archive에 존재 확인)
- [x] `SessionID`를 Codex thread id에 매핑하고 재사용한다.
- [x] `turn/start` request를 `ExecutionSpec.Input` 기반 prompt로 보낸다.
- [x] `item/agentMessage/delta`, `item/completed`, `turn/completed`, `error` notification을 runtime event로 변환한다.
- [x] runtime event metadata에 app-server source와 thread/turn id를 남긴다.
- [x] session list와 terminate 동작을 app-server session에 맞게 구현한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 코드 리뷰 전용 체크리스트
- [ ] response delta가 빈 문자열을 emit 하지 않는다.
- [ ] completed event가 정확히 한 번 emit 된다.
- [ ] error notification에서 session lock이 풀린다.
- [ ] `/sessions`와 `/terminate-session`이 app-server session을 다룬다.

### 리뷰 루프 종결 체크리스트
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-cloud-G07.md`를 `code_review_cloud_G07_0.log`로 아카이브한다.
- [x] active `PLAN-cloud-G07.md`를 `plan_cloud_G07_0.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] FAIL이고 user-review gate가 트리거되지 않았으므로 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] PASS 전용 `complete.log` 작성과 task directory archive 이동은 적용하지 않는다.
- [ ] USER_REVIEW 작성은 적용하지 않는다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 검증 출력
```text
$ go test ./apps/node/internal/adapters/cli -count=1 -run 'TestCodexAppServerSessionThreadMapping|TestCodexAppServerEventMap|TestHandleSessionList_CodexAppServerVisible|TestTerminateSession_CodexAppServerRemovesFromList' -v
=== RUN   TestHandleSessionList_CodexAppServerVisible
--- PASS: TestHandleSessionList_CodexAppServerVisible (0.00s)
=== RUN   TestTerminateSession_CodexAppServerRemovesFromList
--- PASS: TestTerminateSession_CodexAppServerRemovesFromList (0.00s)
=== RUN   TestCodexAppServerSessionThreadMapping
--- PASS: TestCodexAppServerSessionThreadMapping (0.00s)
=== RUN   TestCodexAppServerEventMap_Delta
--- PASS: TestCodexAppServerEventMap_Delta (0.00s)
=== RUN   TestCodexAppServerEventMap_TurnCompleted
--- PASS: TestCodexAppServerEventMap_TurnCompleted (0.00s)
=== RUN   TestCodexAppServerEventMap_Error
--- PASS: TestCodexAppServerEventMap_Error (0.00s)
=== RUN   TestCodexAppServerEventMap_TurnFailed
--- PASS: TestCodexAppServerEventMap_TurnFailed (0.00s)
PASS
ok  	iop/apps/node/internal/adapters/cli	0.006s

$ go test ./apps/node/internal/adapters/cli -count=1
ok  	iop/apps/node/internal/adapters/cli	42.559s

$ go test ./apps/node/... -count=1
ok  	iop/apps/node/cmd/node	0.009s
ok  	iop/apps/node/internal/adapters	0.009s
ok  	iop/apps/node/internal/adapters/cli	42.535s
ok  	iop/apps/node/internal/adapters/cli/status	39.856s
ok  	iop/apps/node/internal/adapters/ollama	0.008s
ok  	iop/apps/node/internal/adapters/vllm	0.007s
ok  	iop/apps/node/internal/bootstrap	0.263s
ok  	iop/apps/node/internal/node	0.118s
ok  	iop/apps/node/internal/router	0.004s
ok  	iop/apps/node/internal/store	0.045s
ok  	iop/apps/node/internal/terminal	0.420s
ok  	iop/apps/node/internal/transport	5.138s
```

## 리뷰어 체크포인트
- `test -f agent-task/m-codex-app-server-streaming-migration/01_appserver_rpc_client/complete.log`
- `go test ./apps/node/internal/adapters/cli -count=1`
- `go test ./apps/node/... -count=1`

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - plan deviation: Fail
  - verification trust: Fail
- 발견된 문제:
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:527`의 `thread/start` 응답 파싱이 실제 app-server 계약과 다릅니다. `/tmp/iop-codex-appserver-ts/v2/ThreadStartResponse.ts`는 응답이 `result.thread.id` 형태임을 보여주는데, 구현은 `result.threadId` 또는 top-level `result.id`만 읽습니다. 실제 app-server에서는 세션 초기화가 `server did not return a thread id`로 실패할 수 있습니다. `thread.id` nested 값을 파싱하고, 테스트 fixture도 실제 `ThreadStartResponse` shape로 바꾸세요.
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:111`의 `turn/start` 응답 파싱도 실제 계약과 다릅니다. `/tmp/iop-codex-appserver-ts/v2/TurnStartResponse.ts`는 `result.turn.id`인데 구현은 `turnId`/`id`만 읽어 `turnID`가 빈 값으로 남습니다. 이 때문에 plan이 요구한 turn id metadata가 빠지고, 후속 이벤트 상관관계가 깨집니다. `turn.id`를 파싱하고 누락 시 error를 반환하는 테스트를 추가하세요.
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:148`의 event metadata 생성은 plan의 `source와 thread/turn id` 요구 중 thread id를 전혀 싣지 않습니다. `Execute`/`codexAppServerDrainNotifications`/`decodeAppServerNotification`에 thread id를 전달하거나 notification params의 `threadId`를 읽어서 `metadata["thread_id"]`를 채우고, delta/complete/error 테스트에서 `thread_id`와 `turn_id`를 함께 검증하세요.
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:172`는 모든 `turn/completed`를 성공 complete로 처리합니다. 실제 `TurnCompletedNotification`은 `params.turn.status`가 `failed`일 수 있고 `turn.error.message`를 포함합니다. failed status는 `RuntimeEvent{Type: error}`와 Go error로 surfaced 되어야 하며, 성공 complete로 저장되면 실패 turn이 정상 완료로 보입니다.
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:181`은 `error` notification에서 `params.message`만 읽습니다. 실제 `ErrorNotification`은 `params.error.message` 형태라 실제 오류 메시지가 `codex app-server error`로 뭉개집니다. `error.message`를 우선 파싱하고 `threadId`/`turnId` metadata도 함께 보존하세요.
  - Required: `apps/node/internal/adapters/cli/codex_app_server.go:377`에서 notification channel이 가득 차면 `default`로 notification을 조용히 버립니다. delta 손실뿐 아니라 `turn/completed`/`error`까지 버려져 실행이 context timeout까지 멈출 수 있습니다. read loop가 terminal notification을 drop하지 않도록 backpressure, larger drain path, 또는 lossless handoff로 바꾸고 overflow 재현 테스트를 추가하세요.
- 다음 단계: FAIL이므로 user-review gate 없이 후속 `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성한다.
