<!-- task=09+07_cli_process_runtime_rollout plan=1 tag=REVIEW_SYSTEM_RUNTIME_CLI_ROLLOUT -->

# Plan - REVIEW_SYSTEM_RUNTIME_CLI_ROLLOUT

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

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

## 배경

`SYSTEM_RUNTIME_CLI_ROLLOUT` 리뷰에서 Unix printer runtime 이전 후 CLI help 출력이 사람이 읽는 문자열이 아니라 byte list로 출력되는 회귀가 발견되었다.
기존 `Process.run(shellExe, [temp.path])` 호출은 stdout을 기본 encoding으로 decode했지만, runtime wrapper 경유 후 `stdoutEncoding: null`이 전달되어 `ProcessResult.stdout`이 `List<int>`가 된다.

## 의존 관계 및 구현 순서

선행 구현 로그는 `plan_cloud_G07_0.log`와 `code_review_cloud_G07_0.log`에 보관되어 있다.
이 follow-up은 CLI printer 출력 계약 복구만 다룬다.

## 분석 결과

### 읽은 파일

- `lib/cli/printer.dart`
- `lib/oto/core/system_runtime.dart`
- `test/oto_cli_runtime_test.dart`
- `agent-task/09+07_cli_process_runtime_rollout/code_review_cloud_G07_0.log`

### 실패 재현

```bash
dart run bin/main.dart | sed -n '1,12p'
```

현재 결과는 `A command-line oto.`가 아니라 `[10, 65, 32, ...]` 형태의 byte list다.

### 범위 결정 근거

포함: Unix `Printer`의 stdout/stderr decoding 복구, CLI runtime 테스트 또는 smoke 검증 보강.
제외: scheduler, command runtime, core composer의 별도 경고/후속 작업.

### 빌드 등급

`cloud-G07`: CLI 터미널 출력 계약과 실제 `dart run bin/main.dart` smoke 결과를 다루는 terminal-agent follow-up이다.

## 구현 체크리스트

- [ ] Unix `Printer` stdout/stderr decoding을 복구한다.
- [ ] CLI runtime regression 테스트가 byte-list 출력 회귀를 잡도록 보강한다.
- [ ] 중간 검증과 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_SYSTEM_RUNTIME_CLI_ROLLOUT-1] Unix Printer stdout decoding 회귀 수정

#### 문제

`lib/cli/printer.dart:202`의 Unix printer runtime 호출이 encoding을 지정하지 않는다.
`DefaultSystemRuntime.runExecutable`이 nullable encoding 값을 그대로 `Process.run`에 넘기면 stdout decoding이 비활성화되어 `ProcessResult.stdout`이 `List<int>`가 되고, CLI help가 byte list로 출력된다.

#### 해결 방법

좁은 수정으로 Unix printer의 runtime 호출에서 stdout/stderr encoding을 명시해 기존 CLI 출력 계약을 복구한다.
runtime wrapper 쪽에서 null encoding 처리 방식을 고치는 선택지는 더 넓은 API 영향이 있으므로, 필요하다고 판단될 때만 최소 변경으로 검토한다.

Before (`lib/cli/printer.dart`):
```dart
await runtime
    .runExecutable(shellExe, [temp.path]).then((ProcessResult result) {
  print(result.stdout);
});
```

After:
```dart
await runtime
    .runExecutable(shellExe, [temp.path],
        stdoutEncoding: utf8, stderrEncoding: utf8)
    .then((ProcessResult result) {
  print(result.stdout);
});
```

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

- [ ] `lib/cli/printer.dart`: Unix printer `runExecutable` 호출의 stdout/stderr decoding 복구.
- [ ] `test/oto_cli_runtime_test.dart`: fake runtime이 Unix printer encoding 인자를 검증하거나, byte-list 회귀를 잡는 CLI smoke 검증을 추가한다.

#### 테스트 작성

작성한다.
`test/oto_cli_runtime_test.dart`의 fake runtime call record에 stdout/stderr encoding을 보관하고 Unix printer 테스트에서 `utf8` 전달을 검증한다.
추가로 최종 검증에서 `dart run bin/main.dart | rg "A command-line oto\\.|Usage:|Available Command:"`를 실행해 실제 CLI 출력이 byte list가 아닌 텍스트임을 확인한다.

#### 중간 검증

```bash
dart test test/oto_cli_runtime_test.dart
dart run bin/main.dart | rg "A command-line oto\\.|Usage:|Available Command:"
```

기대 결과: CLI runtime 테스트 통과, smoke grep이 help 텍스트를 찾는다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/cli/printer.dart` | REVIEW_SYSTEM_RUNTIME_CLI_ROLLOUT-1 |
| `test/oto_cli_runtime_test.dart` | REVIEW_SYSTEM_RUNTIME_CLI_ROLLOUT-1 |

## 최종 검증

```bash
dart format --output=none --set-exit-if-changed lib/cli/printer.dart test/oto_cli_runtime_test.dart
dart analyze
dart test test/oto_cli_runtime_test.dart
dart run bin/main.dart
dart run bin/main.dart | rg "A command-line oto\\.|Usage:|Available Command:"
rg --sort path -n "ProcessExecutor\\.(start|run)|Process\\.run|Process\\.start" lib/cli/printer.dart lib/cli/commands/install/regist_path.dart
```

기대 결과: 포맷/analyze/test 성공, CLI help가 텍스트로 출력되고 grep 검증이 성공, 직접 process 호출 검색 stdout 없음.

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