<!-- task=m-workspace-port-env-standardization/02+01_runtime_config_docs plan=0 tag=REFACTOR -->

# Plan - REFACTOR

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

`CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 채우기 전까지 구현은 완료가 아니다. 먼저 같은 task group의 `01_compose_env_defaults`가 PASS되어 `complete.log`를 남겼는지 확인한다. 검증 출력, 계획 대비 변경, 설계 결정을 review stub에 기록하고 active 파일을 유지한 채 리뷰 준비 상태로 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다.

## 배경

compose/env slot을 정렬해도 runtime config와 app defaults가 그대로면 사용자는 서로 다른 기본 포트를 보게 된다. 현재 CP HTTP는 `9080`, Edge OpenAI는 `8080`, Edge metrics는 `9092`, client HTTP default는 `9080`에 머문다. 이 subtask는 source-of-truth 변경 뒤 코드 default, config examples, README, local test rules를 한 기준으로 정리한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 채팅에서 직접 질문하거나 선택지를 제시하지 않으며, code-review가 사용자 리뷰 요청의 정당성을 판단한다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/automation-runtime-bridge/milestones/workspace-port-env-standardization.md`
- `packages/go/config/config.go`
- `packages/go/config/config_test.go`
- `packages/go/hostsetup/templates.go`
- `configs/control-plane.yaml`
- `configs/edge.yaml`
- `configs/node.yaml`
- `configs/worker.yaml`
- `apps/control-plane/cmd/control-plane/main.go`
- `apps/control-plane/cmd/control-plane/main_test.go`
- `apps/control-plane/README.md`
- `apps/client/lib/client_config.dart`
- `apps/client/test/client_config_test.dart`
- `apps/client/README.md`
- `apps/client/Dockerfile`
- `scripts/dev/web.sh`
- `Makefile`
- `apps/edge/internal/openai/server.go`
- `apps/edge/internal/input/a2a/server.go`
- `apps/edge/internal/edgecmd/smoke_openai.go`
- `apps/edge/README.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/control-plane-smoke.md`
- `agent-test/local/client-smoke.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/testing-smoke.md`

### 테스트 환경 규칙

- 선택 test_env: `local`
- `agent-test/local/rules.md`를 읽었다.
- 적용 profile: `platform-common-smoke`, `control-plane-smoke`, `client-smoke`, `edge-smoke`, `testing-smoke`
- Go quick check는 local에서 실행한다.
- Flutter client와 Docker compose/runtime evidence는 원격 runner/code-server에서 수행한다.
- `agent-test/local`은 host, token, credential 원문을 tracked docs/roadmap에 쓰지 말라고 요구한다.

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

- config default 변경은 `packages/go/config`와 `apps/control-plane` tests가 일부 커버한다.
- Edge OpenAI/A2A fallback default는 서버 fallback과 config default 양쪽 테스트가 필요하다.
- client default 변경은 `apps/client/test/client_config_test.dart`가 커버하지만 Flutter 실행은 원격 runner evidence가 필요하다.
- README와 local test rule drift는 unit test로 커버되지 않으므로 deterministic `rg` 검증과 review 확인이 필요하다.

### 심볼 참조

- none

### 분할 판단

- split decision policy를 먼저 평가했다.
- 이 plan은 `02+01_runtime_config_docs`이므로 predecessor `01`이 필요하다.
- predecessor `01`: `agent-task/m-workspace-port-env-standardization/01_compose_env_defaults/complete.log` 또는 matching archive `complete.log`가 필요하다. 작성 시점에는 아직 missing이다.
- shared task group: `m-workspace-port-env-standardization`
- 후속 `03+01,02_remote_smoke_evidence`가 runtime evidence와 Roadmap Target completion을 담당한다.

## 의존 관계 및 구현 순서

- `01_compose_env_defaults`의 `complete.log`가 없으면 구현을 시작하지 않는다.
- `01` 완료 후 source-of-truth 값과 이 plan의 제안이 다르면 `계획 대비 변경 사항`에 실제 source-of-truth를 기록하고 그 값에 맞춰 진행한다.

### 범위 결정 근거

- 이 subtask는 runtime/config/docs/test-rule 정렬만 수행한다.
- `.env.example`와 `docker-compose.yml`은 `01` 소유이므로 직접 수정하지 않는다. 단, `01` 결과와 불일치한 app docs/build args가 있으면 이 subtask에서 맞춘다.
- 원격 compose 기동과 full-cycle smoke evidence는 `03` 소유다.
- A2A는 host publish가 없는 disabled-by-default internal surface로 유지할 수 있다. 공개 host slot을 새로 열어야 하면 review stub에 범위 변경으로 기록한다.

### 빌드 등급

- `cloud-G06`: config defaults, app docs, client defaults, Edge input defaults, tests가 교차되어 있으며 line-level 수정은 작아도 blast radius와 verification matrix가 넓다.

## 구현 체크리스트

- [ ] predecessor `01_compose_env_defaults`의 `complete.log`를 확인한다.
- [ ] Control Plane HTTP/client defaults를 backend slot 기준으로 정렬하고 관련 Go/Dart tests를 갱신한다.
- [ ] Edge OpenAI-compatible, metrics, A2A 예외 또는 default를 slot plan 기준으로 정리하고 관련 tests/docs를 갱신한다.
- [ ] `configs/*.yaml`, host setup templates, app README, `scripts/dev/web.sh`, `Makefile`, `agent-test/local`의 설명과 명령을 같은 기준으로 맞춘다.
- [ ] Go quick check와 가능한 Flutter unit command를 실행하거나 원격 runner 필요 사유를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR-1] Align Control Plane and client defaults

문제: `apps/control-plane/cmd/control-plane/main.go:84-93`, `configs/control-plane.yaml:1-4`, `apps/client/lib/client_config.dart:1-10`, `scripts/dev/web.sh:8-12`, `Makefile:129-132`, `apps/client/README.md:38-49`가 CP HTTP default를 `9080`으로 둔다. `01`의 compose/env source-of-truth가 backend `18000`으로 확정되면 client-facing defaults가 drift가 된다.

해결 방법:

```go
// before: apps/control-plane/cmd/control-plane/main.go:84-88
cfg.Server.Listen = "0.0.0.0:9080"
cfg.Server.WireListen = "0.0.0.0:19080"
cfg.Server.EdgeWireListen = "0.0.0.0:19081"
```

```go
// after
cfg.Server.Listen = "0.0.0.0:18000"
cfg.Server.WireListen = "0.0.0.0:19080"
cfg.Server.EdgeWireListen = "0.0.0.0:19081"
```

수정 파일 및 체크리스트:

- [ ] `apps/control-plane/cmd/control-plane/main.go`
- [ ] `apps/control-plane/cmd/control-plane/main_test.go`
- [ ] `configs/control-plane.yaml`
- [ ] `apps/control-plane/README.md`
- [ ] `apps/client/lib/client_config.dart`
- [ ] `apps/client/test/client_config_test.dart`
- [ ] `apps/client/README.md`
- [ ] `apps/client/Dockerfile`
- [ ] `scripts/dev/web.sh`
- [ ] `Makefile`

테스트 작성: 기존 Go/Dart tests를 새 default에 맞춰 갱신한다. 새 test file은 필요 없다.

중간 검증:

```bash
go test ./apps/control-plane/...
```

기대 결과: 통과.

### [REFACTOR-2] Align Edge input and metrics defaults

문제: `packages/go/config/config.go:261-279`, `configs/edge.yaml:27-52`, `packages/go/hostsetup/templates.go:12-30`, `apps/edge/internal/openai/server.go:40-46`, `apps/edge/internal/input/a2a/server.go:43-52`, `apps/edge/internal/edgecmd/smoke_openai.go:79-99`가 Edge OpenAI `8080`, A2A `8081`, metrics `9092`를 기본값으로 둔다. local rules는 field OpenAI `18081`, Edge metrics `19092`를 표준 슬롯으로 쓴다.

해결 방법:

```go
// before: packages/go/config/config.go:262-279
v.SetDefault("server.listen", "0.0.0.0:9090")
v.SetDefault("bootstrap.listen", "0.0.0.0:18080")
v.SetDefault("openai.listen", "0.0.0.0:8080")
v.SetDefault("a2a.listen", "0.0.0.0:8081")
v.SetDefault("metrics.port", 9092)
```

```go
// after
v.SetDefault("server.listen", "0.0.0.0:9090")
v.SetDefault("bootstrap.listen", "0.0.0.0:18080")
v.SetDefault("openai.listen", "0.0.0.0:18081")
v.SetDefault("a2a.listen", "0.0.0.0:8081") // internal disabled-by-default exception unless host publish is added
v.SetDefault("metrics.port", 19092)
```

수정 파일 및 체크리스트:

- [ ] `packages/go/config/config.go`
- [ ] `packages/go/config/config_test.go`
- [ ] `configs/edge.yaml`
- [ ] `apps/edge/internal/openai/server.go`
- [ ] `apps/edge/internal/edgecmd/smoke_openai.go`
- [ ] `packages/go/hostsetup/templates.go`
- [ ] `apps/edge/README.md`
- [ ] A2A remains documented as disabled internal exception or is explicitly added to host slot plan before changing

테스트 작성: 기존 config/edge command tests를 새 default에 맞춰 갱신한다. A2A host publish 변경을 추가하면 regression test를 추가한다.

중간 검증:

```bash
go test ./packages/go/... ./apps/edge/...
```

기대 결과: 통과.

### [REFACTOR-3] Align local test rules and documentation

문제: `agent-test/local/rules.md:24-29`는 main remote slots를 정리하지만 backend `18000`, CP-Edge wire `19081`, DB/cache `15400/16300` 설명이 부족하다. `agent-test/local/control-plane-smoke.md:31-45`와 `agent-test/local/client-smoke.md:30-50`도 CP HTTP `9080`을 compose health/internal 용도와 host-facing default로 섞어 말할 수 있다.

해결 방법:

- local rules에는 host/credential 원문 없이 slot만 기록한다.
- CP HTTP `18000`은 host-facing backend slot, `9080`은 container/internal health port로 분리해 쓴다.
- DB/cache `15400/16300`은 host publish slot이고 container internal은 `5432/6379`임을 적는다.

수정 파일 및 체크리스트:

- [ ] `agent-test/local/rules.md`
- [ ] `agent-test/local/control-plane-smoke.md`
- [ ] `agent-test/local/client-smoke.md`
- [ ] `agent-test/local/platform-common-smoke.md`
- [ ] `agent-test/local/testing-smoke.md`

테스트 작성: 문서/규칙 정렬이므로 새 테스트 파일은 작성하지 않는다.

중간 검증:

```bash
rg --sort path 'localhost:9080|`9080`|5432|6379|8080' agent-test/local apps/client apps/control-plane apps/edge configs packages/go Makefile scripts/dev/web.sh
```

기대 결과: 남은 값은 internal/container 예외, test fixture, migration note로 설명되어 있어야 한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/control-plane/cmd/control-plane/main.go` | REFACTOR-1 |
| `apps/control-plane/cmd/control-plane/main_test.go` | REFACTOR-1 |
| `configs/control-plane.yaml` | REFACTOR-1 |
| `apps/client/lib/client_config.dart` | REFACTOR-1 |
| `apps/client/test/client_config_test.dart` | REFACTOR-1 |
| `apps/client/README.md` | REFACTOR-1 |
| `apps/client/Dockerfile` | REFACTOR-1 |
| `scripts/dev/web.sh` | REFACTOR-1 |
| `Makefile` | REFACTOR-1 |
| `packages/go/config/config.go` | REFACTOR-2 |
| `packages/go/config/config_test.go` | REFACTOR-2 |
| `configs/edge.yaml` | REFACTOR-2 |
| `apps/edge/internal/openai/server.go` | REFACTOR-2 |
| `apps/edge/internal/edgecmd/smoke_openai.go` | REFACTOR-2 |
| `packages/go/hostsetup/templates.go` | REFACTOR-2 |
| `apps/edge/README.md` | REFACTOR-2 |
| `agent-test/local/*.md` | REFACTOR-3 |

## 최종 검증

```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
go test ./packages/go/... ./apps/control-plane/... ./apps/edge/...
cd apps/client && flutter test
```

기대 결과: predecessor `complete.log`가 확인되고 Go tests가 통과한다. Flutter command는 원격 runner 또는 Flutter SDK가 있는 환경에서 실행해야 하며, 실행 불가 시 실제 stdout/stderr와 차단 사유를 review stub에 기록한다.

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