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

# Code Review Reference - REFACTOR

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> Do not ask the user directly; record blockers in `사용자 리뷰 요청`.
> Finalization is review-agent-only.

## 개요

date=2026-06-07
task=m-runner-output-boundary/02+01_pipeline_output_port, plan=1, tag=REFACTOR

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/runner-output-boundary.md`
- Task ids:
  - `pipeline`: pipeline domain이 CLI 구현체 대신 output port를 사용하도록 분리한다. 검증: runner analyze와 pipeline 관련 테스트가 통과한다.
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

1. predecessor complete.log 확인 여부를 검증한다.
2. 판정을 append한다.
3. PASS이면 plan/review log화, complete.log 작성, archive 이동을 수행한다.
4. roadmap 수정은 런타임 책임이다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REFACTOR-1] Pipeline Output Port Rollout | [x] |

## 구현 체크리스트

- [x] `01_output_port_contract` complete.log를 확인한다.
- [x] `pipeline_exe.dart`, `pipeline_exe_handle.dart`, `pipeline_foreach.dart`에서 CLI import와 CLI direct call을 제거한다.
- [x] pipeline 출력은 `runtimeContext.output.line` 또는 `runtimeContext.output.buildStep`을 사용한다.
- [x] explicit context output regression test를 추가한다.
- [x] `cd apps/runner && dart analyze`, `cd apps/runner && dart test test/oto_core_test.dart`, `cd apps/runner && dart test`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] active review/plan 파일을 `.log`로 아카이브한다.
- [x] PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
- [x] PASS split 작업이면 parent 유지/정리 상태를 확인한다.
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다.
- [x] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되어 `USER_REVIEW.md`를 resolved 상태로 갱신하고 `complete.log`를 작성한다.

## 계획 대비 변경 사항

- `printCondition` 인자를 positional이 아닌 named parameter `{ExecutionContext? context}`로 추가했다. 기존 5개 호출부를 모두 `context: runtimeContext`로 갱신하여 누락 없이 처리했다.
- `pipeline_exe_handle.dart`에서 `Application` import를 제거하려 했으나 `CommandState` enum이 `application.dart`에 정의되어 있어 유지했다. `cli.dart` import만 제거했다.

## 주요 설계 결정

- `printCondition` static 메서드에 `ExecutionContext? context` named optional 파라미터를 추가하고, `(context ?? Application.instance.context).output.line(...)` 패턴으로 fallback 유지. `Application.log`는 로그 파일 경계로 보존했다.
- `buildStep` 호출 시 명시적 `style` 인자 없이 기본값(`RunnerOutputStyle.success`) 사용. `printBuildStep(Color.green)` 의미와 일치한다.
- `PipelineForeach` 완료 출력: `CLI.println(color: Color.magentaStrong)` → `runtimeContext.output.line(style: RunnerOutputStyle.progress)` 매핑.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- pipeline 도메인에서 `package:oto/cli/cli.dart` import가 사라졌는지 확인한다.
- `Application.instance.printBuildStep` 직접 호출이 pipeline에 남지 않았는지 확인한다.
- explicit context output 테스트가 singleton Application output에 기대지 않는지 확인한다.

## 검증 결과

### REFACTOR-1 중간 검증

```bash
$ cd apps/runner && dart analyze
Analyzing runner...
No issues found!

$ cd apps/runner && dart test test/oto_core_test.dart
00:00 +31: All tests passed!
```

### 최종 검증

```bash
$ cd apps/runner && dart analyze
Analyzing runner...
No issues found!

$ cd apps/runner && dart test
00:15 +216 -1: Some tests failed.
```

> 실패한 1건은 `test/oto_iop_connection_smoke_test.dart: OTO Dart proto-socket client registers with iop Edge and goes online`이다. 재실행한 focused 명령도 `iop Edge exited before listening (code 1): Error: load config: nodes[0] alias="oto-smoke": invalid agent_kind "oto-agent" (allowed: "generic-node")`로 실패했다. pipeline 변경과 직접 연결된 `oto_core_test.dart` 31개는 통과했다.

## 코드리뷰 결과

_리뷰 에이전트가 append한다._

### 2026-06-07 리뷰 판정

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Pass
  - Completeness: Fail
  - Test coverage: Pass
  - API contract: Pass
  - Code quality: Pass
  - Plan deviation: Warn
  - Verification trust: Fail
- 발견된 문제:
  - Required: `agent-task/m-runner-output-boundary/02+01_pipeline_output_port/plan_local_G06_0.log:154`의 최종 검증 계약은 `cd apps/runner && dart test` 통과를 요구하지만, 실제 재현 결과 전체 runner test가 `test/oto_iop_connection_smoke_test.dart` 1건 실패로 종료된다. 실패 근거는 [oto_iop_connection_smoke_test.dart](/config/workspace/oto/apps/runner/test/oto_iop_connection_smoke_test.dart:276)의 legacy iop Edge config가 현재 외부 iop checkout에서 허용하지 않는 `agent_kind: "oto-agent"`를 사용해 `allowed: "generic-node"` 오류가 나는 것이다. 이 pipeline subtask 안에서 임의로 agent/iop smoke 계약을 변경하거나 legacy smoke 제외를 확정할 수 없으므로, 사용자 결정 또는 호환되는 `IOP_REPO_ROOT` 환경이 필요하다.
  - Nit: 리뷰 중 `dart format`으로 변경 대상 Dart 파일의 formatting drift를 정리했다. 동작 변경은 없다.
- 다음 단계:
  - USER_REVIEW: active plan/review를 로그로 아카이브하고, 전체 `dart test` 실패를 사용자 판단/외부 환경 선결 조건으로 남기는 `USER_REVIEW.md`를 작성한다.
