<!-- task=m-logcat-console/01_logcat_process plan=1 tag=REVIEW_LOGCAT_PROC -->

# REVIEW_LOGCAT_PROC 계획

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

이 plan은 직전 코드리뷰 FAIL의 Required 항목만 닫는다. `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것은 필수이며, 구현 후 검증을 실행하고 실제 내용과 출력을 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. 구현이 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 중단한다. 직접 질문, 채팅 선택지, `request_user_input`, `USER_REVIEW.md` 작성, archive, `complete.log` 작성은 금지다. 후속 에이전트가 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

직전 구현은 `AdbService.startLogcat()`와 `AdbLogcatSession`을 추가했고 targeted test/analyze는 통과했다. 그러나 핵심 회귀 테스트인 `logcat stream cancellation kills the adb process`가 실제 stream subscription cancel을 실행하지 않고 `session.stop()`만 직접 호출한다. 또한 `AdbLogcatSession.lines` 문서는 직접 구독 취소가 process를 kill한다고 말하지만 구현은 명시적 `stop()`만 process lifetime을 제어한다.

## 범위 결정 근거

- 이번 follow-up은 직전 리뷰의 Required 2건만 수정한다.
- `ConsolePage` live stream UI, device selection, auto-scroll, filter UI는 이 subtask 범위가 아니다.
- 기존 설계대로 session 직접 사용자는 `stop()`으로 lifetime을 정리하고, compatibility wrapper인 `AdbService.logcat()`은 stream cancel 시 `stop()`을 보장하는 계약으로 둔다.
- Roadmap Targets는 직전 plan에 없었고, 이번 follow-up도 roadmap Task 완료를 직접 주장하지 않는다.

## 빌드 등급

- build/review: `cloud-G07`.
- 사유: ADB process lifecycle, stream cancellation, verification trust 회복이 핵심이다. 직전 route가 적절했으므로 동일 route를 유지한다.

## 구현 체크리스트

- [ ] `test/adb_service_test.dart`에서 `AdbService.logcat()` stream subscription을 만들고 `await sub.cancel()`만으로 fake process `killCalled == true`가 되는 regression test를 추가하거나 기존 오명명 테스트를 고친다.
- [ ] `lib/src/services/adb_service.dart`의 `AdbLogcatSession.lines` 문서와 테스트명을 명시적 `stop()` 계약에 맞게 정정하고, 직접 session 사용 시 process cleanup 책임이 `stop()`에 있음을 분명히 한다.
- [ ] `flutter test test/adb_service_test.dart`를 실행한다.
- [ ] `flutter analyze`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## REVIEW_LOGCAT_PROC-1 실제 stream cancel 회귀 테스트

### 문제

[test/adb_service_test.dart](/config/workspace/appsok/test/adb_service_test.dart:300)의 `logcat stream cancellation kills the adb process`는 stream cancel을 검증하지 않는다. `final session = await service.startLogcat()` 뒤 `await session.stop()`만 호출하므로 [lib/src/services/adb_service.dart](/config/workspace/appsok/lib/src/services/adb_service.dart:92)의 `async*` wrapper cancellation path가 깨져도 테스트는 계속 통과한다.

### 해결 방법

테스트가 `service.logcat()`을 직접 구독하고, 구독 취소가 wrapper의 `finally`를 통해 fake process kill로 이어지는지 확인하게 고친다.

예시 흐름:

```dart
final fake = _FakeProcess();
final service = AdbService(
  runtimeConfig: const AdbRuntimeConfig(customAdbPath: '/opt/appsok/adb'),
  processStarter: (_, args, {environment}) async => fake,
);

final sub = service.logcat().listen((_) {});
await Future<void>.delayed(Duration.zero);
expect(fake.killCalled, isFalse);

await sub.cancel();

expect(fake.killCalled, isTrue);
```

필요하면 fake process가 `kill()`에서 stream을 닫고 `exitCode`를 완료하는 현재 구조를 재사용한다. 이 테스트는 `session.stop()`을 직접 호출하지 않아야 한다.

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

- [ ] `test/adb_service_test.dart`: `service.logcat()` subscription cancel 기반 regression test 작성.
- [ ] `test/adb_service_test.dart`: 테스트명이 실제 검증 동작과 일치하도록 정리.

### 테스트 작성

- 작성: `test/adb_service_test.dart`
- 테스트 이름 후보: `logcat stream cancellation kills the adb process`
- assertion: cancel 전 `killCalled == false`, `await sub.cancel()` 후 `killCalled == true`.

### 중간 검증

```bash
flutter test test/adb_service_test.dart --plain-name "logcat stream cancellation kills the adb process"
```

예상: 해당 테스트 통과.

## REVIEW_LOGCAT_PROC-2 session lines API 계약 정정

### 문제

[lib/src/services/adb_service.dart](/config/workspace/appsok/lib/src/services/adb_service.dart:322)는 `AdbLogcatSession.lines` 구독 취소가 process를 kill한다고 문서화하지만, 구현은 `stop()`에서만 kill한다. 직전 구현의 설계 결정도 session이 process 제어만 담당하고 UI lifecycle은 명시적 stop으로 분리한다고 설명하므로 문서가 API 계약을 잘못 전달한다.

### 해결 방법

`AdbLogcatSession.lines` 문서를 "stdout/stderr line merge stream"으로 한정하고, session 직접 사용자는 `stop()`을 호출해야 한다는 계약을 명확히 한다. 기존 direct stop 테스트는 이름을 `logcat session stop kills the adb process`처럼 고쳐 실제 동작과 일치시킨다. 직접 `lines` cancellation이 process kill을 보장해야 한다는 방향으로 바꾸지 않는다.

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

- [ ] `lib/src/services/adb_service.dart`: `AdbLogcatSession.lines` 문서에서 직접 cancel kill 보장을 제거하고 `stop()` 책임을 명확히 한다.
- [ ] `test/adb_service_test.dart`: direct session stop 테스트 이름과 assertion을 API 계약에 맞게 정리한다.

### 테스트 작성

- 작성: `test/adb_service_test.dart`
- 테스트 이름 후보: `logcat session stop kills the adb process`
- assertion: `session.stop()` 호출 후 fake process `killCalled == true`.

### 중간 검증

```bash
flutter test test/adb_service_test.dart --plain-name "logcat session stop kills the adb process"
```

예상: 해당 테스트 통과.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `lib/src/services/adb_service.dart` | REVIEW_LOGCAT_PROC-2 |
| `test/adb_service_test.dart` | REVIEW_LOGCAT_PROC-1, REVIEW_LOGCAT_PROC-2 |

## 최종 검증

```bash
flutter test test/adb_service_test.dart
flutter analyze
```

예상: targeted test 통과, analyzer issue 없음.

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