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

# Plan - REVIEW_REAUTH_UI

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

이 파일은 `code_review_cloud_G06_0.log`의 FAIL 판정을 해결하는 후속 구현 계획이다. 구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경, secret, 서비스 준비가 막히면 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 멈춘다. 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. archive, `complete.log`, task-directory 이동은 code-review 전용이다.

## Roadmap Targets

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

## 배경

1차 리뷰에서 구현 보고서는 원인별 restore UI와 retry가 완료됐다고 기록했지만 실제 소스는 아직 `Future<bool>` 기반 restore와 generic `Jenkins 연결 필요` UI를 유지하고 있었다. `03_reauth_status_model`은 이미 `agent-task/archive/2026/06/m-jenkins-credential/03_reauth_status_model/complete.log`로 완료되어 있으므로, 이번 루프는 그 상세 result API를 app-shell/builds/settings UI에 실제로 연결한다.

## 범위 결정 근거

- 포함: `AppSokApp` restore result wiring, `AppSokShell` pass-through, `BuildsPage` 원인별 상태 UI/retry, `SettingsPage` URL 변경 stale 상태 정리, 원인별 widget/app tests, 검증 증거 복구, 계획 외 `pubspec.lock` diff 정리.
- 제외: Jenkins WebView bridge 자체, Jenkins API parsing, Keychain entitlement, 실제 Jenkins endpoint 수동 smoke.
- `Roadmap Targets`는 기존 plan과 동일하게 `reauth`만 유지한다.

## 구현 체크리스트

- [ ] `AppSokApp`이 `restoreDetailed()` 결과를 보존하고 `JenkinsSessionRestoreKind`를 UI용 connection status로 변환해 `AppSokShell`에 전달하게 한다.
- [ ] `AppSokShell`이 connection status와 retry callback을 `BuildsPage`로 전달하게 한다.
- [ ] `BuildsPage`가 credential 없음, 재로그인 필요, 네트워크 실패, 서버 실패를 서로 다른 label/description/action 상태로 보여주고 network/server 실패에서 retry를 호출하게 한다.
- [ ] `SettingsPage`가 Jenkins URL 변경 시 Web Login 결과, token 저장 상태, credential 삭제 상태를 모두 초기화하게 한다.
- [ ] 원인별 app/builds/settings regression tests를 추가하고 구현 보고서의 검증 출력이 실제 실행 결과와 일치하게 기록한다.
- [ ] 계획 범위 밖 `pubspec.lock` downgrade를 정리하거나, 의도된 SDK 해상도 변경이면 근거와 검증을 기록한다.
- [ ] `git diff --check`, focused Flutter tests, `flutter analyze`, `flutter test`를 실행하고 가능한 remote runner evidence를 정확히 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REAUTH_UI-1] App Restore Result Wiring

#### 문제

`lib/src/app.dart:52`가 `Future<bool>`와 `_session.restore()`를 사용한다. 이 때문에 `JenkinsSessionRestoreKind.reauthRequired`, `networkFailure`, `serverFailure`, `missing`이 모두 `false`로 축약된다.

#### 해결 방법

- `_startup`을 `Future<JenkinsSessionRestoreResult>`로 바꾸고 초기 restore, Web Login 이후 restore, retry restore가 모두 `restoreDetailed()`를 사용하게 한다.
- `JenkinsSessionRestoreKind`를 `JenkinsConnectionStatus`로 변환하는 helper를 추가한다.
- restored가 아니면 loader는 null로 유지하되, detailed status는 shell/builds까지 전달한다.
- network/server failure에서는 저장 credential을 유지하고 retry callback으로 다시 `restoreDetailed()`를 실행한다.

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

- [ ] `lib/src/app.dart`: `_startup`, `_onSessionSaved`, `_clearSession`, `_saveToken`, `_retryRestore`, `FutureBuilder`를 detailed result 기반으로 갱신.
- [ ] `lib/src/app.dart`: 401/403 validation 실패는 clear 유지, network/server failure는 credential 보존 정책을 깨지 않는지 확인.

#### 테스트 결정

- `test/app_auth_test.dart`에 startup 401/403 reauth UI, startup network failure credential 보존 + retry, retry 성공 후 job loader 활성화 test를 추가한다.

#### 중간 검증

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

### [REVIEW_REAUTH_UI-2] Shell And BuildsPage Status UI Contract

#### 문제

`lib/src/features/builds/builds_page.dart:39`의 `BuildsPage` API에는 connection status와 retry callback이 없다. `lib/src/features/builds/builds_page.dart:157`은 loader가 없으면 항상 generic missing 상태만 보여준다.

#### 해결 방법

- `BuildsPage`가 소비할 public `JenkinsConnectionStatus` enum을 추가하거나, 기존 local pattern에 맞는 동등한 public value를 추가한다.
- `AppSokShell`이 app에서 받은 status/retry를 `BuildsPage`에 전달한다.
- `BuildsPage`는 상태별 UI를 구분한다.
  - missing: Jenkins URL과 인증 정보 입력 안내.
  - reauthRequired: token 폐기 또는 권한 없음으로 Web Login 재실행 안내.
  - networkFailure: 네트워크 연결 실패와 retry action.
  - serverFailure: Jenkins 응답 오류와 retry action.
- retry action button에는 안정적인 key를 부여한다.

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

- [ ] `lib/src/features/app_shell.dart`: status/retry constructor field와 `BuildsPage` 전달 추가.
- [ ] `lib/src/features/builds/builds_page.dart`: status enum/value, `connectionStatus`, `onRetryConnection`, 상태별 `_JobStatus` action 추가.
- [ ] 기존 job loader 401/403/build failure UI와 새 startup restore failure UI를 혼동하지 않도록 테스트 이름과 UI 문구를 구분한다.

#### 테스트 결정

- `test/builds_page_test.dart`에 reauthRequired/networkFailure/serverFailure/missing 상태, retry callback 호출 test를 추가한다.

#### 중간 검증

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

### [REVIEW_REAUTH_UI-3] Settings URL Change Clears Stale State

#### 문제

`lib/src/features/settings/settings_page.dart:77`의 `_onBaseUrlChanged()`가 `setState`만 호출해 이전 Web Login 결과, token save success/error, clear success/error가 새 Jenkins URL 입력 뒤에도 남을 수 있다.

#### 해결 방법

- URL 변경 listener에서 `_webLoginError`, `_lastLoginResult`, `_tokenSaved`, `_tokenSaveError`, `_sessionCleared`, `_sessionClearError`를 초기화한다.
- 현재 저장/로그인/삭제 작업 중 URL이 바뀌는 edge case는 기존 busy 버튼 disable 정책과 충돌하지 않게 유지한다.

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

- [ ] `lib/src/features/settings/settings_page.dart`: `_onBaseUrlChanged()` transient state reset.
- [ ] state reset이 URL validation error 표시와 충돌하지 않는지 확인.

#### 테스트 결정

- `test/settings_page_test.dart`에 Web Login success/token saved/session cleared 상태 후 URL 변경 시 stale label이 사라지는 test를 추가한다.

#### 중간 검증

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

### [REVIEW_REAUTH_UI-4] Required Regression Tests And Verification Evidence

#### 문제

1차 보고서는 원인별 regression tests와 `+73` focused test 결과를 주장했지만 실제 focused command는 `+64`로 통과했고, 계획의 원인별 tests가 없다.

#### 해결 방법

- 새 구현과 직접 연결된 app/builds/settings tests를 추가한다.
- 검증 결과에는 실제 stdout/stderr를 붙이고, counts를 재구성하지 않는다.
- remote runner가 같은 변경을 포함하지 않으면 PASS처럼 기록하지 말고 exact command, `git rev-parse HEAD`, `git status --short`, 미반영 사유를 기록한다.

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

- [ ] `test/app_auth_test.dart`: startup restore 401, 403, network failure/retry success.
- [ ] `test/builds_page_test.dart`: status별 label/action/retry.
- [ ] `test/settings_page_test.dart`: URL 변경 stale state reset.
- [ ] `CODE_REVIEW-cloud-G06.md`: 실제 실행 출력만 기록.

#### 중간 검증

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

### [REVIEW_REAUTH_UI-5] Unplanned pubspec.lock Diff

#### 문제

`pubspec.yaml` 변경 없이 `pubspec.lock`에서 `meta`와 `test_api`가 downgrade되어 있다. 이번 계획과 무관한 dependency churn이다.

#### 해결 방법

- 현재 Flutter SDK에서 의도된 lockfile 해상도인지 확인한다.
- 의도되지 않았다면 이번 task diff에서 제거한다.
- 의도된 변경이면 `계획 대비 변경 사항`에 SDK/version 근거와 `flutter pub get`/test 결과를 기록한다.

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

- [ ] `pubspec.lock`: 불필요한 downgrade 제거 또는 근거 기록.
- [ ] `git diff -- pubspec.yaml pubspec.lock`로 dependency 변경 범위를 확인.

#### 중간 검증

```bash
git diff -- pubspec.yaml pubspec.lock
```

## 최종 검증

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

remote runner가 같은 변경을 포함하지 않아 검증할 수 없으면 그 사실을 PASS로 바꿔 쓰지 말고, 실행한 command와 실제 출력, 남은 위험을 `CODE_REVIEW-cloud-G06.md`에 그대로 남긴다.
