feat: logcat console implementation with auto-scroll and test
This commit is contained in:
parent
76827f9ae6
commit
c7ab0fa151
9 changed files with 1018 additions and 11 deletions
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
- [ ] [stream] 선택 device의 `adb logcat` stream을 시작하고 화면에 append한다.
|
||||
- [ ] [lifecycle] 화면 이탈 또는 device 변경 시 logcat process를 정리한다. 검증: stream subscription/process cleanup unit 또는 integration test 후보를 마련한다.
|
||||
- [ ] [level-color] info/debug/warn/error를 구분해 읽기 쉽게 표시한다.
|
||||
- [x] [level-color] info/debug/warn/error를 구분해 읽기 쉽게 표시한다.
|
||||
- [ ] [auto-scroll] 새 로그가 들어올 때 자동 스크롤하되 pause 상태에서는 사용자의 위치를 유지한다.
|
||||
|
||||
### Epic: [filter] 필터와 작업 도구
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
<!-- task=m-logcat-console/01_logcat_process plan=0 tag=LOGCAT_PROC -->
|
||||
|
||||
# Code Review Reference - LOGCAT_PROC
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-14
|
||||
task=m-logcat-console/01_logcat_process, plan=0, tag=LOGCAT_PROC
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` → `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` → `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-logcat-console/01_logcat_process/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [LOGCAT_PROC-1] Logcat Session API | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `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의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-logcat-console/01_logcat_process/`를 `agent-task/archive/YYYY/MM/m-logcat-console/01_logcat_process/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-logcat-console/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `Process.start` 직접 의존이 test-injectable seam으로 바뀌었는가.
|
||||
- stream cancel 또는 session stop이 fake process kill로 검증되는가.
|
||||
- 기존 `listDevices`, `installApk`, diagnostics 동작이 깨지지 않았는가.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### LOGCAT_PROC-1 중간 검증
|
||||
```bash
|
||||
$ flutter test test/adb_service_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ flutter analyze
|
||||
(output)
|
||||
|
||||
$ flutter test test/adb_service_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
181
agent-task/m-logcat-console/01_logcat_process/PLAN-cloud-G07.md
Normal file
181
agent-task/m-logcat-console/01_logcat_process/PLAN-cloud-G07.md
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<!-- 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<!-- task=m-logcat-console/02+01_console_stream plan=0 tag=LOGCAT_STREAM -->
|
||||
|
||||
# Code Review Reference - LOGCAT_STREAM
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-14
|
||||
task=m-logcat-console/02+01_console_stream, plan=0, tag=LOGCAT_STREAM
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/usable-mvp/milestones/logcat-console.md`
|
||||
- Task ids:
|
||||
- `stream`: 선택 device의 `adb logcat` stream을 시작하고 화면에 append한다.
|
||||
- `lifecycle`: 화면 이탈 또는 device 변경 시 logcat process를 정리한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G06.md` → `code_review_local_G06_N.log`, `PLAN-local-G06.md` → `plan_local_G06_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-logcat-console/02+01_console_stream/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [LOGCAT_STREAM-1] Shell Device Selection | [ ] |
|
||||
| [LOGCAT_STREAM-2] Live Stream Lifecycle | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `AppSokApp`에서 logcat session starter를 `AppSokShell`로 주입한다.
|
||||
- [ ] `DevicesPage` logcat 버튼이 ready device를 console 선택 상태로 넘기고 콘솔 탭으로 이동하게 한다.
|
||||
- [ ] `ConsolePage`가 선택 device의 logcat stream을 시작하고 화면에 append하며 dispose/device 변경 시 session을 정리한다.
|
||||
- [ ] `flutter test test/widget_test.dart test/console_page_test.dart`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G06_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_local_G06_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-logcat-console/02+01_console_stream/`를 `agent-task/archive/YYYY/MM/m-logcat-console/02+01_console_stream/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-logcat-console/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G06.md`와 `CODE_REVIEW-local-G06.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `02+01_console_stream` 구현이 `01_logcat_process` complete.log 이후 시작됐는가.
|
||||
- ready device logcat 버튼만 console handoff를 수행하는가.
|
||||
- 화면 이탈과 device 변경 모두 session stop을 보장하는가.
|
||||
- Roadmap Targets의 `stream`, `lifecycle` completion 근거가 충분한가.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### LOGCAT_STREAM-1 중간 검증
|
||||
```bash
|
||||
$ flutter test test/widget_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### LOGCAT_STREAM-2 중간 검증
|
||||
```bash
|
||||
$ flutter test test/console_page_test.dart test/widget_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ flutter analyze
|
||||
(output)
|
||||
|
||||
$ flutter test test/widget_test.dart test/console_page_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
<!-- task=m-logcat-console/02+01_console_stream plan=0 tag=LOGCAT_STREAM -->
|
||||
|
||||
# LOGCAT_STREAM 계획
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것은 필수입니다. 구현 후 검증을 실행하고 실제 내용과 출력을 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고하세요. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용입니다. 구현이 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 중단하세요. 직접 질문, 채팅 선택지, `request_user_input`, `USER_REVIEW.md` 작성, archive, `complete.log` 작성은 금지입니다. 후속 에이전트가 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아닙니다.
|
||||
|
||||
## 배경
|
||||
|
||||
Console 화면은 현재 샘플 로그만 렌더링하고 실제 device 선택이나 ADB stream과 연결되지 않습니다. Devices 화면의 logcat 버튼도 no-op이라 사용자가 설치 후 바로 로그를 볼 수 없습니다. `01_logcat_process`의 session API 위에 shell/device 선택과 console stream lifecycle을 얹어 `stream`, `lifecycle` Task를 완료 가능한 단위로 만듭니다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록합니다. 구현 에이전트는 사용자에게 직접 질문하지 않으며, code-review가 blocker 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정합니다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/usable-mvp/milestones/logcat-console.md`
|
||||
- Task ids:
|
||||
- `stream`: 선택 device의 `adb logcat` stream을 시작하고 화면에 append한다.
|
||||
- `lifecycle`: 화면 이탈 또는 device 변경 시 logcat process를 정리한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `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 없이 남지 않아야 함.
|
||||
- 이 plan은 fake stream/session으로 widget lifecycle을 검증하고, 실제 ADB runtime은 보조 smoke 후보로 남긴다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `ConsolePage`가 live stream을 append하는 test 없음. 새 widget test 필요.
|
||||
- 화면 이탈/device 변경 시 session stop이 호출되는 test 없음. fake session stop counter로 검증해야 한다.
|
||||
- 실제 `adb logcat` runtime smoke는 자동 unit test로 대체할 수 없다. remote runner에서 `adb devices -l`은 보조 확인으로 기록한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- `ConsolePage` call site: `lib/src/features/app_shell.dart:201-205`.
|
||||
- device logcat button: `lib/src/features/devices/devices_page.dart:368-372`.
|
||||
- `AdbService` default wiring: `lib/src/app.dart:49`, `lib/src/app.dart:150-158`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 평가했다.
|
||||
- 공유 task group: `m-logcat-console`
|
||||
- 이 subtask는 `02+01_console_stream`이며 predecessor `01`은 `agent-task/m-logcat-console/01_logcat_process/complete.log` 또는 archive matching `complete.log`가 필요하다.
|
||||
- 현재 확인 결과 predecessor complete.log는 없음. 구현 시작 전 `01_logcat_process` PASS 완료가 필요하다.
|
||||
- `03+02_auto_scroll`은 이 plan의 live console UI에 의존한다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- auto-scroll의 pause-aware scroll positioning은 제외하고 `03+02_auto_scroll`에서 처리한다.
|
||||
- package/tag/text filter, level filter, export/clipboard, clear button은 `filter` 에픽 소속이므로 제외한다.
|
||||
- macOS entitlements와 bundled adb 배포는 변경하지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build/review: `local-G06`. UI wiring과 lifecycle test가 필요하지만, fake session과 widget tests로 범위를 제어할 수 있다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `AppSokApp`에서 logcat session starter를 `AppSokShell`로 주입한다.
|
||||
- [ ] `DevicesPage` logcat 버튼이 ready device를 console 선택 상태로 넘기고 콘솔 탭으로 이동하게 한다.
|
||||
- [ ] `ConsolePage`가 선택 device의 logcat stream을 시작하고 화면에 append하며 dispose/device 변경 시 session을 정리한다.
|
||||
- [ ] `flutter test test/widget_test.dart test/console_page_test.dart`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- Directory dependency: `02+01_console_stream` -> `01_logcat_process`
|
||||
- 구현 전 `agent-task/m-logcat-console/01_logcat_process/complete.log` 또는 archive `agent-task/archive/*/*/m-logcat-console/01_logcat_process/complete.log`가 있어야 한다. 현재는 없음.
|
||||
|
||||
## LOGCAT_STREAM-1 Shell Device Selection
|
||||
|
||||
### 문제
|
||||
|
||||
[devices_page.dart](/config/workspace/appsok/lib/src/features/devices/devices_page.dart:368)의 logcat 버튼은 no-op이고, [app_shell.dart](/config/workspace/appsok/lib/src/features/app_shell.dart:201)는 console을 `const ConsolePage()`로 고정한다.
|
||||
|
||||
```dart
|
||||
368 _CompactIconButton(
|
||||
369 tooltip: 'logcat',
|
||||
370 icon: Icons.terminal,
|
||||
371 onPressed: () {},
|
||||
372 ),
|
||||
```
|
||||
|
||||
```dart
|
||||
201 const _ShellPage(
|
||||
202 label: '콘솔',
|
||||
203 icon: Icons.terminal_outlined,
|
||||
204 selectedIcon: Icons.terminal,
|
||||
205 child: ConsolePage(),
|
||||
206 ),
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`DevicesPage`에 `ValueChanged<AdbDevice>? onLogcatRequested`를 추가하고 ready device의 logcat 버튼만 활성화한다. `AppSokShell`은 `_selectedConsoleDevice`를 저장하고 `_selectedIndex = 2`로 이동하며, `ConsolePage`에 device와 starter를 전달한다.
|
||||
|
||||
```dart
|
||||
void _handleLogcatRequested(AdbDevice device) {
|
||||
setState(() {
|
||||
_selectedConsoleDevice = device;
|
||||
_selectedIndex = 2;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `lib/src/features/devices/devices_page.dart`: `onLogcatRequested` prop과 ready device button enablement 추가.
|
||||
- [ ] `lib/src/features/app_shell.dart`: selected console device state와 navigation handoff 추가.
|
||||
- [ ] `lib/src/app.dart`: `AdbService.startLogcat` starter를 shell에 전달.
|
||||
- [ ] `test/widget_test.dart`: ready device logcat 버튼이 console로 이동하고 device label을 표시하는 test 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- 작성: `test/widget_test.dart`
|
||||
- 테스트 이름 후보: `ready device logcat button opens console for that device`
|
||||
- assertion: ready device button tap 후 navigation marker는 `콘솔`, selected device serial/display name이 console에 표시된다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
flutter test test/widget_test.dart
|
||||
```
|
||||
|
||||
예상: shell/device widget tests 통과.
|
||||
|
||||
## LOGCAT_STREAM-2 Live Stream Lifecycle
|
||||
|
||||
### 문제
|
||||
|
||||
[console_page.dart](/config/workspace/appsok/lib/src/features/console/console_page.dart:69)는 `_logLines` 정적 목록만 사용하고, stream subscription이나 dispose cleanup이 없다.
|
||||
|
||||
```dart
|
||||
69 child: ListView.builder(
|
||||
70 padding: const EdgeInsets.all(16),
|
||||
71 itemCount: _logLines.length,
|
||||
72 itemBuilder: (context, index) {
|
||||
73 final line = _logLines[index];
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`ConsolePage`를 `StatefulWidget`으로 바꾸고 `AdbLogcatSessionStarter? logcatStarter`, `AdbDevice? device`를 받는다. `initState`/`didUpdateWidget`에서 device가 바뀌면 기존 subscription/session을 정리하고 새 stream을 시작한다. append buffer는 MVP에서 1000줄 정도로 cap을 두어 무한 메모리 증가를 막는다.
|
||||
|
||||
```dart
|
||||
class ConsolePage extends StatefulWidget {
|
||||
const ConsolePage({super.key, this.device, this.logcatStarter});
|
||||
|
||||
final AdbDevice? device;
|
||||
final AdbLogcatSessionStarter? logcatStarter;
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `lib/src/features/console/console_page.dart`: Stateful conversion, stream start/append, error state, stop/dispose 처리.
|
||||
- [ ] `test/console_page_test.dart`: fake session stream append와 dispose stop 검증 추가.
|
||||
- [ ] `test/widget_test.dart`: navigation away 또는 device 변경 시 stop counter 증가 검증 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- 작성: `test/console_page_test.dart`, `test/widget_test.dart`
|
||||
- assertion: stream line이 화면에 append되고, widget dispose/device change 후 fake session `stopCalls == 1`.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
flutter test test/console_page_test.dart test/widget_test.dart
|
||||
```
|
||||
|
||||
예상: console focused test와 shell widget tests 통과.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `lib/src/app.dart` | LOGCAT_STREAM-1 |
|
||||
| `lib/src/features/app_shell.dart` | LOGCAT_STREAM-1 |
|
||||
| `lib/src/features/devices/devices_page.dart` | LOGCAT_STREAM-1 |
|
||||
| `lib/src/features/console/console_page.dart` | LOGCAT_STREAM-2 |
|
||||
| `test/widget_test.dart` | LOGCAT_STREAM-1, LOGCAT_STREAM-2 |
|
||||
| `test/console_page_test.dart` | LOGCAT_STREAM-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
flutter analyze
|
||||
flutter test test/widget_test.dart test/console_page_test.dart
|
||||
```
|
||||
|
||||
예상: analyzer issue 없음, targeted widget tests 통과.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
<!-- task=m-logcat-console/03+02_auto_scroll plan=0 tag=LOGCAT_SCROLL -->
|
||||
|
||||
# Code Review Reference - LOGCAT_SCROLL
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-14
|
||||
task=m-logcat-console/03+02_auto_scroll, plan=0, tag=LOGCAT_SCROLL
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/usable-mvp/milestones/logcat-console.md`
|
||||
- Task ids:
|
||||
- `auto-scroll`: 새 로그가 들어올 때 자동 스크롤하되 pause 상태에서는 사용자의 위치를 유지한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-local-G05.md` → `code_review_local_G05_N.log`, `PLAN-local-G05.md` → `plan_local_G05_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-logcat-console/03+02_auto_scroll/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [LOGCAT_SCROLL-1] Pause-Aware Auto Scroll | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `ConsolePage`에 `ScrollController`와 pause 상태를 추가하고 새 로그 append 후 unpaused 상태에서 bottom으로 이동한다.
|
||||
- [ ] pause/resume 버튼이 auto-scroll 정책을 제어하고 pause 중 사용자 scroll offset을 유지한다.
|
||||
- [ ] `test/console_page_test.dart`에서 unpaused auto-scroll과 paused offset 유지 동작을 검증한다.
|
||||
- [ ] `flutter test test/console_page_test.dart`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G05_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_local_G05_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-logcat-console/03+02_auto_scroll/`를 `agent-task/archive/YYYY/MM/m-logcat-console/03+02_auto_scroll/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-logcat-console/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G05.md`와 `CODE_REVIEW-local-G05.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `03+02_auto_scroll` 구현이 `02+01_console_stream` complete.log 이후 시작됐는가.
|
||||
- unpaused 상태에서 새 로그가 bottom으로 이동하는가.
|
||||
- paused 상태에서 사용자 scroll offset을 보존하는가.
|
||||
- Roadmap Targets의 `auto-scroll` completion 근거가 충분한가.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### LOGCAT_SCROLL-1 중간 검증
|
||||
```bash
|
||||
$ flutter test test/console_page_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ flutter analyze
|
||||
(output)
|
||||
|
||||
$ flutter test test/console_page_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
167
agent-task/m-logcat-console/03+02_auto_scroll/PLAN-local-G05.md
Normal file
167
agent-task/m-logcat-console/03+02_auto_scroll/PLAN-local-G05.md
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<!-- task=m-logcat-console/03+02_auto_scroll plan=0 tag=LOGCAT_SCROLL -->
|
||||
|
||||
# LOGCAT_SCROLL 계획
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것은 필수입니다. 구현 후 검증을 실행하고 실제 내용과 출력을 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고하세요. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용입니다. 구현이 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 중단하세요. 직접 질문, 채팅 선택지, `request_user_input`, `USER_REVIEW.md` 작성, archive, `complete.log` 작성은 금지입니다. 후속 에이전트가 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아닙니다.
|
||||
|
||||
## 배경
|
||||
|
||||
Live logcat stream은 새 로그가 계속 들어오기 때문에 console의 scroll 정책이 명확해야 합니다. 사용자가 pause한 상태에서 자동으로 바닥으로 끌려가면 디버깅 중 읽던 위치를 잃습니다. `02+01_console_stream`으로 live append가 들어온 뒤, pause-aware auto-scroll을 별도 리뷰 단위로 구현합니다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록합니다. 구현 에이전트는 사용자에게 직접 질문하지 않으며, code-review가 blocker 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정합니다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/usable-mvp/milestones/logcat-console.md`
|
||||
- Task ids:
|
||||
- `auto-scroll`: 새 로그가 들어올 때 자동 스크롤하되 pause 상태에서는 사용자의 위치를 유지한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `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`.
|
||||
- 이 plan은 widget-level scroll behavior가 핵심이므로 `test/console_page_test.dart`를 중간 검증으로 둔다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- 새 로그 append 시 bottom auto-scroll test 없음.
|
||||
- pause 상태에서 scroll offset이 유지되는 test 없음.
|
||||
- clear/export/filter는 이 plan의 대상이 아니므로 coverage 공백으로 남긴다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- pause button 현재 위치: `lib/src/features/console/console_page.dart:46-50`.
|
||||
- log list 현재 위치: `lib/src/features/console/console_page.dart:69-88`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 평가했다.
|
||||
- 공유 task group: `m-logcat-console`
|
||||
- 이 subtask는 `03+02_auto_scroll`이며 predecessor `02`는 `agent-task/m-logcat-console/02+01_console_stream/complete.log` 또는 archive matching `complete.log`가 필요하다.
|
||||
- 현재 확인 결과 predecessor complete.log는 없음. 구현 시작 전 `02+01_console_stream` PASS 완료가 필요하다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- `stream`, `lifecycle`은 `02+01_console_stream` 책임이며 여기서는 이미 live append가 있다고 가정한다.
|
||||
- query/level filter, clear, export는 `filter` 에픽 소속이므로 제외한다.
|
||||
- 실제 ADB device smoke는 scroll logic 검증의 필수 조건이 아니므로 widget test로 판단한다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build/review: `local-G05`. UI state와 scroll controller 동작만 다루며 fake stream 기반 widget test로 검증 가능하다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] `ConsolePage`에 `ScrollController`와 pause 상태를 추가하고 새 로그 append 후 unpaused 상태에서 bottom으로 이동한다.
|
||||
- [ ] pause/resume 버튼이 auto-scroll 정책을 제어하고 pause 중 사용자 scroll offset을 유지한다.
|
||||
- [ ] `test/console_page_test.dart`에서 unpaused auto-scroll과 paused offset 유지 동작을 검증한다.
|
||||
- [ ] `flutter test test/console_page_test.dart`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
- Directory dependency: `03+02_auto_scroll` -> `02+01_console_stream`
|
||||
- 구현 전 `agent-task/m-logcat-console/02+01_console_stream/complete.log` 또는 archive `agent-task/archive/*/*/m-logcat-console/02+01_console_stream/complete.log`가 있어야 한다. 현재는 없음.
|
||||
|
||||
## LOGCAT_SCROLL-1 Pause-Aware Auto Scroll
|
||||
|
||||
### 문제
|
||||
|
||||
[console_page.dart](/config/workspace/appsok/lib/src/features/console/console_page.dart:46)의 pause 버튼은 no-op이고 [console_page.dart](/config/workspace/appsok/lib/src/features/console/console_page.dart:69)의 log list는 controller 없이 렌더링된다.
|
||||
|
||||
```dart
|
||||
46 IconButton.filledTonal(
|
||||
47 tooltip: '일시정지',
|
||||
48 onPressed: () {},
|
||||
49 icon: const Icon(Icons.pause),
|
||||
50 ),
|
||||
```
|
||||
|
||||
```dart
|
||||
69 child: ListView.builder(
|
||||
70 padding: const EdgeInsets.all(16),
|
||||
71 itemCount: _logLines.length,
|
||||
72 itemBuilder: (context, index) {
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`ScrollController`와 `_isPaused`를 도입한다. stream line append 후 `WidgetsBinding.instance.addPostFrameCallback`에서 unpaused일 때만 `animateTo/maxScrollExtent` 또는 `jumpTo`를 실행한다. pause 중에는 기존 offset을 건드리지 않는다.
|
||||
|
||||
```dart
|
||||
void _appendLine(String line) {
|
||||
setState(() => _lines.add(line));
|
||||
if (!_isPaused) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `lib/src/features/console/console_page.dart`: `ScrollController`, `_isPaused`, pause/resume icon/tooltip 상태 추가.
|
||||
- [ ] `lib/src/features/console/console_page.dart`: append 후 unpaused auto-scroll과 paused no-scroll 적용.
|
||||
- [ ] `test/console_page_test.dart`: fake stream으로 scroll behavior 검증 추가.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
- 작성: `test/console_page_test.dart`
|
||||
- 테스트 이름 후보:
|
||||
- `auto-scrolls to the newest log while running`
|
||||
- `keeps user scroll position while paused`
|
||||
- assertion: unpaused append 후 list bottom marker가 보이고, pause 후 추가 line이 들어와도 pre-pause scroll offset이 유지된다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
flutter test test/console_page_test.dart
|
||||
```
|
||||
|
||||
예상: console focused widget tests 통과.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `lib/src/features/console/console_page.dart` | LOGCAT_SCROLL-1 |
|
||||
| `test/console_page_test.dart` | LOGCAT_SCROLL-1 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
flutter analyze
|
||||
flutter test test/console_page_test.dart
|
||||
```
|
||||
|
||||
예상: analyzer issue 없음, targeted widget tests 통과.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -94,17 +94,32 @@ class ConsolePage extends StatelessWidget {
|
|||
}
|
||||
|
||||
Color _lineColor(String line) {
|
||||
if (line.contains(' E ')) {
|
||||
return const Color(0xFFFFA3A3);
|
||||
}
|
||||
if (line.contains(' W ')) {
|
||||
return const Color(0xFFFFD28A);
|
||||
}
|
||||
if (line.contains(' D ')) {
|
||||
return const Color(0xFF8DE1D7);
|
||||
}
|
||||
return switch (_LogcatLevel.fromLine(line)) {
|
||||
_LogcatLevel.debug => const Color(0xFF8DE1D7),
|
||||
_LogcatLevel.info => const Color(0xFFBDEFA8),
|
||||
_LogcatLevel.warn => const Color(0xFFFFD28A),
|
||||
_LogcatLevel.error => const Color(0xFFFFA3A3),
|
||||
null => const Color(0xFFE6EEE9),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return const Color(0xFFE6EEE9);
|
||||
enum _LogcatLevel {
|
||||
debug,
|
||||
info,
|
||||
warn,
|
||||
error;
|
||||
|
||||
static _LogcatLevel? fromLine(String line) {
|
||||
final match = RegExp(r'\s([DIWEF])\s').firstMatch(line);
|
||||
|
||||
return switch (match?.group(1)) {
|
||||
'D' => _LogcatLevel.debug,
|
||||
'I' => _LogcatLevel.info,
|
||||
'W' => _LogcatLevel.warn,
|
||||
'E' || 'F' => _LogcatLevel.error,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
34
test/console_page_test.dart
Normal file
34
test/console_page_test.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:appsok/src/features/console/console_page.dart';
|
||||
import 'package:appsok/src/theme/app_theme.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('colors sample logcat rows by parsed level', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: AppTheme.light(),
|
||||
home: const Scaffold(body: ConsolePage()),
|
||||
),
|
||||
);
|
||||
|
||||
const infoLine =
|
||||
'06-08 10:41:22.121 1832 2110 I ActivityTaskManager: START u0 com.toki.app/.MainActivity';
|
||||
const debugLine =
|
||||
'06-08 10:41:22.339 5521 5521 D AppSokInstall: install session committed';
|
||||
const warnLine =
|
||||
'06-08 10:41:22.921 5521 5540 W NetworkMonitor: vpn route not available, using usb install cache';
|
||||
const errorLine =
|
||||
'06-08 10:41:23.411 5521 5521 E Sample: placeholder error row for filter styling';
|
||||
|
||||
expect(_textColor(tester, infoLine), const Color(0xFFBDEFA8));
|
||||
expect(_textColor(tester, debugLine), const Color(0xFF8DE1D7));
|
||||
expect(_textColor(tester, warnLine), const Color(0xFFFFD28A));
|
||||
expect(_textColor(tester, errorLine), const Color(0xFFFFA3A3));
|
||||
});
|
||||
}
|
||||
|
||||
Color? _textColor(WidgetTester tester, String text) {
|
||||
return tester.widget<Text>(find.text(text)).style?.color;
|
||||
}
|
||||
Loading…
Reference in a new issue