appsok/agent-task/m-logcat-console/01_logcat_process/PLAN-cloud-G07.md

7.7 KiB

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.dartProcess.start를 직접 호출하고 반환 stream 취소와 process lifecycle을 연결하지 않는다.

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를 주입하고, AdbLogcatSessionlinesstop()을 소유하게 한다. 기존 logcat()은 compatibility wrapper로 유지하되 session cancel을 사용한다.

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.

중간 검증

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

최종 검증

flutter analyze
flutter test test/adb_service_test.dart

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

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