<!-- task=01+03_command_io_ports plan=0 tag=REFACTOR -->

# Command IO Ports Plan

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

**필수 경고: 구현 마지막 단계에서 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 모두 채워야 한다. 이 파일이 채워지기 전에는 작업이 완료된 것이 아니다.**
구현 체크리스트와 검증 명령을 그대로 수행하고 실제 stdout/stderr를 review stub에 기록한다.
review 파일의 아카이브 지시와 `코드리뷰 전용 체크리스트`는 리뷰 에이전트 전용이다.

## 배경

커맨드 구현이 `ProcessExecutor`, `File`, `Directory`, `Process.start`에 직접 의존한다.
AI-first 전환을 위해 pilot 범위에서 command runtime port를 만들고 fake runtime 테스트로 외부 IO 없이 검증 가능하게 한다.

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/rules/project/domain/command/rules.md`
- `agent-ops/rules/project/domain/framework/rules.md`
- `lib/oto/commands/command.dart`
- `lib/oto/commands/shell/shell.dart`
- `lib/oto/commands/git/git.dart`
- `lib/oto/commands/file/copy.dart`
- `lib/oto/commands/process/process.dart`
- `lib/oto/data/command_data.dart`
- `lib/oto/data/base_data.dart`

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

- Shell/Git command가 어떤 command buffer를 실행하는지 fake로 검증하는 테스트 없음.
- ProcessRun detached 실행이 실제 프로세스를 띄우지 않고 검증되는 테스트 없음.
- file copy는 실제 filesystem 의존이 크며 이번 pilot에서 전면 추상화하지 않는다.

### 심볼 참조

- rename/remove 예정 심볼: none.
- pilot 대상 직접 process call: `lib/oto/commands/shell/shell.dart:20`, `46`; `lib/oto/commands/git/git.dart:23`, `45`, `61`, `83`, `101`, `116`, `133`, `152`, `171`, `190`, `211`, `215`, `236`, `257`; `lib/oto/commands/process/process.dart:40`.

### 범위 결정 근거

- 이 task는 Shell/Git/ProcessRun pilot만 다룬다.
- `build_ios.dart`, `ftp.dart`, Slack/Jira/Jenkins는 외부 환경 의존성이 커서 후속 IO migration plan으로 남긴다.
- `Copy`는 경로 계산 테스트만 가능하게 하고 실제 copy abstraction은 후속으로 남긴다.

### 빌드 등급

- build lane: `cloud-G08` — 프로세스 실행, detached process, stdout/stderr 계약이 포함된 terminal-agent 성격의 리팩토링이다.
- review lane: `cloud-G08` — fake runtime이 실제 IO를 막는지와 command buffer 신뢰성을 확인해야 한다.

## 구현 체크리스트

- [ ] [REFACTOR-1] `CommandRuntime` pilot을 추가하고 Shell/Git/ProcessRun 실행을 위임한다.
- [ ] 모든 중간 검증과 최종 검증을 실행하고 실제 출력을 `CODE_REVIEW-*-G??.md`에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- `agent-task/01_test_baseline/complete.log`가 있어야 시작한다.
- `agent-task/01+02_runtime_context/complete.log`가 있어야 시작한다.
- 이 task는 `01+04_command_catalog`와 병렬 가능하지만 최종 통합 검증은 둘 다 끝난 뒤 수행한다.

### [REFACTOR-1] CommandRuntime pilot 추가

#### 문제

`Shell`은 실행기를 직접 호출한다.

```dart
// lib/oto/commands/shell/shell.dart:19
var process =
    await ProcessExecutor.start(shell, logHandler: Application.logWithType);
```

`ProcessRun`은 임시 파일과 detached process를 직접 생성한다.

```dart
// lib/oto/commands/process/process.dart:38
var file =
    File('${Directory.systemTemp.path}/${ProcessExecutor.tempFileName}');
await file.writeAsString(shell.toString());
await Process.start(file.path, [], runInShell: true, ...);
```

#### 해결 방법

`lib/oto/commands/command_runtime.dart`를 추가한다.
`Command`에 기본 runtime을 두고, 테스트에서는 fake runtime을 command 인스턴스에 주입한다.

```dart
// lib/oto/commands/command_runtime.dart
abstract class CommandRuntime {
  Future<ProcessData> start(StringBuffer command, {LogHandler? logHandler});
  Future<ProcessData> run(StringBuffer command, {String? workspace});
  Future<void> startDetached(StringBuffer command, {required String workspace});
}
```

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

- [ ] `lib/oto/commands/command_runtime.dart` 추가
- [ ] `lib/oto/commands/command.dart`에 runtime 필드와 기본 runtime 추가
- [ ] `lib/oto/commands/shell/shell.dart`의 process 실행을 runtime으로 위임
- [ ] `lib/oto/commands/git/git.dart`의 pilot process 실행을 runtime으로 위임
- [ ] `lib/oto/commands/process/process.dart`의 detached 실행을 runtime으로 위임
- [ ] `lib/oto/commands/file/copy.dart`는 이번 task에서 직접 copy abstraction 변경 금지

#### 테스트 작성

- 작성: `test/oto_command_runtime_test.dart`
- 테스트명: `shell command delegates process start to runtime`, `git command delegates command buffer to runtime`, `process run delegates detached start to runtime`
- 목적: 실제 프로세스를 실행하지 않고 command buffer와 workspace를 검증

#### 중간 검증

```bash
dart test test/oto_command_runtime_test.dart
```

예상: fake runtime 기반 테스트 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/oto/commands/command_runtime.dart` | REFACTOR-1 |
| `lib/oto/commands/command.dart` | REFACTOR-1 |
| `lib/oto/commands/shell/shell.dart` | REFACTOR-1 |
| `lib/oto/commands/git/git.dart` | REFACTOR-1 |
| `lib/oto/commands/process/process.dart` | REFACTOR-1 |
| `test/oto_command_runtime_test.dart` | REFACTOR-1 |

## 최종 검증

```bash
dart analyze
```

예상: `No issues found!`

```bash
dart test
```

예상: 모든 테스트 통과.

```bash
rg --sort path -n "ProcessExecutor\\.(start|run)|Process\\.start" lib/oto/commands/shell lib/oto/commands/git lib/oto/commands/process
```

예상: pilot 대상 파일에서는 직접 호출이 없거나 의도적 예외가 review stub에 기록되어야 한다.

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