<!-- task=m-workspace-port-env-standardization/03+01,02_remote_smoke_evidence plan=0 tag=TEST -->

# Code Review Reference - TEST

> **[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.

## 개요

date=2026-06-07
task=m-workspace-port-env-standardization/03+01,02_remote_smoke_evidence, plan=0, tag=TEST

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/workspace-port-env-standardization.md`
- Task ids:
  - `port-default-rollout`: `.env.example`, `docker-compose.yml`, config/default, app README, `agent-test/local`의 포트 기본값과 검증 명령을 표준 후보에 맞춰 정렬한다.
- Completion mode: check-on-pass

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

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

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
4. PASS이고 task group이 `m-workspace-port-env-standardization`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정이나 `update-roadmap` 직접 호출은 하지 않는다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [TEST-1] Confirm predecessor completion and local regression | [x] |
| [TEST-2] Verify remote compose slots | [ ] blocked |
| [TEST-3] Verify wire, client, and Edge input smoke | [x] |

## 구현 체크리스트

- [x] predecessor `01_compose_env_defaults`와 `02+01_runtime_config_docs`의 `complete.log`를 확인한다.
- [x] local quick regression을 실행하고 결과를 기록한다.
- [ ] 원격 runner/code-server에서 compose render와 compose service health를 새 host slot으로 확인한다. Render는 통과했으나 full compose는 web image build blocker, web 제외 service health는 `19080` port collision으로 차단됨.
- [x] Control Plane-Edge wire, edge-node smoke, OpenAI-compatible smoke 중 변경 범위에 맞는 smoke를 실행하고 pass/blocker를 분리해 기록한다.
- [x] 검증 결과에 host/token/credential 원문이 남지 않도록 출력 sanitization 여부를 확인한다.
- [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_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 task artifact를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-workspace-port-env-standardization`이면 Roadmap Completion metadata를 보고한다.

## 계획 대비 변경 사항

- 원격 runner 기본 checkout은 local HEAD와 달랐고 dirty 상태였으므로 사용하지 않았다. remote repo에서 `git fetch origin main` 후 local HEAD와 같은 commit `dd488f1e566899881fca7371ca71d417f90a34ed`로 별도 임시 worktree를 만들었다.
- `docker-compose.yml`의 build context가 parent directory의 `iop/...` 경로를 전제로 하므로, 임시 parent 아래 `iop` 이름으로 worktree를 배치했다.
- 원격 runner에 `rg`가 없어 compose render slot 확인은 `grep -E` fallback으로 수행했다.
- 원격 shell의 `IOP_EDGE_NODE_TOKEN`은 unset이었다. Compose render/build isolation에는 secret이 아닌 임시 smoke token을 주입했으며, review output에는 token 원문을 기록하지 않았다.
- 기존 remote Docker project를 건드리지 않기 위해 Compose project name은 `iop-g07-port-smoke`로 분리했다. host publish port 값은 `.env.example` 표준 slot 그대로 검증했다.
- full compose가 web image build blocker로 실패한 뒤, 원인 분리를 위해 `postgres redis control-plane edge`만 추가 실행했다. 이 추가 실행도 host `19080` collision으로 Control Plane publish가 실패했다.
- 검증 후 임시 remote worktree와 Compose project는 정리했다. Remote 기본 checkout은 수정하지 않았다.

## 주요 설계 결정

- Source 수정 없음. 이 split은 검증/evidence 기록 전용으로 유지했다.
- remote evidence는 dirty 기본 checkout이 아니라 isolated worktree 기준으로 수집했다.
- host/token/credential 원문은 검증 결과에 기록하지 않았다. Remote endpoint는 `<remote-runner>`로 표기하고, node token은 `<set>` 또는 `<sanitized-test-token>`으로만 표기했다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다._

- 상태: 요청
- 사유 유형: 사용자 소유 외부 환경 전제 + 범위 충돌 가능성
- 결정 필요:
  - 원격 runner shell에 실제 `IOP_EDGE_NODE_TOKEN`을 설정하고 표준 host slot `13000`, `19080`을 비울지, 또는 이번 smoke에서 임시 override port 사용을 허용할지 결정이 필요하다.
  - `apps/client/Dockerfile`이 `proto-socket/dart/pubspec.lock`을 필수로 `COPY`하지만 remote sibling `proto-socket/dart/`에 해당 lock file이 없어 web image build가 실패한다. 이 build contract를 이번 task에서 수정할지 별도 follow-up으로 보낼지 판단이 필요하다.
- 차단 근거:
  - `test -n "${IOP_EDGE_NODE_TOKEN:?set IOP_EDGE_NODE_TOKEN in the remote shell}"` failed with `IOP_EDGE_NODE_TOKEN: set IOP_EDGE_NODE_TOKEN in the remote shell`.
  - `docker compose --env-file .env.example ... up --build -d postgres redis control-plane edge web` failed while building `web`: `"/proto-socket/dart/pubspec.lock": not found`.
  - `docker compose --env-file .env.example ... up --build -d postgres redis control-plane edge` built Go services but failed to start Control Plane because `0.0.0.0:19080` was already allocated. Remote `lsof` showed Docker listening on `19080`; Docker also occupied `13000`.
- 실행한 검증/명령:
  - predecessor complete log checks: PASS
  - `go test ./...`: PASS
  - remote compose render: PASS with `grep -E` fallback; standard slots `18000`, `13000`, `15400`, `16300`, `19080`, `19081`, `19090` present
  - remote full compose up with `web`: BLOCKED by `proto-socket/dart/pubspec.lock` missing
  - remote compose up without `web`: BLOCKED by `19080` port collision after `postgres`/`redis` became healthy
  - `make test-control-plane-edge-wire`: PASS
  - remote `cd apps/client && flutter test`: PASS, 27 tests
  - remote `make client-build-web`: PASS
  - `./scripts/e2e-smoke.sh`: PASS
  - `./scripts/e2e-openai-ollama.sh`: PASS
- 자동 후속 불가 이유: remote service stop/restart or port freeing is user-owned environment control, and changing Dockerfile/proto-socket lock behavior exceeds this validation-only split without review direction.
- 재개 조건: remote token configured, standard ports freed or override approved, and web Docker build contract fixed or explicitly excluded from this validation.

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

- predecessor `01`, `02` 완료 근거가 확인되었는지 확인한다.
- remote compose output에서 token/host secret이 노출되지 않았는지 확인한다.
- OpenAI smoke 실패가 port regression인지 외부 provider/environment blocker인지 분리되었는지 확인한다.
- PASS이면 `Roadmap Targets`의 `port-default-rollout`만 completion 대상으로 보고한다.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

### TEST-1 중간 검증

```bash
$ test -f agent-task/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log || find agent-task/archive -path '*/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log' -print -quit
agent-task/archive/2026/06/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log

$ test -f agent-task/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log || find agent-task/archive -path '*/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log' -print -quit
agent-task/archive/2026/06/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log

$ go test ./...
ok  	iop/apps/control-plane/cmd/control-plane	(cached)
ok  	iop/apps/control-plane/internal/wire	(cached)
ok  	iop/apps/edge/...	(cached)
ok  	iop/apps/node/internal/adapters/cli	42.319s
ok  	iop/apps/node/...	(cached)
ok  	iop/packages/go/...	(cached)
?   	iop/proto/gen/iop	[no test files]
```

### TEST-2 중간 검증

```bash
$ test -n "${IOP_EDGE_NODE_TOKEN:?set IOP_EDGE_NODE_TOKEN in the remote shell}"
zsh:2: IOP_EDGE_NODE_TOKEN: set IOP_EDGE_NODE_TOKEN in the remote shell

$ docker compose --env-file .env.example config >/tmp/iop-compose-config.out
(remote isolated worktree, token value sanitized) exit=0

$ rg --sort path '18000|13000|15400|16300|19080|19081|19090' /tmp/iop-compose-config.out
(remote `rg` missing; used `grep -E` fallback)
        - http://127.0.0.1:18000/healthz
        target: 18000
        published: "18000"
        target: 19080
        published: "19080"
        target: 19081
        published: "19081"
      IOP_CONTROL_PLANE_EDGE_WIRE_ADDR: control-plane:19081
        published: "19090"
        published: "15400"
        published: "16300"
        IOP_CONTROL_PLANE_HTTP_URL: http://localhost:18000
        IOP_CONTROL_PLANE_WIRE_URL: ws://localhost:19080/client
        published: "13000"

$ docker compose --env-file .env.example up --build -d postgres redis control-plane edge web
BLOCKED. Full compose build failed in `web` image:
COPY proto-socket/dart/pubspec.yaml proto-socket/dart/pubspec.lock /workspace/proto-socket/dart/
failed to calculate checksum ... "/proto-socket/dart/pubspec.lock": not found

Additional isolation run without `web`:
postgres and redis became healthy, control-plane/edge images built, then Control Plane failed to start:
Bind for 0.0.0.0:19080 failed: port is already allocated

$ curl -fsS http://127.0.0.1:18000/healthz
curl: (7) Failed to connect to 127.0.0.1 port 18000 after 0 ms: Couldn't connect to server

$ curl -fsS http://127.0.0.1:18000/readyz
curl: (7) Failed to connect to 127.0.0.1 port 18000 after 0 ms: Couldn't connect to server

$ docker compose ps
NAME                            SERVICE    STATUS                    PORTS
iop-g07-port-smoke-postgres-1   postgres   Up 10 seconds (healthy)   127.0.0.1:15400->5432/tcp
iop-g07-port-smoke-redis-1      redis      Up 10 seconds (healthy)   127.0.0.1:16300->6379/tcp

Remote port owner check:
TCP *:19080 (LISTEN) by Docker
TCP *:13000 (LISTEN) by Docker

$ docker compose down
Removed `iop-g07-port-smoke` containers/network. `docker ps -a --filter name=iop-g07-port-smoke` returned no containers.
```

### TEST-3 중간 검증

```bash
$ make test-control-plane-edge-wire
[cp-edge-wire] CP: hello accepted
[cp-edge-wire] Edge: connected to control plane
[cp-edge-wire] CP: edge disconnected
[cp-edge-wire] Control Plane-Edge wire smoke PASSED.

$ cd apps/client && flutter test
remote isolated worktree:
00:03 +27: All tests passed!

$ make client-build-web
remote isolated worktree:
flutter build web --dart-define=IOP_CONTROL_PLANE_HTTP_URL=http://localhost:18000 --dart-define=IOP_CONTROL_PLANE_WIRE_URL=ws://localhost:19080/client
Compiling lib/main.dart for the Web... 24.7s
✓ Built build/web

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

$ ./scripts/e2e-openai-ollama.sh
[openai-ollama] OpenAI-compatible Ollama serving test PASSED.
```

### 최종 검증

```bash
$ test -f agent-task/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log || find agent-task/archive -path '*/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log' -print -quit
agent-task/archive/2026/06/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log

$ test -f agent-task/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log || find agent-task/archive -path '*/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log' -print -quit
agent-task/archive/2026/06/m-workspace-port-env-standardization/02+01_runtime_config_docs/complete.log

$ go test ./...
PASS. Same run as TEST-1; all Go packages completed successfully.

$ test -n "${IOP_EDGE_NODE_TOKEN:?set IOP_EDGE_NODE_TOKEN in the remote shell}"
BLOCKED. Remote shell token is unset.

$ docker compose --env-file .env.example config >/tmp/iop-compose-config.out
PASS in remote isolated worktree with sanitized temporary token.

$ rg --sort path '18000|13000|15400|16300|19080|19081|19090' /tmp/iop-compose-config.out
BLOCKED for exact command because remote `rg` is unavailable. `grep -E` fallback confirmed all listed slots.

$ docker compose --env-file .env.example up --build -d postgres redis control-plane edge web
BLOCKED. `web` image build failed because `proto-socket/dart/pubspec.lock` is missing.

$ curl -fsS http://127.0.0.1:18000/healthz
BLOCKED. Control Plane did not start due `19080` host port collision.

$ curl -fsS http://127.0.0.1:18000/readyz
BLOCKED. Control Plane did not start due `19080` host port collision.

$ docker compose down
PASS cleanup. No `iop-g07-port-smoke` containers remained.

$ make test-control-plane-edge-wire
PASS.

$ cd apps/client && flutter test
PASS, 27 tests.

$ make client-build-web
PASS, `✓ Built build/web`.

$ ./scripts/e2e-smoke.sh
PASS, auxiliary mock smoke.

$ ./scripts/e2e-openai-ollama.sh
PASS.
```

Overall final verification status: BLOCKED on TEST-2 remote compose service health. Passing checks are sufficient to show Go/client/wire/e2e smoke did not regress, but not sufficient to mark `port-default-rollout` complete because standard-slot Compose health could not be proven.

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - plan deviation: Fail
  - verification trust: Fail
- 발견된 문제:
  - Required: `apps/client/Dockerfile:14`가 sibling `proto-socket/dart/pubspec.lock`을 필수로 `COPY`하지만 해당 파일은 sibling repo에서 추적되지 않고 `.gitignore`로 무시된다(`../proto-socket/.gitignore:5`). clean/isolated runner에서는 review evidence처럼 web image build가 `"/proto-socket/dart/pubspec.lock": not found`로 실패하므로 compose web build 계약이 재현 가능하지 않다. 후속에서 Dockerfile이 ignored lock file에 의존하지 않도록 고치거나, 의도된 lock artifact를 추적 가능한 계약으로 만들고 clean remote context에서 `docker compose --env-file .env.example up --build -d postgres redis control-plane edge web`를 재검증한다.
  - Required: `agent-task/m-workspace-port-env-standardization/03+01,02_remote_smoke_evidence/CODE_REVIEW-cloud-G07.md:48`의 핵심 구현 체크리스트인 원격 compose service health가 미완료이고, 최종 검증도 `/healthz`와 `/readyz`를 `19080` host port collision 때문에 확인하지 못했다(`agent-task/m-workspace-port-env-standardization/03+01,02_remote_smoke_evidence/CODE_REVIEW-cloud-G07.md:241`). 이 task는 `port-default-rollout` completion evidence를 내는 split이므로 standard-slot compose health를 실제로 통과시키거나, repo-owned 이슈를 해결한 뒤에도 user-owned 포트 점유가 남는다는 evidence로 사용자 리뷰 stop을 다시 기록해야 한다.
- 다음 단계: WARN/FAIL 후속 plan/review를 작성한다.
