<!-- task=m-control-plane-separation-migration/01_runner_app plan=0 tag=MONOREPO_RUNNER -->

# Implementation Plan - MONOREPO_RUNNER

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 마지막 단계다. 검증을 실행하고, active 파일은 그대로 둔 채 리뷰 준비 상태를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 남기고 중단한다. 구현 에이전트는 `USER_REVIEW.md`, archive log, `complete.log`를 만들지 않는다.

## 배경

현재 OTO는 루트 단일 Dart package로 실행되며 Milestone은 이 런타임을 `apps/runner`로 옮기라고 지정한다. 이 작업은 이후 Flutter client와 Go core service가 같은 repo에 들어올 수 있게 하는 첫 경계다. package 이름은 `oto`로 유지해 Dart import 계약을 보존한다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 결정이 필요한 경우 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 해당 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 따른다. code-review가 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- Task ids:
  - `runner-app`: 현재 OTO CLI/runtime 소스, 테스트, pubspec, 실행 entrypoint가 `apps/runner` 기준으로 이동되어 기존 로컬 CLI 실행이 유지된다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/ROADMAP.md`
- `agent-roadmap/phase/independent-control-plane/PHASE.md`
- `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/agent-smoke.md`
- `agent-test/local/cli-smoke.md`
- `agent-test/local/command-smoke.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/framework-smoke.md`
- `agent-test/local/pipeline-smoke.md`
- `agent-test/local/sample-smoke.md`
- `agent-test/local/scheduler-smoke.md`
- `agent-ops/rules/project/domain/agent/rules.md`
- `agent-ops/rules/project/domain/cli/rules.md`
- `agent-ops/rules/project/domain/command/rules.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/framework/rules.md`
- `agent-ops/rules/project/domain/pipeline/rules.md`
- `agent-ops/rules/project/domain/sample/rules.md`
- `agent-ops/rules/project/domain/scheduler/rules.md`
- `bin/main.dart`
- `lib/cli/**`
- `lib/oto/agent/agent_config.dart`
- `lib/oto/agent/agent_runner.dart`
- `lib/oto/agent/edge_registration_client.dart`
- `lib/oto/core/**`
- `lib/oto/pipeline/**`
- `pubspec.yaml`
- `analysis_options.yaml`
- `README.md`
- `assets/script/shell/build.sh`
- `assets/script/batch/build.bat`
- `test/*.dart`

### 테스트 환경 규칙

- test_env: `local`
- `agent-test/local/rules.md`가 없어 `create-test`로 baseline을 생성했고 다시 읽었다.
- 적용 profiles: `agent-smoke`, `cli-smoke`, `command-smoke`, `core-smoke`, `framework-smoke`, `pipeline-smoke`, `sample-smoke`, `scheduler-smoke`.
- 적용 명령: `dart pub get`, `dart analyze`, `dart test`; runner 이동 후에는 `cd apps/runner && dart pub get && dart analyze && dart test`.
- 확인한 도구: `dart`는 `/sdk/flutter/bin/dart`, Dart SDK `3.11.3`.
- 현재 기준선: `dart analyze` 통과, `dart test` 통과.

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

- 기존 tests는 CLI/runtime 동작을 폭넓게 검증하지만 실제 binary 실행 문자열이 `bin/main.dart`에 고정되어 있어 이동 후 보정이 필요하다.
- root helper 위임은 이 plan 범위가 아니므로 `04+01,02,03_root_entrypoints`에서 별도 검증한다.

### 심볼 참조

- renamed/removed symbols: none.
- package name은 `oto`로 유지한다.
- path 문자열 call sites: `README.md`, `assets/script/shell/build.sh`, `assets/script/batch/build.bat`, `lib/cli/cli.dart`, `test/oto_agent_cli_test.dart`, `test/oto_application_test.dart`, `test/oto_catalog_cli_test.dart`, `test/oto_validate_cli_test.dart`.

### 분할 판단

- split decision policy를 먼저 적용했다.
- 공유 task group: `m-control-plane-separation-migration`.
- siblings: `01_runner_app` 독립 선행, `02_client_app` 독립, `03_core_service` 독립, `04+01,02,03_root_entrypoints`는 세 앱/서비스 scaffold 완료 후 실행.
- runner 이동은 Dart package 경계와 기존 source/test 이동을 함께 다루므로 단일 subtask로 유지한다. client/core/root helper는 다른 toolchain과 검증 전략이라 분리했다.

### 범위 결정 근거

- 이 plan은 `apps/runner` 이동만 수행한다.
- `apps/client`, `services/core`, root `Makefile`/README 위임 정리는 제외한다.
- `packages/` 공유 패키지는 만들지 않는다.
- generated `*.g.dart`, protobuf 산출물은 이동만 하고 수동 수정하지 않는다.

### 빌드 등급

- `cloud-G07`: CLI 실행 경로, package root, path dependency, tests/scripts/README를 함께 바꾸는 repo 구조 전환이며 터미널 검증 신뢰도가 중요하다.

## 구현 체크리스트

- [ ] 현재 root Dart package 파일을 `apps/runner` 아래로 이동하고 package name `oto`를 유지한다.
- [ ] `apps/runner/pubspec.yaml`의 `proto_socket` path dependency를 새 깊이에 맞게 조정하고 generated analyzer exclude 경로를 유지한다.
- [ ] 실제 binary 실행 tests와 script의 `bin/main.dart` 참조를 runner package 기준으로 갱신한다.
- [ ] runner 경로에서 `dart pub get`, `dart analyze`, `dart test`를 실행해 `runner-app` 검증을 충족한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [MONOREPO_RUNNER-1] Move Dart Runner Package

문제: Milestone은 현재 root runtime을 `apps/runner`로 옮기라고 지정하지만 현재 package files는 root에 있다. `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md:28`와 `:40`이 target이다.

해결 방법:

Before (`pubspec.yaml:23`):
```yaml
  proto_socket:
    path: ../proto-socket/dart
```

After (`apps/runner/pubspec.yaml`):
```yaml
  proto_socket:
    path: ../../../proto-socket/dart
```

수정 파일 및 체크리스트:
- [ ] `bin/` -> `apps/runner/bin/`
- [ ] `lib/` -> `apps/runner/lib/`
- [ ] `test/` -> `apps/runner/test/`
- [ ] `assets/` 중 runner runtime이 소비하는 `script/`, `template/`, `yaml/sample/` -> `apps/runner/assets/`
- [ ] `pubspec.yaml` -> `apps/runner/pubspec.yaml`
- [ ] `analysis_options.yaml` -> `apps/runner/analysis_options.yaml`
- [ ] root ignored `pubspec.lock`는 강제 이동하지 않고 `apps/runner`에서 `dart pub get`으로 재생성한다.

테스트 작성: 새 테스트보다 기존 runner test suite를 이동/보정한다. 파일 이동 자체의 신규 unit test는 만들지 않는다.

중간 검증:
```bash
cd apps/runner && dart pub get && dart analyze
```
예상 결과: dependency resolution 성공, `No issues found!`.

### [MONOREPO_RUNNER-2] Preserve CLI Execution Contracts

문제: debug executable name과 integration tests/scripts가 root `bin/main.dart`를 전제로 한다. 예: `lib/cli/cli.dart:23-26`, `test/oto_catalog_cli_test.dart:97-113`, `test/oto_validate_cli_test.dart:176-179`, `assets/script/shell/build.sh:24-28`, `assets/script/batch/build.bat:22-25`.

해결 방법:

Before (`lib/cli/cli.dart:23`):
```dart
    if (fileName == 'dart') {
      //debug
      fileName = 'dart bin/main.dart';
    }
```

After:
```dart
    if (fileName == 'dart') {
      fileName = 'dart bin/main.dart';
    }
```

실행 표시 문자열은 runner package 내부에서는 그대로 `bin/main.dart`가 맞다. root에서 실행하는 tests/scripts는 `workingDirectory: apps/runner` 또는 `cd apps/runner` 기준으로 보정한다.

수정 파일 및 체크리스트:
- [ ] `apps/runner/test/oto_agent_cli_test.dart`
- [ ] `apps/runner/test/oto_catalog_cli_test.dart`
- [ ] `apps/runner/test/oto_validate_cli_test.dart`
- [ ] `apps/runner/test/oto_application_test.dart`
- [ ] `apps/runner/assets/script/shell/build.sh`
- [ ] `apps/runner/assets/script/batch/build.bat`

테스트 작성: 기존 integration tests를 유지하고 working directory/path만 runner 기준으로 고친다.

중간 검증:
```bash
cd apps/runner && dart test test/oto_agent_cli_test.dart test/oto_catalog_cli_test.dart test/oto_validate_cli_test.dart test/oto_application_test.dart
```
예상 결과: 지정 테스트 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/runner/bin/**` | MONOREPO_RUNNER-1 |
| `apps/runner/lib/**` | MONOREPO_RUNNER-1 |
| `apps/runner/test/**` | MONOREPO_RUNNER-1, MONOREPO_RUNNER-2 |
| `apps/runner/assets/**` | MONOREPO_RUNNER-1, MONOREPO_RUNNER-2 |
| `apps/runner/pubspec.yaml` | MONOREPO_RUNNER-1 |
| `apps/runner/analysis_options.yaml` | MONOREPO_RUNNER-1 |

## 최종 검증

```bash
cd apps/runner && dart pub get && dart analyze && dart test
```
예상 결과: dependency resolution 성공, analyzer issue 없음, 모든 runner tests 통과.

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