ㄹ<!-- task=02_build_flutter_contract plan=0 tag=BUILD_FLUTTER_CONTRACT -->

# Plan - BUILD_FLUTTER_CONTRACT

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

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

## 배경

`BuildFlutter`는 YAML 샘플, 데이터 모델, shell 생성 로직의 계약이 어긋나 있다.
샘플의 `targetPath`와 `flavor`는 현재 모델에서 무시되고, `platform: android`는 `flutter build android`로 생성된다.
Flutter 빌드 명령은 외부 CLI 계약이므로 별도 계획으로 격리한다.

## 분석 결과

### 읽은 파일

- `lib/oto/commands/build/build_dart.dart`
- `lib/oto/commands/build/build_flutter.dart`
- `lib/oto/data/build_data.dart`
- `assets/yaml/sample/11_build_flutter.yaml`
- `test/oto_command_runtime_test.dart`
- `test/oto_command_catalog_test.dart`

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

- `BuildFlutter.appendShell()`이 태그 치환된 param을 쓰는지: 기존 테스트 없음.
- `flavor`가 shell에 `--flavor`로 반영되는지: 기존 테스트 없음.
- 샘플이 실제 모델 필드만 쓰는지: `BuildFlutter` 전용 테스트 없음.

### 심볼 참조

- 이름 변경/삭제 없음.
- `BuildFlutter` 참조: `lib/oto/commands/command_registry.dart`, `lib/oto/commands/build/build_flutter.dart`, `lib/oto/data/build_data.dart`, `assets/yaml/sample/11_build_flutter.yaml`, `test/oto_command_catalog_test.dart`.

### 분할 판단

분할 정책을 먼저 평가했다.
이 작업은 BuildFlutter 모델/샘플/테스트로 하나의 ownership 경계 안에 있다.
exit-status 계약과는 독립적이므로 `01_exit_status_contracts`와 분리하지만, 내부에서는 모델과 샘플을 함께 바꾸는 편이 계약 drift를 막는다.
형제 task: `01_exit_status_contracts` 독립, `02_build_flutter_contract` 독립.

### 범위 결정 근거

- 포함: `BuildFlutter`, `DataBuildFlutter`, 생성 파일, `11_build_flutter.yaml`, 관련 테스트.
- 제외: `BuildDart`의 기본 clean/pub/build_runner 흐름은 기존 상위 빌드 계약이므로 유지한다.
- 제외: 실제 `flutter` CLI 실행 스모크는 로컬 환경 의존성이 커서 fake runtime 테스트로 검증한다.

### 빌드 등급

`cloud-G07`: 외부 CLI shell 생성 계약과 YAML/API 동기화를 다룬다.

## 구현 체크리스트

- [ ] `DataBuildFlutter`와 `BuildFlutter.appendShell()`의 YAML 계약을 정리하고 태그 치환된 param을 사용하게 한다.
- [ ] `11_build_flutter.yaml`과 샘플 검증 테스트를 실제 모델 필드에 맞춘다.
- [ ] 중간 검증과 최종 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [BUILD_FLUTTER_CONTRACT-1] BuildFlutter 모델과 shell 생성 계약 정리

#### 문제

`lib/oto/commands/build/build_flutter.dart:15`가 `command.param` 원본을 직접 파싱해 태그 치환과 workspace resolve를 건너뛴다.
`lib/oto/data/build_data.dart:73`의 `DataBuildFlutter`에는 `flavor`가 없지만 샘플은 `flavor`를 제공한다.
`assets/yaml/sample/11_build_flutter.yaml:48`의 `platform: android`는 현재 구현에서 `flutter build android`로 생성된다.

#### 해결 방법

`BuildFlutter.appendShell()`에서 `DataBuildFlutter.fromJson(getParam(command))`를 사용한다.
`DataBuildFlutter`에 `String? flavor`를 추가하고, 값이 있으면 `--flavor <value>`를 shell에 추가한다.
Android 샘플은 Flutter CLI target인 `apk` 또는 `appbundle`을 사용한다.

Before (`lib/oto/commands/build/build_flutter.dart:15`):
```dart
var data = DataBuildFlutter.fromJson(command.param);
var platform =
    data.platform.toString().replaceAll('BuildPlatform.', '').toLowerCase();
var additional = data.additional == null ? '' : ' ${data.additional!}';
```

After:
```dart
var data = DataBuildFlutter.fromJson(getParam(command));
var platform = data.platform.toLowerCase();
var flavor = data.flavor == null ? '' : ' --flavor ${data.flavor!}';
var additional = data.additional == null ? '' : ' ${data.additional!}';
```

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

- [ ] `lib/oto/data/build_data.dart`: `DataBuildFlutter.flavor` 추가.
- [ ] `lib/oto/data/build_data.g.dart`: build_runner로 생성 갱신.
- [ ] `lib/oto/commands/build/build_flutter.dart`: `getParam(command)` 사용, `--flavor` 반영.
- [ ] `test/oto_command_runtime_test.dart`: 태그 치환, workspace, flavor, platform shell 테스트 추가.

#### 테스트 작성

작성한다.
`test/oto_command_runtime_test.dart`에 `BuildFlutter uses resolved workspace, flavor, and platform in shell`을 추가한다.
`Application.instance.property`에 `workspace`, `flavor`, `platform`을 넣고 `BuildFlutter`를 fake runtime으로 실행해 shell이 `cd`, `flutter build apk`, `--flavor production`, `--release`를 포함하는지 검증한다.

#### 중간 검증

```bash
dart run build_runner build --delete-conflicting-outputs
dart test test/oto_command_runtime_test.dart
```
기대 결과: 생성 파일 갱신 후 런타임 테스트 통과.

### [BUILD_FLUTTER_CONTRACT-2] BuildFlutter 샘플 계약 회귀 방지

#### 문제

`assets/yaml/sample/11_build_flutter.yaml:39`와 `47`의 `targetPath`는 모델에 없는 필드다.
`flavor`는 샘플에 있지만 모델에 없어서 현재 무시된다.
샘플이 AI의 우선 참조 소스라서 이 drift는 반복 생성 오류로 이어진다.

#### 해결 방법

샘플에서 `targetPath`를 `workspace`로 바꾼다.
Android 플랫폼 값은 `apk`로 바꾼다.
샘플 테스트에 `11_build_flutter.yaml`이 `targetPath`를 쓰지 않고 `workspace`, `flavor`, `platform`을 쓰는지 확인하는 회귀 테스트를 추가한다.

Before (`assets/yaml/sample/11_build_flutter.yaml:39`):
```yaml
    targetPath: <!property.workspace>
    platform: android
    flavor: <!property.flavor>
```

After:
```yaml
    workspace: <!property.workspace>
    platform: apk
    flavor: <!property.flavor>
```

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

- [ ] `assets/yaml/sample/11_build_flutter.yaml`: 모델과 CLI target에 맞게 필드 수정.
- [ ] `test/oto_command_catalog_test.dart`: BuildFlutter 샘플 stale field 회귀 테스트 추가.

#### 테스트 작성

작성한다.
`test/oto_command_catalog_test.dart`에 `BuildFlutter sample uses supported fields and flutter targets` 테스트를 추가한다.
`targetPath`가 없고 각 `BuildFlutter` param에 `workspace`, `platform`이 있으며 플랫폼 값이 `ios`, `apk`, `appbundle`, `web`, `macos`, `windows`, `linux` 중 하나인지 검증한다.

#### 중간 검증

```bash
dart test test/oto_command_catalog_test.dart
```
기대 결과: 샘플 계약 테스트 전체 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/oto/data/build_data.dart` | BUILD_FLUTTER_CONTRACT-1 |
| `lib/oto/data/build_data.g.dart` | BUILD_FLUTTER_CONTRACT-1 |
| `lib/oto/commands/build/build_flutter.dart` | BUILD_FLUTTER_CONTRACT-1 |
| `assets/yaml/sample/11_build_flutter.yaml` | BUILD_FLUTTER_CONTRACT-2 |
| `test/oto_command_runtime_test.dart` | BUILD_FLUTTER_CONTRACT-1 |
| `test/oto_command_catalog_test.dart` | BUILD_FLUTTER_CONTRACT-2 |

## 최종 검증

```bash
dart run build_runner build --delete-conflicting-outputs
dart format lib/oto/data/build_data.dart lib/oto/data/build_data.g.dart lib/oto/commands/build/build_flutter.dart test/oto_command_runtime_test.dart test/oto_command_catalog_test.dart
dart test test/oto_command_runtime_test.dart test/oto_command_catalog_test.dart
dart analyze
```
기대 결과: 모든 명령 성공. Dart test cache 개념은 적용하지 않는다.

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