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.mdagent-roadmap/phase/usable-mvp/PHASE.mdagent-roadmap/phase/usable-mvp/milestones/logcat-console.mdagent-ops/rules/project/domain/device-console/rules.mdagent-test/local/rules.mdagent-test/local/device-console-smoke.mdlib/src/services/adb_service.dartlib/src/features/console/console_page.dartlib/src/features/app_shell.dartlib/src/app.dartlib/src/features/devices/devices_page.dartlib/src/models/adb_device.dartpubspec.yamltest/adb_service_test.darttest/widget_test.darttest/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 logcatruntime 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 체크는 하지 않는다.
범위 결정 근거
ConsolePagelive 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는 Process.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를 주입하고, AdbLogcatSession이 lines와 stop()을 소유하게 한다. 기존 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: fakeProcess로 args/environment, stdout/stderr line merge, cancel/stop kill 검증 추가.
테스트 작성
- 작성:
test/adb_service_test.dart - 테스트 이름 후보:
startLogcat runs adb logcat for the requested seriallogcat session merges stdout and stderr lineslogcat stream cancellation kills the adb process
- assertion: args는
['-s', serial, 'logcat'], environment는 resolved ADB port, cancel 후 fake processkillCalled == 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의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.