<!-- task=m-usb-install/01_adb_bundle plan=2 tag=REVIEW_REVIEW_API -->

# Plan - REVIEW_REVIEW_API

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

구현이 끝나면 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 검증을 실행하고, active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log` 작성, task archive 이동은 code-review 전용이다. 사용자만 결정할 수 있는 외부 환경, secret, 범위 충돌로 막히면 리뷰 stub의 `사용자 리뷰 요청`에 정확한 증거와 재개 조건을 남기고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니라 검증 보완 대상으로 처리한다.

## 배경

plan=1 구현은 `adb` 누락과 pinned version mismatch를 build failure로 바꿨다. 하지만 `Copy ADB Runtime` phase가 외부 SDK `adb`를 동적으로 찾으면서 `alwaysOutOfDate` 없이 `source.properties`만 input으로 선언해, 증분 빌드에서 검사와 복사가 건너뛰어질 수 있다. 이 follow-up은 phase 재실행 계약과 clean 없는 build evidence만 보완한다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사유는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 프롬프트는 금지이며, code-review가 요청 타당성과 `USER_REVIEW.md` 작성 여부를 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/usable-mvp/milestones/usb-install.md`
- Task ids:
  - `adb-bundle`: macOS app bundle에 pin된 최소 ADB subset을 포함한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-task/m-usb-install/01_adb_bundle/plan_cloud_G07_1.log`
- `agent-task/m-usb-install/01_adb_bundle/code_review_cloud_G07_1.log`
- `agent-task/m-usb-install/01_adb_bundle/plan_cloud_G07_0.log`
- `agent-task/m-usb-install/01_adb_bundle/code_review_cloud_G07_0.log`
- `macos/Runner.xcodeproj/project.pbxproj`
- `macos/Runner/Resources/adb-runtime/NOTICE.txt`
- `macos/Runner/Resources/adb-runtime/source.properties`
- `lib/src/services/adb_service.dart`
- `test/adb_service_test.dart`
- `.gitignore`

### 테스트 환경 규칙

- test_env: `local`
- env rules: `agent-test/local/rules.md`는 세션 최초에 읽었고, macOS platform 변경 기준은 remote Mac runner checkout `$HOME/docker/services/code-server/data/volume/workspace/appsok`이다.
- matched profiles:
  - `agent-test/local/macos-platform-smoke.md`: macOS platform 변경 시 `flutter analyze`, `flutter test`, `flutter build macos`.
  - `agent-test/local/macos-remote-build-smoke.md`: remote checkout 상태와 build evidence 기록.
  - `agent-test/local/device-console-smoke.md`: ADB runtime evidence는 remote runner 기준.
- 적용 명령:
  - `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && git status --short --branch'`
  - `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter pub get'`
  - `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'`
  - `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && test -x build/macos/Build/Products/Release/AppSok.app/Contents/Resources/adb-runtime/adb'`
- `<확인 필요>` 값 없음. Notary credential은 이 작업 범위가 아니다.

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

- Xcode script phase rerun policy는 Dart unit test로 커버되지 않는다. `project.pbxproj` 구조 확인과 remote `flutter build macos` evidence가 필요하다.
- `AdbRuntimeConfig` path behavior는 바꾸지 않으므로 기존 `test/adb_service_test.dart`가 충분하다.

### 심볼 참조

- Renamed/removed symbols: none.
- Dart call sites unchanged.

### 분할 판단

- split decision policy evaluated.
- Current subtask: `agent-task/m-usb-install/01_adb_bundle`, no predecessor index.
- Sibling `02+01_adb_diagnostics` still depends on index `01`; this follow-up remains in `01_adb_bundle`.

### 범위 결정 근거

- Include: `Copy ADB Runtime` phase의 reliable rerun 설정, clean 없는 remote build evidence.
- Exclude: ADB version parsing 재설계, `AdbService` API, UI diagnostics, notarization, USB install smoke.
- Keep final bundle path contract as `AppSok.app/Contents/Resources/adb-runtime/adb`.

### 빌드 등급

- `cloud-G07`: Xcode shell phase와 remote Mac build evidence가 중심이고, 이전 루프에서 verification trust 문제가 남았다.

## 구현 체크리스트

- [ ] `Copy ADB Runtime` PBXShellScriptBuildPhase가 dynamic SDK lookup을 위해 `alwaysOutOfDate = 1;`로 매 build마다 실행되게 한다.
- [ ] clean 없이 `flutter build macos`를 remote Mac runner checkout에서 실행하고 bundled `adb` version match를 다시 검증한다.
- [ ] `flutter analyze`, `flutter test`, `flutter build macos`를 remote Mac runner checkout에서 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

1. `01_adb_bundle` follow-up을 완료한다.
2. PASS 후 code-review가 `complete.log`를 만들면 `02+01_adb_diagnostics`를 시작할 수 있다.

### [REVIEW_REVIEW_API-1] Force ADB Runtime Phase Rerun

#### 문제

- `macos/Runner.xcodeproj/project.pbxproj:330`의 `Copy ADB Runtime` phase는 외부 Android SDK에서 `adb`를 찾지만 `inputPaths`에는 `source.properties`만 있다.
- `macos/Runner.xcodeproj/project.pbxproj:323-341`에는 해당 phase의 `alwaysOutOfDate = 1;`이 없다.
- 구현 검증은 `flutter clean && flutter build macos` 기준이라, 계획의 clean 없는 `flutter build macos` 계약과 증분 build rerun 조건을 아직 만족하지 않는다.

#### 해결 방법

Before:

```text
macos/Runner.xcodeproj/project.pbxproj:323
A1B2C3D4E5F60001A2B3C4D5 /* Copy ADB Runtime */ = {
  isa = PBXShellScriptBuildPhase;
  buildActionMask = 2147483647;
```

After:

```text
macos/Runner.xcodeproj/project.pbxproj
A1B2C3D4E5F60001A2B3C4D5 /* Copy ADB Runtime */ = {
  isa = PBXShellScriptBuildPhase;
  alwaysOutOfDate = 1;
  buildActionMask = 2147483647;
```

- Add `alwaysOutOfDate = 1;` to the `Copy ADB Runtime` phase only.
- Keep the missing/mismatch failure behavior from plan=1.
- Keep `source.properties` and `NOTICE.txt` content unchanged unless a verification result proves an update is required.
- Do not use `flutter clean` in the required final build evidence for this follow-up.

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

- [ ] `macos/Runner.xcodeproj/project.pbxproj`: add `alwaysOutOfDate = 1;` to `A1B2C3D4E5F60001A2B3C4D5 /* Copy ADB Runtime */`.
- [ ] `macos/Runner.xcodeproj/project.pbxproj`: preserve existing missing adb and version mismatch `exit 1` behavior.
- [ ] `macos/Runner/Resources/adb-runtime/source.properties`: leave pinned revision consistent with copied runtime.
- [ ] `macos/Runner/Resources/adb-runtime/NOTICE.txt`: leave pinned version statement consistent with `source.properties`.

#### 테스트 작성

- New Dart unit tests are not required because Dart behavior does not change.
- Remote smoke evidence is required for Xcode phase behavior.

#### 중간 검증

```bash
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && awk "/A1B2C3D4E5F60001A2B3C4D5 \\/\\* Copy ADB Runtime \\*\\//,/shellScript =/ {print}" macos/Runner.xcodeproj/project.pbxproj | grep "alwaysOutOfDate = 1;"'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && ! grep -E "skipping bundle copy|adb not found.*exit 0" macos/Runner.xcodeproj/project.pbxproj'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter build macos'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && EXPECTED="$(grep "^Pkg.Revision=" macos/Runner/Resources/adb-runtime/source.properties | cut -d= -f2)" && ACTUAL="$(build/macos/Build/Products/Release/AppSok.app/Contents/Resources/adb-runtime/adb version | grep "^Version " | awk "{print \\$2}" | cut -d- -f1)" && test "$ACTUAL" = "$EXPECTED" && printf "adb runtime version %s matches source.properties\n" "$ACTUAL"'
```

Expected: phase contains `alwaysOutOfDate = 1;`; no silent skip branch remains; clean 없는 build succeeds; actual bundled version equals `Pkg.Revision`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `macos/Runner.xcodeproj/project.pbxproj` | REVIEW_REVIEW_API-1 |
| `macos/Runner/Resources/adb-runtime/source.properties` | REVIEW_REVIEW_API-1 |
| `macos/Runner/Resources/adb-runtime/NOTICE.txt` | REVIEW_REVIEW_API-1 |

## 최종 검증

```bash
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && git status --short --branch'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter pub get'
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" && awk "/A1B2C3D4E5F60001A2B3C4D5 \\/\\* Copy ADB Runtime \\*\\//,/shellScript =/ {print}" macos/Runner.xcodeproj/project.pbxproj | grep "alwaysOutOfDate = 1;"'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && ! grep -E "skipping bundle copy|adb not found.*exit 0" macos/Runner.xcodeproj/project.pbxproj'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter build macos'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && test -x build/macos/Build/Products/Release/AppSok.app/Contents/Resources/adb-runtime/adb'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && EXPECTED="$(grep "^Pkg.Revision=" macos/Runner/Resources/adb-runtime/source.properties | cut -d= -f2)" && ACTUAL="$(build/macos/Build/Products/Release/AppSok.app/Contents/Resources/adb-runtime/adb version | grep "^Version " | awk "{print \\$2}" | cut -d- -f1)" && test "$ACTUAL" = "$EXPECTED" && printf "adb runtime version %s matches source.properties\n" "$ACTUAL"'
zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && build/macos/Build/Products/Release/AppSok.app/Contents/Resources/adb-runtime/adb version'
```

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