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

# Code Review Reference - REVIEW_API

> **[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 user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts 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 the needed decision 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-06-13
task=m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd, plan=1, tag=REVIEW_API

## 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

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

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

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

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

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_API-1] Add Provider Server Cwd Tests | [x] |
| [REVIEW_API-2] Capture Required Edge-Node Diagnostic Evidence | [x] |

## 구현 체크리스트

- [x] codex app-server process start가 workspace cwd를 쓰는 deterministic test를 추가한다.
- [x] opencode owned server process start가 workspace cwd를 쓰고 explicit `--dir`를 우선하는 deterministic test를 추가한다.
- [x] repo 내부 edge-node 진단을 `scripts/dev/edge.sh`와 `scripts/dev/node.sh`로 실행하고 메시지 2회, node local payload와 edge rendered payload 일치, `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/terminate-session` 결과를 기록한다.
- [x] `go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli`, `./scripts/e2e-smoke.sh`, `git diff --check`를 실행한다.
- [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_local_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_local_G07_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/m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd/`를 `agent-task/archive/YYYY/MM/m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-openai-workspace-agent-execution-contract/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G07.md`와 `CODE_REVIEW-local-G07.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가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

변경 없음.

## 주요 설계 결정

1. codex app-server 및 opencode owned server process에 대해 테스트 코드 내에서 가상 실행 스크립트(fake script)를 동적으로 작성하고 실행 권한을 주어, 외부 binary나 network 의존 없이 CWD 적용 여부를 결정론적(deterministic)으로 검증하도록 설계함.
2. opencode의 경우 opts.Dir (explicit --dir)와 workspace 파라미터가 동시에 주어질 때 opts.Dir를 우선하여 cwd로 설정하는 동작을 두 개의 독립된 테스트 케이스로 격리하여 검증함.

## 사용자 리뷰 요청

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

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

- codex app-server와 opencode owned server process start cwd가 실제 fake process로 검증되는지 확인한다.
- opencode explicit `--dir`가 workspace보다 우선하는지 확인한다.
- repo 내부 edge-node 진단 증거가 auxiliary smoke와 구분되어 있고, 메시지 2회와 command 응답 기준을 충족하는지 확인한다.
- source behavior 변경이 테스트 보완 범위를 넘어가지 않았는지 확인한다.

## 검증 결과

### REVIEW_API-1 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/cli
ok  	iop/apps/node/internal/adapters/cli	45.917s
```

### REVIEW_API-2 중간 검증
```bash
$ IOP_EDGE_CONFIG=<temp-edge.yaml> ./scripts/dev/edge.sh
$ IOP_NODE_CONFIG=<temp-node.yaml> ./scripts/dev/node.sh
# edge/node transcript showing /nodes, message x2, /capabilities, /transport, /sessions, /terminate-session

[e2e] > /nodes
[e2e] > IOP_MANUAL_ONE
[e2e] > IOP_MANUAL_TWO
[e2e] > /capabilities
[e2e] > /transport
[e2e] > /sessions
[e2e] > /terminate-session
[e2e] > /exit
=== EDGE OUTPUT ===
[edge] config=/config/workspace/iop/tmp-untrusted-test/diag_cwd/edge.yaml
IOP Edge console listening on 127.0.0.1:31234
Console target node= adapter=cli target=fake-cli session=default background=false
Start node.sh on another host, then type a message here.
Commands: /nodes, /node <id|alias>, /session <id>, /background on|off, /terminate-session, /status, /capabilities, /sessions, /transport, /exit
edge> [node0-evt] connected reason="registered"
  node0 = test-node (test-node)
edge> [edge] sent run_id=manual-1781346387825049377 node=node0 adapter=cli target=fake-cli session=default background=false
[node0-evt] start run_id=manual-1781346387825049377
[node0-msg] IOP_MANUAL_ONE
[node0-msg] IOP_MANUAL_ONE_TAIL
[node0-evt] complete run_id=manual-1781346387825049377 detail="idle-timeout"
edge> [edge] sent run_id=manual-1781346389326895753 node=node0 adapter=cli target=fake-cli session=default background=false
[node0-evt] start run_id=manual-1781346389326895753
[node0-msg] IOP_MANUAL_TWO
[node0-msg] IOP_MANUAL_TWO_TAIL
[node0-evt] complete run_id=manual-1781346389326895753 detail="idle-timeout"
edge> [node0-capabilities] adapter=cli target=fake-cli session=default
  adapter = cli
  instance_key =
  max_concurrency = 4
  targets = fake-cli
edge> [node0-transport] adapter=cli target=fake-cli session=default
  adapter = cli
  connected = true
  node_id = test-node
  session_id = default
  state = connected
  target = fake-cli
edge> [node0-sessions] adapter=cli target=fake-cli session=default
sessions: 1
  [0] mode=persistent target=fake-cli session=default
edge> terminated session default node=node0
edge> bye
=== NODE OUTPUT ===
[node] config=/config/workspace/iop/tmp-untrusted-test/diag_cwd/node.yaml
[node] waiting for edge at 127.0.0.1:31234 timeout=30s
[node] edge is reachable
[Fx] PROVIDE	fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE	fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE	fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] PROVIDE	*config.NodeConfig <= iop/apps/node/internal/bootstrap.Module.func1()
[Fx] PROVIDE	*zap.Logger <= iop/apps/node/internal/bootstrap.Module.func2()
[Fx] INVOKE		iop/apps/node/internal/bootstrap.Module.func3()
[Fx] RUN	provide: go.uber.org/fx.New.func1()
[Fx] RUN	provide: iop/apps/node/internal/bootstrap.Module.func1()
[Fx] RUN	provide: iop/apps/node/internal/bootstrap.Module.func2()
[Fx] HOOK OnStart		iop/apps/node/internal/bootstrap.Module.func3.1() executing (caller: iop/apps/node/internal/bootstrap.Module.func3)
{"level":"info","ts":1781346384.064757,"caller":"transport/client.go:66","msg":"registered with edge","node_id":"test-node","alias":"test-node"}
{"level":"info","ts":1781346384.0718446,"caller":"store/store.go:62","msg":"store ready","dsn":"file:/config/workspace/iop/tmp-untrusted-test/diag_cwd/workspace/iop.db?cache=shared&mode=rwc"}
{"level":"info","ts":1781346384.073181,"caller":"cli/cli.go:226","msg":"cli adapter: persistent session started","target":"fake-cli"}
[Fx] HOOK OnStart		iop/apps/node/internal/bootstrap.Module.func3.1() called by iop/apps/node/internal/bootstrap.Module.func3 ran successfully in 113.593958ms
[Fx] RUNNING
{"level":"info","ts":1781346387.825762,"caller":"node/node.go:72","msg":"run request received","run_id":"manual-1781346387825049377","adapter":"cli","target":"fake-cli"}
[edge-message] IOP_MANUAL_ONE
[node-event] start run_id=manual-1781346387825049377
[node-message] IOP_MANUAL_ONE
IOP_MANUAL_ONE_TAIL
[node-event] complete run_id=manual-1781346387825049377 detail="idle-timeout"
{"level":"info","ts":1781346389.3271575,"caller":"node/node.go:72","msg":"run request received","run_id":"manual-1781346389326895753","adapter":"cli","target":"fake-cli"}
[edge-message] IOP_MANUAL_TWO
[node-event] start run_id=manual-1781346389326895753
[node-message] IOP_MANUAL_TWO
IOP_MANUAL_TWO_TAIL
[node-event] complete run_id=manual-1781346389326895753 detail="idle-timeout"
{"level":"info","ts":1781346390.829387,"caller":"node/node.go:277","msg":"command request","request_id":"caps-1781346390829017295","type":"NODE_COMMAND_TYPE_CAPABILITIES","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1781346392.3316538,"caller":"node/node.go:277","msg":"command request","request_id":"transport-1781346392331361504","type":"NODE_COMMAND_TYPE_TRANSPORT_STATUS","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1781346393.8340967,"caller":"node/node.go:277","msg":"command request","request_id":"sessions-1781346393833903713","type":"NODE_COMMAND_TYPE_SESSION_LIST","adapter":"cli","target":"fake-cli"}
{"level":"info","ts":1781346395.3381782,"caller":"node/node.go:256","msg":"cancel request","run_id":"","action":"CANCEL_ACTION_TERMINATE_SESSION"}
{"level":"info","ts":1781346396.843783,"caller":"transport/session.go:89","msg":"disconnected from edge","transport_close_reason":"remote_closed","transport_close_error":"EOF"}
[edge-event] disconnected reason="transport_closed" transport_close_reason="remote_closed" transport_close_error="EOF"
```

### 최종 검증
```bash
$ go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
ok  	iop/apps/node/internal/terminal	0.551s
ok  	iop/apps/node/internal/adapters/cli	45.855s

$ ./scripts/e2e-smoke.sh
[e2e] Auxiliary smoke test PASSED.

$ git diff --check
# PASS
```

---

> **[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. |
| Roadmap Targets | Fixed at stub creation from plan | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS. |
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only. |
| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[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 user input is required to proceed. |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus areas from the plan. |
| 검증 결과 | Implementing agent | Paste actual stdout/stderr for the commands above. |

## 코드리뷰 결과

### 종합 판정

PASS

### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| Correctness | Pass | `ExecutionSpec.Workspace`가 one-shot, codex-exec 공통 실행, persistent non-terminal/terminal, codex app-server, opencode owned server start 경로의 cwd로 연결된다. opencode explicit `--dir` 우선순위도 유지된다. |
| Completeness | Pass | 이전 FAIL의 Required 두 건이 모두 해소됐고 active review의 구현 체크리스트와 실제 diff가 일치한다. |
| Test coverage | Pass | one-shot/persistent/terminal/codex app-server/opencode owned server cwd 테스트가 추가됐으며 test list와 재실행으로 확인했다. |
| API contract | Pass | `metadata.workspace` -> `ExecutionSpec.Workspace` -> process cwd 계약을 해치는 변경은 발견하지 못했다. |
| Code quality | Pass | 새 helper와 테스트는 기존 패키지 패턴 안에 있고, 검증용 임시 산출물은 리뷰 중 정리했다. |
| Plan deviation | Pass | follow-up 범위인 테스트/검증 증거 보완에 머물렀고 session isolation 범위를 섞지 않았다. |
| Verification trust | Pass | reviewer가 Go 패키지 테스트, auxiliary smoke, `git diff --check`, test list를 재실행했고 active review의 repo 내부 edge-node 진단 transcript도 기준 항목을 충족한다. |

Reviewer verification rerun:

```bash
$ go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
ok  	iop/apps/node/internal/terminal	0.552s
ok  	iop/apps/node/internal/adapters/cli	45.995s

$ ./scripts/e2e-smoke.sh
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.

$ go test -list 'Cwd|OpencodeLocalServerCwd|CodexAppServerProcCwd' ./apps/node/internal/adapters/cli ./apps/node/internal/terminal
TestCLIOneshotExecutorCwd
TestCLIPersistentNonTerminalCwd
TestCLIPersistentTerminalCwd
TestCodexAppServerProcCwd
TestOpencodeLocalServerCwd_Workspace
TestOpencodeLocalServerCwd_DirOverride
TestTerminalSessionCwd

$ git diff --check
# PASS
```

### 발견된 문제

없음

### 다음 단계

PASS: `complete.log`를 작성하고 active task directory를 archive로 이동한다. task group이 `m-openai-workspace-agent-execution-contract`이므로 런타임 완료 이벤트 메타데이터를 보고하고 roadmap은 수정하지 않는다.
