<!-- task=m-jenkins-credential/04+03_reauth_ui plan=0 tag=REAUTH_UI -->

# Plan - REAUTH_UI

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

이 파일은 구현 루프의 입력이다. 구현 전 같은 task group의 `03_reauth_status_model`이 `complete.log`를 냈는지 확인한다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채우고, active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경, secret, 서비스 준비가 막히면 리뷰 stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. archive, `complete.log`, task-directory 이동은 code-review 전용이다.

## 배경

`reauth` 기능 Task는 token 폐기, 권한 없음, Jenkins URL 변경, 네트워크 실패를 사용자가 구분할 수 있어야 완료된다. 현재 앱 루트는 restore 결과를 bool로만 보고 [app.dart](/config/workspace/appsok/lib/src/app.dart:104)에서 job loader를 켜거나 끄며, [builds_page.dart](/config/workspace/appsok/lib/src/features/builds/builds_page.dart:157)는 loader가 없으면 항상 같은 `Jenkins 연결 필요` 문구만 보여준다. `03_reauth_status_model`의 상세 restore 결과를 받아 app/build/settings UI에 재로그인 또는 재시도 안내를 연결한다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 결정이나 사용자 소유 환경 때문에 멈춰야 하면 active review stub의 `사용자 리뷰 요청` 섹션을 채운다. 직접 사용자에게 질문하지 않으며, code-review가 그 요청을 검증하고 필요 시 `USER_REVIEW.md`를 작성한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/usable-mvp/milestones/jenkins-credential.md`
- Task ids:
  - `reauth`: token 폐기, 권한 없음, Jenkins URL 변경, 네트워크 실패를 구분해 재로그인 또는 재시도 흐름으로 안내한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/usable-mvp/PHASE.md`
- `agent-roadmap/phase/usable-mvp/milestones/jenkins-credential.md`
- `agent-ops/rules/project/domain/artifact-flow/rules.md`
- `agent-ops/rules/project/domain/app-shell/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/artifact-flow-smoke.md`
- `agent-test/local/app-shell-smoke.md`
- `lib/src/app.dart`
- `lib/src/features/app_shell.dart`
- `lib/src/features/builds/builds_page.dart`
- `lib/src/features/settings/settings_page.dart`
- `lib/src/services/jenkins_artifact_session.dart`
- `lib/src/services/jenkins_client.dart`
- `test/app_auth_test.dart`
- `test/builds_page_test.dart`
- `test/settings_page_test.dart`
- `test/widget_test.dart`
- `test/jenkins_artifact_session_test.dart`

### 테스트 환경 규칙

- test_env: `local`
- `agent-test/local/rules.md`를 읽었다. 완료 evidence는 standard remote Mac runner 기준이다.
- matched profiles:
  - `agent-test/local/artifact-flow-smoke.md`: Jenkins 설정 UI, Jenkins 인증 흐름, build 조회 상태 검증에 적용한다.
  - `agent-test/local/app-shell-smoke.md`: `AppSokApp`/`AppSokShell` wiring과 navigation 상태 검증에 적용한다.
- 필수 최종 검증은 remote runner에서 `flutter analyze`, `flutter test`다. focused preflight로 `flutter test test/app_auth_test.dart test/builds_page_test.dart test/settings_page_test.dart test/jenkins_artifact_session_test.dart`를 사용한다.

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

- `BuildsPage`는 job/build loader의 401/403과 server failure 화면을 테스트하지만, startup restore 실패 원인별 화면은 없다.
- `AppSokApp`은 저장 session 성공/실패와 token save rollback을 테스트하지만, 네트워크 실패 시 credential 보존과 retry, 401/403 재로그인 필요 표시, URL 변경 후 재로그인 안내를 구분하지 않는다.
- Settings URL 변경 시 기존 성공/오류 상태를 어떻게 재로그인 안내로 바꿀지 검증이 없다.

### 심볼 참조

- `03_reauth_status_model`에서 추가될 상세 restore API를 사용한다.
- 새 UI 상태 타입을 추가하면 call site는 `lib/src/app.dart`, `lib/src/features/app_shell.dart`, `lib/src/features/builds/builds_page.dart`, 관련 widget tests다.
- 제거 예정 symbol 없음.

### 분할 판단

- split decision policy를 먼저 평가했다.
- 공유 task group: `m-jenkins-credential`
- sibling plans:
  - `03_reauth_status_model`: restore 원인 보존 모델. 이 task의 predecessor다.
  - `04+03_reauth_ui`: app/build/settings UI rollout. 이 plan이다.
- predecessor `03`: 현재 active path `agent-task/m-jenkins-credential/03_reauth_status_model/`에 plan/review가 있으며 아직 `complete.log`는 없다. 구현은 해당 predecessor PASS 후 시작해야 한다.

### 범위 결정 근거

- 이 task는 `reauth` 사용자 안내와 retry/re-login flow만 다룬다.
- `logout-clear`는 작은 작업으로 직접 구현되어 제외한다.
- Jenkins API 요청/파싱 자체와 WebView token bridge는 변경하지 않는다.
- 실제 Jenkins endpoint 수동 smoke는 credential/endpoint가 제공될 때만 보조 검증이다. tracked 문서나 로그에 secret 원문을 기록하지 않는다.

### 빌드 등급

- `cloud-G06`: auth 상태, storage side effect, app-shell wiring, user-visible UI copy가 얽힌 cross-domain 작업이며 원인별 회귀 테스트가 필요하다.

## 구현 체크리스트

- [ ] `03_reauth_status_model`의 `complete.log`가 active 또는 archive 경로에 있는지 확인하고 상세 restore 결과 API 이름을 실제 구현과 맞춘다.
- [ ] `AppSokApp`이 상세 restore 결과를 보존하고 `AppSokShell`/`BuildsPage`에 connection 상태와 retry callback을 전달하게 한다.
- [ ] `BuildsPage`가 credential 없음, 재로그인 필요, 네트워크 실패, 서버 실패를 서로 다른 label/description/action 상태로 보여주게 한다.
- [ ] `SettingsPage`가 Jenkins URL 변경과 Web Login 성공/clear 후 상태를 충돌 없이 정리하고 재로그인 안내 흐름과 맞게 동작하게 한다.
- [ ] 원인별 widget/app regression tests를 추가한다.
- [ ] `flutter test test/app_auth_test.dart test/builds_page_test.dart test/settings_page_test.dart test/jenkins_artifact_session_test.dart`로 중간 검증한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- 이 subtask directory `04+03_reauth_ui`는 predecessor index `03`에 의존한다.
- 구현 전 다음 중 하나가 있어야 한다.
  - active: `agent-task/m-jenkins-credential/03_reauth_status_model/complete.log`
  - archive: `agent-task/archive/*/*/m-jenkins-credential/03_reauth_status_model/complete.log`
- 현재 계획 작성 시점에는 predecessor `complete.log`가 없다.

### [REAUTH_UI-1] App Restore Status Wiring

#### 문제

[app.dart](/config/workspace/appsok/lib/src/app.dart:52)는 `_startup`을 `Future<bool>`로 저장하고 [app.dart](/config/workspace/appsok/lib/src/app.dart:104)에서 `FutureBuilder<bool>`로만 판단한다. [app.dart](/config/workspace/appsok/lib/src/app.dart:90)의 token save validation도 bool만 사용한다.

Before:

```dart
// lib/src/app.dart:52
late Future<bool> _startup = _session.restore();

// lib/src/app.dart:104
return FutureBuilder<bool>(
  future: _startup,
  builder: (context, snapshot) {
    final isReady = snapshot.data ?? false;
```

이 구조에서는 401/403, credential 없음, IOException, Jenkins 500 계열 실패가 모두 같은 "not ready"로 떨어진다.

#### 해결 방법

- `03_reauth_status_model`의 상세 restore result를 `_startup` future 타입으로 사용한다.
- `isReady`는 상세 result의 restored 여부로 계산한다.
- network/server failure에는 retry callback이 다시 상세 restore를 실행한다.
- reauth required에는 저장 credential이 이미 삭제된 상태를 UI에 전달한다.
- token save validation은 상세 result를 사용해 auth 실패와 기타 실패를 명확히 처리하되, 기존 rollback 안전성은 유지한다.

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

- [ ] `lib/src/app.dart`: `_startup` 타입, `_onSessionSaved`, `_clearSession`, `_saveToken`, `FutureBuilder` 업데이트.
- [ ] `lib/src/features/app_shell.dart`: connection status/retry callback을 shell에서 `BuildsPage`로 전달.

#### 테스트 작성

- `test/app_auth_test.dart`: 저장 credential 401/403이면 재로그인 필요 UI가 보이고 저장 session이 삭제된다.
- `test/app_auth_test.dart`: 저장 credential 네트워크 실패이면 재시도 UI가 보이고 저장 session은 남는다.
- `test/app_auth_test.dart`: retry callback 성공 후 build loader가 활성화된다.

#### 중간 검증

```bash
flutter test test/app_auth_test.dart test/jenkins_artifact_session_test.dart
```

예상: app-level auth/session tests 통과.

### [REAUTH_UI-2] Reauth And Retry UI States

#### 문제

[builds_page.dart](/config/workspace/appsok/lib/src/features/builds/builds_page.dart:157)는 `jobLoader == null`이면 항상 `Jenkins 연결 필요`만 보여준다. 같은 파일의 [builds_page.dart](/config/workspace/appsok/lib/src/features/builds/builds_page.dart:35)는 loader 실행 중의 `forbidden`과 `failure`만 구분하므로 startup restore 실패 원인에는 적용되지 않는다.

Before:

```dart
// lib/src/features/builds/builds_page.dart:157
if (widget.jobLoader == null) {
  return const _JobStatus(
    icon: Icons.login,
    label: 'Jenkins 연결 필요',
    description: '설정에서 Jenkins URL과 인증 정보를 입력하세요.',
  );
}
```

#### 해결 방법

- `BuildsPage`에 connection status와 retry callback을 받는 public enum/value를 추가한다.
- 상태별 UI를 분리한다.
  - missing/urlChanged: `Jenkins 연결 필요`, 설정에서 URL 확인 후 Web Login.
  - reauthRequired: `재로그인 필요`, token 폐기 또는 권한 없음으로 Web Login 재실행 안내.
  - networkFailure: `Jenkins 연결 실패`, retry 버튼 제공.
  - serverFailure: `Jenkins 응답 오류`, retry 버튼 제공.
- `SettingsPage`의 Base URL 변경은 token save success/clear success와 충돌하지 않게 transient success state를 초기화하고 Web Login 재실행 경로를 유지한다.

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

- [ ] `lib/src/features/builds/builds_page.dart`: connection status value와 status UI/action 추가.
- [ ] `lib/src/features/settings/settings_page.dart`: Base URL 변경 시 login/token/clear transient state 정리.
- [ ] `test/builds_page_test.dart`: 상태별 label/action regression tests 추가.
- [ ] `test/settings_page_test.dart`: URL 변경 시 stale success label 제거 테스트 추가.

#### 테스트 작성

- `BuildsPage renders reauth required state without job loader`
- `BuildsPage renders network retry state and calls retry`
- `BuildsPage renders server failure retry state`
- `SettingsPage clears transient login/save/clear state when base url changes`

#### 중간 검증

```bash
flutter test test/builds_page_test.dart test/settings_page_test.dart
```

예상: 상태별 UI tests 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/src/app.dart` | REAUTH_UI-1 |
| `lib/src/features/app_shell.dart` | REAUTH_UI-1 |
| `lib/src/features/builds/builds_page.dart` | REAUTH_UI-2 |
| `lib/src/features/settings/settings_page.dart` | REAUTH_UI-2 |
| `test/app_auth_test.dart` | REAUTH_UI-1 |
| `test/builds_page_test.dart` | REAUTH_UI-2 |
| `test/settings_page_test.dart` | REAUTH_UI-2 |
| `test/jenkins_artifact_session_test.dart` | REAUTH_UI-1 |

## 최종 검증

```bash
git diff --check
flutter test test/app_auth_test.dart test/builds_page_test.dart test/settings_page_test.dart test/jenkins_artifact_session_test.dart
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'zsh -lc '\''cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter analyze && flutter test'\'''
```

예상: 공백 오류 없음, focused tests 통과, remote analyzer issue 없음, remote full test 통과.

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