<!-- task=m-flutter-console-skeleton/01_preview_flow plan=0 tag=UI -->

# Plan - UI Preview Flow

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

구현 완료 여부는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션까지 실제 내용과 검증 출력으로 채운 뒤에만 판단한다.
검증을 실행하고 실제 stdout/stderr와 증거 경로를 기록한 뒤 active 파일은 그대로 두고 리뷰 준비를 보고한다.
최종 판정, 로그 아카이브, `complete.log` 작성, task directory archive 이동은 code-review-skill 전용이다.
구현 중 사용자만 결정할 수 있는 항목, 사용자 소유 외부 환경, scope conflict가 있으면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 적고 멈춘다.
구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다.
후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

`preview-flow`는 Flutter client가 단위 테스트를 넘어 실제 web preview 첫 화면에서 Core endpoint와 UI skeleton을 안정적으로 렌더링한다는 증거를 남기는 작업이다.
코드 구조는 이미 `OtoConsoleConfig.serverHttpUrl`과 client web target을 가지고 있으나, 마일스톤 완료에는 원격 preview 명령 기준 smoke 결과가 필요하다.
이 계획은 코드 변경보다 원격 Core preflight, Flutter web launch, 화면 증거 수집을 분리해 검증한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다.
이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 사용한다.
직접 사용자 질문은 금지이며, code-review가 사용자 리뷰 필요 여부와 `USER_REVIEW.md` 생성을 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-product-surface/milestones/flutter-console-skeleton.md`
- Task ids:
  - `preview-flow`: 원격 preview 명령 `make client-run-web OTO_SERVER_HTTP_URL=http://toki-labs.com:18020` 기준으로 첫 화면이 Core API endpoint와 UI 스켈레톤을 안정적으로 렌더링한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/ROADMAP.md`
- `agent-roadmap/phase/control-plane-product-surface/PHASE.md`
- `agent-roadmap/phase/control-plane-product-surface/milestones/flutter-console-skeleton.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
- `agent-test/local/rules.md`
- `agent-test/local/framework-smoke.md`
- `agent-ops/rules/project/domain/framework/rules.md`
- `Makefile`
- `apps/client/lib/main.dart`
- `apps/client/lib/src/app/bootstrap.dart`
- `apps/client/lib/src/app/oto_client_app.dart`
- `apps/client/lib/src/app/core_connection_client.dart`
- `apps/client/pubspec.yaml`
- `apps/client/test/widget_test.dart`
- `packages/flutter/oto_console/lib/oto_console.dart`
- `packages/flutter/oto_console/lib/src/oto_console_contract.dart`
- `packages/flutter/oto_console/lib/src/oto_console_models.dart`
- `packages/flutter/oto_console/lib/src/oto_console_overview.dart`
- `packages/flutter/oto_console/lib/src/oto_console_shell.dart`
- `packages/flutter/oto_console/test/oto_console_test.dart`
- `services/core/internal/httpserver/routes.go`
- `services/core/internal/httpserver/runner_handlers.go`

### 테스트 환경 규칙

test_env는 `local`이다.
`agent-test/local/rules.md`를 읽었고, local 기준 기본 runner는 `ssh toki@toki-labs.com`, checkout은 `/Users/toki/agent-work/oto`, Core HTTP port는 `18020`, Flutter web preview port는 `13020`이다.
매칭 profile로 `agent-test/local/framework-smoke.md`를 읽었다.
적용 명령은 `cd apps/client && flutter pub get`, `cd apps/client && flutter analyze`, `cd apps/client && flutter test`, `make client-run-web OTO_SERVER_HTTP_URL=http://toki-labs.com:18020`이다.
`preview-flow` 화면 캡처는 framework-smoke에 구체 명령이 없어 Makefile과 local rule의 web preview 표준을 fallback source로 사용한다.

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

- Core health/readiness 상태 모델과 widget 렌더링은 `apps/client/test/widget_test.dart`와 `packages/flutter/oto_console/test/oto_console_test.dart`가 커버한다.
- 실제 remote Core URL, Flutter web compile/run, 브라우저 첫 화면 렌더링은 unit/widget test로 커버되지 않는다. 이 plan의 smoke 증거가 그 공백을 닫는다.

### 심볼 참조

none. 이 plan은 심볼 rename/remove를 수행하지 않는다.

### 분할 판단

split decision policy를 plan 파일 선택 전에 평가했다.
공유 task group은 `m-flutter-console-skeleton`이고 sibling은 `01_preview_flow` 하나다.
`01_preview_flow`는 predecessor가 없으며, browser/remote-run 증거 수집이 중심이라 직접 구현 범위와 분리했다.
새 코드/API foundation 변경, broad call-site rollout, schema 변경은 포함하지 않는다.

### 범위 결정 근거

이 plan은 `preview-flow` smoke 증거 수집만 다룬다.
Core CORS middleware 추가, same-origin proxy 구현, runner/job/execution CRUD, production 정적 배포 파이프라인은 마일스톤 범위 제외 또는 후속 작업이므로 제외한다.
Tracked 문서에는 credential, private release host, token 원문을 기록하지 않는다.

### 빌드 등급

`cloud-G07`로 둔다. Flutter web preview와 브라우저 화면 증거가 핵심이고, 장기 실행 command 및 외부 endpoint 상태를 함께 판정해야 한다.

## 구현 체크리스트

- [ ] 원격 Core endpoint preflight로 `http://toki-labs.com:18020/healthz`와 `/readyz`가 HTTP 200 `OK`를 반환하는지 기록한다.
- [ ] `make client-run-web OTO_SERVER_HTTP_URL=http://toki-labs.com:18020` 기준으로 Flutter web preview를 port `13020`에 띄우고 실행 로그를 `/tmp/oto-client-run-web.log`에 남긴다.
- [ ] preview 첫 화면 증거를 수집하고 `OTO_SERVER_HTTP_URL`, Overview, Runners/Pipelines/Executions/Artifacts/Settings skeleton 확인 결과를 기록한다.
- [ ] preview 프로세스를 종료하고 실패 시 CORS, Chrome/device, Core availability 중 어느 계층인지 로그로 분류한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [UI-1] Remote Core Preflight

문제: `preview-flow`는 원격 Core URL 기준 검증을 요구하지만, 단위 테스트는 실제 `http://toki-labs.com:18020` 상태를 보장하지 않는다. 마일스톤의 `preview-flow` 항목은 원격 preview 기준을 명시한다(`agent-roadmap/phase/control-plane-product-surface/milestones/flutter-console-skeleton.md:43`).

해결 방법: preview 실행 전에 `/healthz`와 `/readyz`를 직접 확인한다. 둘 중 하나라도 실패하면 Flutter UI 문제가 아니라 Core availability 또는 network 문제로 분류한다.

수정 파일 및 체크리스트:

- [ ] 코드 파일 수정 없음
- [ ] `CODE_REVIEW-cloud-G07.md`에 curl stdout/stderr와 HTTP body를 기록

테스트 작성: 작성하지 않는다. 이 항목은 외부 endpoint smoke evidence이며 기존 widget test가 대체할 수 없다.

중간 검증:

```bash
curl -fsS http://toki-labs.com:18020/healthz && printf '\n' && curl -fsS http://toki-labs.com:18020/readyz && printf '\n'
```

기대 결과: 두 응답이 각각 `OK`이고 명령 exit code가 0이다.

### [UI-2] Flutter Web Preview Smoke

문제: `Makefile:56`부터 `client-run-web`는 Chrome device와 `OTO_SERVER_HTTP_URL` dart-define을 사용하지만, 현재 마일스톤 완료 근거에는 이 명령으로 첫 화면이 안정적으로 뜬 증거가 없다.

해결 방법: 원격 runner 또는 local smoke 환경에서 preview를 시작하고 `/tmp/oto-client-run-web.log`에 compile/run 로그를 남긴다. 2분 안에 launch evidence가 없으면 blind retry를 멈추고 focused log를 review stub에 기록한다.

수정 파일 및 체크리스트:

- [ ] 코드 파일 수정 없음
- [ ] `/tmp/oto-client-run-web.log`에 launch log 저장
- [ ] preview PID를 `/tmp/oto-client-run-web.pid`에 저장하고 검증 후 종료

테스트 작성: 작성하지 않는다. 이 항목은 Flutter web runtime smoke이고, code-level widget test는 이미 별도 검증으로 유지한다.

중간 검증:

```bash
bash -lc 'set -euo pipefail; rm -f /tmp/oto-client-run-web.log /tmp/oto-client-run-web.pid; make client-run-web OTO_SERVER_HTTP_URL=http://toki-labs.com:18020 > /tmp/oto-client-run-web.log 2>&1 & echo $! > /tmp/oto-client-run-web.pid; for i in $(seq 1 60); do if grep -Eq "Flutter run key commands|An Observatory debugger|Debug service listening" /tmp/oto-client-run-web.log; then tail -80 /tmp/oto-client-run-web.log; exit 0; fi; if ! kill -0 "$(cat /tmp/oto-client-run-web.pid)" 2>/dev/null; then cat /tmp/oto-client-run-web.log; exit 1; fi; sleep 2; done; tail -120 /tmp/oto-client-run-web.log; exit 1'
```

기대 결과: Flutter web이 compile되고 Chrome/web preview launch evidence가 log에 남는다.

### [UI-3] First Screen Evidence

문제: Flutter web compile 성공만으로는 첫 화면이 Core endpoint와 skeleton을 렌더링했는지 알 수 없다. `OtoClientApp`는 `OTO_SERVER_HTTP_URL`로 health/readiness adapter를 실행하고 Overview에 상태를 전달한다(`apps/client/lib/src/app/oto_client_app.dart:79`).

해결 방법: preview가 떠 있는 동안 browser screenshot 또는 UI-tree equivalent를 수집한다. 화면 증거에는 `http://toki-labs.com:18020`, `Build Operations Overview`, `Runners`, `Pipelines`, `Executions`, `Artifacts`, `Settings` 중 확인 가능한 항목을 기록한다.

수정 파일 및 체크리스트:

- [ ] 코드 파일 수정 없음
- [ ] screenshot/UI evidence path를 `CODE_REVIEW-cloud-G07.md`에 기록
- [ ] capture가 불가능하면 command/output과 불가능 사유를 기록

테스트 작성: 작성하지 않는다. 이 항목은 사람이 보는 preview smoke evidence이며 자동 widget test와 목적이 다르다.

중간 검증:

```bash
bash -lc 'test -f /tmp/oto-client-run-web.pid && kill -0 "$(cat /tmp/oto-client-run-web.pid)" && printf "preview-process=running\n" && tail -80 /tmp/oto-client-run-web.log'
```

기대 결과: preview process가 실행 중이고 log에 launch evidence가 남아 있다. screenshot 또는 UI evidence는 review stub에 경로로 남긴다.

### [UI-4] Cleanup

문제: `flutter run`은 장기 실행 프로세스라 검증 후 남겨두면 후속 테스트의 port `13020`을 점유할 수 있다.

해결 방법: `/tmp/oto-client-run-web.pid`의 프로세스를 종료하고 log tail을 남긴다.

수정 파일 및 체크리스트:

- [ ] 코드 파일 수정 없음
- [ ] preview process 종료 확인 기록

테스트 작성: 작성하지 않는다. cleanup verification만 수행한다.

중간 검증:

```bash
bash -lc 'if test -f /tmp/oto-client-run-web.pid; then kill "$(cat /tmp/oto-client-run-web.pid)" 2>/dev/null || true; sleep 2; kill -0 "$(cat /tmp/oto-client-run-web.pid)" 2>/dev/null && exit 1 || true; fi; tail -80 /tmp/oto-client-run-web.log'
```

기대 결과: preview process가 종료되고 log tail이 확인 가능하다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| 코드 파일 없음 | UI-1, UI-2, UI-3, UI-4 |
| `agent-task/m-flutter-console-skeleton/01_preview_flow/CODE_REVIEW-cloud-G07.md` | UI-1, UI-2, UI-3, UI-4 |

## 최종 검증

아래 네 명령을 순서대로 실행한다.

```bash
cd /config/workspace/oto && curl -fsS http://toki-labs.com:18020/healthz && printf '\n' && curl -fsS http://toki-labs.com:18020/readyz && printf '\n'
cd /config/workspace/oto && make client-test
cd /config/workspace/oto && bash -lc 'set -euo pipefail; rm -f /tmp/oto-client-run-web.log /tmp/oto-client-run-web.pid; make client-run-web OTO_SERVER_HTTP_URL=http://toki-labs.com:18020 > /tmp/oto-client-run-web.log 2>&1 & echo $! > /tmp/oto-client-run-web.pid; for i in $(seq 1 60); do if grep -Eq "Flutter run key commands|An Observatory debugger|Debug service listening" /tmp/oto-client-run-web.log; then tail -80 /tmp/oto-client-run-web.log; exit 0; fi; if ! kill -0 "$(cat /tmp/oto-client-run-web.pid)" 2>/dev/null; then cat /tmp/oto-client-run-web.log; exit 1; fi; sleep 2; done; tail -120 /tmp/oto-client-run-web.log; exit 1'
cd /config/workspace/oto && bash -lc 'test -f /tmp/oto-client-run-web.pid && kill "$(cat /tmp/oto-client-run-web.pid)" 2>/dev/null || true; tail -80 /tmp/oto-client-run-web.log'
```

기대 결과: Core endpoint preflight는 `OK`, `make client-test`는 pass, web preview log는 launch evidence를 포함한다. screenshot/UI evidence path는 review stub에 추가로 기록한다.

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