<!-- task=control_plane_phase_exit_followups/02_legacy_iop_boundary plan=1 tag=REVIEW_BOUNDARY -->

# Review follow-up: neutral runner registration boundary

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

이 plan의 구현이 끝나면 active `CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채워야 한다. 구현 완료 후에도 active 파일은 그대로 두고 리뷰를 요청하며, `코드리뷰 결과`, 로그 rename, `complete.log`, archive 이동은 code-review 전용이다.

구현 중 사용자만 결정할 수 있는 범위 충돌, 사용자 소유 외부 환경, secret, 서비스 준비가 필요하면 사용자에게 직접 묻지 말고 `CODE_REVIEW-cloud-G06.md`의 `사용자 리뷰 요청` 섹션에 결정 항목, 근거, 실행 명령 출력, 자동 후속 불가 이유, 재개 조건을 기록한 뒤 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니라 일반 follow-up 항목이다.

## 배경

이전 구현은 OTO Server HTTP 등록 코드를 `registration_client.dart`로 분리했고 analyzer와 runner 테스트는 통과했다. 하지만 default runner와 neutral registration 파일의 타입 표면에 legacy Edge 명칭이 남아 있어, 계획의 “production runner path에서 legacy iop Edge 개념 분리” 조건이 아직 충족되지 않았다. 또한 이전 active review file이 구현 증거를 담지 않은 pending stub이라 loop 완료 증거를 신뢰할 수 없다.

## 사용자 리뷰 요청 흐름

구현 중 blocker가 생기면 active review stub의 `사용자 리뷰 요청` 섹션을 사용한다. 구현 에이전트는 직접 사용자 질문, 채팅 선택지 제시, `request_user_input`, `USER_REVIEW.md` 생성, 로그 archive, `complete.log` 작성을 하지 않는다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-roadmap/current.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/rules/project/domain/agent/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/agent-smoke.md`
- `agent-task/control_plane_phase_exit_followups/02_legacy_iop_boundary/plan_cloud_G06_0.log`
- `agent-task/control_plane_phase_exit_followups/02_legacy_iop_boundary/code_review_cloud_G06_0.log`
- `apps/runner/lib/oto/agent/agent_runner.dart`
- `apps/runner/lib/oto/agent/registration_client.dart`
- `apps/runner/lib/oto/agent/edge_registration_client.dart`
- `apps/runner/test/oto_agent_registration_test.dart`
- `apps/runner/test/oto_server_connection_smoke_test.dart`
- `apps/runner/test/oto_iop_connection_smoke_test.dart`
- `apps/runner/test/oto_agent_migration_plan_test.dart`

### 테스트 환경 규칙

- test_env: local
- 적용 규칙: `agent-test/local/rules.md`, `agent-test/local/agent-smoke.md`
- agent 변경 필수 검증: `cd apps/runner && dart analyze`
- agent 관련 테스트: `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_server_connection_smoke_test.dart`
- 전체 회귀: `cd apps/runner && dart test`
- 이 리뷰에서 확인한 결과: `dart analyze` 통과, `dart test test/oto_agent_registration_test.dart test/oto_agent_migration_plan_test.dart test/oto_server_connection_smoke_test.dart` 통과, `dart test` 통과.

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

- OTO Server registration, heartbeat, disconnect, job claim/report/cancel/status/self-update는 기존 `oto_agent_registration_test.dart`와 `oto_server_connection_smoke_test.dart`가 계속 커버한다.
- legacy iop smoke는 `OTO_ENABLE_LEGACY_IOP_SMOKE=1` opt-in으로 유지된다.
- default runner/neutral boundary에 legacy `EdgeAgentSession`, `EdgeEndpoint`, iop/proto_socket import가 남지 않았는지는 테스트 assertion보다 deterministic `rg --sort path` 검증으로 확인한다.

### 심볼 참조

- 변경 대상 legacy symbol: `EdgeAgentSession`, `EdgeEndpoint`
- 현재 남은 참조:
  - `apps/runner/lib/oto/agent/agent_runner.dart:23`
  - `apps/runner/lib/oto/agent/agent_runner.dart:30`
  - `apps/runner/lib/oto/agent/agent_runner.dart:41`
  - `apps/runner/lib/oto/agent/registration_client.dart:16`
  - `apps/runner/lib/oto/agent/registration_client.dart:126`
  - `apps/runner/test/oto_agent_registration_test.dart:16`
  - `apps/runner/test/oto_agent_registration_test.dart:29`
  - `apps/runner/test/oto_agent_registration_test.dart:36`
  - `apps/runner/test/oto_agent_registration_test.dart:63`
  - `apps/runner/test/oto_agent_registration_test.dart:83`
  - `apps/runner/test/oto_agent_registration_test.dart:1290`
- legacy-only references may remain in `apps/runner/lib/oto/agent/edge_registration_client.dart` and `apps/runner/test/oto_iop_connection_smoke_test.dart`.

### 분할 판단

단일 follow-up으로 유지한다. 수정은 하나의 agent boundary 안에서 default runner, neutral/legacy registration 파일, 관련 tests의 naming/import surface를 정합화하는 좁은 refactor다. API-vs-call-site 분리가 가능하지만 변경량이 작고 같은 검증 명령으로 확인되므로 split coordination 비용이 더 크다.

### 범위 결정 근거

프로토콜 동작, generated protobuf, OTO Server endpoint, heartbeat/disconnect 로직, remote job execution 로직은 변경하지 않는다. `edge_registration_client.dart`의 legacy iop 등록 기능은 유지하되, default runner와 neutral file이 legacy iop/proto_socket 또는 Edge 명칭을 기본 표면으로 노출하지 않도록만 정리한다. broad migration docs는 작성하지 않는다.

### 빌드 등급

route: `cloud-G06`. 동작 변경은 작지만 agent boundary, legacy compatibility, smoke evidence를 함께 검토해야 하며 이전 리뷰가 verification trust Fail을 기록했으므로 기존 cloud route를 유지한다.

## 구현 체크리스트

- [ ] `registration_client.dart`와 `agent_runner.dart`의 production/neutral 타입 표면에서 legacy `EdgeAgentSession`/`EdgeEndpoint` 의존을 제거한다.
- [ ] `oto_agent_registration_test.dart`의 fake client/session과 one-shot registration test 명칭을 neutral naming으로 정리하고, legacy endpoint parsing coverage는 explicit legacy iop path로 옮기거나 유지 근거를 기록한다.
- [ ] `cd apps/runner && dart analyze`, agent 관련 focused test, `cd apps/runner && dart test`, deterministic `rg --sort path` boundary checks를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_BOUNDARY-1] Neutral/legacy registration symbol boundary

문제: `apps/runner/lib/oto/agent/agent_runner.dart:23`과 `apps/runner/lib/oto/agent/agent_runner.dart:41`이 default runner path에서 `EdgeAgentSession`을 사용한다. `apps/runner/lib/oto/agent/registration_client.dart:16`과 `apps/runner/lib/oto/agent/registration_client.dart:126`도 neutral file에 legacy `EdgeEndpoint`와 `EdgeAgentSession` alias를 둔다.

해결 방법:

```dart
// before: apps/runner/lib/oto/agent/agent_runner.dart:23
final Future<void> Function(AgentConfig, EdgeAgentSession)? _runJobs;

// after
final Future<void> Function(AgentConfig, AgentSession)? _runJobs;
```

```dart
// before: apps/runner/lib/oto/agent/registration_client.dart:126
typedef EdgeAgentSession = AgentSession;

// after
// Remove this alias from the neutral file. If compatibility is needed,
// define it in edge_registration_client.dart only.
```

수정 파일 및 체크리스트:

- [ ] `apps/runner/lib/oto/agent/agent_runner.dart`: `_runJobs`, constructor `runJobs`, local `session` 타입을 `AgentSession`으로 변경한다. `OtoServerJobSession` type check는 유지한다.
- [ ] `apps/runner/lib/oto/agent/registration_client.dart`: `EdgeEndpoint`와 `EdgeAgentSession`을 제거하고 neutral/OTO Server registration symbols만 남긴다.
- [ ] `apps/runner/lib/oto/agent/edge_registration_client.dart`: legacy `EdgeEndpoint`와 필요 시 `typedef EdgeAgentSession = AgentSession`을 이 파일로 옮긴다. iop/proto_socket imports는 이 파일에만 남긴다.
- [ ] `apps/runner/test/oto_agent_registration_test.dart`: default runner tests가 `AgentSession` 기준 fake를 사용하도록 업데이트한다.

테스트 작성: 새 테스트 파일은 만들지 않는다. 기존 unit/smoke tests를 업데이트하고, legacy endpoint parsing assertion이 필요하면 `oto_iop_connection_smoke_test.dart` 안의 explicit legacy path에 둔다.

중간 검증:

```bash
rg --sort path "EdgeAgentSession|EdgeEndpoint" apps/runner/lib/oto/agent/agent_runner.dart apps/runner/lib/oto/agent/registration_client.dart apps/runner/test/oto_agent_registration_test.dart
```

기대 결과: 출력 없음.

```bash
rg --sort path "proto_socket|oto/agent/iop|runtime.pb" apps/runner/lib/oto/agent/agent_runner.dart apps/runner/lib/oto/agent/registration_client.dart
```

기대 결과: 출력 없음.

### [REVIEW_BOUNDARY-2] Review evidence completion

문제: archived `code_review_cloud_G06_0.log`는 active 시점에 `Status: pending implementation` 상태였고 구현 체크리스트, 설계 결정, 검증 stdout/stderr가 없었다. code-review가 명령을 재실행해 통과를 확인했지만, loop artifact 자체가 구현 완료 증거를 담지 못했다.

해결 방법: 이번 후속 구현의 active `CODE_REVIEW-cloud-G06.md`에서 구현 항목별 완료 여부, 구현 체크리스트, 계획 대비 변경 사항, 주요 설계 결정, 사용자 리뷰 요청 상태, 검증 결과를 실제 내용으로 채운다. 검증 결과에는 요약이 아니라 실제 stdout/stderr 또는 저장한 출력 파일 경로와 생성 명령을 남긴다.

수정 파일 및 체크리스트:

- [ ] `agent-task/control_plane_phase_exit_followups/02_legacy_iop_boundary/CODE_REVIEW-cloud-G06.md`: 구현 소유 섹션을 전부 채운다.
- [ ] 검증 명령을 변경했다면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.

테스트 작성: 테스트 코드 변경이 아니라 loop artifact completion이다. 검증은 최종 검증 명령과 `rg --sort path` 출력으로 확인한다.

중간 검증:

```bash
rg --sort path "상태: 없음|dart analyze|dart test|EdgeAgentSession|EdgeEndpoint" agent-task/control_plane_phase_exit_followups/02_legacy_iop_boundary/CODE_REVIEW-cloud-G06.md
```

기대 결과: 사용자 리뷰 요청이 `상태: 없음`이고, 실행한 검증 명령과 boundary check 출력이 active review stub에 기록되어 있다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/runner/lib/oto/agent/agent_runner.dart` | REVIEW_BOUNDARY-1 |
| `apps/runner/lib/oto/agent/registration_client.dart` | REVIEW_BOUNDARY-1 |
| `apps/runner/lib/oto/agent/edge_registration_client.dart` | REVIEW_BOUNDARY-1 |
| `apps/runner/test/oto_agent_registration_test.dart` | REVIEW_BOUNDARY-1 |
| `apps/runner/test/oto_iop_connection_smoke_test.dart` | REVIEW_BOUNDARY-1 |
| `agent-task/control_plane_phase_exit_followups/02_legacy_iop_boundary/CODE_REVIEW-cloud-G06.md` | REVIEW_BOUNDARY-2 |

## 최종 검증

```bash
cd apps/runner && dart analyze
```

기대 결과: `No issues found!`

```bash
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_server_connection_smoke_test.dart
```

기대 결과: `All tests passed!`

```bash
cd apps/runner && dart test
```

기대 결과: `All tests passed!`; legacy iop smoke는 환경변수 미설정 시 opt-in skip이어야 한다.

```bash
rg --sort path "EdgeAgentSession|EdgeEndpoint" apps/runner/lib/oto/agent/agent_runner.dart apps/runner/lib/oto/agent/registration_client.dart apps/runner/test/oto_agent_registration_test.dart
```

기대 결과: 출력 없음.

```bash
rg --sort path "proto_socket|oto/agent/iop|runtime.pb" apps/runner/lib/oto/agent/agent_runner.dart apps/runner/lib/oto/agent/registration_client.dart
```

기대 결과: 출력 없음.

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