<!-- task=m-runner-output-boundary/03+01,02_core_cli_adapter plan=1 tag=REFACTOR -->

# Plan - REFACTOR

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/runner-output-boundary.md`
- Task ids:
  - `core`: data composition 경로의 printer/color 의존을 output port 또는 domain-neutral 계약으로 정리한다.
  - `cli-adapter`: 기존 CLI 출력 의미를 유지하는 adapter를 연결한다. 검증: CLI smoke 출력이 회귀하지 않는다.
- Completion mode: check-on-pass

## 배경

이 작업은 output port 계약과 pipeline rollout이 끝난 뒤 Application/DataComposer/CLI adapter를 연결하는 마무리 단계다. 완료 후 `apps/runner/lib/oto/core/data_composer.dart`와 `apps/runner/lib/oto/pipeline/**`는 CLI 구현 타입을 직접 import하지 않아야 한다.

## Roadmap Dependency

- predecessor: `agent-task/archive/2026/06/m-runner-output-boundary/01_output_port_contract/complete.log`
- predecessor: `agent-task/m-runner-output-boundary/02+01_pipeline_output_port/complete.log`
- 상태: 부분 충족. `01_output_port_contract`는 PASS 완료되었고, `02+01_pipeline_output_port` 완료 후 이 subtask를 실행한다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/independent-control-plane/milestones/runner-output-boundary.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/cli/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/cli-smoke.md`
- `apps/runner/lib/oto/application.dart`
- `apps/runner/lib/oto/core/data_composer.dart`
- `apps/runner/lib/oto/core/execution_context.dart`
- `apps/runner/lib/cli/cli.dart`
- `apps/runner/lib/cli/printer.dart`
- `apps/runner/lib/cli/cli_style.dart`
- `apps/runner/test/oto_application_test.dart`
- `apps/runner/test/oto_core_test.dart`
- `apps/runner/test/oto_cli_runtime_test.dart`
- `apps/runner/test/oto_validate_cli_test.dart`

### 테스트 환경 규칙

- test_env: `local`
- env rule: `agent-test/local/rules.md`
- matched profiles: `core-smoke`, `cli-smoke`
- 적용 명령:
  - `cd apps/runner && dart analyze`
  - `cd apps/runner && dart test test/oto_application_test.dart test/oto_context_test.dart test/oto_core_test.dart test/oto_system_runtime_test.dart`
  - `cd apps/runner && dart test test/oto_cli_runtime_test.dart test/oto_catalog_cli_test.dart test/oto_validate_cli_test.dart`
  - CLI smoke: `cd apps/runner && dart run bin/main.dart --help`

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

- DataComposer가 CLI `Color` 없이 output port를 사용하는지 직접 검증하는 테스트가 없다. `oto_application_test.dart` 또는 `oto_core_test.dart`에 fake output/data compose 경로 테스트를 추가한다.
- CLI adapter의 style mapping은 unit으로 직접 검증하기 어렵다. `dart run bin/main.dart --help`와 기존 validate JSON no-log test로 회귀를 확인한다.

### 심볼 참조

- 변경 대상:
  - `DataComposer.compose(String?, Future Function(String, Color?) printer)`
  - `JenkinsEnvironmentCollector.collect(Future Function(String, Color?) printer)`
  - `Application.printBuildStep(String, Color?)`
- call sites:
  - `Application.build` line 102 passes `printBuildStep`
  - `DataComposerJenkins.compose` line 174 passes printer into collector
  - `DataComposerJenkins.compose` line 179 calls printer
  - `JenkinsEnvironmentCollector.collect` line 63 calls printer
  - `Application.build` lines 123, 159, 160, 161, 169 use CLI or printBuildStep

### 분할 판단

- split decision policy 평가 완료.
- 이 subtask는 `03+01,02_core_cli_adapter`이며 `01`, `02` 완료 후 진행한다.
- core DataComposer 변경과 CLI adapter 연결은 같은 runtime entrypoint에서 맞물리므로 한 subtask로 묶는다.

### 범위 결정 근거

- command implementations의 direct CLI usage는 제외한다.
- agent domain output (`apps/runner/lib/oto/agent/**`)은 제외한다.
- 로그 포맷 전면 개편, remote log streaming, YAML 문법 변경은 제외한다.

### 빌드 등급

- build lane: `cloud-G07`
- review lane: `cloud-G07`
- 근거: Application build path, DataComposer, CLI output adapter가 교차하고 CLI JSON/log suppression 회귀 위험이 있어 더 큰 문맥과 세밀한 검증이 필요하다.

## 구현 체크리스트

- [ ] `01_output_port_contract`와 `02+01_pipeline_output_port` complete.log를 확인한다.
- [ ] DataComposer/Jenkins collector signature를 CLI `Color` 없는 output port 또는 domain-neutral style 계약으로 바꾼다.
- [ ] Application이 build 시작 시 context output을 CLI adapter로 설정하고 `logEnable=false` 및 scheduler log 경계를 유지한다.
- [ ] CLI adapter가 기존 `CLI.printString`, `CLI.println`, build step banner 의미를 보존하도록 연결한다.
- [ ] `apps/runner/lib/oto/core/data_composer.dart`와 `apps/runner/lib/oto/pipeline/**`에 `package:oto/cli` import 또는 `CLI.` direct call이 남지 않게 한다.
- [ ] core/CLI 관련 테스트와 CLI smoke를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- 디렉터리명 `03+01,02_core_cli_adapter` 기준 predecessor는 `01_output_port_contract`, `02+01_pipeline_output_port`다.
- 구현 시작 전 두 predecessor의 active 또는 archived `complete.log`를 확인한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/runner/lib/oto/core/data_composer.dart` | REFACTOR-1 |
| `apps/runner/lib/oto/application.dart` | REFACTOR-1, REFACTOR-2 |
| `apps/runner/lib/cli/runner_output_adapter.dart` 또는 기존 CLI 파일 | REFACTOR-2 |
| `apps/runner/test/oto_application_test.dart` | REFACTOR-1, REFACTOR-2 |
| `apps/runner/test/oto_core_test.dart` | REFACTOR-1 |

## [REFACTOR-1] DataComposer Neutral Output

### 문제

[data_composer.dart](/config/workspace/oto/apps/runner/lib/oto/core/data_composer.dart:5)는 core 도메인인데 `package:oto/cli/cli.dart`를 import한다. [data_composer.dart](/config/workspace/oto/apps/runner/lib/oto/core/data_composer.dart:15)의 compose signature가 `Color`를 노출하고, [data_composer.dart](/config/workspace/oto/apps/runner/lib/oto/core/data_composer.dart:63)는 CLI color를 core error path로 사용한다.

Before:

```dart
// apps/runner/lib/oto/core/data_composer.dart:15
Future compose(String? yaml, Future Function(String, Color?) printer) async {
  return simpleFuture;
}
```

### 해결 방법

선행 output port 계약을 받아 `DataComposer.compose(String? yaml, RunnerOutputPort output)` 형태 또는 equivalent domain-neutral callback으로 바꾼다. `RunnerOutputStyle.error/success`를 사용하고 CLI `Color`는 adapter에서만 매핑한다.

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

- [ ] `data_composer.dart`에서 `package:oto/cli/cli.dart` import 제거
- [ ] compose/collector signature를 output port 기반으로 변경
- [ ] unsupported OS/Jenkins Variables 출력이 output port를 통해 발생하도록 변경
- [ ] 관련 테스트에서 fake output으로 메시지를 검증

### 테스트 작성

- 작성: `apps/runner/test/oto_core_test.dart` 또는 `apps/runner/test/oto_application_test.dart`
- 목표: DataComposer/Jenkins collector가 CLI type 없이 output port로 message를 emit하는지 검증한다.

### 중간 검증

```bash
cd apps/runner && dart analyze
cd apps/runner && dart test test/oto_core_test.dart test/oto_application_test.dart
```

## [REFACTOR-2] CLI Adapter Wiring

### 문제

[application.dart](/config/workspace/oto/apps/runner/lib/oto/application.dart:8)는 core orchestration인데 CLI package를 import하고, [application.dart](/config/workspace/oto/apps/runner/lib/oto/application.dart:123), [application.dart](/config/workspace/oto/apps/runner/lib/oto/application.dart:159), [application.dart](/config/workspace/oto/apps/runner/lib/oto/application.dart:301)는 직접 CLI 출력과 `Color`를 사용한다. CLI 출력 의미는 유지하되 domain 실행 로직이 CLI 구현을 몰라야 한다.

Before:

```dart
// apps/runner/lib/oto/application.dart:123
await CLI.printString(
  '$enter*********************************  Build Data  *************************************',
  color: Color.magenta,
);
```

### 해결 방법

CLI domain에 `CliRunnerOutputPort` adapter를 추가한다. `Application`은 runner entrypoint로서 adapter를 context에 주입하되, pipeline/core domain은 adapter 구현을 직접 import하지 않는다. `Application.printBuildStep`는 adapter로 이동하거나 compatibility wrapper만 남긴다.

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

- [ ] `apps/runner/lib/cli/runner_output_adapter.dart` 추가 또는 `cli.dart` 내부 adapter 추가
- [ ] neutral style -> `Color` mapping을 adapter 안에 둔다
- [ ] `Application.build`가 `_logEnable`과 scheduler logFunc 흐름을 유지하며 output adapter를 설정한다
- [ ] `Application.printBuildStep` 직접 사용을 제거하거나 deprecated wrapper로 제한한다
- [ ] validate `--json` stdout에 execute/build logs가 섞이지 않는 기존 테스트를 유지한다

### 테스트 작성

- 작성: `apps/runner/test/oto_application_test.dart`
- 보강: build success/failure가 output port로 build step을 emit하는지 또는 log disabled일 때 emit하지 않는지 검증한다.
- 기존: `apps/runner/test/oto_validate_cli_test.dart`의 JSON no execute logs 테스트를 유지한다.

### 중간 검증

```bash
cd apps/runner && dart analyze
cd apps/runner && dart test test/oto_application_test.dart test/oto_cli_runtime_test.dart test/oto_validate_cli_test.dart
```

## 최종 검증

```bash
cd apps/runner && dart analyze
cd apps/runner && dart test test/oto_application_test.dart test/oto_context_test.dart test/oto_core_test.dart test/oto_system_runtime_test.dart
cd apps/runner && dart test test/oto_cli_runtime_test.dart test/oto_catalog_cli_test.dart test/oto_validate_cli_test.dart
cd apps/runner && dart run bin/main.dart --help
! rg --sort path -n "package:oto/cli|CLI\\.|Color\\." apps/runner/lib/oto/pipeline apps/runner/lib/oto/core/data_composer.dart
```

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