<!-- task=m-logcat-console/01_logcat_process plan=0 tag=LOGCAT_PROC -->

# LOGCAT_PROC 계획

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

`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.logcat()`은 `Process.start`를 직접 호출하고 `yield*`로 stdout/stderr를 병합합니다. stream 구독 취소가 실제 process kill로 이어지는지 테스트할 수 없고, UI lifecycle 작업의 선행 기반이 약합니다. 먼저 cancelable logcat session API와 fake process 기반 unit test를 만든 뒤 UI 작업이 안전하게 의존하도록 합니다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록합니다. 구현 에이전트는 사용자에게 직접 질문하지 않으며, code-review가 blocker 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정합니다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/usable-mvp/PHASE.md`
- `agent-roadmap/phase/usable-mvp/milestones/logcat-console.md`
- `agent-ops/rules/project/domain/device-console/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/device-console-smoke.md`
- `lib/src/services/adb_service.dart`
- `lib/src/features/console/console_page.dart`
- `lib/src/features/app_shell.dart`
- `lib/src/app.dart`
- `lib/src/features/devices/devices_page.dart`
- `lib/src/models/adb_device.dart`
- `pubspec.yaml`
- `test/adb_service_test.dart`
- `test/widget_test.dart`
- `test/console_page_test.dart`

### 테스트 환경 규칙

- test_env: `local`
- `agent-test/local/rules.md` 존재 및 읽음.
- 매칭 profile: `agent-test/local/device-console-smoke.md`
- 적용 명령: remote runner 기준 `flutter analyze`, `flutter test`; ADB process stream은 dispose 없이 남지 않아야 함.
- 이번 선행 작업은 unit test 가능한 service seam이므로 fallback 없이 `flutter test test/adb_service_test.dart`를 중간 검증으로 둔다.

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

- `AdbService.logcat()`의 process 시작 arguments, stdout/stderr line merge, cancel 시 process kill 동작은 기존 test에 없다. 새 regression test가 필요하다.
- 실제 Android `adb logcat` runtime smoke는 이 선행 API 변경만으로 완료 판정하지 않는다. 후속 UI plan에서 remote/ADB smoke 후보로 남긴다.

### 심볼 참조

- renamed/removed symbol: none.
- `AdbService.logcat()` 참조: `lib/src/services/adb_service.dart:65`에 정의만 있고 call site 없음.

### 분할 판단

- split decision policy를 먼저 평가했다.
- 공유 task group: `m-logcat-console`
- `01_logcat_process`: process/session foundation, 선행 의존 없음.
- `02+01_console_stream`: `01`의 cancelable session API에 의존.
- `03+02_auto_scroll`: `02`의 live console UI에 의존.
- 이 plan은 service/process foundation만 다루며 Roadmap Targets를 쓰지 않는다. PASS되어도 roadmap Task 체크는 하지 않는다.

### 범위 결정 근거

- `ConsolePage` live stream UI, shell/device selection, auto-scroll은 제외한다. 이들은 각각 `02+01_console_stream`, `03+02_auto_scroll`에서 처리한다.
- package/tag/level filter, clear/export는 `filter` 에픽 소속이므로 제외한다.

### 빌드 등급

- build/review: `cloud-G07`. ADB process lifecycle, stdout/stderr stream merge, cancel/kill contract가 핵심이라 process-control 판단과 테스트 seam 설계가 중요하다.

## 구현 체크리스트

- [ ] `AdbService`에 test-injectable logcat process starter와 cancelable session API를 추가한다.
- [ ] stdout/stderr line merge와 cancel 시 process kill 동작을 `test/adb_service_test.dart`에서 검증한다.
- [ ] `flutter test test/adb_service_test.dart`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## LOGCAT_PROC-1 Logcat Session API

### 문제

[adb_service.dart](/config/workspace/appsok/lib/src/services/adb_service.dart:65)는 `Process.start`를 직접 호출하고 반환 stream 취소와 process lifecycle을 연결하지 않는다.

```dart
65  Stream<String> logcat({String? serial}) async* {
66    final args = [
67      if (serial != null) ...['-s', serial],
68      'logcat',
69    ];
70    final runtime = this.runtime;
71    final process = await Process.start(
72      runtime.executablePath,
73      args,
74      environment: runtime.environment,
75    );
76    final stdout = process.stdout
77        .transform(utf8.decoder)
78        .transform(const LineSplitter());
79    final stderr = process.stderr
80        .transform(utf8.decoder)
81        .transform(const LineSplitter());
82
83    yield* StreamGroup.merge([stdout, stderr]);
84  }
```

### 해결 방법

`Process.start`를 감싼 typedef를 주입하고, `AdbLogcatSession`이 `lines`와 `stop()`을 소유하게 한다. 기존 `logcat()`은 compatibility wrapper로 유지하되 session cancel을 사용한다.

```dart
typedef AdbProcessStarter =
    Future<Process> Function(
      String executable,
      List<String> arguments, {
      Map<String, String>? environment,
    });

class AdbService {
  AdbService({
    ...
    AdbProcessStarter processStarter = Process.start,
  }) : ...,
       _processStarter = processStarter;

  Future<AdbLogcatSession> startLogcat({String? serial}) async {
    final args = [if (serial != null) ...['-s', serial], 'logcat'];
    final runtime = this.runtime;
    final process = await _processStarter(
      runtime.executablePath,
      args,
      environment: runtime.environment,
    );
    return AdbLogcatSession(process);
  }
}
```

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

- [ ] `lib/src/services/adb_service.dart`: process starter typedef, constructor injection, `startLogcat`, `AdbLogcatSession` 추가.
- [ ] `lib/src/services/adb_service.dart`: `logcat()` wrapper가 session stop을 보장하도록 정리.
- [ ] `test/adb_service_test.dart`: fake `Process`로 args/environment, stdout/stderr line merge, cancel/stop kill 검증 추가.

### 테스트 작성

- 작성: `test/adb_service_test.dart`
- 테스트 이름 후보:
  - `startLogcat runs adb logcat for the requested serial`
  - `logcat session merges stdout and stderr lines`
  - `logcat stream cancellation kills the adb process`
- assertion: args는 `['-s', serial, 'logcat']`, environment는 resolved ADB port, cancel 후 fake process `killCalled == true`.

### 중간 검증

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

예상: `All tests passed!`

## 수정 파일 요약

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

## 최종 검증

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

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

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