# Milestone: 워크스페이스 포트/환경 표준화

## 위치

- Roadmap: [ROADMAP.md](../../../../ROADMAP.md)
- Phase: [PHASE.md](../../../../phase/automation-runtime-bridge/PHASE.md)

## 목표

IOP의 Control Plane, Edge, Node, Client, OpenAI-compatible/A2A surface, native wire, metrics, DB/cache host publish 포트를 workspace 공통 대역으로 정렬한다. 기존 remote/field 포트와 `docker-compose.yml`, `.env.example`, `agent-test/local` 문서가 서로 다른 기본값을 말하지 않도록 source of truth를 맞춘다.

## 상태

[완료]

## 구현 잠금

- 상태: 해제
- 결정 필요: 없음

## 범위

- Client web/dev preview는 frontend 대역 후보로 정리
- Control Plane HTTP, Edge OpenAI-compatible, Edge A2A, bootstrap/artifact HTTP는 backend 또는 artifact/model HTTP 대역으로 분리
- Control Plane wire, Edge-Node wire, Edge runtime TCP는 wire/agent transport 대역으로 유지하되 충돌 여부를 점검
- metrics/diagnostics와 DB/cache host publish를 공통 대역 또는 compose-internal-only 기준으로 정리
- `.env.example`, `docker-compose.yml`, `agent-test/local`의 기본값 불일치를 해소하기 위한 migration checklist 작성

## 기능

### Epic: [inventory] IOP environment inventory

IOP runtime surface별 포트와 원격 테스트 환경을 한 번에 읽을 수 있게 정리한다.

- [x] [compose-env-map] `docker-compose.yml`, `.env.example`, config 파일의 host publish와 내부 listen 포트를 표로 대조한다.
- [x] [remote-env-map] `agent-test/local`의 remote runner, artifact/base URL, Edge runtime, OpenAI-compatible, CP wire, metrics 포트 설명을 raw secret 없이 정리한다.
- [x] [slot-plan] IOP 기본 후보를 frontend `13000`, backend `18000`, artifact/model `18080/18081`, wire `19080/19081/19090`, metrics `19092`, DB/cache `15400/16300`으로 문서화한다.
- [x] [compat-checklist] 기존 field 포트 `18080/18081/19080/19090`을 즉시 바꿀지 유지할지 결정하기 전에 필요한 smoke, client config, remote runner 확인 항목을 남긴다.

### Epic: [rollout] IOP port rollout

Inventory에서 확인된 drift를 실제 기본값, 문서, smoke evidence로 정리한다.

- [x] [port-default-rollout] `.env.example`, `docker-compose.yml`, config/default, app README, `agent-test/local`의 포트 기본값과 검증 명령을 표준 후보에 맞춰 정렬한다.

## 작업 산출물

### Source inventory (2026-06-07)

| Surface | 현재 source | 내부 listen/container | host publish 또는 외부 슬롯 | 표준 후보 | 상태 |
|---------|-------------|-----------------------|-----------------------------|-----------|------|
| Client web/dev preview | `docker-compose.yml`, `.env.example`, `scripts/dev/web.sh` | container/nginx `3000` | compose/script/env `13000` | frontend `13000` | `01`에서 compose/env 정렬 |
| Control Plane HTTP | `configs/control-plane.yaml`, `apps/control-plane/cmd/control-plane/main.go`, `docker-compose.yml` | `9080` | compose/env `18000`, app/config direct default `9080` | backend `18000` | `01`/`02`에서 compose/env/runtime/config/docs 정렬 |
| Control Plane Client WS wire | `configs/control-plane.yaml`, client defaults, compose | `19080` | `19080` | wire `19080` | 일치 |
| Control Plane-Edge TCP wire | `configs/control-plane.yaml`, compose | `19081` | `19081` | wire `19081` | 일치 |
| Edge-Node TCP transport | `configs/edge.yaml`, compose, node config | Edge internal `9090`, node local `localhost:9090` | compose/env/field `19090` | wire `19090` | host slot 일치, internal 유지; `04`에서 code-server publish cleanup 확인 |
| Edge artifact/bootstrap HTTP | `configs/edge*.yaml*`, host setup, bootstrap pack | `18080` | field `18080` | artifact `18080` | 일치 |
| Edge OpenAI-compatible HTTP | `configs/edge.yaml`, edge command/docs | internal/default `8080` | field/model endpoint `18081` | model `18081` | `02`에서 field/model slot `18081`과 docs 정렬, internal/default 분리 유지 |
| Edge A2A HTTP | `configs/edge.yaml`, edge input server | internal/default `8081` | 명시 host publish 없음 | backend/model 후보 검토 | rollout 결정 필요 |
| Metrics | `configs/*.yaml`, host setup, local rules | node `9091`, edge `9092`, CP `9093`, worker `9094` | Edge metrics field `19092` | metrics `19092` | `02`에서 Edge metrics field `19092` 정렬, `04`에서 code-server publish cleanup 확인 |
| DB/cache | `docker-compose.yml`, `.env.example` | Postgres `5432`, Redis `6379` | compose/env host `15400/16300` | DB/cache `15400/16300` | `01`에서 compose/env 정렬 |

### Remote environment map

- remote runner 값은 tracked roadmap에 host, token, credential 원문을 남기지 않는다.
- `agent-test/local` 기준 공개 가능한 슬롯은 web/dev preview `13000-13099`, artifact/bootstrap HTTP `18080`, OpenAI-compatible HTTP `18081`, CP Client wire `19080`, CP-Edge wire `19081`, Edge-Node transport `19090`, Edge metrics `19092`, domain agent reserve `19190`이다.
- Docker compose, Flutter client runtime evidence, field/bootstrap, 외부 runtime evidence는 현재 컨테이너가 아니라 원격 runner/code-server에서 확인한다.
- 원격 runner 검증은 local uncommitted 변경이 동기화되지 않으면 현재 변경분 검증으로 보지 않는다.

### Slot plan

| 대역 | 후보 |
|------|------|
| frontend | `13000-13099` |
| backend | `18000` |
| artifact/model HTTP | `18080`, `18081` |
| wire/agent transport | `19080`, `19081`, `19090` |
| metrics | `19092` |
| DB/cache host publish | `15400`, `16300` |

### Compatibility checklist

- Smoke: `make test-control-plane-edge-wire`, `./scripts/e2e-smoke.sh`, `./scripts/e2e-openai-ollama.sh`가 새 host slot 또는 명시 override에서 통과해야 한다.
- Client config: `apps/client/lib/client_config.dart`, `apps/client/Dockerfile`, [README.md](../../../../../apps/client/README.md), `scripts/dev/web.sh`, `Makefile client-build-web`, compose build args의 HTTP/WS URL 기본값을 같은 기준으로 맞춘다.
- Remote runner: Docker compose 검증은 원격 runner/code-server에서 수행하고, 포트 `13000`, `18000`, `18080`, `18081`, `19080`, `19081`, `19090`, `19092`, `15400`, `16300` 충돌 여부를 확인한다.
- Field compatibility: 기존 field 슬롯 `18080`, `18081`, `19080`, `19090`은 smoke evidence가 확보될 때까지 유지하거나 명시 migration note를 남긴다.
- Secret hygiene: host, token, credential 원문은 tracked docs/roadmap/plan에 기록하지 않는다.

### 현 작업 현황

- [x] `agent-task/archive/2026/06/m-workspace-port-env-standardization/01_compose_env_defaults/` — PASS. `.env.example`과 `docker-compose.yml`의 compose/env host publish defaults를 정렬했다.
- [x] `agent-task/archive/2026/06/m-workspace-port-env-standardization/02+01_runtime_config_docs/` — PASS. runtime/config/docs/test-rule defaults를 `18000`, `18081`, `19092` 기준으로 정렬했고 regression assertions를 보강했다.
- [x] `agent-task/archive/2026/06/m-workspace-port-env-standardization/03+01,02_remote_smoke_evidence/` — PASS. `Roadmap Completion`에서 `port-default-rollout` PASS를 선언했고, Go/Flutter/client web build 및 remote code-server compose config render 증거를 남겼다.
- [x] `agent-task/archive/2026/06/m-workspace-port-env-standardization/04+03_standard_slot_runtime_check/` — PASS. 사용자 결정에 따라 `code-server` 재시작/재생성 없이 remote compose config에서 `19090`, `19092`, `19190` publish 제거를 확인했고, container/host 접근 확인은 `192.168.0.97` 기준으로 기록했다.
- [ ] 후속 운영 확인 — 사용자 관리 `code-server` recreate 이후 standard-slot full runtime compose smoke를 다시 실행할 수 있다. 현재 마일스톤 완료 판정의 필수 조건으로는 남기지 않는다.

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-06-08
- 완료 근거:
  - Inventory 기능 task 4개는 이미 완료 상태이며, rollout 기능 task `port-default-rollout`은 `03+01,02_remote_smoke_evidence/complete.log`의 `Roadmap Completion`에서 PASS로 선언됐다.
  - `01`/`02`에서 compose/env/runtime/config/docs/test-rule defaults를 표준 후보에 맞춰 정렬했고, `go test`, `flutter test`, `make client-build-web` 검증 증거가 archive에 있다.
  - `04`에서 사용자 금지 조건을 지켜 `code-server` stop/start/restart/recreate 없이 remote compose config만 수정했고, `19090`, `19092`, `19190` publish 제거와 `192.168.0.97` 기준 closed 확인을 기록했다.
- 리뷰 필요:
- [x] 사용자가 완료 결과를 확인했다
- [x] archive 이동을 승인했다
- 리뷰 코멘트: 사용자가 완료 결과를 확인한 뒤 2026-07-20 현재 완료 상태로 활성 경로에 남은 항목의 archive 이동을 요청했다.

## 범위 제외

- 원격 field runtime 포트 즉시 변경
- 모델 provider, routing, MCP/tool policy, RAG, output validation 구현
- remote terminal bridge 기능 구현
- private token, credential, host secret 기록

## 작업 컨텍스트

- 관련 경로: `docker-compose.yml`, `.env.example`, `agent-test/local/`, `apps/client/`, `apps/control-plane/`, `apps/edge/`, `apps/node/`, `proto/`
- 표준선(선택): container 내부 listen 포트는 runtime 기본값을 유지하고, 표준화 대상은 host publish, 문서화된 remote endpoint, smoke command다.
- 선행 작업: workspace 공통 포트 inventory 정리
- 후속 작업: 사용자 관리 `code-server` recreate 이후 standard-slot full runtime compose smoke 재실행
- 확인 필요: 없음
