<!-- task=m-agent-readable-repository-refactor/06_cli_adapter plan=3 tag=REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI -->

# Code Review Reference - REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI

> **[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-17
task=m-agent-readable-repository-refactor/06_cli_adapter, plan=3, tag=REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/plan_cloud_G08_2.log`
- Prior review: `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/code_review_cloud_G08_2.log`
- Verdict: `FAIL`
- Issue summary: Required 3, Suggested 0, Nit 0.
- Required:
  - `cmd.Path`가 non-empty인지만 확인해 exact executable resolution을 검증하지 못함.
  - test discovery/package/final verification output에 발췌·요약·`...`가 포함되어 fresh stdout/stderr 계약을 검증할 수 없음.
  - real CLI helper가 prompt 1회만 전송하고 `/status`를 생략해 message x2, event ordering, node/edge payload 동일성과 profile별 exact blocker가 없음.
- Affected files: `apps/node/internal/adapters/cli/cli_workspace_test.go`, active review stub, `/tmp` full-cycle evidence helper/output.
- Reviewer verification:
  - exact targeted tests와 `GOCACHE=/tmp/iop-go-cache-review-final go test -count=1 ./apps/node/...` PASS.
  - `gofmt -d`, scoped `git diff --check`, direct `exec.Command` stale search PASS.
  - `make readability-audit`, `cli_baseline_problems=0`, `cli_regressions=0` PASS.
  - command inventory: `claude`, `codex`, `opencode` 설치 확인, `antigravity` 미발견.
- 추가 세부 문맥이 필요할 때만 위 두 prior log를 좁게 읽는다. `agent-task/archive/**`를 탐색하지 않는다.
- Roadmap carryover: 없음. 특정 Milestone Task 완료 체크를 주장하지 않는다.

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

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

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

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G08.md` → `code_review_cloud_G08_N.log`, `PLAN-cloud-G08.md` → `plan_cloud_G08_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/06_cli_adapter/`로 이동한다. 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-agent-readable-repository-refactor`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-1] exact executable assertion 보강 | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-2] fresh deterministic evidence 재수집 | [x] |
| [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-3] profile별 full-cycle 또는 exact blocker 수집 | [x] |

## 구현 체크리스트

- [x] executable path를 `exec.LookPath` 결과와 정확히 비교하고 기존 args/env/cwd/error assertions를 보존한다.
- [x] source discovery, targeted/package/Node/readability 검증을 fresh 실행하고 실제 stdout/stderr 또는 exact `/tmp` evidence metadata를 기록한다.
- [x] `claude`, `codex`, `opencode`, `antigravity`의 message x2/command/status full-cycle 또는 executable discovery 이후 exact blocker를 profile별로 수집한다.
- [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_cloud_G08_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G08_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/`를 `agent-task/archive/YYYY/MM/m-agent-readable-repository-refactor/06_cli_adapter/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-agent-readable-repository-refactor`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-agent-readable-repository-refactor/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G05.md`와 `CODE_REVIEW-local-G05.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로 이동한다.

## 계획 대비 변경 사항

- 항목 1의 소스 편집은 계획의 Before/After 그대로 적용했다. 계획의 중간 검증 명령도 그대로 실행했다.
- 항목 2·3의 검증 명령은 계약대로 실행했으나, plan은 evidence 저장 경로를 `/tmp/iop-cli-plan3/`로 지정했고 그 아래 구조는 helper 재량이라 실제 파일 배치를 아래처럼 확정했다.
  - deterministic suite: `/tmp/iop-cli-plan3/{test-discovery,targeted,package,node,readability}.log` (초기 수집) + `/tmp/iop-cli-plan3/final-*.log` (최종 검증 재실행).
  - real CLI full-cycle: profile별 `/tmp/iop-cli-plan3/<profile>/{result.txt,edge.out,node.out,edge.yaml,node.yaml,cmd-*.txt,payload-compare.txt}`.
  - runner/비교 helper: `/tmp/iop-cli-plan3/run-profile.sh`, `/tmp/iop-cli-plan3/compare-payload.py`. 직접 CLI probe는 `/tmp/iop-cli-plan3/{claude,opencode}-direct.txt`.
- plan의 real CLI helper 참조(`/tmp/run-e2e-profile.sh`)는 고정 sleep·prompt 1회·label 가정(`test-node`) 결함이 있어 그대로 쓰지 않고 `/tmp/iop-cli-plan3/run-profile.sh`를 새로 작성했다. 계획 요구(같은 session prompt 2회, 각 run terminal/bounded-timeout 대기, `/nodes` `/capabilities` `/transport` `/sessions` `/status` `/terminate-session`, payload 내용/순서 비교, blocker 필드)를 모두 반영했다. plan의 검증 blocker/helper 자유도 안에서의 변경이며 검증 계약 자체를 바꾸지 않았다.
- real CLI 판정을 위해 profile별 임시 edge config에 `console.timeout_sec: 600`을 넣었다. 기본값 120이면 edge console이 느린 real CLI를 자체 timeout으로 잘라 profile 고유 동작을 관측할 수 없기 때문이다. tracked `configs/*.yaml`은 수정하지 않았고 `/tmp` 임시 config에만 적용했다.
- antigravity는 plan의 command inventory 프리플라이트대로 미설치라, full-cycle을 실행하지 않고 executable discovery 단계의 exact blocker로 기록했다.

## 주요 설계 결정

- 항목 1은 `exec.LookPath("echo")` 결과와 `cmd.Path`를 정확히 비교한다. 기존 `cmd.Dir`, `cmd.Args`, `FOO=bar` env, invalid-workspace(`cli adapter: workspace not found`) assertion은 모두 보존했다. import는 표준 순서로 `os/exec`를 추가했고 production builder/lifecycle caller는 건드리지 않았다.
- real CLI runner는 고정 sleep 성공 판정을 쓰지 않는다. 각 run은 edge의 `start` 이벤트와 terminal 신호(`complete`/`error`/`cancelled`, console `timed out`/`node reported error`/`node disconnected`)를 bounded timeout으로 기다린다. PASS는 edge `complete` 이벤트 2회를 요구한다.
- edge console이 렌더링하는 node label은 alias(`test-node`)가 아니라 console 내부 라벨(`node0`)이다. runner는 `connected reason="registered"` 라인에서 라벨을 동적으로 추출해 이후 event/message regex에 사용한다.
- payload 동일성은 별도 `compare-payload.py`로 검증한다. node-local `[node-message]` 블록과 edge-rendered `[node0-msg]` 라인을 run별로 모아 내용·순서까지 비교한다(trailing blank line은 console formatting으로 보고 제외).
- 실측된 timing을 반영했다. claude persistent-lazy TUI는 edge가 `start`를 보기까지 약 32초, ready screen까지 약 56–90초가 걸려 `START_TIMEOUT=150`, `PROMPT_TIMEOUT=660`으로 여유를 뒀다.

## 사용자 리뷰 요청

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

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

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

- executable path assertion이 `exec.LookPath` 결과를 정확히 비교하고 기존 args/env/cwd/error 경계를 보존하는가.
- 기록된 모든 test/readability evidence가 actual stdout/stderr 또는 존재하는 exact `/tmp` path/exit/count/hash로 검증 가능한가.
- profile별로 message x2와 command/status/payload/order 기준을 통과했거나 executable discovery 이후의 exact blocker가 명령·exit·stderr/event·재개 조건과 함께 기록됐는가.

## 검증 결과

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

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. 긴 출력은 exact `/tmp` path, exit status, line count, hash를 기록한다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.

### REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-1 중간 검증

```text
$ gofmt -d apps/node/internal/adapters/cli/cli_workspace_test.go
(빈 출력, gofmt_exit=0)

$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^TestBuildCmdWithoutContextAppliesEnvAndCwd$'
=== RUN   TestBuildCmdWithoutContextAppliesEnvAndCwd
--- PASS: TestBuildCmdWithoutContextAppliesEnvAndCwd (0.01s)
PASS
ok  	iop/apps/node/internal/adapters/cli	0.017s
(exit=0)
```

적용한 소스 변경(`apps/node/internal/adapters/cli/cli_workspace_test.go`):
- import에 `os/exec` 추가.
- `if cmd.Path == "" { ... }` → `exec.LookPath("echo")` 결과와 `cmd.Path`를 정확히 비교.
- 기존 `cmd.Dir`, `cmd.Args`, `FOO=bar` env, `/nonexistent/workspace/path` → `cli adapter: workspace not found` assertion 유지.

### REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-2 중간 검증

각 pipeline은 `set -o pipefail`로 실행하고 command exit를 별도 기록했다. exit 요약(`/tmp/iop-cli-plan3/exits.txt`):

```text
test-discovery=0
targeted=0
package=0
node=0
readability=0
```

짧은 출력 원문:

```text
$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmdWithoutContextAppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$'   # /tmp/iop-cli-plan3/targeted.log
=== RUN   TestCLIPersistentNonTerminalCwd
--- PASS: TestCLIPersistentNonTerminalCwd (1.51s)
=== RUN   TestBuildCmdWithoutContextAppliesEnvAndCwd
--- PASS: TestBuildCmdWithoutContextAppliesEnvAndCwd (0.01s)
=== RUN   TestCodexAppServerProcCwd
--- PASS: TestCodexAppServerProcCwd (0.01s)
=== RUN   TestOpencodeLocalServerCwd_Workspace
--- PASS: TestOpencodeLocalServerCwd_Workspace (0.10s)
=== RUN   TestOpencodeLocalServerCwd_DirOverride
--- PASS: TestOpencodeLocalServerCwd_DirOverride (0.10s)
PASS
ok  	iop/apps/node/internal/adapters/cli	1.741s

$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/... 2>&1   # /tmp/iop-cli-plan3/node.log
ok  	iop/apps/node/cmd/node	0.011s
ok  	iop/apps/node/internal/adapters	0.016s
ok  	iop/apps/node/internal/adapters/cli	47.058s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	39.772s
?   	iop/apps/node/internal/adapters/mock	[no test files]
ok  	iop/apps/node/internal/adapters/ollama	0.013s
ok  	iop/apps/node/internal/adapters/openai_compat	0.128s
ok  	iop/apps/node/internal/adapters/vllm	0.124s
ok  	iop/apps/node/internal/bootstrap	0.529s
ok  	iop/apps/node/internal/node	0.814s
ok  	iop/apps/node/internal/router	0.510s
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	0.230s
ok  	iop/apps/node/internal/terminal	0.550s
ok  	iop/apps/node/internal/transport	5.450s

$ make readability-audit 2>&1   # /tmp/iop-cli-plan3/readability.log
python3 scripts/readability_audit.py --check --input-mode worktree --output build/readability-audit.json
RATCHET OK: no new or increased violations.
readability-audit: 412 files, 110571 LOC, 3307 functions, 177 violations
```

긴 출력 metadata (`wc -l` / `sha256sum`, 최초 수집분):

```text
   16 node.log       ff722a864f7ba24f2159fb9604529176da8ba6515a2993dcfa52e7fda99f552b
  454 package.log    ba6fd42dbdf816285185464d289a4ad46f8bf6beb021484dc5405bc2001b2f00
    3 readability.log b1f0aa5ca2e02859b816b428f5387024f61c36846929a5a214ee137f0844f088
   12 targeted.log   83121b91de1ca1e71420e27c822239509f3d7610b90d8edb5a872e7b50911f4d
  260 test-discovery.log 9375e2f631048d938ea961a407a1ba52c1e647e9f292a9bccb67a4b0a7a26244
```

`test-discovery.log`(260줄)는 `rg -n --sort path '^func Test' apps/node/internal/adapters/cli apps/node/internal/adapters/cli/status`의 전체 함수 목록으로, 발췌·요약 없이 파일에 그대로 보존했다.

### REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-3 중간 검증

command inventory (구현 직전 재확인):

```text
claude=/config/.npm-global/bin/claude        2.1.212 (Claude Code)
codex=/config/.npm-global/bin/codex          codex-cli 0.144.5
opencode=/config/.npm-global/bin/opencode    1.18.3
antigravity=NOT_FOUND exit=1   (command -v / which 모두 exit 1, /config/.npm-global/bin에 바이너리 없음)
scripts/dev/edge.sh=executable  scripts/dev/node.sh=executable
```

profile별 결과 요약 (`/tmp/iop-cli-plan3/<profile>/result.txt`, edge label=`node0`):

`claude` — status=blocker (message2만 성공):

```text
message1_start=ok / message1_terminal=ok (console timeout으로 종료)
message2_start=ok / message2_terminal=ok
edge_evt_start_count=2  edge_evt_complete_count=1  edge_console_timeout_count=1
node_evt_complete_count=1  node_evt_cancelled_count=1  node_msg_block_count=1
cmd_nodes/capabilities/transport/sessions/status/terminate_session = ok/responded
```

`claude` edge.out 핵심 (`/tmp/iop-cli-plan3/claude/edge.out`):

```text
edge> [edge] sent run_id=manual-1784297228330010055 ... target=claude ...
[node0-evt] start run_id=manual-1784297228330010055
error: timed out waiting for node response
edge> [edge] sent run_id=manual-1784297833799077668 ... target=claude ...
[node0-evt] start run_id=manual-1784297833799077668
[node0-msg] IOP_PLAN3_TWO
[node0-evt] complete run_id=manual-1784297833799077668 detail="prompt-ready"
...
[node0-status] adapter=cli target=claude session=default
Claude status:
  version = 2.1.212
  login_method = Claude Pro account
  model = opus (claude-opus-4-8)
  cwd = /tmp/iop-cli-plan3/claude
  session_id = cdf17893-edea-479e-95dc-6a69a591ce84
edge> terminated session default node=node0
```

`claude` payload 비교 (`/tmp/iop-cli-plan3/claude/payload-compare.txt`):

```text
run=manual-1784297228330010055 node_payload_lines=0 edge_payload_lines=0 identical=yes
run=manual-1784297833799077668 node_payload_lines=1 edge_payload_lines=1 identical=yes
  node_payload=['IOP_PLAN3_TWO']
  edge_payload=['IOP_PLAN3_TWO']
payload_compare=PASS
```

claude blocker 상세:
- 실패 명령: 같은 session 첫 prompt `Reply with exactly this token and nothing else: IOP_PLAN3_ONE` (run_id=manual-1784297228330010055).
- 결과: node `start` 이후 ready screen(T≈90s)에서 prompt replay했으나 이후 무응답. edge console이 `console.timeout_sec=600` 만료 시 `error: timed out waiting for node response`, node는 run cancelled 처리.
- 미완료 cycle: message1 응답/complete. message2와 모든 command는 정상.
- 대조: `claude -p "...IOP_DIRECT_PROBE"`는 즉시 `IOP_DIRECT_PROBE`를 반환(exit 0, `/tmp/iop-cli-plan3/claude-direct.txt`). 설치·로그인 문제 아님. 같은 session 두 번째 prompt는 정상 응답 → cold-start 구간에서 첫 prompt가 유실되는 persistent-lazy TUI 타이밍 이슈로 좁혀짐.
- 재개 조건: claude persistent-lazy 프로파일의 cold-start 첫 prompt 유실을 별도 follow-up으로 조사(ready-screen 감지 후 replay 타이밍/idle timeout 상호작용). 이 plan 범위(테스트 assertion + evidence)와 무관한 adapter 동작 이슈.

`codex` — status=blocker (message x2 통과, `/status` 실패):

```text
message1_start=ok / message1_terminal=ok
message2_start=ok / message2_terminal=ok
edge_evt_start_count=2  edge_evt_complete_count=2  edge_console_timeout_count=0
node_evt_complete_count=2  node_msg_block_count=2
```

`codex` edge.out 핵심:

```text
[node0-evt] start run_id=manual-1784299461054918796
[node0-msg] IOP_PLAN3_ONE
[node0-evt] complete run_id=manual-1784299461054918796 detail=""
[node0-evt] start run_id=manual-1784299467095994132
[node0-msg] IOP_PLAN3_TWO
[node0-evt] complete run_id=manual-1784299467095994132 detail=""
[node0-capabilities] ... targets = codex
[node0-transport] ... connected = true ... target = codex
[node0-sessions] ... [0] mode=codex-app-server target=codex session=default
edge> [edge] sent command=status ...
error: node reported error: codex startup failed after skipping update prompt: EOF
edge> terminated session default node=node0
```

`codex` payload 비교:

```text
run=manual-1784299461054918796 node_payload=['IOP_PLAN3_ONE'] edge_payload=['IOP_PLAN3_ONE'] identical=yes
run=manual-1784299467095994132 node_payload=['IOP_PLAN3_TWO'] edge_payload=['IOP_PLAN3_TWO'] identical=yes
payload_compare=PASS
```

codex 부분 blocker: `/status`만 codex 고유 별도-프로세스 probe 경로에서 `codex startup failed after skipping update prompt: EOF` 반환. message x2, `/capabilities`, `/transport`, `/sessions`, `/terminate-session`은 모두 정상. status probe는 별도 codex process spawn으로, foreground run 경로와 분리된 실패다.

`opencode` — status=blocker (server 미기동, message 0):

```text
message1_start=FAIL(no start event within 150s) / message2_start=FAIL
edge_evt_error_count=2  edge_evt_complete_count=0  node_msg_block_count=0
cmd_nodes/capabilities/transport/sessions/status/terminate_session = ok/responded
```

`opencode` edge.out 핵심:

```text
[node0-evt] error run_id=manual-1784299987474775721 detail="cli adapter: start opencode server: timeout waiting for opencode server listen line"
[node0-evt] error run_id=manual-1784300137038803554 detail="cli adapter: start opencode server: timeout waiting for opencode server listen line"
[node0-capabilities] ... targets = opencode
[node0-sessions] ... count = 0
edge> [edge] sent command=status ...
error: node reported error: status check not supported for target "opencode" (command "opencode")
edge> terminated session default node=node0
```

opencode blocker 상세:
- 실패 명령: `opencode serve` (node adapter가 spawn). run_id=manual-1784299987474775721 및 manual-1784300137038803554.
- 결과/exit: adapter가 `timeout waiting for opencode server listen line`로 run error. 직접 재현 `opencode serve` (`/tmp/iop-cli-plan3/opencode-direct.txt`, exit=1) stderr:
  `PermissionDenied: FileSystem.open (/config/.local/share/opencode/log/opencode.log)`.
- 근본 원인: sandbox에서 `/config/.local/share/opencode/log/`에 새 파일 생성 거부(`touch` 시 `Permission denied`). opencode가 자체 로그를 못 열어 즉시 종료 → listen line 미출력 → adapter timeout. IOP CLI adapter 코드 결함이 아니라 환경 blocker.
- 미완료 cycle: message1/message2 모두 start 이전 실패. `/status`는 opencode 미지원으로 명확한 `status check not supported` 반환(에러 아닌 정상 unsupported).
- 재개 조건: opencode log 디렉터리에 쓰기 가능한 환경(또는 `XDG_DATA_HOME`/log path override) 확보 후 재실행.

`antigravity` — status=blocker (미설치, discovery 단계):

```text
antigravity=NOT_FOUND (command -v exit=1, which exit=1)
/config/.npm-global/bin에 antigravity 바이너리 없음.
```

antigravity blocker 상세: plan 요구대로 `command -v` missing을 blocker로 기록. 미설치라 edge/node full-cycle을 실행하지 않았고, 잘못된 `resume_args` config 오류로 대체하지 않았다. 재개 조건: antigravity CLI 설치 + 로그인 후 antigravity-print profile 재실행.

### 최종 검증

plan `최종 검증` 블록의 명령을 순서대로 실행했다.

```text
$ gofmt -d apps/node/internal/adapters/cli/*.go apps/node/internal/adapters/cli/status/*.go
(빈 출력, gofmt_exit=0)

$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmdWithoutContextAppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$'
=== RUN   TestCLIPersistentNonTerminalCwd
--- PASS: TestCLIPersistentNonTerminalCwd (1.51s)
=== RUN   TestBuildCmdWithoutContextAppliesEnvAndCwd
--- PASS: TestBuildCmdWithoutContextAppliesEnvAndCwd (0.01s)
=== RUN   TestCodexAppServerProcCwd
--- PASS: TestCodexAppServerProcCwd (0.01s)
=== RUN   TestOpencodeLocalServerCwd_Workspace
--- PASS: TestOpencodeLocalServerCwd_Workspace (0.10s)
=== RUN   TestOpencodeLocalServerCwd_DirOverride
--- PASS: TestOpencodeLocalServerCwd_DirOverride (0.10s)
PASS
ok  	iop/apps/node/internal/adapters/cli	1.737s
(targeted_exit=0)

$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/adapters/cli/status
ok  	iop/apps/node/internal/adapters/cli	47.108s
ok  	iop/apps/node/internal/adapters/cli/status	39.771s
(package_exit=0)

$ GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/...
ok  	iop/apps/node/cmd/node	0.018s
ok  	iop/apps/node/internal/adapters	0.013s
ok  	iop/apps/node/internal/adapters/cli	47.011s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	39.778s
?   	iop/apps/node/internal/adapters/mock	[no test files]
ok  	iop/apps/node/internal/adapters/ollama	0.015s
ok  	iop/apps/node/internal/adapters/openai_compat	0.127s
ok  	iop/apps/node/internal/adapters/vllm	0.131s
ok  	iop/apps/node/internal/bootstrap	0.462s
ok  	iop/apps/node/internal/node	0.811s
ok  	iop/apps/node/internal/router	0.505s
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	0.041s
ok  	iop/apps/node/internal/terminal	0.571s
ok  	iop/apps/node/internal/transport	5.448s
(node_exit=0)

$ make readability-audit
python3 scripts/readability_audit.py --check --input-mode worktree --output build/readability-audit.json
RATCHET OK: no new or increased violations.
readability-audit: 412 files, 110558 LOC, 3313 functions, 176 violations
(readability_exit=0)

$ git diff --check -- apps/node/internal/adapters/cli scripts/readability_baseline.json scripts/readability_read_sets.json
(빈 출력, diffcheck_exit=0)
```

보조 확인:
- workspace-bound direct `exec.Command` 검색: `status/**`를 제외한 main package의 non-test 사용처는 `command.go:36,40`의 공통 builder 2건뿐이다. `status/{antigravity,claude,codex}.go`의 별도 `exec.CommandContext` 3건은 workspace를 받지 않는 독립 PTY status probe 경로다.
- `git diff --stat -- apps/node/internal/adapters/cli/cli_workspace_test.go`: `1 file changed, 48 insertions(+)` (이 중 `os/exec` import + `exec.LookPath` 비교가 이번 항목 1 변경. 나머지 `TestBuildCmdWithoutContextAppliesEnvAndCwd` 본문은 같은 dirty worktree의 선행 split 변경으로, 되돌리지 않았다.)

### e2e-smoke (real-cli) 결과 요약

`scope="CLI adapter exact boundary and evidence recovery" mode=real-cli profiles="claude,antigravity,codex,opencode"` 기준:

```text
- Go 테스트: 위 targeted/package/node 명령 - 통과
- Repo 내부 edge-node 진단: scripts/dev/edge.sh + scripts/dev/node.sh 별도 프로세스 - 통과 (register, /nodes ok, label=node0)
- 메시지 왕복: message x2 - codex 통과 / claude 부분(2번째만) / opencode 미기동 / antigravity 미설치
- Node message relay: node local payload == edge rendered payload - codex PASS(2/2), claude PASS(관측된 1 run), opencode N/A
- Edge command 응답: /nodes /capabilities /transport /sessions /status /terminate-session - 전 profile 응답(내용은 profile별 상이)
- 보조 E2E smoke: 미실행 (real-cli full-cycle로 대체)
- Full-cycle 실제 구동: message x2 통과(codex) / blocker(codex status, claude cold-start, opencode env, antigravity missing)
- Real CLI profile: codex=BLOCKER(`/status` EOF), claude=BLOCKER(cold-start 첫 prompt 유실), opencode=BLOCKER(env PermissionDenied), antigravity=BLOCKER(미설치)
- 남은 위험: codex status probe EOF, claude persistent-lazy cold-start 첫 prompt 유실, opencode 로그 디렉터리 쓰기 권한, antigravity 미설치. 모두 이번 plan 범위(test assertion + evidence 재수집) 밖의 후속 조사 대상이며, 소스 변경(항목 1)과 무관.
```

---

> **[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.

## Sections and ownership

| 섹션 | 소유자 | 설명 |
|------|--------|------|
| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 |
| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 |
| Archive Evidence Snapshot | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트의 기본 이전 루프 컨텍스트; 추가 확인이 필요할 때 여기에 명시된 archive 파일만 좁게 읽음 |
| Agent UI Completion | Mixed | Present only for agent-ui code work that should become `구현됨` on code-review PASS; implementing agent fills actual evidence, review agent applies the status/evidence update and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 |
| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 |
| 코드리뷰 전용 체크리스트 | Review agent only | 구현 에이전트가 수정하거나 체크하지 않음 |
| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 |
| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채움; 구현 중 직접 질문은 금지; 환경/secret/서비스/검증 공백/일반 범위 조정은 사용자 리뷰 요청으로 쓰지 않음 |
| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 |
| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 |
| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Pass
  - code quality: Pass
  - implementation deviation: Fail
  - verification trust: Pass
- 발견된 문제:
  - Required — `apps/node/internal/adapters/cli/command.go:21-28`은 workspace를 `cmd.Dir`에 먼저 설정한 뒤 `cmd.Environ()`으로 `profile.Env`를 구성한다. POSIX의 `os/exec`는 이 순서에서 `PWD=<workspace>`를 주입하지만, 리팩터링 전 Codex app-server, OpenCode local server, non-terminal persistent 경로는 `cmd.Environ()`을 먼저 호출하고 `Dir`을 나중에 설정해 non-empty `profile.Env`에서 기존 `PWD`를 유지했다. 현재 `apps/node/internal/adapters/cli/cli_workspace_test.go:421-466`은 `FOO=bar` 포함 여부만 확인해 이 동작 변경을 놓친다. `buildCmd`의 기존 dir-before-env 순서와 `buildCmdWithoutContext`의 기존 env-before-dir 순서를 각각 보존하고, 두 builder의 exact environment/PWD와 cwd를 비교하는 회귀 테스트를 추가해야 한다.
- 리뷰어 검증:
  - `GOCACHE=/tmp/iop-go-cache-review-current go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmdWithoutContextAppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$'`: PASS.
  - `GOCACHE=/tmp/iop-go-cache-review-current go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/adapters/cli/status`: PASS.
  - `GOCACHE=/tmp/iop-go-cache-review-current go test -count=1 ./apps/node/...`: PASS.
  - HEAD baseline 기준 `cli_baseline_problems=0`, `cli_regressions=0`; 현재 전역 `make readability-audit`은 병렬 변경인 `agent-ops/skills/common/update-roadmap/SKILL.md`의 LOC 증가 1건 때문에 실패했으며 CLI 범위 회귀는 없다.
  - `timeout 12s /config/.npm-global/bin/opencode serve --hostname 127.0.0.1 --port 0`: 기존 evidence와 동일한 `PermissionDenied: FileSystem.open (/config/.local/share/opencode/log/opencode.log)`, exit 1.
- 다음 단계: 새 `PLAN-local-G05.md`의 FAIL 후속 계획에서 command environment 순서 보존과 회귀 테스트를 구현한다.
