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

# CLI adapter exact 경계·full-cycle evidence 완결 계획

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

검증 실행과 active `CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션 작성이 구현의 마지막 단계다. 실제 구현 내용, 계획 대비 변경, 설계 결정, 명령별 실제 stdout/stderr 또는 exact `/tmp` evidence path를 채운 뒤 active 파일을 그대로 두고 리뷰 준비 완료를 보고한다. 최종 판정, log rename, `complete.log`, task archive는 code-review 전용이다.

선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 review stub의 `사용자 리뷰 요청`에 정확한 연결 대상과 근거를 기록하고 중단한다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`를 만들거나 active 파일을 archive하거나 `complete.log`를 쓰지 않는다. 외부 CLI 로그인·provider·endpoint·secret·service 준비, 일반 범위 충돌, 반복 실패, 검증 증거 공백은 사용자 리뷰 요청이 아니라 검증 blocker 또는 다음 follow-up 근거다.

## 배경

세 번째 리뷰에서 command builder 구현, targeted/Node 회귀, formatting, readability ratchet은 통과했다. 그러나 executable assertion은 path가 비어 있지 않은지만 확인했고, review의 fresh output은 요약·생략되어 신뢰할 수 없었다. real CLI helper도 prompt 1회와 일부 command만 실행해 계획의 message x2, `/status`, payload/order 기준을 충족하지 못했다.

## 사용자 리뷰 요청 흐름

구현 에이전트는 선택된 Milestone lock 결정만 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식의 active review `사용자 리뷰 요청`에 기록한다. 직접 사용자 prompt는 금지하며, code-review가 요청의 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## 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 완료 체크를 주장하지 않는다.

## 분석 결과

### 읽은 파일

- 규칙/라우팅:
  - `agent-ops/rules/project/rules.md`
  - `agent-ops/rules/common/rules-roadmap.md`
  - `agent-ops/rules/project/domain/node/rules.md`
  - `agent-ops/rules/project/domain/testing/rules.md`
  - `agent-ops/rules/common/rules-agent-spec.md`
  - `agent-ops/skills/common/router.md`
  - `agent-ops/skills/common/code-review/SKILL.md`
  - `agent-ops/skills/common/plan/SKILL.md`
  - `agent-ops/skills/project/e2e-smoke/SKILL.md`
  - `agent-test/local/rules.md`
  - `agent-test/local/node-smoke.md`
  - `agent-test/local/testing-smoke.md`
- roadmap/spec/contract:
  - `agent-roadmap/current.md`
  - `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
  - `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
  - `agent-spec/index.md`
  - `agent-spec/runtime/edge-node-execution.md`
  - `agent-contract/index.md`
  - `agent-contract/inner/edge-node-runtime-wire.md`
- source/tests/evidence:
  - `apps/node/internal/adapters/cli/command.go`
  - `apps/node/internal/adapters/cli/workspace.go`
  - `apps/node/internal/adapters/cli/oneshot.go`
  - `apps/node/internal/adapters/cli/codex_app_server_process.go`
  - `apps/node/internal/adapters/cli/opencode_sse.go`
  - `apps/node/internal/adapters/cli/persistent_process.go`
  - `apps/node/internal/adapters/cli/cli_workspace_test.go`
  - `scripts/readability_baseline.json`
  - `scripts/readability_audit.py`
  - `scripts/dev/edge.sh`
  - `scripts/dev/node.sh`
  - `configs/edge.yaml`
  - `configs/node.yaml`
  - `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/plan_cloud_G08_2.log`
  - `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/code_review_cloud_G08_2.log`
  - `/tmp/run-e2e-profile.sh`

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema나 runtime 책임 경계를 바꾸지 않는 동일 package 테스트 보강과 기존 실행 경로 검증이다.

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`, `agent-test/local/node-smoke.md`, `agent-test/local/testing-smoke.md`를 읽고 Node package 회귀와 testing domain의 real CLI full-cycle 기준을 적용한다.
- fresh output이 판정 대상이므로 Go 명령은 `-count=1`, pipeline은 `set -o pipefail`을 사용한다.
- 긴 출력은 `/tmp/iop-cli-plan3/`에 저장하고 active review에 exact command, path, exit status, `wc -l`, `sha256sum`을 기록한다. `PASS`, `...`, 합성 count로 대체하지 않는다.
- tracked `configs/*.yaml`과 `scripts/dev/*.sh`는 수정하지 않는다.

테스트 환경 프리플라이트:

- runner/workdir: 현재 local runner, `/config/workspace/iop`.
- branch/HEAD/dirty state: `main`, `57f0eaa71766a2b4eb53a94ec4df60a577d4a760`, 여러 split subtask가 공유하는 dirty worktree. 다른 subtask 변경을 되돌리지 않는다.
- source sync: 현재 worktree를 `go test`와 `scripts/dev/{edge,node}.sh`로 직접 실행한다.
- command inventory: `claude=/config/.npm-global/bin/claude` `2.1.212`, `codex=/config/.npm-global/bin/codex` `0.144.5`, `opencode=/config/.npm-global/bin/opencode` `1.18.3`, `antigravity` 미발견. 구현 직전 다시 확인한다.
- config/runtime: profile별 `/tmp` config와 충돌하지 않는 localhost port를 사용하고 Edge/Node를 별도 process로 실행한다. 시작 전 선택 port listen 상태를 확인한다.
- external state: 로그인, quota, provider, endpoint 결과는 실행 후 실제 stderr/event/exit status로만 blocker를 판정한다.
- OS/arch: `Linux 6.10.14-linuxkit aarch64`.
- secret/token 원문은 review나 evidence에 기록하지 않고, 누락 CLI를 다운로드하지 않는다.

### 테스트 커버리지 공백

- `TestBuildCmdWithoutContextAppliesEnvAndCwd`는 args/env/cwd/error를 검사하지만 executable path는 non-empty만 확인한다.
- package와 Node 회귀는 fresh reviewer rerun에서 통과했다.
- real CLI evidence는 profile별 message x2, `/status`, node-local/edge-rendered payload 동일성 및 exact blocker가 없다.

### 심볼 참조

- production symbol 변경은 없다.
- test import에 `os/exec`를 추가해 `exec.LookPath`를 사용한다.
- `buildCmdWithoutContext` 호출자는 `codex_app_server_process.go`, `opencode_sse.go`, `persistent_process.go`, direct test이며 변경하지 않는다.

### 분할 판단

split decision policy를 평가했다. exact assertion과 evidence 복구는 같은 PASS 판정을 구성하며 별도 subtask로 나누면 동일 active review와 외부 runtime 증거를 중복 소유한다. 새 API/도메인/선행 작업이 없고 수정 파일은 테스트 1개뿐이므로 현재 `06_cli_adapter` 단일 follow-up이 안전하다.

### 범위 결정 근거

- production CLI adapter, runtime event/session/cancellation, config/proto/spec/contract/roadmap 동작은 변경하지 않는다.
- `scripts/dev/*.sh`, `configs/*.yaml`, readability baseline/read-set은 수정하지 않는다.
- 임시 helper/log/config는 `/tmp/iop-cli-plan3/`에만 두고 repo artifact로 만들지 않는다.
- 다른 split subtask의 dirty 변경을 정리하거나 되돌리지 않는다.

### 빌드 등급

`cloud-G08`. 소스 수정은 작지만 외부 CLI process/terminal 동작과 반복된 evidence-trust 실패를 실제 full-cycle로 복구해야 한다.

## 구현 체크리스트

- [ ] executable path를 `exec.LookPath` 결과와 정확히 비교하고 기존 args/env/cwd/error assertions를 보존한다.
- [ ] source discovery, targeted/package/Node/readability 검증을 fresh 실행하고 실제 stdout/stderr 또는 exact `/tmp` evidence metadata를 기록한다.
- [ ] `claude`, `codex`, `opencode`, `antigravity`의 message x2/command/status full-cycle 또는 executable discovery 이후 exact blocker를 profile별로 수집한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-1] exact executable assertion 보강

문제: `apps/node/internal/adapters/cli/cli_workspace_test.go:436-438`은 `cmd.Path != ""`만 확인해 잘못 resolve된 executable도 허용한다.

해결 방법:

Before (`apps/node/internal/adapters/cli/cli_workspace_test.go:436-438`):

```go
if cmd.Path == "" {
	t.Error("expected cmd.Path to not be empty")
}
```

After:

```go
wantPath, err := exec.LookPath("echo")
if err != nil {
	t.Fatalf("LookPath(echo): %v", err)
}
if cmd.Path != wantPath {
	t.Errorf("expected cmd.Path %q, got %q", wantPath, cmd.Path)
}
```

수정 파일 및 체크리스트:

- [ ] `apps/node/internal/adapters/cli/cli_workspace_test.go`: 표준 import 순서로 `os/exec`를 추가한다.
- [ ] 같은 test에서 exact path와 기존 args/env/cwd/invalid workspace assertions를 모두 유지한다.
- [ ] production builder와 lifecycle caller는 변경하지 않는다.

테스트 작성: 기존 regression test를 보강한다. 새 fixture나 production 코드는 추가하지 않는다.

중간 검증:

```bash
gofmt -d apps/node/internal/adapters/cli/cli_workspace_test.go
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^TestBuildCmdWithoutContextAppliesEnvAndCwd$'
```

exact test가 `=== RUN`과 PASS를 출력해야 한다.

### [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-2] fresh deterministic evidence 재수집

문제: prior review `code_review_cloud_G08_2.log:195-233,313-336`은 실제 출력 대신 일부 test 이름, 합성 count, `PASS (same as ...)`, `...`를 기록했다.

해결 방법: `/tmp/iop-cli-plan3/`를 새로 만들고 source discovery, targeted, package/status, Node, readability 출력을 각각 별도 파일에 fresh 저장한다. active review에는 짧은 출력은 원문을 붙이고, 긴 출력은 exact command/path/exit/line count/hash를 붙인다.

수정 파일 및 체크리스트:

- [ ] active `CODE_REVIEW-cloud-G08.md`: 합성·생략 없는 evidence를 기록한다.
- [ ] `/tmp/iop-cli-plan3/`: discovery/test/readability output만 저장하고 repo에는 생성하지 않는다.
- [ ] 각 pipeline의 원래 command exit가 0인지 별도로 기록한다.

테스트 작성: 추가하지 않는다. 항목 1의 보강 test와 기존 suites를 fresh 실행한다.

중간 검증:

```bash
mkdir -p /tmp/iop-cli-plan3
set -o pipefail
rg -n --sort path '^func Test' apps/node/internal/adapters/cli apps/node/internal/adapters/cli/status 2>&1 | tee /tmp/iop-cli-plan3/test-discovery.log
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmdWithoutContextAppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$' 2>&1 | tee /tmp/iop-cli-plan3/targeted.log
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli ./apps/node/internal/adapters/cli/status 2>&1 | tee /tmp/iop-cli-plan3/package.log
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/... 2>&1 | tee /tmp/iop-cli-plan3/node.log
make readability-audit 2>&1 | tee /tmp/iop-cli-plan3/readability.log
wc -l /tmp/iop-cli-plan3/*.log
sha256sum /tmp/iop-cli-plan3/*.log
```

각 pipeline 직후 `PIPESTATUS[0]`이 0이어야 한다. 여러 pipeline을 wrapper로 실행하면 command별 exit를 `/tmp/iop-cli-plan3/exits.txt`에 기록한다.

### [REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-3] profile별 full-cycle 또는 exact blocker 수집

문제: prior helper `/tmp/run-e2e-profile.sh:60-67`은 prompt 1회와 `/terminate-session`만 수행하고 `/status`를 생략했다. prior review의 profile 결과도 필수 event/payload/order 또는 exact blocker를 충족하지 않는다.

해결 방법: `/tmp/iop-cli-plan3/` 아래 profile별 임시 config, edge/node output, result summary를 둔다. 단순 fixed sleep 성공 판정은 금지하고 각 run의 terminal event 또는 bounded timeout을 기다린다. 실행 가능한 profile은 같은 session prompt 2회와 command를 수행하고, 실패 profile은 executable discovery 뒤 실제 실패 명령·exit status·stderr/event·미완료 cycle·재개 조건을 기록한다.

수정 파일 및 체크리스트:

- [ ] `claude`, `codex`, `opencode`, `antigravity` command path/version 또는 missing exit를 기록한다.
- [ ] 설치된 profile마다 `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/status`, 해당 시 `/terminate-session`을 보낸다.
- [ ] 가능한 profile은 같은 session prompt 2회를 각각 sent/start/message/complete까지 기다린다.
- [ ] run별 node-local message payload 목록과 edge-rendered message payload 목록의 내용/순서를 비교한다.
- [ ] timeout/quota/login/provider/endpoint 실패는 exact command, exit status, stderr/event, 미완료 cycle, 재개 조건을 기록한다.
- [ ] `antigravity`는 `command -v` missing을 blocker로 기록하며 잘못된 `resume_args` config 오류를 missing-command 증거로 대체하지 않는다.
- [ ] helper/config/log는 `/tmp/iop-cli-plan3/`에만 두고 tracked config/script와 secret을 오염시키지 않는다.

테스트 작성: Go test는 추가하지 않는다. testing domain과 `e2e-smoke`의 real CLI 절차가 검증이다.

중간 검증:

```bash
for c in claude antigravity codex opencode; do
  if path="$(command -v "$c")"; then
    printf '%s=%s\n' "$c" "$path"
    "$c" --version
  else
    status=$?
    printf '%s=NOT_FOUND exit=%d\n' "$c" "$status"
  fi
done
test -x scripts/dev/edge.sh
test -x scripts/dev/node.sh
find /tmp/iop-cli-plan3 -maxdepth 2 -type f -name 'result.txt' -print -exec sed -n '1,220p' {} \;
```

각 `result.txt`는 `status=pass|blocker`, 실행 명령/exit, message1/message2, event ordering, payload comparison, command 결과, 미완료 cycle, 재개 조건 필드를 가져야 한다. PASS profile은 모든 필드가 통과해야 하고 blocker profile은 실제 downstream 원문과 남은 위험을 가져야 한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/cli/cli_workspace_test.go` | 1 |
| `CODE_REVIEW-cloud-G08.md` | 2, 3 |
| `/tmp/iop-cli-plan3/**` | 2, 3 |

## 의존 관계 및 구현 순서

1. 항목 1의 exact assertion을 보강한다.
2. 항목 2의 deterministic suite를 fresh 실행한다.
3. 같은 source/config로 항목 3의 real CLI full-cycle 또는 blocker를 수집한다.
4. actual output과 exact evidence metadata로 active review를 채운다.

## 최종 검증

```bash
gofmt -d apps/node/internal/adapters/cli/*.go apps/node/internal/adapters/cli/status/*.go
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmdWithoutContextAppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$'
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/adapters/cli/status
GOCACHE=/tmp/iop-go-cache-review-plan3 go test -count=1 ./apps/node/...
make readability-audit
git diff --check -- apps/node/internal/adapters/cli scripts/readability_baseline.json scripts/readability_read_sets.json
```

이후 `e2e-smoke scope="CLI adapter exact boundary and evidence recovery" mode=real-cli profiles="claude,antigravity,codex,opencode" config_strategy="tracked configs unchanged; profile-specific /tmp configs, dynamic localhost ports, prompt-aware bounded waits"`를 수행하고 profile별 PASS 또는 exact blocker를 기록한다.

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