# Complete - m-control-plane-separation-migration/03+02_online_state

## 완료 일시

2026-06-05

## 요약

Runner online-state 구현은 2회 리뷰 후 USER_REVIEW에서 legacy iop smoke 실패 제외 승인을 받아 PASS/RESOLVED로 종료했다.

## 루프 이력

| Plan | Review | Verdict | 메모 |
|------|--------|---------|------|
| `plan_cloud_G06_0.log` | `code_review_cloud_G06_0.log` | FAIL | rejected registration heartbeat, terminal state late heartbeat, path/body runner id mismatch, verification placeholder 문제 |
| `plan_cloud_G06_1.log` | `code_review_cloud_G06_1.log` | FAIL | repo-local fix는 통과했지만 legacy `iop` smoke가 sibling repo 컴파일 오류로 차단 |
| `USER_REVIEW.md` | user decision | PASS/RESOLVED | 사용자가 iop 제거/분리 진행 맥락에서 legacy iop smoke 실패를 이번 online-state 완료 차단에서 제외하도록 승인 |

## 구현/정리 내용

- OTO Server runner registry에 `accepted`, `online`, `disconnected`, `heartbeat_timeout` 상태와 deterministic timeout 판정을 추가했다.
- OTO Server HTTP API에 runner heartbeat, disconnect, registry state 조회 endpoint를 추가했다.
- Dart runner가 accepted registration 이후 heartbeat를 보내고, rejected registration과 session close 이후에는 heartbeat/disconnect를 전송하지 않도록 정리했다.
- Disconnect/timeout terminal state 이후 late heartbeat가 runner를 다시 `online`으로 되살리지 못하도록 registry state transition을 제한했다.
- Heartbeat endpoint에서 path runner id와 body runner id mismatch를 400으로 거부하도록 API contract를 고정했다.
- OTO Server focused runner online/disconnect smoke를 추가하고 agent-smoke local test 계약에 반영했다.

## 최종 검증

- `git diff --check` - PASS; 출력 없음.
- `cd services/core && go test ./...` - PASS; `cmd/oto-core`, `internal/httpserver`, `internal/runnerregistry`, `oto` 패키지가 모두 통과 또는 no test files로 종료했다.
- `cd apps/runner && dart analyze` - PASS; `Analyzing runner... No issues found!`
- `cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_registration_test.dart test/oto_server_connection_smoke_test.dart` - PASS; OTO Server focused migration/registration/online smoke가 통과했다.
- `cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_bootstrap_script_test.dart test/oto_agent_cli_test.dart test/oto_agent_config_test.dart test/oto_agent_registration_test.dart test/oto_iop_connection_smoke_test.dart test/oto_server_connection_smoke_test.dart` - BLOCKED/EXCLUDED by user decision; sibling `iop` repo의 unused import/unused variable 컴파일 오류로 legacy iop smoke가 실패하지만, 사용자가 이번 완료 차단에서 제외하도록 승인했다.

## Roadmap Completion

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- Completed task ids:
  - `online-state`: PASS; evidence=`plan_cloud_G06_1.log`, `code_review_cloud_G06_1.log`, `USER_REVIEW.md`; verification=`git diff --check`, `cd services/core && go test ./...`, `cd apps/runner && dart analyze`, `cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_registration_test.dart test/oto_server_connection_smoke_test.dart`, user-approved exclusion for legacy iop smoke
- Not completed task ids: 없음

## 잔여 Nit

- 없음

## 후속 작업

- iop 제거/분리 작업이 완료되면 legacy iop smoke를 별도 migration evidence로 재검토하거나 agent-smoke 필수 목록에서 정리할 수 있다.
