<!-- task=m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd plan=0 tag=API -->

# Plan - API

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

이 plan은 split dependency가 있다. 구현을 시작하기 전에 같은 task group의 predecessor `02` complete.log가 active 또는 archive 위치에 있는지 확인한다. 구현은 코드 변경, 검증 실행, `CODE_REVIEW-local-G07.md`의 구현 에이전트 소유 섹션 작성까지다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌로 막히면 사용자에게 직접 묻지 말고 review stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 채운다.

## 배경

Edge와 Node는 workspace 값을 `RunRequest.Workspace`와 `ExecutionSpec.Workspace`까지 전달한다. 하지만 CLI adapter process start 경로는 아직 workspace를 `exec.Cmd.Dir` 또는 terminal session cwd로 적용하지 않는다. 이 작업은 workspace-bound agent target이 실제 workspace 안에서 process를 시작하도록 CLI 실행 경로를 닫는다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 결정이 필요하면 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 active `CODE_REVIEW-local-G07.md`의 `사용자 리뷰 요청` 섹션을 채운다. 구현 에이전트는 채팅으로 직접 질문하거나 `request_user_input`을 호출하지 않는다. code-review가 요청 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md`
- Task ids:
  - `agent-cwd`: workspace-bound agent process 실행기가 `ExecutionSpec.Workspace`를 process working directory로 적용한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md`
- `agent-contract/provided/openai-compatible-api.md`
- `agent-ops/rules/project/domain/node/rules.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/node-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `apps/node/internal/adapters/cli/oneshot.go`
- `apps/node/internal/adapters/cli/persistent.go`
- `apps/node/internal/adapters/cli/codex_exec.go`
- `apps/node/internal/adapters/cli/codex_app_server.go`
- `apps/node/internal/adapters/cli/opencode_sse.go`
- `apps/node/internal/adapters/cli/antigravity_print.go`
- `apps/node/internal/adapters/cli/cli.go`
- `apps/node/internal/adapters/cli/codex_exec_blackbox_test.go`
- `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`
- `apps/node/internal/adapters/cli/cli_internal_test.go`
- `apps/node/internal/adapters/cli/codex_app_server_internal_test.go`
- `apps/node/internal/terminal/session.go`
- `apps/node/internal/terminal/session_test.go`
- `apps/node/internal/runtime/types.go`
- `apps/node/internal/router/router.go`
- `apps/node/internal/node/node.go`
- `apps/edge/internal/service/run_dispatch.go`
- `apps/edge/internal/service/service_test.go`
- `proto/iop/runtime.proto`

### 테스트 환경 규칙

`test_env=local`을 선택했다. `agent-test/local/rules.md`를 읽었고, node 변경 기준 `agent-test/local/node-smoke.md`, proto/runtime 계약 확인 기준 `agent-test/local/platform-common-smoke.md`를 읽었다. 적용 명령은 변경 패키지 Go test, CLI adapter focused tests, 필요 시 `./scripts/e2e-smoke.sh`다. 구조적 blank나 `<확인 필요>` 값은 없었다.

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

`executeCommand`, persistent non-terminal, terminal PTY, codex app-server, opencode local server start가 `ExecutionSpec.Workspace`를 process cwd로 쓰는지 직접 검증하는 테스트가 없다. opencode `--dir` parsing test는 있지만 `opts.Dir`가 prompt 또는 process cwd에 적용되는지 검증하지 않는다. 이 plan에서 fake CLI/process tests로 cwd 적용을 검증한다.

### 심볼 참조

renamed/removed symbols: none.

### 분할 판단

split decision policy를 평가했다. shared task group은 `agent-task/m-openai-workspace-agent-execution-contract`다. 기존 완료 predecessor `02+01_workspace_required_enforcement`는 `agent-task/archive/2026/06/m-openai-workspace-agent-execution-contract/02+01_workspace_required_enforcement/complete.log`로 충족된다. 이 subtask는 `03+02_cli_agent_cwd`이며, `04+03_session_workspace_isolation`은 이 subtask 완료 뒤 진행한다. cwd 적용과 session isolation은 모두 CLI adapter에 닿지만 failure mode가 다르므로 분리한다.

### 범위 결정 근거

이 작업은 `ExecutionSpec.Workspace`를 process working directory로 적용하는 데 한정한다. workspace별 logical session key 변경, session list/terminate semantics 변경은 `04+03_session_workspace_isolation` 범위다. workspace 존재 여부, 권한 오류의 OpenAI error mapping은 후속 `failure-surface` 범위다.

### 빌드 등급

build lane은 `local-G07`이다. 변경은 node CLI/terminal process start 경로 여러 곳에 걸치지만 deterministic fake command tests로 검증 가능하고, 외부 CLI 실제 계정 없이 로컬에서 review 가능하다.

## 구현 체크리스트

- [ ] predecessor `02+01_workspace_required_enforcement`의 `complete.log`가 archive 위치에 있는지 확인한다.
- [ ] one-shot/codex-exec 공통 `executeCommand`가 `ExecutionSpec.Workspace`를 `exec.Cmd.Dir`로 적용하고, 빈 workspace에서는 기존 cwd 동작을 유지한다.
- [ ] persistent non-terminal과 terminal session start가 workspace를 process cwd로 적용한다.
- [ ] codex app-server와 opencode owned server process start가 workspace를 process cwd로 적용한다. opencode `--dir`가 명시된 경우 기존 explicit dir 우선순위를 유지한다.
- [ ] cwd 적용 테스트를 `apps/node/internal/adapters/cli`와 `apps/node/internal/terminal`에 추가한다.
- [ ] `go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli`와 `./scripts/e2e-smoke.sh`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

이 directory 이름 `03+02_cli_agent_cwd`의 predecessor는 `02`다. 구현 전 `agent-task/archive/2026/06/m-openai-workspace-agent-execution-contract/02+01_workspace_required_enforcement/complete.log` 존재를 확인한다.

### [API-1] Apply Workspace To CLI Process Starts

문제: `apps/node/internal/adapters/cli/oneshot.go:25`, `apps/node/internal/adapters/cli/persistent.go:375`, `apps/node/internal/adapters/cli/codex_app_server.go:385`, `apps/node/internal/adapters/cli/opencode_sse.go:273`는 process를 시작하지만 `ExecutionSpec.Workspace`를 cwd로 적용하지 않는다. `apps/node/internal/terminal/session.go:21`의 `Options`에도 cwd 필드가 없다.

해결 방법: cwd helper를 만들고, workspace가 비어 있지 않을 때만 `exec.Cmd.Dir` 또는 terminal options에 적용한다. profile/env/args 동작은 유지한다.

Before:

```go
// apps/node/internal/adapters/cli/oneshot.go:25
cmd := exec.CommandContext(ctx, profile.Command, args...)
```

After:

```go
cmd := exec.CommandContext(ctx, profile.Command, args...)
if dir := workspaceDir(spec.Workspace); dir != "" {
	cmd.Dir = dir
}
```

수정 파일 및 체크리스트:

- [ ] `apps/node/internal/adapters/cli/oneshot.go`: `executeCommand` cwd 적용.
- [ ] `apps/node/internal/adapters/cli/persistent.go`: `resolveSession`에서 workspace를 `startProfileSession`으로 전달하고 terminal/non-terminal start에 cwd 적용.
- [ ] `apps/node/internal/adapters/cli/codex_app_server.go`: `startCodexAppServerProc`가 workspace를 받아 `cmd.Dir`에 적용.
- [ ] `apps/node/internal/adapters/cli/opencode_sse.go`: owned local server start가 workspace 또는 explicit `--dir`를 cwd로 적용.
- [ ] `apps/node/internal/terminal/session.go`: `Options.Dir` 추가와 `cmd.Dir` 적용.

테스트 작성: 작성한다. fake shell command가 `pwd`를 출력하거나 marker 파일을 workspace에 쓰는 방식으로 one-shot, persistent non-terminal, terminal, codex app-server/opencode owned server start의 cwd를 검증한다.

중간 검증:

```bash
go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
```

기대 결과: 두 패키지 모두 PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/cli/oneshot.go` | API-1 |
| `apps/node/internal/adapters/cli/persistent.go` | API-1 |
| `apps/node/internal/adapters/cli/codex_app_server.go` | API-1 |
| `apps/node/internal/adapters/cli/opencode_sse.go` | API-1 |
| `apps/node/internal/terminal/session.go` | API-1 |
| `apps/node/internal/adapters/cli/*_test.go` | API-1 |
| `apps/node/internal/terminal/session_test.go` | API-1 |

## 최종 검증

```bash
go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
./scripts/e2e-smoke.sh
```

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