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

# Application Result Contract Plan

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

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

## 배경

`Application.build()`가 성공/실패 시 직접 `exit()`해서 테스트와 재사용이 어렵다.
AI-first 리팩토링을 위해 build 결과를 값으로 반환하고 CLI/scheduler가 종료 정책을 소유하게 만든다.

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/cli/rules.md`
- `agent-ops/rules/project/domain/scheduler/rules.md`
- `lib/oto/application.dart`
- `lib/cli/commands/command_exe.dart`
- `lib/cli/commands/command_scheduler.dart`
- `lib/oto/core/data_composer.dart`
- `lib/oto/data/command_data.dart`
- `test/test.dart`

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

- `Application.build()` 실패가 process exit 대신 반환값으로 전달되는지 검증하는 테스트 없음.
- `CommandExe`가 build result exitCode를 isolate 메시지로 전달하는지 검증하는 테스트 없음.
- scheduler mode가 parent process를 종료하지 않는지 검증하는 테스트 없음.

### 심볼 참조

- rename/remove 예정 심볼: none.
- `Application.build(` call site: `lib/cli/commands/command_exe.dart:123`.
- 직접 `exit(` 대상: `lib/oto/application.dart:127`, `lib/oto/application.dart:134`.
- 의도적 예외 후보: `lib/cli/commands/command_scheduler.dart:36`, `lib/cli/commands/scheduler/scheduler_manager.dart:205`.

### 범위 결정 근거

- 이 task는 build 결과 계약만 다룬다.
- runtime context, command IO, registry catalog는 후속 task에서 처리한다.
- scheduler manager의 기존 `exit(0)` 정책은 별도 scheduler 리팩토링 전까지 유지하고 최종 rg 결과에 예외로 기록한다.

### 빌드 등급

- build lane: `cloud-G07` — CLI/isolate exit-status 계약과 scheduler 비종료 동작에 영향이 있다.
- review lane: `cloud-G07` — 실제 `exit()` 잔존 위치와 검증 출력 신뢰를 확인해야 한다.

## 구현 체크리스트

- [ ] [REFACTOR-1] `BuildResult`를 도입하고 `Application.build()`가 직접 종료하지 않게 한다.
- [ ] 모든 중간 검증과 최종 검증을 실행하고 실제 출력을 `CODE_REVIEW-*-G??.md`에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- `agent-task/01_test_baseline/complete.log`가 있어야 시작한다.
- 이 task 완료 후 `01+02_runtime_context`를 시작한다.

### [REFACTOR-1] BuildResult 계약 도입

#### 문제

`lib/oto/application.dart:123-135`가 예외 출력과 `exit()`를 함께 수행한다.

```dart
// lib/oto/application.dart:123
} catch (e, stacktrace) {
  await CLI.printString(e.toString(), color: Color.redStrong);
  await CLI.printString(stacktrace.toString(), color: Color.yellowStrong);
  await printBuildStep('Build Failed', Color.redStrong);
  exit(10);
}
```

#### 해결 방법

`lib/oto/core/build_result.dart`를 추가하고 `Application.build()`를 `Future<BuildResult>`로 변경한다.
CLI/isolate는 반환된 `exitCode`를 메시지로 전달하고, 실제 종료는 CLI 경계에서만 수행한다.

```dart
// lib/oto/core/build_result.dart
class BuildResult {
  final bool success;
  final int exitCode;
  final Object? error;
  final StackTrace? stackTrace;
  const BuildResult.success()
      : success = true,
        exitCode = 0,
        error = null,
        stackTrace = null;
  const BuildResult.failure(this.error, this.stackTrace, {this.exitCode = 10})
      : success = false;
}
```

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

- [ ] `lib/oto/core/build_result.dart` 추가
- [ ] `lib/oto/application.dart`에서 직접 `exit()` 제거
- [ ] `lib/cli/commands/command_exe.dart`에서 result exitCode 전달
- [ ] scheduler build path는 process 종료 없이 완료 메시지만 보내게 유지
- [ ] 남은 `exit(`는 CLI/scheduler 정책인지 review stub에 기록

#### 테스트 작성

- 작성: `test/oto_application_test.dart`
- 테스트명: `build returns failure instead of exiting on invalid yaml`, `file build returns success for print-only pipeline`
- 목적: build 성공/실패가 process 종료 없이 반환되는지 검증

#### 중간 검증

```bash
dart test test/oto_application_test.dart
```

예상: Application result 테스트 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/oto/core/build_result.dart` | REFACTOR-1 |
| `lib/oto/application.dart` | REFACTOR-1 |
| `lib/cli/commands/command_exe.dart` | REFACTOR-1 |
| `test/oto_application_test.dart` | REFACTOR-1 |

## 최종 검증

```bash
dart analyze
```

예상: `No issues found!`

```bash
dart test
```

예상: 모든 테스트 통과.

```bash
rg --sort path -n "exit\\(" lib/oto/application.dart lib/cli/commands/command_exe.dart lib/cli/commands/command_scheduler.dart lib/cli/commands/scheduler
```

예상: `lib/oto/application.dart`와 `lib/cli/commands/command_exe.dart`에는 결과가 없어야 한다. scheduler 쪽 잔존은 의도적 예외로 기록한다.

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