<!-- task=m-agent-readable-repository-refactor/08_adapter_cli_tests plan=0 tag=TEST -->

# Code Review Reference - TEST

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only Milestone lock decisions in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-16
task=m-agent-readable-repository-refactor/08_adapter_cli_tests, plan=0, tag=TEST

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-local-G08.md` → `code_review_local_G08_N.log`, `PLAN-local-G08.md` → `plan_local_G08_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/08_adapter_cli_tests/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [TEST-1] CLI/PTY/Codex app-server 테스트 분리 | [x] |

## 구현 체크리스트

- [x] [TEST-1] CLI emitter/session, persistent PTY, Codex app-server 테스트를 실행 모드별 파일로 옮기고 Test 173개와 fresh package test를 보존한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [x] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

- support 파일이 계획의 `cli_test_support_test.go` 하나가 아니라 두 개다. 계획은 "helper process와 공통 fixture"를 한 파일에 두도록 했으나 두 대상이 서로 다른 Go test package에 속해 한 파일에 넣을 수 없다. `testSink`/`mockLineEmitter`는 `cli_internal_test.go`(package `cli`)에서 왔고 `cli_*_test.go`와 `codex_app_server_*_test.go`가 함께 쓴다. PTY helper process(`TestRawTUIHelperProcess`와 `runClaudeWarningHelper` 등 8개 helper)는 `persistent_execute_blackbox_test.go`(package `cli_test`)에서 왔고 `-test.run=TestRawTUIHelperProcess` 재실행 대상이라 external test binary에 남아야 한다. 따라서 계획에 없는 `persistent_test_support_test.go`(package `cli_test`)를 추가하고, `cli_test_support_test.go`는 package `cli` 공통 fixture만 갖는다. Test 173개와 helper 재실행 계약은 그대로다.
- 계획의 파일별 책임 설명에 직접 대응하지 않는 잔여 Test를 다음과 같이 배치했다. `TestExecutorForMode`, `TestCLIStartSkipsCodexAppServerPersistentStartup`, `TestCloseProfileSession_PipeFallbackIdempotent`는 mode/session lifecycle이므로 `cli_session_test.go`에, `TestJsonEmitters_RegistryMatchesImpls`는 emitter registry이므로 `cli_emitters_test.go`에, `TestCLIProcessExitFailureRemainsDistinct`는 cwd/preflight 계열 process 실패 구분이므로 `cli_workspace_test.go`에 두었다.
- `persistent_process_test.go`는 계획 설명의 "non-terminal persistent"를 문자 그대로 적용하지 않았다. 원본 persistent Test 대부분이 `Terminal: true`이며 `Terminal: false`는 2개뿐이라 flag 기준 분할은 파일을 성립시키지 못한다. 대신 실행 모드 의미에 맞춰 process/logical session lifecycle(느린 first output, process exit, 연속 execute, 독립 session, require-existing, 100 session)을 두었고, PTY/TUI chrome은 `persistent_terminal_test.go`, cancel/marker/idle은 `persistent_completion_test.go`로 나눴다.
- 중간 검증의 `gofmt -w`와 `test ! -e` 명령은 stdout이 없어 실제 출력 대신 각 명령의 종료 코드(`rc=0`)를 함께 기록했다. 명령 자체는 계획 그대로 실행했다.

## 주요 설계 결정

- 테스트 본문을 손으로 옮겨 적지 않고 `go/ast` 기반 추출기로 각 top-level 선언의 byte offset을 얻어 원문을 그대로 슬라이스했다. PTY timing 상수, helper process 프로토콜, emitter assertion이 이동 중 변형되지 않게 하기 위함이다. 분할 스크립트는 원본의 모든 선언이 정확히 한 대상 파일에 배치되었는지 검사해 누락과 중복을 막았다.
- import는 파일별로 실제 참조되는 spec만 남기고 alias(`osexec`, `clipkg`, `noderuntime`)를 원본 그대로 유지했다. 이동 후 `gofmt -w`로 정렬했고 production 코드와 helper 가시성은 건드리지 않았다.
- `codex_app_server_session_test.go`가 844줄로 가장 크다. `fakeAppServer`, `makeStdioPipes`, `captureWriter` fixture가 init/thread/turn RPC shape Test와 강하게 결합되어 있어 계획의 2분할(session/events)을 유지했다. 1,000줄 gate 아래이며 추가 분할은 계획 범위 밖이다.
- `readFileEventually`는 `TestCodexAppServerProcCwd`만 쓰므로 공통 support가 아니라 `codex_app_server_session_test.go`에 함께 두었다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

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

## 리뷰어를 위한 체크포인트

- PTY timing/helper process 의미와 Test 173개가 보존되었는가.
- 2분 무진행 대응 증거가 필요한 경우 기록되었는가.
- 세 원본 제거와 1,000줄 gate가 통과하는가.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.

### TEST-1 중간 검증
```
$ gofmt -w apps/node/internal/adapters/cli/*_test.go
gofmt -w rc=0

$ test ! -e apps/node/internal/adapters/cli/cli_internal_test.go
test !-e cli_internal_test.go rc=0

$ test ! -e apps/node/internal/adapters/cli/persistent_execute_blackbox_test.go
test !-e persistent_execute_blackbox_test.go rc=0

$ test ! -e apps/node/internal/adapters/cli/codex_app_server_internal_test.go
test !-e codex_app_server_internal_test.go rc=0

$ [ "$(GOCACHE=/tmp/iop-go-build-cache go test -list '^Test' ./apps/node/internal/adapters/cli | sed '/^ok[[:space:]]/d' | wc -l)" -eq 173 ]
test-list -eq 173 rc=0
```

세 원본 삭제, gofmt 성공, Test 173개로 기대 결과와 일치한다. `gofmt -w`와 `test ! -e`는 stdout이 없어 종료 코드를 기록했다.

### 최종 검증
```
$ GOCACHE=/tmp/iop-go-build-cache go test -count=1 ./apps/node/internal/adapters/cli
ok  	iop/apps/node/internal/adapters/cli	46.990s

$ [ "$(GOCACHE=/tmp/iop-go-build-cache go test -list '^Test' ./apps/node/internal/adapters/cli | sed '/^ok[[:space:]]/d' | wc -l)" -eq 173 ]
$ GOCACHE=/tmp/iop-go-build-cache go test -list '^Test' ./apps/node/internal/adapters/cli | sed '/^ok[[:space:]]/d' | wc -l
173

$ python3 -c "from pathlib import Path; p=Path('apps/node/internal/adapters/cli'); bad=sorted((sum(1 for _ in f.open()),str(f)) for f in p.glob('*_test.go') if sum(1 for _ in f.open())>1000); print(bad); raise SystemExit(bool(bad))"
[]
rc=0
```

fresh CLI test PASS(46.990s, 변경 전 기준선 46.960s), Test 173개, 1,000줄 초과 test 없음으로 기대 결과와 일치한다. PTY test 2분 무진행은 발생하지 않아 blind retry 중단과 focused rerun 근거 기록은 해당 없다.

분할 후 파일별 줄 수(참고용 보조 확인, 계획의 검증 명령 아님). `persistent_output_filter_test.go`는 이번 분할 대상이 아닌 기존 파일이며 glob에 함께 잡힌 것이다.
```
$ wc -l apps/node/internal/adapters/cli/{cli,codex_app_server,persistent}*_test.go
   354 apps/node/internal/adapters/cli/cli_emitters_test.go
   415 apps/node/internal/adapters/cli/cli_session_test.go
    29 apps/node/internal/adapters/cli/cli_test_support_test.go
   418 apps/node/internal/adapters/cli/cli_workspace_test.go
   276 apps/node/internal/adapters/cli/codex_app_server_events_test.go
   844 apps/node/internal/adapters/cli/codex_app_server_session_test.go
   390 apps/node/internal/adapters/cli/persistent_completion_test.go
   331 apps/node/internal/adapters/cli/persistent_output_filter_test.go
   324 apps/node/internal/adapters/cli/persistent_process_test.go
   590 apps/node/internal/adapters/cli/persistent_terminal_test.go
   190 apps/node/internal/adapters/cli/persistent_test_support_test.go
  4161 total
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only; final checkbox is mandatory before saving |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation; do not ask the user directly during implementation; environment/secret/service blockers, generic scope changes, and evidence gaps are not user-review requests |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: PASS
- 차원별 평가:
  - correctness: Pass — 삭제된 세 원본과 새 책임별 파일의 package/import를 제외한 코드 라인 multiset이 섹션 주석 3줄 제거 외에는 일치하고, 전체 Test 함수명도 HEAD와 동일하다.
  - completeness: Pass — 계획한 세 원본 삭제, 실행 모드별 파일 분리, helper의 `cli`/`cli_test` package 경계 분리, Test 173개와 1,000줄 gate가 모두 충족됐다.
  - test coverage: Pass — 기존 테스트 본문과 runnable Test 수가 보존됐고 fresh CLI package test 및 전체 Node 테스트가 통과했다.
  - API contract: Pass — production code, 외부 API, proto, config schema, Edge-Node wire 계약 변경이 없다.
  - code quality: Pass — 새 테스트 파일에 gofmt drift, stale 삭제 파일 참조, TODO/debug 출력, 사유 없는 1,000줄 초과가 없다.
  - implementation deviation: Pass — package가 다른 PTY helper를 `persistent_test_support_test.go`로 분리한 변경과 파일별 잔여 Test 배치가 리뷰 문서에 구체적으로 설명됐고 계획 목표를 보존한다.
  - verification trust: Pass — 리뷰어가 계획의 최종 검증을 재실행해 `ok iop/apps/node/internal/adapters/cli 47.112s`, `test_count=173`, 1,000줄 초과 `[]`를 확인했고 `go test ./apps/node/...`도 통과했다.
- 발견된 문제: 없음
- 다음 단계: PASS — `complete.log` 작성 후 task 디렉터리를 `agent-task/archive/2026/07/m-agent-readable-repository-refactor/08_adapter_cli_tests/`로 이동한다.
