<!-- task=flutter_first_portal_migration/05+01,02,04_portal_wire_contract plan=1 tag=REVIEW_PORTAL_WIRE -->

# Plan - REVIEW_PORTAL_WIRE

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

이 계획은 `code_review_cloud_G08_0.log`의 FAIL 판정을 좁게 수습한다. 구현 마지막 단계에서 `CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채운다. 검증 명령은 대체하지 말고, 대체가 필요하면 이유와 실제 명령을 `계획 대비 변경 사항`에 기록한다.

## 배경

첫 리뷰에서 `make proto-dart`가 현재 리뷰 환경에서 재현되지 않았고, 생성된 Dart proto 세트가 `google/protobuf/struct.pb.dart` missing import 상태임이 확인되었다. 현재 Flutter test와 web build는 `control.pb.dart`만 소비해서 이 깨짐을 잡지 못한다. 후속 작업은 Dart proto generation을 재현 가능하게 만들고, 전체 generated contract가 컴파일되는 검증을 추가하는 데 한정한다.

## 구현 체크리스트

- [ ] [REVIEW_PORTAL_WIRE-1] `make proto-dart`가 PATH 보정 없이 재현되도록 protoc Dart plugin discovery와 generated well-known proto 출력을 고친다.
- [ ] [REVIEW_PORTAL_WIRE-2] generated Dart proto 전체가 소비 가능한지 확인하는 compile-focused test를 추가한다.
- [ ] [REVIEW_PORTAL_WIRE-3] 후속 변경 범위의 unused import를 정리하고 실제 검증 출력을 다시 기록한다.
- [ ] 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_PORTAL_WIRE-1] Dart proto generation 재현성 복구

문제: `Makefile:38`의 `proto-dart` target은 `protoc-gen-dart`가 PATH에 있다고 가정한다. 리뷰 환경에서는 `/config/.pub-cache/bin/protoc-gen-dart` 파일은 존재하지만 PATH에는 없어서 `command -v protoc-gen-dart`와 `make proto-dart`가 실패했다. 또한 `runtime.pb.dart`는 `../../google/protobuf/struct.pb.dart`를 import하지만 `google/protobuf/struct.proto`의 Dart 생성물이 없다.

해결 방법: `Makefile`의 `proto-dart` target이 `protoc-gen-dart`를 명시적으로 발견하도록 만든다. 우선 `command -v protoc-gen-dart`를 사용하고, 실패하면 `$(HOME)/.pub-cache/bin/protoc-gen-dart`를 확인해 `protoc --plugin=protoc-gen-dart=...`에 넘긴다. 둘 다 없으면 설치 안내와 함께 실패한다. 같은 target에서 `google/protobuf/struct.proto`를 포함해 `apps/portal/lib/gen/google/protobuf/struct.pb.dart`가 생성되도록 한다.

수정 파일 및 체크리스트:

- [ ] `Makefile`: `proto-dart` plugin discovery와 `google/protobuf/struct.proto` 생성을 추가한다.
- [ ] `apps/portal/lib/gen/google/protobuf/*.dart`: `make proto-dart`로 well-known type 생성물을 추가한다.
- [ ] `apps/portal/lib/gen/proto/iop/*.dart`: 갱신된 target으로 재생성한다.
- [ ] `apps/portal/README.md`: PATH에 없어도 Makefile fallback이 동작한다는 기준이나 실패 시 설치 절차를 실제 동작과 맞게 갱신한다.

중간 검증:

```bash
command -v protoc
make proto-dart
test -f apps/portal/lib/gen/google/protobuf/struct.pb.dart
```

기대 결과: PATH에 `protoc-gen-dart`가 없어도 `make proto-dart`가 repo-local generated Dart files와 `google/protobuf/struct.pb.dart`를 재현한다. plugin 자체가 설치되어 있지 않으면 target이 명확한 오류를 낸다.

### [REVIEW_PORTAL_WIRE-2] Generated proto compile guard 추가

문제: 기존 `apps/portal/test/iop_wire/parser_map_test.dart`는 `control.pb.dart`만 import한다. 그래서 `runtime.pb.dart`, `node.pb.dart`, `job.pb.dart`의 missing import나 생성물 불일치를 잡지 못했다.

해결 방법: `apps/portal/test/iop_wire/generated_proto_import_test.dart`를 추가해 `control.pb.dart`, `runtime.pb.dart`, `node.pb.dart`, `job.pb.dart`를 모두 import하고 대표 message를 instantiate한다. `runtime.pb.dart`에서는 `RunRequest`와 `google.protobuf.Struct` 필드 접근이 컴파일되는지 확인한다.

수정 파일 및 체크리스트:

- [ ] `apps/portal/test/iop_wire/generated_proto_import_test.dart`: generated proto 전체 import/instantiate compile guard를 추가한다.
- [ ] 필요한 경우 `apps/portal/pubspec.yaml`: generated code가 직접 import하는 package를 direct dependency로 추가한다.

중간 검증:

```bash
cd apps/portal && flutter test test/iop_wire
```

기대 결과: parser map/client tests와 generated proto import test가 모두 통과한다.

### [REVIEW_PORTAL_WIRE-3] 검증 신뢰 회복 및 touched file cleanup

문제: 첫 review file에는 `command -v protoc-gen-dart`와 `make proto-dart` 성공 출력이 기록되어 있었지만, 리뷰 재실행에서는 실패했다. 또한 touched Dart files에 unused import가 남아 있었다.

해결 방법: 후속 변경에서 touched 파일의 unused import를 제거한다. 최종 검증은 PATH를 임시 보정하지 않은 루트/앱 명령으로 실행하고, 실제 stdout/stderr를 `CODE_REVIEW-cloud-G08.md`에 기록한다.

수정 파일 및 체크리스트:

- [ ] `apps/portal/lib/iop_wire/portal_wire_client.dart`: unused import를 제거한다.
- [ ] `apps/portal/test/iop_wire/parser_map_test.dart`: unused import를 제거한다.
- [ ] `apps/portal/test/widget_test.dart`: unused import를 제거한다.
- [ ] `CODE_REVIEW-cloud-G08.md`: 실제 재실행 출력만 기록한다.

중간 검증:

```bash
git diff --check
```

기대 결과: whitespace 오류가 없고, 검증 기록이 실제 재실행 결과와 일치한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `Makefile` | REVIEW_PORTAL_WIRE-1 |
| `apps/portal/lib/gen/google/protobuf/*.dart` | REVIEW_PORTAL_WIRE-1 |
| `apps/portal/lib/gen/proto/iop/*.dart` | REVIEW_PORTAL_WIRE-1 |
| `apps/portal/README.md` | REVIEW_PORTAL_WIRE-1 |
| `apps/portal/pubspec.yaml` | REVIEW_PORTAL_WIRE-2 |
| `apps/portal/test/iop_wire/generated_proto_import_test.dart` | REVIEW_PORTAL_WIRE-2 |
| `apps/portal/lib/iop_wire/portal_wire_client.dart` | REVIEW_PORTAL_WIRE-3 |
| `apps/portal/test/iop_wire/parser_map_test.dart` | REVIEW_PORTAL_WIRE-3 |
| `apps/portal/test/widget_test.dart` | REVIEW_PORTAL_WIRE-3 |
| `CODE_REVIEW-cloud-G08.md` | REVIEW_PORTAL_WIRE-3 |

## 최종 검증

```bash
command -v protoc
make proto-dart
test -f apps/portal/lib/gen/google/protobuf/struct.pb.dart
cd apps/portal && flutter pub get
cd apps/portal && flutter test test/iop_wire
cd apps/portal && flutter test
cd apps/portal && flutter build web --dart-define=IOP_CONTROL_PLANE_HTTP_URL=http://localhost:9080 --dart-define=IOP_CONTROL_PLANE_WIRE_URL=ws://localhost:19080/portal
git diff --check
```

기대 결과: Dart proto generation이 PATH 임시 보정 없이 통과하고, generated Dart proto 전체 compile guard와 Flutter tests/web build가 통과한다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 채운다.
