<!-- task=10+07_scheduler_process_runtime_rollout plan=0 tag=SYSTEM_RUNTIME_SCHEDULER_ROLLOUT -->

# Plan - SYSTEM_RUNTIME_SCHEDULER_ROLLOUT

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

**구현의 마지막 단계는 `CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 모두 채우는 것이다. 이 파일이 채워지기 전에는 작업이 완료된 것이 아니다.**
아래 `구현 체크리스트` 순서대로 구현하고 중간/최종 검증을 실행한 뒤, 실제 구현 내용과 stdout/stderr를 `CODE_REVIEW-cloud-G08.md`에 기록한다.
리뷰 파일의 아카이브 지시와 `코드리뷰 전용 체크리스트`는 코드리뷰 에이전트 전용이므로 실행하거나 수정하지 않는다.

## 배경

Scheduler는 백그라운드 daemon 시작/종료와 OS startup 등록을 다루기 때문에 실제 프로세스 호출의 위험이 크다.
`SystemRuntime` foundation 위에서 프로세스 시작/kill/search/exit boundary를 분리해 테스트 가능하게 만든다.

## 의존 관계 및 구현 순서

디렉터리 이름의 `10+07`에 따라 `agent-task/07_system_runtime_foundation/complete.log`가 먼저 있어야 한다.
core/cli rollout과는 독립이며 foundation 이후 병렬 진행 가능하다.

## 분석 결과

### 읽은 파일

- `lib/cli/commands/scheduler/scheduler_manager.dart`
- `lib/cli/commands/scheduler/scheduler_linux.dart`
- `lib/cli/commands/scheduler/scheduler_osx.dart`
- `lib/cli/commands/scheduler/scheduler_windows.dart`
- `lib/cli/commands/command_scheduler.dart`
- `lib/oto/core/system_runtime.dart` (선행 task 예정 파일)
- `test/oto_application_test.dart`

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

- `_startProcess()` Windows/Unix branch: 기존 테스트 없음. fake runtime으로 detached start/runShell 호출을 검증한다.
- `terminate()` pid kill: 기존 테스트 없음. fake runtime으로 killed pid를 검증한다.
- `enableScheduler()`의 `exit(0)`: 현재 실제 process exit 호출이라 테스트하기 어렵다. runtime 또는 exit callback으로 격리해야 한다.
- `_checkProcess()` process search: `findProcess` 직접 호출이라 fake process list 주입 테스트가 없다.

### 심볼 참조

none. public CLI command 이름은 유지한다.

### 분할 판단

분할 정책을 먼저 평가했다.
Scheduler daemon/process-control은 cli 일반 출력/PATH 등록과 위험도가 달라 `09+07`에서 분리한다.
이 task는 `07_system_runtime_foundation`에만 의존한다.

### 범위 결정 근거

포함: `SchedulerManager`의 process start/kill/search/exit boundary, OS subclass constructor 호환, scheduler runtime tests.
제외: `CommandScheduler`의 argument semantics는 유지한다. 필요한 경우 runtime-injected manager 생성 경로만 보강한다.
제외: 실제 OS startup 등록 API는 이번 task에서 fake 가능한 wrapper로만 격리하고 정책 변경은 하지 않는다.

### 빌드 등급

`cloud-G08`: scheduler daemon, process control, process exit boundary를 다루는 고위험 terminal-agent 작업이다.

## 구현 체크리스트

- [ ] `SchedulerManager`가 `SystemRuntime`과 injectable exit callback/process finder를 사용하게 한다.
- [ ] scheduler OS subclass와 `CommandScheduler` 호환 경로를 유지한다.
- [ ] scheduler fake runtime 테스트를 추가한다.
- [ ] 중간 검증과 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1] SchedulerManager process boundary 주입

#### 문제

`lib/cli/commands/scheduler/scheduler_manager.dart:205`는 `exit(0)`를 직접 호출한다.
`lib/cli/commands/scheduler/scheduler_manager.dart:214`는 `Process.killPid`, `236`은 `Process.start`, `244`는 `ProcessExecutor.run`, `255`는 `findProcess`를 직접 사용한다.
이 경로는 daemon 제어라 실제 환경을 건드리지 않고 테스트하기 어렵다.

#### 해결 방법

`SchedulerManager`에 `SystemRuntime runtime`, `Future<List<ProcessInfo>> Function(String) processFinder`, `void Function(int) exitHandler` 주입 경로를 추가한다.
기본값은 기존 `findProcess`/`exit`/`DefaultSystemRuntime` 의미를 보존한다.
`_startProcess`, `terminate`, `_checkProcess`, `enableScheduler`는 이 주입 경로를 사용한다.

Before (`lib/cli/commands/scheduler/scheduler_manager.dart:236`):
```dart
await Process.start(program, arguments,
    mode: ProcessStartMode.detached);
```

After:
```dart
await runtime.startExecutable(program, arguments,
    mode: ProcessStartMode.detached);
```

#### 수정 파일 및 체크리스트

- [ ] `lib/cli/commands/scheduler/scheduler_manager.dart`: runtime/processFinder/exitHandler 주입과 직접 호출 제거.
- [ ] `lib/cli/commands/scheduler/scheduler_linux.dart`: super constructor 호환.
- [ ] `lib/cli/commands/scheduler/scheduler_osx.dart`: super constructor 호환.
- [ ] `lib/cli/commands/scheduler/scheduler_windows.dart`: super constructor 호환.

#### 테스트 작성

작성한다.
`test/oto_scheduler_runtime_test.dart`에 fake subclass를 두고 `_startProcess` 또는 public method를 통해 Unix runShell/Windows startExecutable이 호출되는지 검증한다.
private method 검증이 어려우면 test-only subclass hook을 추가하지 말고 public `regist`/`enableScheduler` 경로를 임시 디렉터리와 fake process finder로 검증한다.

#### 중간 검증

```bash
dart test test/oto_scheduler_runtime_test.dart
```

기대 결과: scheduler runtime 테스트 통과.

### [SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-2] CommandScheduler 호환 유지

#### 문제

`lib/cli/commands/command_scheduler.dart:20`은 `SchedulerManager.create()`를 직접 필드 초기화에 사용한다.
테스트에서 fake manager를 넣을 경로가 없고, manager constructor 변경 시 CLI 호환이 깨질 수 있다.

#### 해결 방법

기존 default constructor 동작은 유지하면서 선택적 manager 주입 생성자를 추가한다.
argument parsing 동작은 변경하지 않는다.

Before (`lib/cli/commands/command_scheduler.dart:20`):
```dart
final SchedulerManager _scheduler = SchedulerManager.create();
```

After:
```dart
CommandScheduler({SchedulerManager? scheduler})
    : _scheduler = scheduler ?? SchedulerManager.create();
```

#### 수정 파일 및 체크리스트

- [ ] `lib/cli/commands/command_scheduler.dart`: optional manager injection 추가, 기존 기본 동작 유지.
- [ ] `test/oto_scheduler_runtime_test.dart`: injected fake manager로 argument routing smoke 추가.

#### 테스트 작성

작성한다.
테스트명: `command_scheduler accepts injected manager for routing`.
검증: `-t`, `-l` 등 최소 하나의 branch가 fake manager로 위임되는지 확인한다.

#### 중간 검증

```bash
dart test test/oto_scheduler_runtime_test.dart
```

기대 결과: 테스트 통과.

### [SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-3] scheduler 직접 process 호출 제거 검증

#### 문제

Scheduler process-control은 회귀 위험이 높아 검색 검증이 없으면 직접 호출이 다시 들어올 수 있다.

#### 해결 방법

최종 검증에 결정적 `rg --sort path`를 포함한다.
`handler.exit()`처럼 isolate handler 종료는 process API가 아니므로 검색 패턴에서 제외한다.

#### 수정 파일 및 체크리스트

- [ ] `test/oto_scheduler_runtime_test.dart`: fake runtime 검증 유지.

#### 테스트 작성

별도 테스트는 위 항목으로 충분하다.

#### 중간 검증

```bash
rg --sort path -n "ProcessExecutor\\.(start|run)|Process\\.run|Process\\.start|Process\\.killPid|exit\\(" lib/cli/commands/scheduler lib/cli/commands/command_scheduler.dart
```

기대 결과: `handler.exit()`를 제외한 직접 process/exit 호출 없음. 출력이 있으면 의도적 예외를 plan deviation에 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/cli/commands/scheduler/scheduler_manager.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1 |
| `lib/cli/commands/scheduler/scheduler_linux.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1 |
| `lib/cli/commands/scheduler/scheduler_osx.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1 |
| `lib/cli/commands/scheduler/scheduler_windows.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1 |
| `lib/cli/commands/command_scheduler.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-2 |
| `test/oto_scheduler_runtime_test.dart` | SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-1, SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-2, SYSTEM_RUNTIME_SCHEDULER_ROLLOUT-3 |

## 최종 검증

```bash
test -f agent-task/archive/2026/05/07_system_runtime_foundation/complete.log || test -f agent-task/07_system_runtime_foundation/complete.log
dart format --output=none --set-exit-if-changed lib/cli/commands/scheduler/scheduler_manager.dart lib/cli/commands/scheduler/scheduler_linux.dart lib/cli/commands/scheduler/scheduler_osx.dart lib/cli/commands/scheduler/scheduler_windows.dart lib/cli/commands/command_scheduler.dart test/oto_scheduler_runtime_test.dart
dart analyze
dart test test/oto_scheduler_runtime_test.dart
rg --sort path -n "ProcessExecutor\\.(start|run)|Process\\.run|Process\\.start|Process\\.killPid|exit\\(" lib/cli/commands/scheduler lib/cli/commands/command_scheduler.dart
```

기대 결과: 선행 complete marker 존재, 포맷/analyze/test 성공, `rg`는 `handler.exit()` 외 직접 process/exit 호출을 보고하지 않음.

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