<!-- task=m-jenkins-credential/01_web_login_entry plan=1 tag=REVIEW_WEB_LOGIN -->

# Plan - REVIEW_WEB_LOGIN

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

이 plan은 이전 코드리뷰 FAIL의 Required 항목만 고친다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, 명령 출력은 `CODE_REVIEW-local-G06.md`에 남긴다. active 파일은 그대로 두고 리뷰 준비 상태를 보고하며, archive/log rename/complete.log 작성은 code-review 전용이다. 사용자만 결정할 수 있는 blocker가 생기면 review stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 `USER_REVIEW.md`, `complete.log`를 직접 만들지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/usable-mvp/milestones/jenkins-credential.md`
- Task ids:
  - `login-webview`: Jenkins 로그인 URL을 WebView로 열고 로그인 완료를 감지한다.
- Completion mode: check-on-pass

## 배경

첫 구현은 remote `flutter analyze`, `flutter test`, `flutter build macos`를 통과했지만 두 Required 이슈가 남았다.

- `lib/src/features/settings/jenkins_web_login_page.dart`의 login/probe URL 조립이 Jenkins base URL의 path를 보존하지 못한다.
- `lib/src/app.dart`에 계획된 optional web login callback surface가 없어 앱 루트에서 후속 인증 저장 흐름을 연결할 수 없다.

## 구현 체크리스트

- [ ] WebView login/probe URL을 `Uri` 기반 helper로 조립해 root URL, trailing slash, context path base URL에서 path를 보존한다.
- [ ] `AppSokApp`에 optional `webLoginLauncher`와 `onWebLoginCompleted` surface를 추가하고 `AppSokShell`로 전달한다.
- [ ] context path/trailing slash URL helper 테스트와 `AppSokApp` callback wiring widget test를 추가한다.
- [ ] focused test 명령을 통과시킨다.
- [ ] 최종 remote 검증 명령을 실행한다.
- [ ] `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_WEB_LOGIN-1] Jenkins Base Path URL 조립 보정

#### 문제

`lib/src/features/settings/jenkins_web_login_page.dart`의 probe는 `fetch('/whoAmI/api/json', ...)`로 origin root를 고정하고, login URL은 문자열 결합으로 `${baseUrl}/login`을 만든다. 이 방식은 `https://host/jenkins` 같은 valid Jenkins base URL에서 `/jenkins/login`과 `/jenkins/whoAmI/api/json`를 보장하지 못한다.

#### 해결 방법

- `Uri.parse(baseUrl.trim())` 결과에 대해 path append helper를 사용한다.
- root base URL은 `/login`, `/whoAmI/api/json`를 만든다.
- `/jenkins`와 `/jenkins/` base URL은 `/jenkins/login`, `/jenkins/whoAmI/api/json`를 만든다.
- JS probe 문자열에는 computed path를 주입하되 `jsonEncode(path)`처럼 안전한 literal 생성을 사용한다.
- `loadRequest`는 문자열 결합 대신 helper가 반환한 `Uri`를 사용한다.

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

- [ ] `lib/src/features/settings/jenkins_web_login_page.dart`: login URI/probe JS helper 추가 및 WebViewController 적용.
- [ ] `test/jenkins_web_login_page_test.dart`: root, trailing slash, context path base URL의 login/probe path 테스트 추가.

#### 중간 검증

```bash
flutter test test/jenkins_web_login_page_test.dart
```

예상: URL helper와 probe parsing tests pass.

### [REVIEW_WEB_LOGIN-2] AppSokApp Web Login Surface Wiring

#### 문제

`lib/src/features/app_shell.dart`에는 `webLoginLauncher`와 `onWebLoginCompleted`가 추가됐지만, `lib/src/app.dart`의 `AppSokApp` 생성자와 shell 생성부에는 같은 surface가 없다. 계획의 WEB_LOGIN-1은 후속 plan에서 쓸 수 있도록 `AppSokApp` callback surface를 보존해야 한다고 했다.

#### 해결 방법

- `AppSokApp` 생성자에 `JenkinsWebLoginLauncher? webLoginLauncher`와 `void Function(JenkinsWebLoginResult result)? onWebLoginCompleted`를 추가한다.
- `MaterialApp`의 `AppSokShell` 생성부로 두 필드를 전달한다.
- token/crumb 저장은 하지 않는다.
- widget test에서 `AppSokApp`으로 launcher와 completion callback을 주입한 뒤 Settings 화면에서 Web Login을 실행해 callback이 호출되는지 검증한다.

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

- [ ] `lib/src/app.dart`: optional web login fields와 shell wiring.
- [ ] `test/widget_test.dart`: `AppSokApp` web login launcher/completion wiring 테스트.

#### 중간 검증

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

예상: 앱 루트와 Settings wiring tests pass.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/src/features/settings/jenkins_web_login_page.dart` | REVIEW_WEB_LOGIN-1 |
| `lib/src/app.dart` | REVIEW_WEB_LOGIN-2 |
| `test/jenkins_web_login_page_test.dart` | REVIEW_WEB_LOGIN-1 |
| `test/widget_test.dart` | REVIEW_WEB_LOGIN-2 |

## 최종 검증

```bash
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter analyze'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter test'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter build macos'
```

예상: analyzer issue 없음, 모든 테스트 통과, macOS build 성공.

