<!-- task=m-control-plane-separation-migration/02+01_runner_registration plan=1 tag=REVIEW_API -->

# Code Review Reference - REVIEW_API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-06-05
task=m-control-plane-separation-migration/02+01_runner_registration, plan=1, tag=REVIEW_API

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- Task ids:
  - `runner-register`: runner 등록 요청/응답은 enrollment token뿐 아니라 runner id, alias, protocol version, command catalog/capability summary를 전달할 수 있게 정의된다.
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-control-plane-separation-migration/02+01_runner_registration/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-control-plane-separation-migration`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_API-1] Restore required iop smoke evidence | [x] |
| [REVIEW_API-2] Re-run registration regression checks | [x] |

## 구현 체크리스트

- [x] [REVIEW_API-1] `oto_iop_connection_smoke_test.dart`의 unconditional skip을 제거하고 agent-smoke 필수 명령이 실제 smoke를 실행하도록 복구한다. 검증: `cd apps/runner && ! rg --sort path "skip:" test/oto_iop_connection_smoke_test.dart`, `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`.
- [x] [REVIEW_API-2] runner registration 회귀 검증을 재실행한다. 검증: `cd services/core && go test ./...`, `cd apps/runner && dart analyze`.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-control-plane-separation-migration/02+01_runner_registration/`를 `agent-task/archive/YYYY/MM/m-control-plane-separation-migration/02+01_runner_registration/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-control-plane-separation-migration/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [x] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [x] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

- 없음

## 주요 설계 결정

- 없음

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 요청됨
- 사유 유형: 사용자 소유 외부 환경/선행 repo 상태
- 결정 필요: 현재 `../iop` (sibling repo) 저장소의 OTO 제거 작업 도중 발생한 컴파일 에러 때문에 legacy smoke test가 실패하는 상황입니다. OTO 제품 독립 검증 수준에서 해당 레거시 smoke를 skip 또는 제외하고 PASS 처리할지, 아니면 iop 측 컴파일 에러가 해결될 때까지 작업을 중지할지 결정이 필요합니다.
- 차단 근거: `test/oto_iop_connection_smoke_test.dart` 가 `../../../iop` 에서 `go run ./apps/edge/cmd/edge` 를 실행할 때, `config.AgentKindOTOAgent` 미정의 컴파일 에러로 인해 실패합니다. 이는 OTO 저장소 코드가 아닌 외부 sibling `iop` 저장소의 OTO 제거 리팩토링 진행 상태의 에러입니다.
- 실행한 검증/명령: `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`
- 자동 후속 불가 이유: 실패 원인이 OTO 프로젝트 경계 바깥의 외부 sibling인 `iop` 디렉토리 소스 코드에 있으므로, OTO 프로젝트 권한 범위 제한에 따라 자동으로 수정하거나 해결할 수 없습니다.
- 재개 조건: 레거시 iop smoke 제외에 대한 최종 승인 및 user-review gate의 PASS 해소 처리, 혹은 sibling iop repo의 빌드 정상화

## 리뷰어를 위한 체크포인트

- `oto_iop_connection_smoke_test.dart`에 unconditional skip이 없다.
- agent-smoke 필수 Dart test 명령이 실제 smoke를 실행했고, skipped test 없이 통과하거나 사용자 리뷰 요청에 정확한 blocker evidence가 있다.
- core Go tests와 Dart analyze가 1차 registration 구현 회귀 없이 통과한다.

## 검증 결과

### REVIEW_API-1 중간 검증
```bash
$ cd apps/runner && ! rg --sort path "skip:" test/oto_iop_connection_smoke_test.dart
(no output, command completed successfully)

$ 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
00:00 +0: loading test/oto_agent_migration_plan_test.dart
00:00 +0: ... plan keeps the existing iop smoke as legacy source evidence only
00:00 +1: ... Linux Bootstrap Script Contract Tests (setUpAll)
00:00 +3: ... Bootstrap Script Contract Tests should contain required flags
00:00 +5: ... Script Contract Tests should support correct architecture assets
00:00 +6: ... Contract Tests should write expected keys to configuration yaml
00:00 +8: test/oto_agent_bootstrap_script_test.dart: ... base URL is not HTTPS
00:00 +9: test/oto_agent_cli_test.dart: ... parses run config and calls runner
00:00 +17: ... standard agent state path when custom config-path is specified
00:00 +27: test/oto_iop_connection_smoke_test.dart: OTO Dart proto-socket client registers with iop Edge and goes online
[oto-smoke] iop repo root: ../../../iop
00:01 +32 -1: test/oto_iop_connection_smoke_test.dart: OTO Dart proto-socket client registers with iop Edge and goes online [E]
  iop Edge exited before listening (code 1):
  # iop/apps/edge/internal/transport
  apps/edge/internal/transport/server.go:166:32: undefined: config.AgentKindOTOAgent
  apps/edge/internal/transport/server.go:197:31: undefined: config.AgentKindOTOAgent
  apps/edge/internal/transport/server.go:218:31: undefined: config.AgentKindOTOAgent
  # iop/apps/edge/internal/service
  apps/edge/internal/service/service.go:561:31: undefined: config.AgentKindOTOAgent
  apps/edge/internal/service/service.go:613:31: undefined: config.AgentKindOTOAgent

  package:matcher                                fail
  test/oto_iop_connection_smoke_test.dart 168:7  _waitForPort
  ===== asynchronous gap ===========================
  test/oto_iop_connection_smoke_test.dart 79:9   main.<fn>

00:09 +35 -1: Some tests failed.
```

### REVIEW_API-2 중간 검증
```bash
$ cd services/core && go test ./...
?   	github.com/toki/oto/services/core/cmd/oto-core	[no test files]
ok  	github.com/toki/oto/services/core/internal/httpserver	(cached)
ok  	github.com/toki/oto/services/core/internal/runnerregistry	(cached)
?   	github.com/toki/oto/services/core/oto	[no test files]

$ cd apps/runner && dart analyze
Analyzing runner...
No issues found!
```

### 최종 검증
```bash
$ cd services/core && go test ./...
?   	github.com/toki/oto/services/core/cmd/oto-core	[no test files]
ok  	github.com/toki/oto/services/core/internal/httpserver	(cached)
ok  	github.com/toki/oto/services/core/internal/runnerregistry	(cached)
?   	github.com/toki/oto/services/core/oto	[no test files]

$ cd apps/runner && dart analyze
Analyzing runner...
No issues found!

$ cd apps/runner && ! rg --sort path "skip:" test/oto_iop_connection_smoke_test.dart
(no output, command completed successfully)

$ 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
00:00 +0: loading test/oto_agent_migration_plan_test.dart
00:00 +0: ... plan keeps the existing iop smoke as legacy source evidence only
00:00 +1: ... Linux Bootstrap Script Contract Tests (setUpAll)
00:00 +3: ... Bootstrap Script Contract Tests should contain required flags
00:00 +5: ... Script Contract Tests should support correct architecture assets
00:00 +6: ... Contract Tests should write expected keys to configuration yaml
00:00 +8: test/oto_agent_bootstrap_script_test.dart: ... base URL is not HTTPS
00:00 +9: test/oto_agent_cli_test.dart: ... parses run config and calls runner
00:00 +17: ... standard agent state path when custom config-path is specified
00:00 +27: test/oto_iop_connection_smoke_test.dart: OTO Dart proto-socket client registers with iop Edge and goes online
[oto-smoke] iop repo root: ../../../iop
00:01 +32 -1: test/oto_iop_connection_smoke_test.dart: OTO Dart proto-socket client registers with iop Edge and goes online [E]
  iop Edge exited before listening (code 1):
  # iop/apps/edge/internal/transport
  apps/edge/internal/transport/server.go:166:32: undefined: config.AgentKindOTOAgent
  apps/edge/internal/transport/server.go:197:31: undefined: config.AgentKindOTOAgent
  apps/edge/internal/transport/server.go:218:31: undefined: config.AgentKindOTOAgent
  # iop/apps/edge/internal/service
  apps/edge/internal/service/service.go:561:31: undefined: config.AgentKindOTOAgent
  apps/edge/internal/service/service.go:613:31: undefined: config.AgentKindOTOAgent

  package:matcher                                fail
  test/oto_iop_connection_smoke_test.dart 168:7  _waitForPort
  ===== asynchronous gap ===========================
  test/oto_iop_connection_smoke_test.dart 79:9   main.<fn>

00:09 +35 -1: Some tests failed.
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## Ownership

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
| Roadmap Targets | Fixed at stub creation from plan | Implementing agent must not modify |
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` to `[x]` only |
| 구현 체크리스트 | Implementing agent | Check `[ ]` to `[x]` only |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 | Implementing agent | Fill command output only |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Pass
  - completeness: Fail
  - test coverage: Fail
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Fail
- 발견된 문제:
  - Required: [apps/runner/test/oto_iop_connection_smoke_test.dart](/config/workspace/oto/apps/runner/test/oto_iop_connection_smoke_test.dart:59)의 필수 legacy iop smoke가 `../../../iop`에서 `go run ./apps/edge/cmd/edge`를 시작하지만, 외부 sibling `iop` 소스의 `config.AgentKindOTOAgent` 미정의 컴파일 에러로 실패합니다. 구현자는 skip 제거와 실패 stdout/stderr 기록을 완료했으나, `agent-smoke` 필수 명령은 여전히 실패하므로 PASS 처리할 수 없습니다. 실패 원인이 OTO repo 밖의 sibling repo 상태에 있어 자동 follow-up으로 안전하게 해결할 수 없으므로 USER_REVIEW로 중단하고, 사용자가 legacy iop smoke 제외/보류 또는 iop repo 정상화 후 재시도를 결정해야 합니다.
- 다음 단계: USER_REVIEW.md를 작성하고 자동 루프를 중단한다.
