<!-- task=m-jenkins-credential/01_session_foundation plan=2 tag=REVIEW_API -->

# Plan - REVIEW_API

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

이 작업은 이전 리뷰 `code_review_local_G05_0.log`의 Required 이슈만 해결한다. 구현 후 active `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 채워야 완료된다. 검증 명령은 계획에 적힌 대로 실행하고 실제 stdout/stderr를 붙여 넣은 뒤, active 파일을 그대로 둔 채 리뷰 준비를 보고한다. `코드리뷰 결과`, log archive, `complete.log`, task directory archive는 code-review 전용이다.

구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 secret/외부 Jenkins 준비, 또는 이 계획과 충돌하는 요구가 발견되면 사용자에게 직접 묻지 않는다. `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청` 섹션에 결정 항목, 근거, 실행한 명령과 출력, 재개 조건을 기록하고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 계획 대비 변경 사항으로 기록한다.

## 배경

첫 리뷰에서 `JenkinsClient.fetchCurrentUser`의 표시명 fallback이 계획/설계와 다르게 동작하는 Required 이슈가 확인됐다. 현재 구현은 `fullName`이 빈 문자열이면 `name` 후보를 확인하지 않고 `id`로 떨어질 수 있다. 후속 wiring은 `JenkinsUser.displayName`을 사용자 표시명으로 사용할 예정이므로 parser 계약을 정확히 닫아야 한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`에서 복사된 형식을 사용한다. 구현 에이전트는 직접 사용자 프롬프트, 채팅 선택지, `request_user_input`, `USER_REVIEW.md` 생성, log archive, `complete.log` 작성을 하지 않는다.

## 분석 결과

### 읽은 파일

- `agent-task/m-jenkins-credential/01_session_foundation/code_review_local_G05_0.log`
- `agent-task/m-jenkins-credential/01_session_foundation/plan_local_G05_0.log`
- `agent-ops/rules/project/domain/artifact-flow/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/artifact-flow-smoke.md`
- `lib/src/services/jenkins_client.dart`
- `test/jenkins_client_test.dart`

### 테스트 환경 규칙

- test_env: local.
- matched profile: `agent-test/local/artifact-flow-smoke.md`.
- 적용 명령: remote runner checkout에서 `flutter test test/jenkins_client_test.dart`, `flutter analyze`, `flutter test`; 보조로 현재 checkout `git diff --check`.
- 실제 Jenkins endpoint/credential은 필요하지 않으며 mock HTTP 테스트로 검증한다.

### 범위 결정 근거

- `TokenStore` session API는 이번 Required 이슈와 무관하므로 변경하지 않는다.
- 앱 root wiring, WebView 로그인, 실제 Jenkins smoke는 이 follow-up 범위가 아니다.
- 첫 리뷰의 유일한 Required 이슈만 닫는다.

### 빌드 등급

- build lane: `local-G06`.
- review lane: `local-G06`.
- 근거: 단일 parser edge case와 테스트 보강이며 deterministic local/remote runner 검증으로 판단 가능하다.

## 구현 체크리스트

- [ ] `fetchCurrentUser` display name parser가 `fullName`, `name`, `id` 중 첫 nonblank 값을 선택하도록 수정한다.
- [ ] `test/jenkins_client_test.dart`에 blank `fullName`과 nonblank `name` 조합이 `name`으로 fallback되는 테스트를 추가한다.
- [ ] `git diff --check`와 remote runner `flutter test test/jenkins_client_test.dart`, `flutter analyze`, `flutter test`를 실행해 결과를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_API-1] Current-user display name fallback

문제: [jenkins_client.dart](/config/workspace/appsok/lib/src/services/jenkins_client.dart:204)는 `fullName`이 빈 문자열이면 null-coalescing이 멈춰 `name` 후보를 건너뛴다.

해결 방법:

Before:

```dart
final displayName =
    (json['fullName'] as String?)?.trim() ??
    (json['name'] as String?)?.trim() ??
    id;
```

After:

```dart
final displayName = _firstNonBlank([
  json['fullName'] as String?,
  json['name'] as String?,
  id,
]);
```

helper 형태는 코드 스타일에 맞게 조정해도 되지만 빈 문자열과 whitespace는 다음 후보로 넘겨야 한다.

수정 파일 및 체크리스트:

- [ ] `lib/src/services/jenkins_client.dart`: blank 값을 null처럼 다루는 fallback 구현.
- [ ] `test/jenkins_client_test.dart`: `{'id': 'grace', 'fullName': '', 'name': 'Grace H.'}`가 `displayName == 'Grace H.'`가 되는 테스트 추가.
- [ ] 기존 `fullName` 성공, `id` fallback, forbidden response 테스트가 계속 통과하는지 확인.

테스트 작성:

- 수정: `test/jenkins_client_test.dart`
- test name 후보:
  - `fetchCurrentUser falls back to name when fullName is blank`

중간 검증:

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

기대 결과: 기존 Jenkins client 테스트와 신규 fallback 테스트가 모두 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/src/services/jenkins_client.dart` | REVIEW_API-1 |
| `test/jenkins_client_test.dart` | REVIEW_API-1 |

## 최종 검증

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

기대 결과: 출력 없음, exit code 0.

```bash
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'"
```

기대 결과: `No issues found!`와 `All tests passed!`.

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