# Milestone: Edge/Node Runtime Reconnect와 Config Refresh

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/update-plane-self-update-foundation/PHASE.md`

## 목표

Edge 재시작이나 일시 단절 뒤에도 Node가 지정된 retry 정책으로 재접속하고, 운영 중 Edge 설정 변경은 프로세스 재시작 없이 refresh로 검증, diff, 적용, Node 전파까지 수행할 수 있게 한다.
1차 목표는 field bootstrap 환경에서 GX10 vLLM과 OneXPlayer Lemonade 같은 provider pool 설정 변경을 Edge 재기동 없이 반영하는 runtime 운영 기반을 만드는 것이다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- SDD: 필요
- SDD 문서: `agent-roadmap/sdd/update-plane-self-update-foundation/runtime-reconnect-config-refresh/SDD.md`
- SDD 사유: Node reconnect retry, Edge config refresh, NodeConfigPayload 재적용, proto/config/command 계약, field smoke 판정이 lifecycle과 운영 계약에 직접 닿는다.
- 잠금 해제 조건:
  - [x] SDD 잠금이 해제되어 있다
  - [x] SDD 사용자 리뷰가 없거나 승인/해결되었다
  - [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다
  - [x] Evidence Map이 완료 시 `Roadmap Completion`과 최종 검증 evidence로 검증 가능하게 연결되어 있다
- 결정 필요: 없음

## 범위

- Edge 연결 단절 후 Node가 10초 간격으로 최대 10회 재접속을 시도하고, 초과 시 연결 루프를 종료한 뒤 Node process를 종료하는 정책
- Node 초기 connect 실패와 연결 후 disconnect의 retry/exit 동작 구분
- 재접속 성공 시 새 `RegisterResponse.NodeConfigPayload`를 기준으로 Node adapter registry, runtime concurrency, store workspace를 재구성하거나 안전하게 유지하는 경계
- Edge runtime config refresh entrypoint와 결과 reporting. CLI command, Edge-local admin API, Control Plane command 중 MVP 표면은 SDD에서 source of truth와 함께 고정한다.
- Edge config 파일 재읽기, validation, diff 산출, mutable/immutable 변경 분류, 적용 실패 시 이전 runtime 상태 유지
- provider pool, `nodes[].providers[]`, `openai.models`/model catalog, NodeConfigPayload, provider capacity/concurrency 변경의 live apply와 연결 Node 전파
- listener address, bootstrap artifact dir, Edge identity처럼 live apply가 위험한 변경의 restart-required 판정과 사용자 보고
- refresh/reconnect event와 로그, field smoke 기준

## 기능

### Epic: [reconnect] Node 재접속과 종료 정책

Node가 Edge 단절을 운영 가능한 transient failure로 처리하되, 무한 루프 없이 명시된 retry 한계에서 종료한다.

- [x] [retry-policy] Node reconnect 기본값이 `interval=10s`, `max_attempts=10`으로 고정되고 config/문서/로그에 노출된다. 검증: reconnect 단위 테스트에서 10초 정책을 fake clock 또는 injectable sleeper로 확인하고, 10회 초과 시 종료 경로를 확인한다.
- [x] [supervisor] Node bootstrap lifecycle이 최초 connect 성공 이후 Edge disconnect를 감지하고 reconnect supervisor로 세션을 재수립한다. 검증: transport fake나 integration test에서 Edge stop/start 후 Node가 재등록된다.
- [x] [exit-limit] 재접속 실패가 retry 한계를 넘으면 Node가 adapter/store/session을 정리하고 process 종료 신호를 반환한다. 검증: 실패 Edge endpoint로 10회 시도 후 non-zero 종료 또는 fx shutdown path가 관측된다.
- [x] [dedupe] Edge registry는 이전 연결 disconnect 정리 뒤 같은 node id의 재등록을 정상 수용하고, 아직 살아 있는 duplicate connection은 계속 거부한다. 검증: duplicate active connection과 reconnect-after-unregister 케이스가 각각 테스트된다.

### Epic: [refresh] Edge config refresh 계약

운영 중 변경 가능한 설정과 재시작이 필요한 설정을 구분하고, refresh entrypoint가 validation과 diff 결과를 명확히 반환한다.

- [x] [entrypoint] refresh MVP entrypoint가 정리되고 구현된다. CLI command/API/Control Plane command 중 선택한 표면은 실행 중 Edge process에 도달해야 하며 dry-run과 apply 결과를 구분한다.
- [x] [diff-classify] config refresh는 기존 runtime config와 새 config의 diff를 산출하고 `applied`, `restart_required`, `rejected`로 변경을 분류한다. 검증: provider capacity 변경은 applied, listen port 변경은 restart_required, invalid YAML은 rejected로 테스트된다.
- [x] [atomic-apply] validation 또는 apply 실패 시 기존 routing/runtime 상태가 유지된다. 검증: 일부 invalid provider 설정을 포함한 refresh가 기존 `/v1/models`와 provider dispatch 상태를 깨지 않는다.
- [x] [ops-report] refresh 결과가 CLI/API 응답, 로그, event 중 최소 한 표면에 남고 변경된 node/provider/model 항목을 확인할 수 있다.

### Epic: [node-config] Node config 재적용

Edge가 refresh된 NodeConfigPayload를 연결 Node에 전달하고, Node는 변경분만 안전하게 반영한다.

- [x] [push-contract] Edge-to-Node config refresh message 또는 command 계약이 proto/config 구조에 추가되고 backward compatibility 기준이 정리된다. 검증: generated proto와 handler 테스트가 통과한다.
- [x] [adapter-diff] Node는 adapter instance 추가/수정/삭제, provider endpoint/header/capacity 변경을 diff로 적용한다. 검증: openai_compat provider capacity 변경과 endpoint 변경이 adapter registry에 반영된다.
- [x] [drain] 진행 중 run이 있는 adapter/provider 변경은 drop하지 않고 기존 run은 이전 adapter snapshot으로 완료시키며, 후속 request는 새 config 기준으로 라우팅한다. 검증: in-flight run 중 refresh 시 run completion과 후속 request routing이 기대대로 동작한다.
- [x] [runtime-config] runtime concurrency와 workspace root 변경의 live apply 가능 여부가 구현되고, 불가능한 변경은 restart_required로 보고된다.

### Epic: [field] Field bootstrap provider pool 검증

현재 dev field 환경의 bootstrap 테스트를 refresh/reconnect 완료 증거로 유지한다.

- [x] [field-reconnect] `toki-labs.com` dev-runtime에서 Edge restart 후 GX10 Linux Node와 OneXPlayer Windows Node가 bootstrap 재실행 없이 재접속한다. 검증: Control Plane status와 Edge node TCP connection 수가 3개 Node 기준으로 회복된다.
- [x] [field-refresh] `gx10-vllm=4`, `onexplayer-lemonade=3` capacity 변경을 refresh로 반영하고 Edge process restart 없이 `qwen3.6:35b` 동시 4개 요청이 성공한다. 검증: aggregate 기대 배정은 capacity 4/3 기준 `gx10-vllm` 2개, `onexplayer-lemonade` 2개이며, 요청별 node id 확정은 dispatch trace가 구현된 경우에만 판정한다.
- [x] [docs] bootstrap/reconnect/refresh 운영 방법과 제한 사항이 `apps/edge/README.md`, `docs/edge-local-dev-guide.md`, agent-test field 기준에 반영된다.

## 완료 리뷰

- 상태: 통과
- 요청일: 2026-06-24
- 완료 근거: `agent-task/archive/2026/06/m-runtime-reconnect-config-refresh/01_reconnect_supervisor/complete.log`의 `Roadmap Completion`에 따라 `retry-policy`, `supervisor`, `exit-limit`가 PASS로 확인되었다. `dedupe`는 `TestEdgeServerDuplicateRegistrationReason`, `TestEdgeServerReconnectAfterUnregisterAccepted`, `TestRegistryRegisterIfAbsentRejectsConcurrentDuplicate`, `TestRegistryUnregisterIfClientIgnoresStaleConnection` 존재와 `go test -count=1 ./apps/edge/internal/node ./apps/edge/internal/transport` PASS로 확인되었다. `02_refresh_contract/complete.log`와 `03+02_edge_refresh_apply/complete.log`의 `Roadmap Completion`에 따라 `entrypoint`, `diff-classify`, `atomic-apply`, `ops-report`가 PASS로 확인되었다. `04+02_config_push_contract/complete.log`, `05+04_node_adapter_diff/complete.log`, `06+05_node_drain_runtime_config/complete.log`의 `Roadmap Completion`에 따라 `push-contract`, `adapter-diff`, `drain`, `runtime-config`가 PASS로 확인되었다. `07+01,03,04,05,06_field_docs_smoke/complete.log`의 `Roadmap Completion`에 따라 `field-reconnect`, `field-refresh`, `docs`가 PASS로 확인되었다. 최종 종결 감사에서 `go test -count=1 ./...`, `./scripts/e2e-smoke.sh`, `git diff --check`가 PASS였다.
- 검토 항목:
  - [x] SDD gate가 승인되어 구현 잠금이 해제되었다
  - [x] 모든 기능 Task와 Task 안의 검증이 충족되었다
  - [x] field dev 환경 결과와 문서/agent-test 반영 근거가 완료 로그와 리뷰 PASS로 확인되었다
- 남은 차단 항목: 없음
- 리뷰 코멘트: 코드 레벨 종결 감사 중 같은 node id의 동시 등록 경쟁 구간을 확인해 `RegisterIfAbsent`와 client identity 기반 unregister로 보강했다. 모든 Milestone 기능 Task와 검증, SDD gate, field evidence, 최종 회귀가 충족되어 완료 archive 대상으로 판단한다.

## 범위 제외

- host-local manager/updater를 통한 binary self-update
- release manifest, artifact staging, rollback pointer 전환
- frontend refresh UI 완성
- 자동 file watcher 기반 hot reload. MVP는 명시 command/API refresh를 기준으로 한다.
- cross-Edge/cloud fallback과 품질 기반 routing 고도화
- 모든 config field의 live apply 보장. 위험한 변경은 restart_required로 보고한다.

## 작업 컨텍스트

- 관련 경로: `apps/node/internal/bootstrap/module.go`, `apps/node/internal/transport/client.go`, `apps/node/internal/transport/session.go`, `apps/node/internal/adapters/**`, `apps/edge/internal/transport/server.go`, `apps/edge/internal/node/**`, `apps/edge/internal/service/**`, `apps/edge/internal/edgecmd/**`, `proto/iop/runtime.proto`, `packages/go/config/**`, `agent-test/local/edge-smoke.md`
- 표준선(선택): Node는 Edge가 사라지는 상황을 transient disconnect로 보고 10초 간격 10회까지만 재접속한다. Edge config refresh는 validate-before-apply, diff reporting, restart_required 분류, previous-state preservation을 기본으로 한다.
- 선행 작업: Model Alias Provider Pool과 Provider Catalog
- 후속 작업: Update Plane 안정 프로토콜, Host-local Manager 기반 자체 업데이트, Edge/Node 롤아웃과 복구 정책
- 구현 분할 권고: `01_reconnect_supervisor`, `02_refresh_contract`, `03+02_edge_refresh_apply`, `04+02_config_push_contract`, `05+04_node_adapter_diff`, `06+05_node_drain_runtime_config`, `07+01,03,04,05,06_field_docs_smoke`
- 확인 필요: 없음
