<!-- task=m-oto-agent-registry-implementation/01_config_store plan=0 tag=OAR01 -->

# OTO Agent Registry - Config and Store

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 변경 내용과 stdout/stderr를 채운 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, 또는 범위 충돌 없이는 진행할 수 없을 때만 review stub의 `사용자 리뷰 요청` 섹션에 근거를 적고 중단한다. 재실행으로 해소 가능한 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

현재 Edge의 등록 토큰 모델은 `nodes[]` 기반 일반 노드만 표현한다. OTO agent를 같은 등록 경로로 받으려면 설정과 `NodeStore`에 agent kind가 먼저 보존되어야 한다. 이 계획은 후속 registry/transport 작업이 의존할 내부 모델 기반만 추가한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`에서 복사된 양식이며, `USER_REVIEW.md` 작성과 검증은 code-review가 담당한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-agent-registry-implementation.md`
- Task ids:
  - `agent-kind-model`: Node/Agent 등록 record가 `generic-node`와 `oto-agent`를 구분한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/domain/edge/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/edge-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/testing-smoke.md`
- `packages/go/config/config.go`
- `packages/go/config/config_test.go`
- `apps/edge/internal/node/store.go`
- `apps/edge/internal/node/store_test.go`
- `configs/edge.yaml`

### 테스트 환경 규칙

`test_env=local`이다. `agent-test/local/rules.md`를 읽었고, 변경 경로 기준으로 `edge-smoke`, `platform-common-smoke`, `testing-smoke` 프로필을 적용한다. 적용 명령은 `go test -count=1 ./packages/go/config ./apps/edge/internal/node`와 최종 `go test -count=1 ./apps/edge/... ./packages/go/config`다. local 규칙은 이번 작업 중 생성된 ignored 로컬 규칙이며 `<확인 필요>` 값은 없다.

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

- `agent_kind` YAML 로딩: 신규 테스트 필요.
- 빈 `agent_kind`의 `generic-node` 기본값: 신규 테스트 필요.
- 허용되지 않는 `agent_kind` 거부: 신규 테스트 필요.
- `NodeStore`가 kind를 보존하고 기존 token/id 중복 정책을 유지: 신규 테스트 필요.

### 심볼 참조

이 계획은 기존 심볼 rename/remove를 하지 않는다. 관련 참조는 `NodeDefinition`, `NodeRecord`, `LoadFromConfig`, `FindByToken`, `FindByID`, `BuildConfigPayload`이며 call site는 `packages/go/config/config.go`, `apps/edge/internal/node/store.go`, `apps/edge/internal/node/mapper.go`, `apps/edge/internal/bootstrap/runtime.go`, `apps/edge/internal/service/service.go`, 관련 테스트다.

### 분할 판단

분할 정책을 먼저 평가했다. 공유 task group은 `m-oto-agent-registry-implementation`이다. 하위 작업은 `01_config_store`(기반 모델), `02+01_registry_transport`(연결 등록과 중복 정책), `03+01,02_service_snapshots`(service snapshot 노출)이다. 이 작업은 선행 의존성이 없는 첫 단계다.

### 범위 결정 근거

이 단계는 `packages/go/config`와 `apps/edge/internal/node/store`의 내부 모델만 수정한다. transport 연결 처리, service snapshot, CLI bootstrap command, Control Plane proto, Client UI, DB persistence, OTO message payload는 후속 작업 범위다.

### 빌드 등급

`local-G06`: 설정/스토어 API 변경이 있지만 범위가 작고 단위 테스트로 결정적으로 검증 가능하다.

## 구현 체크리스트

- [ ] `packages/go/config/config.go`에 agent kind 상수와 `NodeDefinition.AgentKind`를 추가하고 `LoadEdge` 정규화/검증을 구현한다.
- [ ] `apps/edge/internal/node/store.go`의 `NodeRecord`와 `LoadFromConfig`에 agent kind 보존 및 기본값 처리를 추가한다.
- [ ] `packages/go/config/config_test.go`와 `apps/edge/internal/node/store_test.go`에 kind 로딩, 기본값, invalid kind, 중복 정책 회귀 테스트를 추가한다.
- [ ] 중간 및 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [OAR01-1] Config agent kind

#### 문제

`packages/go/config/config.go:43`의 `NodeDefinition`은 OTO agent와 일반 노드를 구분할 필드가 없다. `configs/edge.yaml:88`의 `nodes[]` 예시도 기존 노드만 표현한다.

#### 해결 방법

`NodeDefinition`에 `agent_kind`를 추가하고 빈 값은 `generic-node`로 정규화한다. 허용값은 `generic-node`, `oto-agent`로 제한한다.

Before:

```go
// packages/go/config/config.go:43
type NodeDefinition struct {
	Alias    string       `mapstructure:"alias" yaml:"alias"`
	Role     string       `mapstructure:"role" yaml:"role"`
	Token    string       `mapstructure:"token" yaml:"token"`
	Adapters NodeAdapters `mapstructure:"adapters" yaml:"adapters"`
}
```

After:

```go
const (
	AgentKindGenericNode = "generic-node"
	AgentKindOTOAgent   = "oto-agent"
)

type NodeDefinition struct {
	Alias     string       `mapstructure:"alias" yaml:"alias"`
	Role      string       `mapstructure:"role" yaml:"role"`
	Token     string       `mapstructure:"token" yaml:"token"`
	AgentKind string       `mapstructure:"agent_kind" yaml:"agent_kind"`
	Adapters  NodeAdapters `mapstructure:"adapters" yaml:"adapters"`
}
```

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

- [ ] `packages/go/config/config.go`: 상수, 필드, 정규화/검증 helper 추가.
- [ ] `packages/go/config/config_test.go`: `agent_kind: oto-agent`, 빈 기본값, invalid kind 테스트 추가.
- [ ] `configs/edge.yaml`: 주석 또는 예시를 최소 변경으로 보강한다. 예시가 혼란을 만들면 생략하고 테스트 fixture만 사용한다.

#### 테스트 작성

작성한다. `TestLoadEdge_NodeAgentKind`, `TestLoadEdge_NodeAgentKindDefault`, `TestLoadEdge_NodeAgentKindInvalid`를 추가한다.

#### 중간 검증

```bash
go test -count=1 ./packages/go/config
```

예상 결과: config package 테스트 PASS.

### [OAR01-2] NodeStore record kind

#### 문제

`apps/edge/internal/node/store.go:11`의 `NodeRecord`는 등록 record kind를 보존하지 않는다. 후속 transport 단계에서 token으로 찾은 record가 OTO agent인지 알 수 없다.

#### 해결 방법

`NodeRecord.AgentKind`를 추가하고 `LoadFromConfig`에서 정규화된 `NodeDefinition.AgentKind`를 복사한다. 기존 alias/id/token 생성 규칙은 유지한다.

Before:

```go
// apps/edge/internal/node/store.go:11
type NodeRecord struct {
	ID        string
	Alias     string
	Role      string
	Token     string
	Adapters  config.NodeAdapters
	Connected bool
}
```

After:

```go
type NodeRecord struct {
	ID        string
	Alias     string
	Role      string
	Token     string
	AgentKind string
	Adapters  config.NodeAdapters
	Connected bool
}
```

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

- [ ] `apps/edge/internal/node/store.go`: `NodeRecord.AgentKind` 추가, 기본값 fallback 방어 추가.
- [ ] `apps/edge/internal/node/store_test.go`: kind 보존 및 기존 duplicate token/id 회귀 확인.

#### 테스트 작성

작성한다. `TestLoadFromConfigAgentKind`와 기존 duplicate 테스트 보강을 추가한다.

#### 중간 검증

```bash
go test -count=1 ./apps/edge/internal/node
```

예상 결과: node package 테스트 PASS.

## 의존 관계 및 구현 순서

1. `OAR01-1`을 먼저 구현한다.
2. `OAR01-2`는 config 정규화 결과에 의존한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/go/config/config.go` | OAR01-1 |
| `packages/go/config/config_test.go` | OAR01-1 |
| `configs/edge.yaml` | OAR01-1 |
| `apps/edge/internal/node/store.go` | OAR01-2 |
| `apps/edge/internal/node/store_test.go` | OAR01-2 |

## 최종 검증

```bash
go test -count=1 ./packages/go/config ./apps/edge/internal/node
```

예상 결과: 두 package 테스트 PASS. Go test cache output은 허용하지 않으므로 `-count=1`을 유지한다.

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