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

# CLI command environment 순서 보존 후속 계획

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

검증 실행과 active `CODE_REVIEW-local-G05.md`의 구현 에이전트 소유 섹션 작성이 구현의 마지막 단계다. 실제 구현 내용, 계획 대비 변경, 설계 결정, 명령별 실제 stdout/stderr를 채운 뒤 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 근거다.

## 배경

네 번째 리뷰에서 exact executable assertion과 deterministic/full-cycle evidence는 확보됐다. 그러나 공통 command helper가 workspace를 먼저 적용한 뒤 environment를 계산해, 리팩터링 전 non-context lifecycle의 non-empty `profile.Env`에서 유지되던 `PWD` semantics를 바꾼 사실이 확인됐다. 두 builder의 기존 구성 순서를 명시적으로 보존하고 회귀 테스트로 고정한다.

## 사용자 리뷰 요청 흐름

구현 에이전트는 선택된 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_3.log`
- Prior review: `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/code_review_cloud_G08_3.log`
- Verdict: `FAIL`
- Issue summary: Required 1, Suggested 0, Nit 0.
- Required:
  - `command.go`가 `Dir` 설정 뒤 `cmd.Environ()`을 호출해, 리팩터링 전 non-context lifecycle의 env-before-dir 순서와 `PWD` semantics를 바꿈.
  - 기존 builder test는 `FOO=bar` 포함 여부만 확인해 exact environment 차이를 검출하지 못함.
- Affected files: `apps/node/internal/adapters/cli/command.go`, `apps/node/internal/adapters/cli/cli_workspace_test.go`.
- Reviewer verification:
  - targeted CLI, CLI/status package, `./apps/node/...` fresh 테스트 PASS.
  - HEAD baseline 기준 `cli_baseline_problems=0`, `cli_regressions=0`.
  - 현재 전역 readability 실패는 병렬 `update-roadmap/SKILL.md` LOC 증가이며 CLI 범위 회귀는 없음.
  - OpenCode permission blocker는 fresh direct command에서도 동일하게 재현됨.
- 추가 세부 문맥이 필요할 때만 위 두 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/opencode_sse_events.go`
  - `apps/node/internal/adapters/cli/persistent_process.go`
  - `apps/node/internal/adapters/cli/cli_workspace_test.go`
  - `scripts/readability_audit.py`
  - `scripts/readability_baseline.json`
  - `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/plan_cloud_G08_3.log`
  - `agent-task/m-agent-readable-repository-refactor/06_cli_adapter/code_review_cloud_G08_3.log`

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema나 runtime 책임 경계를 바꾸지 않고 기존 command construction semantics를 복구하는 동일 package 버그 수정이다.

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`, `agent-test/local/node-smoke.md`, `agent-test/local/testing-smoke.md`를 읽고 대상 package와 `./apps/node/...` fresh 회귀를 적용한다.
- exact environment 차이가 판정 대상이므로 Go 테스트는 `-count=1`을 사용한다.
- 전역 readability는 병렬 `agent-ops` 변경 때문에 현재 실패하므로 HEAD baseline 대비 `apps/node/internal/adapters/cli/` 회귀만 결정적으로 필터링한다.
- 외부 CLI full-cycle은 이번 follow-up의 판정 조건으로 다시 실행하지 않는다. prior loop에서 profile별 PASS/blocker evidence를 확보했고, 현재 수정은 두 builder의 pre-refactor command field 구성 순서를 deterministic test로 복구하는 범위다.

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

- runner/workdir: current local runner, `/config/workspace/iop`.
- branch/HEAD/dirty state: `main`, `57f0eaa71766a2b4eb53a94ec4df60a577d4a760`, 여러 split subtask가 공유하는 dirty worktree.
- source sync: 현재 worktree를 직접 테스트하며 다른 subtask 변경을 되돌리지 않는다.
- runtime: Go 1.24 이상, Linux aarch64.
- external state: 없음. external CLI/provider 결과는 이 follow-up의 pass 조건이 아니다.

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

- `TestBuildCmdWithoutContextAppliesEnvAndCwd`는 path/args/Dir과 custom env 존재만 확인하고 `cmd.Env` 전체 또는 `PWD`를 pre-refactor sequence와 비교하지 않는다.
- context builder의 기존 dir-before-env 순서와 non-context builder의 기존 env-before-dir 순서를 함께 고정하는 테스트가 없다.
- existing cwd tests는 실제 working directory를 확인하지만 non-empty custom env일 때의 environment snapshot 차이를 검출하지 못한다.

### 심볼 참조

- `configureCmd`: `command.go` 내부의 `buildCmd`, `buildCmdWithoutContext`만 호출한다.
- `buildCmd`: `oneshot.go` 1곳과 direct test 후보.
- `buildCmdWithoutContext`: `codex_app_server_process.go`, `opencode_sse.go`, `persistent_process.go`, `cli_workspace_test.go`.
- 외부/public symbol rename은 없다.

### 분할 판단

split decision policy를 평가했다. 한 Required가 `command.go`의 두 builder 순서와 `cli_workspace_test.go`의 regression assertion을 함께 요구하며, API·도메인·검증 경계가 분리되지 않는다. 별도 subtask는 동일 helper semantics와 테스트 evidence를 중복 소유하므로 현재 `06_cli_adapter` 단일 follow-up이 안전하다.

### 범위 결정 근거

- `codex_app_server_process.go`, `opencode_sse.go`, `persistent_process.go` 호출부는 helper signature 변경이 불가피할 때만 수정한다.
- Claude cold-start, Codex `/status`, OpenCode permission, Antigravity 설치 blocker는 이번 deterministic preservation fix에 포함하지 않는다.
- config/proto/spec/contract/roadmap, tracked config/script, readability baseline/read-set은 수정하지 않는다.
- 다른 split subtask의 dirty 변경을 정리하거나 되돌리지 않는다.

### 빌드 등급

`local-G05`. 두 내부 builder와 한 regression test로 범위가 닫히며, pre-refactor field 구성 순서를 local inspection과 deterministic Go test로 완전히 검증할 수 있다.

## 구현 체크리스트

- [ ] `buildCmd`와 `buildCmdWithoutContext`의 pre-refactor env/cwd 적용 순서를 각각 보존하고 exact command environment 회귀 테스트를 추가한다.
- [ ] targeted/package/Node 회귀와 CLI-scoped readability/diff 검증을 fresh 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REFACTOR_ADAPTER_CLI-1] builder별 environment 적용 순서 복구

문제: `apps/node/internal/adapters/cli/command.go:21-28`의 `configureCmd`는 `cmd.Dir`을 먼저 설정하고 그 뒤 `cmd.Environ()`을 호출한다. 리팩터링 전 context one-shot은 dir-before-env였지만 Codex app-server, OpenCode local server, non-terminal persistent는 env-before-dir였다. POSIX `os/exec`는 `Dir`이 설정되고 `Env`가 nil일 때 `cmd.Environ()`에 `PWD=<Dir>`을 주입하므로, non-empty `profile.Env`의 environment가 조용히 바뀐다.

해결 방법:

Before (`apps/node/internal/adapters/cli/command.go:21-41`):

```go
func configureCmd(cmd *exec.Cmd, env []string, workspace string) (*exec.Cmd, error) {
	dir, err := prepareWorkspaceDir(workspace)
	if err != nil {
		return nil, err
	}
	if dir != "" {
		cmd.Dir = dir
	}
	if len(env) > 0 {
		cmd.Env = append(cmd.Environ(), env...)
	}
	return cmd, nil
}

func buildCmd(ctx context.Context, command string, args []string, env []string, workspace string) (*exec.Cmd, error) {
	return configureCmd(exec.CommandContext(ctx, command, args...), env, workspace)
}

func buildCmdWithoutContext(command string, args []string, env []string, workspace string) (*exec.Cmd, error) {
	return configureCmd(exec.Command(command, args...), env, workspace)
}
```

After 방향:

```go
func applyCmdEnv(cmd *exec.Cmd, env []string) {
	if len(env) > 0 {
		cmd.Env = append(cmd.Environ(), env...)
	}
}

func applyCmdWorkspace(cmd *exec.Cmd, workspace string) error {
	dir, err := prepareWorkspaceDir(workspace)
	if err != nil {
		return err
	}
	if dir != "" {
		cmd.Dir = dir
	}
	return nil
}
```

`buildCmd`는 `applyCmdWorkspace` 뒤 `applyCmdEnv`를, `buildCmdWithoutContext`는 `applyCmdEnv` 뒤 `applyCmdWorkspace`를 호출해 각 pre-refactor 순서를 보존한다. 동등한 명명 구조는 허용하지만 boolean order flag나 lifecycle별 중복 command construction은 만들지 않는다.

수정 파일 및 체크리스트:

- [ ] `apps/node/internal/adapters/cli/command.go`: env 적용과 workspace 적용을 명명된 helper로 분리하고 두 builder의 기존 순서를 복구한다.
- [ ] `apps/node/internal/adapters/cli/cli_workspace_test.go`: context/non-context reference command를 pre-refactor 순서로 구성해 `Path`, `Args`, `Dir`, exact `Env`를 비교한다.
- [ ] 기존 invalid workspace error와 lifecycle caller signature를 보존한다.

테스트 작성: `apps/node/internal/adapters/cli/cli_workspace_test.go`에 context builder regression을 추가하고 기존 `TestBuildCmdWithoutContextAppliesEnvAndCwd`를 exact environment 비교로 보강한다. fixture는 `t.TempDir()`과 표준 `exec.Command`/`exec.CommandContext` reference command만 사용하며 새 dependency를 추가하지 않는다.

중간 검증:

```bash
gofmt -d apps/node/internal/adapters/cli/command.go apps/node/internal/adapters/cli/cli_workspace_test.go
GOCACHE=/tmp/iop-go-cache-review-plan4 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^TestBuildCmd(WithoutContext)?AppliesEnvAndCwd$'
```

두 builder test가 실행되고 exact environment, path, args, cwd, invalid workspace assertions가 통과해야 한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/cli/command.go` | 1 |
| `apps/node/internal/adapters/cli/cli_workspace_test.go` | 1 |

## 최종 검증

```bash
gofmt -d apps/node/internal/adapters/cli/command.go apps/node/internal/adapters/cli/cli_workspace_test.go
GOCACHE=/tmp/iop-go-cache-review-plan4 go test -count=1 -v ./apps/node/internal/adapters/cli -run '^(TestBuildCmd(WithoutContext)?AppliesEnvAndCwd|TestCodexAppServerProcCwd|TestOpencodeLocalServerCwd_(Workspace|DirOverride)|TestCLIPersistentNonTerminalCwd)$'
GOCACHE=/tmp/iop-go-cache-review-plan4 go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/adapters/cli/status
GOCACHE=/tmp/iop-go-cache-review-plan4 go test -count=1 ./apps/node/...
python3 scripts/readability_audit.py --input-mode worktree --output /tmp/iop-cli-plan4-readability.json
python3 - <<'PY'
import json
import subprocess
from scripts import readability_audit as audit

current = audit.audit(audit.collect_files("worktree"))
baseline = json.loads(subprocess.check_output(
    ["git", "show", "HEAD:scripts/readability_baseline.json"],
    text=True,
))
regressions = [
    issue for issue in audit.ratchet_check(current, baseline, None)
    if issue["path"].startswith("apps/node/internal/adapters/cli/")
]
for issue in regressions:
    print(audit._render_issue(issue))
print(f"cli_regressions={len(regressions)}")
raise SystemExit(1 if regressions else 0)
PY
git diff --check -- apps/node/internal/adapters/cli
```

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