feat: openai-workspace-agent-execution-contract milestone plan & code review, node test fixes

- Update automation-runtime-bridge phase and milestone docs
- Add plan and code review for CLI CWD (G07) and session workspace isolation (G08)
- Fix node and router tests
This commit is contained in:
toki 2026-06-13 19:07:59 +09:00
parent c34fec8f6b
commit 423264feb2
8 changed files with 661 additions and 11 deletions

View file

@ -71,7 +71,7 @@ CLI 실행, specialized agent 등록, bootstrap/enrollment, 원격 터미널 브
- 경로: `agent-roadmap/archive/phase/automation-runtime-bridge/milestones/codex-app-server-streaming-migration.md` - 경로: `agent-roadmap/archive/phase/automation-runtime-bridge/milestones/codex-app-server-streaming-migration.md`
- 요약: Codex CLI target의 기본 `codex` profile을 app-server 기반으로 전환하고 실제 `codex` foreground/background smoke와 app-server session lifecycle 검증을 완료했다. - 요약: Codex CLI target의 기본 `codex` profile을 app-server 기반으로 전환하고 실제 `codex` foreground/background smoke와 app-server session lifecycle 검증을 완료했다.
- [계획] OpenAI Workspace Agent Execution Contract - [진행중] OpenAI Workspace Agent Execution Contract
- 경로: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md` - 경로: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md`
- 요약: NomadCode가 IOP CLI를 직접 실행하지 않고 IOP Edge OpenAI-compatible HTTP 호출의 `metadata.workspace`와 task/source metadata만으로 내부 workspace-bound agent target이 해당 checkout에서 산출물을 만들 수 있게 하는 최우선 contract/serving hardening 작업이다. - 요약: NomadCode가 IOP CLI를 직접 실행하지 않고 IOP Edge OpenAI-compatible HTTP 호출의 `metadata.workspace`와 task/source metadata만으로 내부 workspace-bound agent target이 해당 checkout에서 산출물을 만들 수 있게 하는 최우선 contract/serving hardening 작업이다.

View file

@ -1,4 +1,4 @@
현재# Milestone: OpenAI Workspace Agent Execution Contract # Milestone: OpenAI Workspace Agent Execution Contract
## 위치 ## 위치
@ -14,7 +14,7 @@
## 상태 ## 상태
[계획] [진행중]
## 구현 잠금 ## 구현 잠금
@ -38,15 +38,15 @@
OpenAI-compatible 입력 표면에서 workspace agent 실행 문맥을 별도 wrapper 없이 `metadata.workspace`로 받는 계약을 구현한다. OpenAI-compatible 입력 표면에서 workspace agent 실행 문맥을 별도 wrapper 없이 `metadata.workspace`로 받는 계약을 구현한다.
- [ ] [metadata-schema] `/v1/responses``/v1/chat/completions`가 flat `metadata.workspace`를 파싱해 run workspace로 전달한다. 검증: 기존 `metadata.request_id`, `metadata.nomadcode.*`, `metadata.inference.target`은 유지되고 `metadata.cli`는 계속 거부된다. - [ ] [metadata-schema] `/v1/responses``/v1/chat/completions`가 flat `metadata.workspace`를 파싱해 run workspace로 전달한다. 검증: 기존 `metadata.request_id`, `metadata.nomadcode.*`, `metadata.inference.target`은 유지되고 `metadata.cli`는 계속 거부된다.
- [ ] [workspace-required] 내부 실행 route가 workspace-bound agent target이면 workspace가 비어 있거나 상대 경로일 때 OpenAI-compatible error로 거부한다. 검증: workspace가 필요 없는 inference route는 기존 동작을 유지하고, workspace-bound route만 필수 조건을 적용한다. - [x] [workspace-required] 내부 실행 route가 workspace-bound agent target이면 workspace가 비어 있거나 상대 경로일 때 OpenAI-compatible error로 거부한다. 검증: workspace가 필요 없는 inference route는 기존 동작을 유지하고, workspace-bound route만 필수 조건을 적용한다.
- [ ] [route-catalog] 외부 `model` route가 내부 `adapter + target`으로 해석되는 기준을 config와 smoke fixture에 남긴다. 검증: `model: "codex"` 같은 route가 명시적으로 workspace-bound agent target으로 수렴한다. - [x] [route-catalog] 외부 `model` route가 내부 `adapter + target`으로 해석되는 기준을 config와 smoke fixture에 남긴다. 검증: `model: "codex"` 같은 route가 명시적으로 workspace-bound agent target으로 수렴한다.
### Epic: [edge-node-workspace] Edge-Node Workspace Propagation ### Epic: [edge-node-workspace] Edge-Node Workspace Propagation
Edge service와 Node runtime 사이에서 workspace가 metadata 문자열로만 남지 않고 실행 spec의 작업 디렉터리로 이어지게 한다. Edge service와 Node runtime 사이에서 workspace가 metadata 문자열로만 남지 않고 실행 spec의 작업 디렉터리로 이어지게 한다.
- [ ] [run-workspace] `SubmitRunRequest``BuildRunRequest`가 workspace를 명시 필드로 갖고 proto `RunRequest.Workspace`에 채운다. 검증: service unit test가 metadata와 workspace를 별도 필드로 보존하는지 확인한다. - [x] [run-workspace] `SubmitRunRequest``BuildRunRequest`가 workspace를 명시 필드로 갖고 proto `RunRequest.Workspace`에 채운다. 검증: service unit test가 metadata와 workspace를 별도 필드로 보존하는지 확인한다.
- [ ] [node-spec] Node router가 `RunRequest.Workspace``ExecutionSpec.Workspace`로 유지한다. 검증: router/node test에서 workspace 값이 adapter execution spec까지 보존된다. - [x] [node-spec] Node router가 `RunRequest.Workspace``ExecutionSpec.Workspace`로 유지한다. 검증: router/node test에서 workspace 값이 adapter execution spec까지 보존된다.
- [ ] [agent-cwd] workspace-bound agent process 실행기가 `ExecutionSpec.Workspace`를 process working directory로 적용한다. 검증: 이 마일스톤에서 지원하는 agent target profile의 cwd 적용 test가 통과한다. - [ ] [agent-cwd] workspace-bound agent process 실행기가 `ExecutionSpec.Workspace`를 process working directory로 적용한다. 검증: 이 마일스톤에서 지원하는 agent target profile의 cwd 적용 test가 통과한다.
- [ ] [session-workspace] logical session이 다른 workspace를 같은 target/session으로 재사용하지 않는다. 검증: 같은 target/session이라도 workspace가 다르면 별도 session을 만들거나 명시 오류를 반환한다. - [ ] [session-workspace] logical session이 다른 workspace를 같은 target/session으로 재사용하지 않는다. 검증: 같은 target/session이라도 workspace가 다르면 별도 session을 만들거나 명시 오류를 반환한다.

View file

@ -0,0 +1,145 @@
<!-- task=m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd plan=0 tag=API -->
# Code Review Reference - 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=0, tag=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` 위치에서 체크한 뒤 보고한다.
---
## 구현 항목별 완료 여부
| 항목 | 완료 여부 |
|------|---------|
| [API-1] Apply Workspace To CLI Process Starts | [ ] |
## 구현 체크리스트
- [ ] 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의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 코드리뷰 전용 체크리스트
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
- [ ] `코드리뷰 결과``PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [ ] active `CODE_REVIEW-*-G??.md``code_review_local_G07_N.log`로 아카이브한다.
- [ ] active `PLAN-*-G??.md``plan_local_G07_M.log`로 아카이브한다.
- [ ] `.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-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 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] 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로 이동한다.
## 계획 대비 변경 사항
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
## 주요 설계 결정
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
## 사용자 리뷰 요청
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음
## 리뷰어를 위한 체크포인트
- `ExecutionSpec.Workspace`가 모든 CLI process start 경로에서 cwd로 적용되는지 확인한다.
- 빈 workspace에서 기존 cwd 동작이 유지되는지 확인한다.
- `session-workspace` 범위인 session key 변경을 이 plan에서 섞지 않았는지 확인한다.
## 검증 결과
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
### API-1 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
(output)
```
### 최종 검증
```bash
$ go test -count=1 ./apps/node/internal/terminal ./apps/node/internal/adapters/cli
(output)
$ ./scripts/e2e-smoke.sh
(output)
```
---
> **[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. |

View file

@ -0,0 +1,155 @@
<!-- 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.

View file

@ -0,0 +1,153 @@
<!-- task=m-openai-workspace-agent-execution-contract/04+03_session_workspace_isolation plan=0 tag=API -->
# Code Review Reference - 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/04+03_session_workspace_isolation, plan=0, tag=API
## Roadmap Targets
- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md`
- Task ids:
- `session-workspace`: logical session이 다른 workspace를 같은 target/session으로 재사용하지 않는다.
- Completion mode: check-on-pass
## 이 파일을 읽는 리뷰 에이전트에게
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
1. 판정을 append한다.
2. `CODE_REVIEW-local-G08.md` -> `code_review_local_G08_N.log`, `PLAN-local-G08.md` -> `plan_local_G08_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-openai-workspace-agent-execution-contract/04+03_session_workspace_isolation/`로 이동한다. 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` 위치에서 체크한 뒤 보고한다.
---
## 구현 항목별 완료 여부
| 항목 | 완료 여부 |
|------|---------|
| [API-1] Make CLI Session Keys Workspace-Aware | [ ] |
| [API-2] Preserve Clear Terminate And Session List Semantics | [ ] |
## 구현 체크리스트
- [ ] predecessor `03+02_cli_agent_cwd``complete.log`가 active 또는 archive 위치에 있는지 확인한다.
- [ ] `sessionKey`가 workspace를 포함하도록 확장하고, empty workspace는 기존 no-workspace key와 label을 유지한다.
- [ ] persistent, codex-exec, codex app-server, antigravity-print, opencode-sse executors가 workspace-aware key를 사용한다.
- [ ] session list response가 workspace별 session을 구분할 수 있도록 structured `session.N.workspace`를 채우고, label 충돌을 피한다.
- [ ] `TerminateSession(target, sessionID)`가 workspace-aware key 도입 후에도 기존 command로 명확히 동작하도록 처리한다. workspace가 없는 command에서는 같은 target/session의 workspace variants를 모두 종료하거나, 구현에서 더 안전한 명시 오류를 선택하고 테스트로 고정한다.
- [ ] 같은 target/session이 workspace별로 분리되는 테스트와 기존 no-workspace 동작 회귀 테스트를 추가한다.
- [ ] `go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 코드리뷰 전용 체크리스트
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
- [ ] `코드리뷰 결과``PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [ ] active `CODE_REVIEW-*-G??.md``code_review_local_G08_N.log`로 아카이브한다.
- [ ] active `PLAN-*-G??.md``plan_local_G08_M.log`로 아카이브한다.
- [ ] `.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-openai-workspace-agent-execution-contract/04+03_session_workspace_isolation/``agent-task/archive/YYYY/MM/m-openai-workspace-agent-execution-contract/04+03_session_workspace_isolation/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-openai-workspace-agent-execution-contract/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G08.md``CODE_REVIEW-local-G08.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로 이동한다.
## 계획 대비 변경 사항
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
## 주요 설계 결정
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
## 사용자 리뷰 요청
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음
## 리뷰어를 위한 체크포인트
- 같은 target/sessionID의 다른 workspace가 같은 CLI process/thread/server/conversation을 재사용하지 않는지 확인한다.
- no-workspace 기존 session behavior와 labels가 깨지지 않았는지 확인한다.
- terminate-session command의 workspace-aware semantics가 테스트와 일치하는지 확인한다.
## 검증 결과
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
### API-1 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/cli
(output)
```
### API-2 중간 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node
(output)
```
### 최종 검증
```bash
$ go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node
(output)
$ ./scripts/e2e-smoke.sh
(output)
```
---
> **[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. |

View file

@ -0,0 +1,187 @@
<!-- task=m-openai-workspace-agent-execution-contract/04+03_session_workspace_isolation plan=0 tag=API -->
# Plan - API
## 이 파일을 읽는 구현 에이전트에게
이 plan은 split dependency가 있다. 구현을 시작하기 전에 같은 task group의 predecessor `03` complete.log가 active 또는 archive 위치에 있는지 확인한다. 구현은 코드 변경, 검증 실행, `CODE_REVIEW-local-G08.md`의 구현 에이전트 소유 섹션 작성까지다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌로 막히면 사용자에게 직접 묻지 말고 review stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 채운다.
## 배경
CLI logical sessions are currently keyed by `(target, sessionID)`. After workspace cwd support, reusing the same session for a different workspace could silently run in the original checkout. This work makes workspace a logical session boundary while keeping existing no-workspace sessions compatible.
## 사용자 리뷰 요청 흐름
구현 중 사용자 결정이 필요하면 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 active `CODE_REVIEW-local-G08.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:
- `session-workspace`: logical session이 다른 workspace를 같은 target/session으로 재사용하지 않는다.
- 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/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/node-smoke.md`
- `apps/node/internal/adapters/cli/cli.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_internal_test.go`
- `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`
- `apps/node/internal/adapters/cli/codex_app_server_internal_test.go`
- `apps/node/internal/runtime/types.go`
- `apps/node/internal/node/node.go`
### 테스트 환경 규칙
`test_env=local`을 선택했다. `agent-test/local/rules.md`를 읽었고 node 변경 기준 `agent-test/local/node-smoke.md`를 읽었다. 적용 명령은 CLI adapter focused tests와 node package tests다. 구조적 blank나 `<확인 필요>` 값은 없었다.
### 테스트 커버리지 공백
현재 tests는 같은 target에서 session id가 다르면 분리되는지 검증하지만, 같은 target/session id에서 workspace가 다를 때 분리되는지는 검증하지 않는다. `handleSessionList`도 workspace를 표시하지 않아 workspace별 session이 생기면 human-readable labels가 충돌할 수 있다.
### 심볼 참조
renamed/removed symbols: none.
### 분할 판단
split decision policy를 평가했다. shared task group은 `agent-task/m-openai-workspace-agent-execution-contract`다. 이 subtask는 `04+03_session_workspace_isolation`이며 predecessor `03`은 아직 active plan 상태라 `complete.log`가 없다. 구현 에이전트는 `agent-task/m-openai-workspace-agent-execution-contract/03+02_cli_agent_cwd/complete.log` 또는 matching archive complete.log가 생긴 뒤 시작해야 한다.
### 범위 결정 근거
이 작업은 CLI adapter logical session key와 list/terminate behavior에 한정한다. process cwd 적용은 predecessor `03+02_cli_agent_cwd` 범위다. Edge/OpenAI workspace validation, Node router workspace propagation, actual authoring smoke는 이 plan에서 변경하지 않는다.
### 빌드 등급
build lane은 `local-G08`이다. 여러 CLI session reporter/executor map key와 command semantics에 닿지만, 변경은 한 package 안에 있고 deterministic unit/blackbox tests로 검증 가능하다.
## 구현 체크리스트
- [ ] predecessor `03+02_cli_agent_cwd``complete.log`가 active 또는 archive 위치에 있는지 확인한다.
- [ ] `sessionKey`가 workspace를 포함하도록 확장하고, empty workspace는 기존 no-workspace key와 label을 유지한다.
- [ ] persistent, codex-exec, codex app-server, antigravity-print, opencode-sse executors가 workspace-aware key를 사용한다.
- [ ] session list response가 workspace별 session을 구분할 수 있도록 structured `session.N.workspace`를 채우고, label 충돌을 피한다.
- [ ] `TerminateSession(target, sessionID)`가 workspace-aware key 도입 후에도 기존 command로 명확히 동작하도록 처리한다. workspace가 없는 command에서는 같은 target/session의 workspace variants를 모두 종료하거나, 구현에서 더 안전한 명시 오류를 선택하고 테스트로 고정한다.
- [ ] 같은 target/session이 workspace별로 분리되는 테스트와 기존 no-workspace 동작 회귀 테스트를 추가한다.
- [ ] `go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 의존 관계 및 구현 순서
이 directory 이름 `04+03_session_workspace_isolation`의 predecessor는 `03`이다. 구현 전 `03+02_cli_agent_cwd``complete.log`를 확인한다. 없으면 구현하지 말고 계획 미충족 상태로 보고한다.
### [API-1] Make CLI Session Keys Workspace-Aware
문제: `apps/node/internal/adapters/cli/cli.go:45``sessionKey``target``sessionID`만 포함한다. `persistent.go:314`, `codex_exec.go:49`, `codex_app_server.go:301`, `antigravity_print.go:63`, `opencode_sse.go:183` 모두 이 key를 사용하므로 같은 target/session의 두 workspace 요청이 같은 process/thread/server/conversation을 재사용할 수 있다.
해결 방법: `sessionKey`에 normalized workspace를 추가하고 helper로 생성한다. workspace가 비어 있으면 기존 label과 behavior를 유지한다. workspace가 있으면 key가 분리되며, session list에는 workspace를 structured field로 노출한다.
Before:
```go
// apps/node/internal/adapters/cli/cli.go:45
type sessionKey struct {
target string
sessionID string
}
```
After:
```go
type sessionKey struct {
target string
sessionID string
workspace string
}
```
수정 파일 및 체크리스트:
- [ ] `apps/node/internal/adapters/cli/cli.go`: `sessionKey`, `sessionListEntry`, label/result 생성 확장.
- [ ] `apps/node/internal/adapters/cli/persistent.go`: `resolveSession`, `Sessions`, `Terminate` workspace-aware key 적용.
- [ ] `apps/node/internal/adapters/cli/codex_exec.go`: session map key와 list/terminate 적용.
- [ ] `apps/node/internal/adapters/cli/codex_app_server.go`: session map key와 list/terminate 적용.
- [ ] `apps/node/internal/adapters/cli/antigravity_print.go`: session map key와 list/terminate 적용.
- [ ] `apps/node/internal/adapters/cli/opencode_sse.go`: session map key와 list/terminate 적용.
테스트 작성: 작성한다. `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`에 같은 target/session이 다른 workspace에서 별도 persistent session을 만드는 테스트를 추가한다. `cli_internal_test.go`와 codex/opencode 관련 internal tests의 sessionKey literals를 업데이트하고 workspace field assertions를 추가한다.
중간 검증:
```bash
go test -count=1 ./apps/node/internal/adapters/cli
```
기대 결과: CLI adapter package PASS.
### [API-2] Preserve Clear Terminate And Session List Semantics
문제: `apps/node/internal/adapters/cli/cli.go:400``TerminateSession` command는 workspace를 받지 않는다. workspace-aware key 도입 뒤 기존 command가 어떤 session을 종료하는지 불명확하면 운영 UX가 깨진다.
해결 방법: workspace가 없는 command contract를 명확히 고정한다. 기본 권장 구현은 같은 target/sessionID의 모든 workspace variants를 종료하는 것이다. 더 안전한 명시 오류를 택한다면 `계획 대비 변경 사항`에 이유를 남기고 tests로 고정한다. `handleSessionList``session.N.workspace`를 포함해 사용자가 variants를 구분할 수 있어야 한다.
Before:
```go
// apps/node/internal/adapters/cli/cli.go:408
terminated, err := reporter.Terminate(ctx, target, sessionID)
```
After:
```go
// reporter.Terminate must handle workspace-aware variants for the target/sessionID command.
terminated, err := reporter.Terminate(ctx, target, sessionID)
```
수정 파일 및 체크리스트:
- [ ] `apps/node/internal/adapters/cli/cli.go`: session list result에 workspace field 추가.
- [ ] `apps/node/internal/adapters/cli/*`: `Terminate` implementations가 workspace-aware map에서 명확히 동작하도록 보정.
- [ ] `apps/node/internal/adapters/cli/cli_internal_test.go`: list result와 terminate semantics regression 추가.
테스트 작성: 작성한다. 같은 target/sessionID에 workspace 두 개가 있을 때 session list count/fields가 구분되고 terminate command가 계획된 contract대로 동작함을 검증한다.
중간 검증:
```bash
go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node
```
기대 결과: 두 패키지 모두 PASS.
## 수정 파일 요약
| 파일 | 항목 |
|------|------|
| `apps/node/internal/adapters/cli/cli.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/persistent.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/codex_exec.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/codex_app_server.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/antigravity_print.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/opencode_sse.go` | API-1, API-2 |
| `apps/node/internal/adapters/cli/*_test.go` | API-1, API-2 |
## 최종 검증
```bash
go test -count=1 ./apps/node/internal/adapters/cli ./apps/node/internal/node
./scripts/e2e-smoke.sh
```
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.

View file

@ -23,14 +23,16 @@ import (
type countingAdapter struct { type countingAdapter struct {
executeCalls int32 executeCalls int32
lastSpec runtime.ExecutionSpec
} }
func (a *countingAdapter) Name() string { return "test" } func (a *countingAdapter) Name() string { return "test" }
func (a *countingAdapter) Capabilities(_ context.Context) (runtime.Capabilities, error) { func (a *countingAdapter) Capabilities(_ context.Context) (runtime.Capabilities, error) {
return runtime.Capabilities{AdapterName: "test", Targets: []string{"v1"}, MaxConcurrency: 1}, nil return runtime.Capabilities{AdapterName: "test", Targets: []string{"v1"}, MaxConcurrency: 1}, nil
} }
func (a *countingAdapter) Execute(_ context.Context, _ runtime.ExecutionSpec, _ runtime.EventSink) error { func (a *countingAdapter) Execute(_ context.Context, spec runtime.ExecutionSpec, _ runtime.EventSink) error {
atomic.AddInt32(&a.executeCalls, 1) atomic.AddInt32(&a.executeCalls, 1)
a.lastSpec = spec
return nil return nil
} }
@ -254,9 +256,10 @@ func TestOnRunRequest_Success(t *testing.T) {
n, st := makeNode(t, router) n, st := makeNode(t, router)
err := n.OnRunRequest(context.Background(), &transport.Session{}, &iop.RunRequest{ err := n.OnRunRequest(context.Background(), &transport.Session{}, &iop.RunRequest{
RunId: "run-1", RunId: "run-1",
Adapter: "test", Adapter: "test",
Target: "v1", Target: "v1",
Workspace: "/config/workspace/iop",
}) })
if err != nil { if err != nil {
t.Fatalf("run request: %v", err) t.Fatalf("run request: %v", err)
@ -264,6 +267,9 @@ func TestOnRunRequest_Success(t *testing.T) {
if got := atomic.LoadInt32(&adapter.executeCalls); got != 1 { if got := atomic.LoadInt32(&adapter.executeCalls); got != 1 {
t.Fatalf("expected 1 execute call, got %d", got) t.Fatalf("expected 1 execute call, got %d", got)
} }
if adapter.lastSpec.Workspace != "/config/workspace/iop" {
t.Fatalf("adapter workspace: got %q", adapter.lastSpec.Workspace)
}
run, err := st.GetRun(context.Background(), "run-1") run, err := st.GetRun(context.Background(), "run-1")
if err != nil { if err != nil {

View file

@ -32,6 +32,7 @@ func TestResolve_PreservesSessionFields(t *testing.T) {
SessionID: "session-a", SessionID: "session-a",
SessionMode: runtime.SessionModeRequireExisting, SessionMode: runtime.SessionModeRequireExisting,
Background: true, Background: true,
Workspace: "/config/workspace/iop",
} }
spec, err := r.Resolve(context.Background(), req) spec, err := r.Resolve(context.Background(), req)
@ -47,6 +48,9 @@ func TestResolve_PreservesSessionFields(t *testing.T) {
if spec.Background != req.Background { if spec.Background != req.Background {
t.Errorf("Background: got %v want %v", spec.Background, req.Background) t.Errorf("Background: got %v want %v", spec.Background, req.Background)
} }
if spec.Workspace != req.Workspace {
t.Errorf("Workspace: got %q want %q", spec.Workspace, req.Workspace)
}
} }
func TestResolveAdapter_Found(t *testing.T) { func TestResolveAdapter_Found(t *testing.T) {