<!-- task=03_stable_edge_node_id plan=0 tag=REFACTOR -->

# Stable Edge and Node Identity

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

**구현 완료 후 `CODE_REVIEW-cloud-G06.md`의 모든 섹션을 채우는 것이 필수 마지막 단계입니다.** 체크리스트 완료 여부, 중간/최종 검증 결과, 계획 대비 변경 사항, 주요 설계 결정을 실제 내용으로 채우세요. `CODE_REVIEW-cloud-G06.md`의 아카이브 지시는 리뷰 스킬 전용이며 구현 에이전트가 실행하지 않습니다.

## 배경

Control Plane 구현은 아직 범위 밖이지만 향후 여러 Edge와 여러 Node를 구분할 안정 ID 기반은 필요하다. 현재 node id는 edge config에서 지정할 수 있으나 예시는 UUID fallback을 전제로 한다. Edge 자신의 id/name config는 없다.

## 분석 결과

### 읽은 파일

- `packages/config/config.go`
- `packages/config/config_test.go`
- `configs/edge.yaml`
- `configs/node.yaml`
- `apps/edge/internal/node/store.go`
- `apps/edge/internal/node/store_test.go`
- `apps/edge/internal/node/registry.go`
- `apps/edge/internal/transport/server.go`
- `apps/edge/internal/transport/integration_test.go`
- `apps/edge/internal/bootstrap/module.go`
- `apps/node/internal/bootstrap/module.go`
- `apps/node/internal/transport/client.go`
- `proto/iop/node.proto`
- `proto/iop/runtime.proto`

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

- `store_test.go` covers explicit ID and UUID auto-ID but not duplicate explicit ID.
- `config_test.go` does not cover edge identity config because it does not exist yet.
- Integration test currently derives expected node ID from store record; use explicit ID to verify stable config identity.

### 심볼 참조

- `config.EdgeConfig`: `packages/config/config.go:13-20`.
- `config.NodeDefinition.ID`: `packages/config/config.go:24`.
- UUID fallback: `apps/edge/internal/node/store.go:82-85`.
- Node registration response uses store ID in `apps/edge/internal/transport/server.go`.
- Node runtime uses edge-assigned `RegisterResponse.NodeId` in `apps/node/internal/bootstrap/module.go`.

### 범위 결정 근거

- Do not implement Control Plane registration, Edge DB, auth, permission, mTLS, federation, or inventory UI.
- Node identity source of truth remains edge-side `nodes[].id` for now.
- `proto/iop/node.proto` is only reference context; do not complete control-plane protocol here.

### 빌드 등급

build=`cloud-G06`, review=`cloud-G06`. Config and registration semantics cross edge/node boundaries, but behavior change is intentionally small.

### [REFACTOR-1] Edge identity config

#### 문제

`packages/config/config.go:13-20` has no edge identity block. Future Control Plane attachment would need a stable edge id, but implementing Control Plane is out of scope.

#### 해결 방법

Add a small edge identity config, used for loading and logging only.

```yaml
edge:
  id: "edge-dgx-group"
  name: "DGX Group"
```

#### 수정 파일 및 체크리스트

- [ ] Add `EdgeInfo` or `IdentityConf` to `packages/config`.
- [ ] Add `Edge EdgeInfo` to `EdgeConfig`.
- [ ] Add defaults only if they do not imply production stability.
- [ ] Use edge id/name in bootstrap or console logs only if helpful.
- [ ] Do not add Control Plane registration.

#### 테스트 작성

Add a `packages/config` load test for `edge.id` and `edge.name`.

#### 중간 검증

```bash
go test ./packages/config
```

예상 결과: config tests pass.

### [REFACTOR-2] Prefer stable node ID in config

#### 문제

`configs/edge.yaml:22` says id is optional and UUID is auto-assigned. `apps/edge/internal/node/store.go:82-85` generates UUID when id is empty, so sample config encourages unstable node identity.

#### 해결 방법

Make sample config show explicit `nodes[].id`. Keep UUID fallback for dev mode.

#### 수정 파일 및 체크리스트

- [ ] Update `configs/edge.yaml` node sample to include `id: "node-dgx-01"` or similarly stable sample.
- [ ] Update `NodeDefinition.ID` comment to say missing ID is a dev fallback.
- [ ] Keep `alias` behavior for console lookup/display.
- [ ] Do not require production mode yet.

#### 테스트 작성

Existing explicit-ID test remains. Update tests only if sample or comments require compile changes.

#### 중간 검증

```bash
go test ./apps/edge/internal/node/...
```

예상 결과: existing explicit ID and UUID fallback tests pass.

### [REFACTOR-3] Duplicate ID validation

#### 문제

`apps/edge/internal/node/store.go:68-81` validates token and alias duplicates but not explicit ID duplicates before adding to `byID`.

#### 해결 방법

Add `seenID` validation only when the config ID is non-empty. Generated fallback UUIDs should remain unique by generation.

#### 수정 파일 및 체크리스트

- [ ] Add duplicate explicit ID check in `LoadFromConfig`.
- [ ] Preserve empty ID -> UUID fallback.
- [ ] Add `TestLoadFromConfig_DuplicateID`.

#### 테스트 작성

Add store test for duplicate explicit IDs.

#### 중간 검증

```bash
go test ./apps/edge/internal/node/...
```

예상 결과: duplicate ID is rejected; fallback tests still pass.

### [REFACTOR-4] Registration path stays stable

#### 문제

Stable ID changes must not break token-based registration. Edge sends `RegisterResponse.NodeId`; node uses it as runtime node ID.

#### 해결 방법

Update integration/bootstrap tests to use explicit stable IDs where possible and confirm registration still works.

#### 수정 파일 및 체크리스트

- [ ] Edge integration test uses explicit node ID in config.
- [ ] Node bootstrap tests do not require node-side local ID.
- [ ] No handshake protocol change unless strictly needed.

#### 테스트 작성

Update existing integration test assertions; add no broad new integration harness.

#### 중간 검증

```bash
go test ./apps/edge/internal/transport/... ./apps/node/internal/bootstrap/...
```

예상 결과: registration and bootstrap tests pass.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/config/config.go` | REFACTOR-1 |
| `packages/config/config_test.go` | REFACTOR-1 |
| `configs/edge.yaml` | REFACTOR-2 |
| `apps/edge/internal/node/store.go` | REFACTOR-2, REFACTOR-3 |
| `apps/edge/internal/node/store_test.go` | REFACTOR-3 |
| `apps/edge/internal/transport/integration_test.go` | REFACTOR-4 |

## 최종 검증

```bash
gofmt -w <changed-go-files>
go test ./packages/config ./apps/edge/internal/node/... ./apps/edge/internal/transport/... ./apps/node/internal/bootstrap/...
go test ./...
rg -n "uuid.NewString|UUID v4|auto-assigned|NodeDefinition|EdgeConfig|NodeConfig" apps packages configs proto/iop --glob '!**/README.md'
```

예상 결과: stable edge/node identity is configurable; UUID remains dev fallback. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 전체 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
