diff --git a/agent-roadmap/phase/usable-mvp/milestones/product-baseline.md b/agent-roadmap/phase/usable-mvp/milestones/product-baseline.md index 7d54b42..e806698 100644 --- a/agent-roadmap/phase/usable-mvp/milestones/product-baseline.md +++ b/agent-roadmap/phase/usable-mvp/milestones/product-baseline.md @@ -35,7 +35,7 @@ AppSok의 Flutter macOS scaffold를 실제 기능 구현이 가능한 기준선 빌드, 디바이스, 콘솔, 설정 화면으로 바로 진입할 수 있는 macOS 업무 앱 구조를 만든다. -- [ ] [nav-shell] 좌측 navigation과 상단 Jenkins/ADB 상태 영역을 실제 상태 연결 전에도 안정적으로 표시한다. 검증: `flutter test`에서 app shell 렌더링과 navigation 전환이 통과한다. +- [x] [nav-shell] 좌측 navigation과 상단 Jenkins/ADB 상태 영역을 실제 상태 연결 전에도 안정적으로 표시한다. 검증: `flutter test`에서 app shell 렌더링과 navigation 전환이 통과한다. - [ ] [responsive-ui] 기본 테스트 viewport에서 텍스트/버튼 overflow가 없도록 card, badge, toolbar 크기를 안정화한다. 검증: `flutter test`가 overflow exception 없이 통과한다. - [x] [theme] AppSok 이름과 업무 앱 성격에 맞는 Material theme 기준선을 유지한다. diff --git a/agent-task/m-product-baseline/01_shell_nav_status/CODE_REVIEW-local-G05.md b/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/code_review_local_G05_0.log similarity index 65% rename from agent-task/m-product-baseline/01_shell_nav_status/CODE_REVIEW-local-G05.md rename to agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/code_review_local_G05_0.log index 6ec8147..457248f 100644 --- a/agent-task/m-product-baseline/01_shell_nav_status/CODE_REVIEW-local-G05.md +++ b/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/code_review_local_G05_0.log @@ -42,41 +42,46 @@ task=m-product-baseline/01_shell_nav_status, plan=0, tag=SHELL_NAV | 항목 | 완료 여부 | |------|---------| -| [SHELL_NAV-1] Shell navigation/status baseline | [ ] | +| [SHELL_NAV-1] Shell navigation/status baseline | [x] | ## 구현 체크리스트 -- [ ] 좌측 navigation destination 4개와 상단 `Jenkins 대기`/`ADB 대기` 상태 pill이 실제 상태 연결 전에도 안정적으로 렌더링되도록 `AppSokShell` 구조를 정리한다. 검증: `flutter test`에서 app shell 렌더링과 navigation 전환이 통과한다. -- [ ] `test/widget_test.dart`에 빌드, 디바이스, 콘솔, 설정 전환 및 top status 표시 assertion을 추가한다. -- [ ] `dart format lib/src/features/app_shell.dart test/widget_test.dart`를 실행한다. -- [ ] `flutter analyze`와 `flutter test`를 실행한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. +- [x] 좌측 navigation destination 4개와 상단 `Jenkins 대기`/`ADB 대기` 상태 pill이 실제 상태 연결 전에도 안정적으로 렌더링되도록 `AppSokShell` 구조를 정리한다. 검증: `flutter test`에서 app shell 렌더링과 navigation 전환이 통과한다. +- [x] `test/widget_test.dart`에 빌드, 디바이스, 콘솔, 설정 전환 및 top status 표시 assertion을 추가한다. +- [x] `dart format lib/src/features/app_shell.dart test/widget_test.dart`를 실행한다. +- [x] `flutter analyze`와 `flutter test`를 실행한다. +- [x] 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-product-baseline/01_shell_nav_status/`를 `agent-task/archive/YYYY/MM/m-product-baseline/01_shell_nav_status/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. -- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-product-baseline/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G05_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_local_G05_M.log`로 아카이브한다. +- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [x] PASS이면 active task 디렉터리 `agent-task/m-product-baseline/01_shell_nav_status/`를 `agent-task/archive/YYYY/MM/m-product-baseline/01_shell_nav_status/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-product-baseline/`를 제거하거나, 남은 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로 이동한다. ## 계획 대비 변경 사항 -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ +- `NavigationRailDestination`은 widget tree에서 직접 찾히는 위젯 타입이 아니므로, 테스트 탭 대상은 `NavigationRail` 내부 label 텍스트를 찾는 방식으로 구현했습니다. +- 상단 상태 pill에 `ValueKey`와 tooltip 문구를 추가해 테스트에서 `Jenkins 대기`, `ADB 대기` placeholder 표시를 안정적으로 조회할 수 있게 정리했습니다. +- 콘솔 화면 marker는 로그 리스트 하단 항목 대신 즉시 렌더링되는 `패키지 또는 태그` 필드 label로 검증했습니다. +- 테스트는 한 건의 통합 test로 4개 destination 전환을 순회하며, 페이지별 대표 텍스트와 top status pill 노출을 동시에 검증하도록 구성했습니다. ## 주요 설계 결정 -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ +- shell에는 Jenkins API, Keychain, ADB Process 호출을 추가하지 않고 route wiring과 상태 placeholder 표시만 유지했습니다. +- status pill은 `ValueKey('status-jenkins')`, `ValueKey('status-adb')`로 고정해 실제 상태 연결 전에도 테스트가 안정적으로 확인할 수 있게 했습니다. +- navigation 전환 테스트는 navigation rail의 label을 사용자 탭 대상처럼 사용해 destination 4개를 모두 검증합니다. ## 사용자 리뷰 요청 @@ -110,19 +115,59 @@ _구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 ### SHELL_NAV-1 중간 검증 ```bash $ flutter test -(output) +Waiting for another flutter command to release the startup lock... +00:00 +0: loading /config/workspace/appsok/test/widget_test.dart +00:00 +0: keeps the AppSok work app theme baseline +00:00 +1: renders the app shell +00:00 +2: switches between all shell destinations with status placeholders +00:01 +3: All tests passed! ``` ### 최종 검증 ```bash $ dart format lib/src/features/app_shell.dart test/widget_test.dart -(output) +Formatted test/widget_test.dart +Formatted 2 files (1 changed) in 0.03 seconds. $ flutter analyze -(output) +Analyzing appsok... +No issues found! (ran in 3.3s) $ flutter test -(output) +Waiting for another flutter command to release the startup lock... +00:00 +0: loading /config/workspace/appsok/test/widget_test.dart +00:00 +0: keeps the AppSok work app theme baseline +00:00 +1: renders the app shell +00:00 +2: switches between all shell destinations with status placeholders +00:01 +3: All tests passed! +``` + +Remote Mac runner 최종 evidence: + +```bash +$ zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter analyze' +Resolving dependencies... +Downloading packages... + app_links 7.0.0 (7.1.1 available) + flutter_secure_storage_darwin 0.3.2 (0.4.0 available) + matcher 0.12.19 (0.12.20 available) + meta 1.18.0 (1.18.3 available) + test_api 0.7.11 (0.7.12 available) + vector_math 2.2.0 (2.4.0 available) + webview_flutter_android 4.12.0 (4.13.0 available) + webview_flutter_wkwebview 3.25.1 (3.26.0 available) +Got dependencies! +8 packages have newer versions incompatible with dependency constraints. +Try `flutter pub outdated` for more information. +Analyzing appsok... +No issues found! (ran in 1.7s) + +$ zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter test' +00:00 +0: loading /Users/toki/docker/services/code-server/data/volume/workspace/appsok/test/widget_test.dart +00:00 +0: keeps the AppSok work app theme baseline +00:00 +1: renders the app shell +00:00 +2: switches between all shell destinations with status placeholders +00:01 +3: All tests passed! ``` --- @@ -145,3 +190,22 @@ $ flutter test | 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan. | | 검증 결과 | Implementing agent | Fill command output only; command changes require a `계획 대비 변경 사항` entry. | | 코드리뷰 결과 | Review agent appends | Not included until review. | + +## 코드리뷰 결과 + +- 종합 판정: PASS +- 차원별 평가: + - correctness: Pass + - completeness: Pass + - test coverage: Pass + - API contract: Pass + - code quality: Pass + - plan deviation: Pass + - verification trust: Pass +- 발견된 문제: 없음 +- 다음 단계: PASS - `complete.log` 작성 후 active task directory를 archive로 이동한다. +- 리뷰어 검증: + - `dart format --output=none --set-exit-if-changed lib/src/features/app_shell.dart test/widget_test.dart` - PASS; `Formatted 2 files (0 changed) in 0.02 seconds.` + - `flutter analyze` - PASS; `No issues found!` + - `flutter test` - PASS; `All tests passed!` + - `git diff --check` - PASS; 출력 없음 diff --git a/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/complete.log b/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/complete.log new file mode 100644 index 0000000..045458e --- /dev/null +++ b/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/complete.log @@ -0,0 +1,44 @@ +# Complete - m-product-baseline/01_shell_nav_status + +## 완료 일시 + +2026-06-09 + +## 요약 + +SHELL_NAV plan 0 first review completed with PASS; app shell navigation/status baseline and widget coverage are in place. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_local_G05_0.log` | `code_review_local_G05_0.log` | PASS | Navigation/status baseline matched the plan; no Required/Suggested issues found. | + +## 구현/정리 내용 + +- Added stable keys and tooltips to the Jenkins/ADB status placeholders in `AppSokShell`. +- Expanded widget coverage to cycle through build, device, console, and settings destinations while asserting the top status placeholders remain visible. + +## 최종 검증 + +- `dart format --output=none --set-exit-if-changed lib/src/features/app_shell.dart test/widget_test.dart` - PASS; `Formatted 2 files (0 changed) in 0.02 seconds.` +- `flutter analyze` - PASS; `No issues found!` +- `flutter test` - PASS; `All tests passed!` +- `git diff --check` - PASS; output was empty. +- `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter analyze'` - PASS; implementation evidence recorded `No issues found!`. +- `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter test'` - PASS; implementation evidence recorded `All tests passed!`. + +## Roadmap Completion + +- Milestone: `agent-roadmap/phase/usable-mvp/milestones/product-baseline.md` +- Completed task ids: + - `nav-shell`: PASS; evidence=`agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/plan_local_G05_0.log`, `agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/code_review_local_G05_0.log`; verification=`flutter analyze`, `flutter test` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/m-product-baseline/01_shell_nav_status/PLAN-local-G05.md b/agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/plan_local_G05_0.log similarity index 100% rename from agent-task/m-product-baseline/01_shell_nav_status/PLAN-local-G05.md rename to agent-task/archive/2026/06/m-product-baseline/01_shell_nav_status/plan_local_G05_0.log diff --git a/agent-task/m-product-baseline/02+01_responsive_ui/CODE_REVIEW-local-G05.md b/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/code_review_local_G05_0.log similarity index 70% rename from agent-task/m-product-baseline/02+01_responsive_ui/CODE_REVIEW-local-G05.md rename to agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/code_review_local_G05_0.log index 24b772d..84b07b3 100644 --- a/agent-task/m-product-baseline/02+01_responsive_ui/CODE_REVIEW-local-G05.md +++ b/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/code_review_local_G05_0.log @@ -42,42 +42,46 @@ task=m-product-baseline/02+01_responsive_ui, plan=0, tag=RESPONSIVE_UI | 항목 | 완료 여부 | |------|---------| -| [RESPONSIVE_UI-1] Overflow-safe shell and placeholder screens | [ ] | +| [RESPONSIVE_UI-1] Overflow-safe shell and placeholder screens | [x] | ## 구현 체크리스트 -- [ ] `agent-task/m-product-baseline/01_shell_nav_status/complete.log`가 존재하는지 확인하고 없으면 이 plan 구현을 시작하지 않는다. -- [ ] top bar, toolbar, card, badge, button layout이 기본 테스트 viewport에서 overflow를 내지 않도록 `AppSokShell`과 placeholder page layout을 안정화한다. 검증: `flutter test`가 overflow exception 없이 통과한다. -- [ ] `test/widget_test.dart`에 작은 viewport smoke test를 추가하고 build/device/console/settings 화면을 순회해 overflow exception이 없음을 검증한다. -- [ ] `dart format lib/src/features/app_shell.dart lib/src/features/builds/builds_page.dart lib/src/features/devices/devices_page.dart lib/src/features/console/console_page.dart lib/src/features/settings/settings_page.dart test/widget_test.dart`를 실행한다. -- [ ] `flutter analyze`와 `flutter test`를 실행한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. +- [x] `agent-task/m-product-baseline/01_shell_nav_status/complete.log`가 존재하는지 확인하고 없으면 이 plan 구현을 시작하지 않는다. +- [x] top bar, toolbar, card, badge, button layout이 기본 테스트 viewport에서 overflow를 내지 않도록 `AppSokShell`과 placeholder page layout을 안정화한다. 검증: `flutter test`가 overflow exception 없이 통과한다. +- [x] `test/widget_test.dart`에 작은 viewport smoke test를 추가하고 build/device/console/settings 화면을 순회해 overflow exception이 없음을 검증한다. +- [x] `dart format lib/src/features/app_shell.dart lib/src/features/builds/builds_page.dart lib/src/features/devices/devices_page.dart lib/src/features/console/console_page.dart lib/src/features/settings/settings_page.dart test/widget_test.dart`를 실행한다. +- [x] `flutter analyze`와 `flutter test`를 실행한다. +- [x] 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-product-baseline/02+01_responsive_ui/`를 `agent-task/archive/YYYY/MM/m-product-baseline/02+01_responsive_ui/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. -- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-product-baseline/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G05_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_local_G05_M.log`로 아카이브한다. +- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [x] PASS이면 active task 디렉터리 `agent-task/m-product-baseline/02+01_responsive_ui/`를 `agent-task/archive/YYYY/MM/m-product-baseline/02+01_responsive_ui/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-product-baseline/`를 제거하거나, 남은 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로 이동한다. ## 계획 대비 변경 사항 -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ +- `settings_page.dart`를 수정 대상 파일 목록에 추가했다. 계획의 수정 파일 요약에는 포함되었으나 수정 체크리스트에서는 명시적으로 다루지 않았다. compact viewport(600×400)에서 `SettingsPage`의 outer `Column`이 `Expanded` 없이 두 Material 카드(~384px)를 나열해 300px 제약을 116px 초과하는 수직 overflow가 발생했다. `Padding` → `SingleChildScrollView(padding:...)` 교체로 해소했다. +- 선행 의존성 확인에서 `agent-task/m-product-baseline/01_shell_nav_status/complete.log` 원래 경로가 없고 해당 파일들이 `agent-task/archive/`로 이동된 상태였다. git status와 archive 디렉터리 생성으로 완료를 간접 확인했다. ## 주요 설계 결정 -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ +- **toolbar Row → Wrap**: toolbar에서 `Row(Expanded+Expanded+Button)` 패턴을 `Wrap(spacing:12, runSpacing:8/10)` + `ConstrainedBox(min/max)`로 교체했다. TextField에 `Expanded`가 없는 Wrap에서는 `ConstrainedBox`로 최소/최대 폭을 지정해 좁은 폭에서 자연스럽게 줄바꿈되게 했다. +- **`_TopBar` title**: `Text(title)` + `Spacer()` → `Expanded(Text(title, maxLines:1, overflow:ellipsis))`로 교체해 title이 status pill과의 공간을 flex로 공유하며 ellipsis 처리되도록 했다. +- **`SettingsPage` 스크롤화**: settings는 카드 높이가 고정되어 좁은 viewport에서 수직 overflow 발생. `SingleChildScrollView`로 전환했다. 향후 Jenkins/ADB 설정 항목이 늘어도 스크롤로 대응 가능하다. +- **console toolbar icon buttons**: Wrap 안에서 두 `IconButton`을 `Row(mainAxisSize: MainAxisSize.min)` 으로 묶어 항상 같은 행에 위치하도록 했다. ## 사용자 리뷰 요청 @@ -111,19 +115,31 @@ _구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 ### RESPONSIVE_UI-1 중간 검증 ```bash $ flutter test -(output) +00:00 +0: loading /config/workspace/appsok/test/widget_test.dart +00:00 +0: keeps the AppSok work app theme baseline +00:00 +1: renders the app shell +00:00 +2: renders shell pages without overflow at compact viewport +00:00 +3: switches between all shell destinations with status placeholders +00:01 +4: All tests passed! ``` ### 최종 검증 ```bash $ dart format lib/src/features/app_shell.dart lib/src/features/builds/builds_page.dart lib/src/features/devices/devices_page.dart lib/src/features/console/console_page.dart lib/src/features/settings/settings_page.dart test/widget_test.dart -(output) +Formatted test/widget_test.dart +Formatted 6 files (1 changed) in 0.05 seconds. $ flutter analyze -(output) +Analyzing appsok... +No issues found! (ran in 4.9s) $ flutter test -(output) +00:00 +0: loading /config/workspace/appsok/test/widget_test.dart +00:00 +0: keeps the AppSok work app theme baseline +00:00 +1: renders the app shell +00:00 +2: renders shell pages without overflow at compact viewport +00:00 +3: switches between all shell destinations with status placeholders +00:01 +4: All tests passed! ``` --- @@ -146,3 +162,17 @@ $ flutter test | 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan. | | 검증 결과 | Implementing agent | Fill command output only; command changes require a `계획 대비 변경 사항` entry. | | 코드리뷰 결과 | Review agent appends | Not included until review. | + +## 코드리뷰 결과 + +- 종합 판정: PASS +- 차원별 평가: + - correctness: Pass + - completeness: Pass + - test coverage: Pass + - API contract: Pass + - code quality: Pass + - plan deviation: Pass + - verification trust: Pass +- 발견된 문제: 없음 +- 다음 단계: PASS이므로 `complete.log`를 작성하고 task directory를 archive로 이동한다. `m-product-baseline` 완료 이벤트 메타데이터를 보고하며 roadmap은 code-review에서 직접 수정하지 않는다. diff --git a/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/complete.log b/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/complete.log new file mode 100644 index 0000000..39c7248 --- /dev/null +++ b/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/complete.log @@ -0,0 +1,46 @@ +# Complete - m-product-baseline/02+01_responsive_ui + +## 완료 일시 + +2026-06-09 + +## 요약 + +RESPONSIVE_UI plan 0 first review completed with PASS; compact viewport overflow coverage and responsive placeholder layout updates are in place. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_local_G05_0.log` | `code_review_local_G05_0.log` | PASS | Overflow-safe shell/page layout matched the plan; no Required/Suggested issues found. | + +## 구현/정리 내용 + +- Made the shell top bar title/status area shrink safely with ellipsis and stable status keys. +- Replaced fixed toolbar rows in build/device/console placeholder pages with wrap-based compact layouts. +- Made the settings placeholder scroll vertically at compact heights. +- Added a compact viewport widget smoke test that cycles build, device, console, and settings pages and checks for overflow exceptions. + +## 최종 검증 + +- `dart format --output=none --set-exit-if-changed lib/src/features/app_shell.dart lib/src/features/builds/builds_page.dart lib/src/features/devices/devices_page.dart lib/src/features/console/console_page.dart lib/src/features/settings/settings_page.dart test/widget_test.dart` - PASS; `Formatted 6 files (0 changed) in 0.05 seconds.` +- `git diff --check` - PASS; output was empty. +- `flutter analyze` - PASS; `No issues found!` +- `flutter test` - PASS; `All tests passed!` +- `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter analyze'` on remote Mac runner - PASS; `No issues found!` +- `zsh -lc 'cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && flutter test'` on remote Mac runner - PASS; `All tests passed!` + +## Roadmap Completion + +- Milestone: `agent-roadmap/phase/usable-mvp/milestones/product-baseline.md` +- Completed task ids: + - `responsive-ui`: PASS; evidence=`agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/plan_local_G05_0.log`, `agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/code_review_local_G05_0.log`; verification=`flutter analyze`, `flutter test`, remote Mac runner `flutter analyze`, remote Mac runner `flutter test` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/m-product-baseline/02+01_responsive_ui/PLAN-local-G05.md b/agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/plan_local_G05_0.log similarity index 100% rename from agent-task/m-product-baseline/02+01_responsive_ui/PLAN-local-G05.md rename to agent-task/archive/2026/06/m-product-baseline/02+01_responsive_ui/plan_local_G05_0.log diff --git a/lib/src/features/app_shell.dart b/lib/src/features/app_shell.dart index 6974443..17db85c 100644 --- a/lib/src/features/app_shell.dart +++ b/lib/src/features/app_shell.dart @@ -154,11 +154,27 @@ class _TopBar extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 28), child: Row( children: [ - Text(title, style: Theme.of(context).textTheme.titleLarge), - const Spacer(), - const _StatusPill(icon: Icons.key_outlined, label: 'Jenkins 대기'), + Expanded( + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + const _StatusPill( + key: ValueKey('status-jenkins'), + icon: Icons.key_outlined, + label: 'Jenkins 대기', + tooltip: 'Jenkins 상태 연결 대기', + ), const SizedBox(width: 8), - const _StatusPill(icon: Icons.usb_outlined, label: 'ADB 대기'), + const _StatusPill( + key: ValueKey('status-adb'), + icon: Icons.usb_outlined, + label: 'ADB 대기', + tooltip: 'ADB 상태 연결 대기', + ), ], ), ), @@ -167,10 +183,16 @@ class _TopBar extends StatelessWidget { } class _StatusPill extends StatelessWidget { - const _StatusPill({required this.icon, required this.label}); + const _StatusPill({ + super.key, + required this.icon, + required this.label, + required this.tooltip, + }); final IconData icon; final String label; + final String tooltip; @override Widget build(BuildContext context) { @@ -187,7 +209,10 @@ class _StatusPill extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: 16, color: colorScheme.onSurfaceVariant), + Tooltip( + message: tooltip, + child: Icon(icon, size: 16, color: colorScheme.onSurfaceVariant), + ), const SizedBox(width: 6), Text( label, diff --git a/lib/src/features/builds/builds_page.dart b/lib/src/features/builds/builds_page.dart index 30d9513..bb04b02 100644 --- a/lib/src/features/builds/builds_page.dart +++ b/lib/src/features/builds/builds_page.dart @@ -14,10 +14,13 @@ class BuildsPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( + Wrap( + spacing: 12, + runSpacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, children: [ - Expanded( - flex: 3, + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 180, maxWidth: 360), child: TextField( decoration: InputDecoration( labelText: 'Jenkins job URL', @@ -30,9 +33,8 @@ class BuildsPage extends StatelessWidget { ), ), ), - const SizedBox(width: 12), - Expanded( - flex: 2, + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 160, maxWidth: 260), child: TextField( decoration: InputDecoration( labelText: '빌드 검색', @@ -45,7 +47,6 @@ class BuildsPage extends StatelessWidget { ), ), ), - const SizedBox(width: 12), FilledButton.icon( onPressed: () {}, icon: const Icon(Icons.login), diff --git a/lib/src/features/console/console_page.dart b/lib/src/features/console/console_page.dart index d79f1e2..36d2266 100644 --- a/lib/src/features/console/console_page.dart +++ b/lib/src/features/console/console_page.dart @@ -12,9 +12,13 @@ class ConsolePage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( + Wrap( + spacing: 12, + runSpacing: 8, + crossAxisAlignment: WrapCrossAlignment.center, children: [ - Expanded( + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 160, maxWidth: 320), child: TextField( decoration: InputDecoration( labelText: '패키지 또는 태그', @@ -27,7 +31,6 @@ class ConsolePage extends StatelessWidget { ), ), ), - const SizedBox(width: 12), SegmentedButton( segments: const [ ButtonSegment(value: 'all', label: Text('ALL')), @@ -37,17 +40,21 @@ class ConsolePage extends StatelessWidget { selected: const {'all'}, onSelectionChanged: (_) {}, ), - const SizedBox(width: 12), - IconButton.filledTonal( - tooltip: '일시정지', - onPressed: () {}, - icon: const Icon(Icons.pause), - ), - const SizedBox(width: 8), - IconButton.outlined( - tooltip: '지우기', - onPressed: () {}, - icon: const Icon(Icons.delete_outline), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton.filledTonal( + tooltip: '일시정지', + onPressed: () {}, + icon: const Icon(Icons.pause), + ), + const SizedBox(width: 8), + IconButton.outlined( + tooltip: '지우기', + onPressed: () {}, + icon: const Icon(Icons.delete_outline), + ), + ], ), ], ), diff --git a/lib/src/features/devices/devices_page.dart b/lib/src/features/devices/devices_page.dart index 79f2a1b..c87ccc8 100644 --- a/lib/src/features/devices/devices_page.dart +++ b/lib/src/features/devices/devices_page.dart @@ -14,7 +14,10 @@ class DevicesPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( + Wrap( + spacing: 12, + runSpacing: 8, + crossAxisAlignment: WrapCrossAlignment.center, children: [ SegmentedButton( segments: const [ @@ -37,7 +40,6 @@ class DevicesPage extends StatelessWidget { selected: const {'all'}, onSelectionChanged: (_) {}, ), - const Spacer(), OutlinedButton.icon( onPressed: () {}, icon: const Icon(Icons.refresh), diff --git a/lib/src/features/settings/settings_page.dart b/lib/src/features/settings/settings_page.dart index a83a531..0b7efbb 100644 --- a/lib/src/features/settings/settings_page.dart +++ b/lib/src/features/settings/settings_page.dart @@ -7,7 +7,7 @@ class SettingsPage extends StatelessWidget { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - return Padding( + return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(28, 0, 28, 28), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/test/widget_test.dart b/test/widget_test.dart index 4101eb3..f49c36d 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -26,12 +26,54 @@ void main() { expect(find.text('설정'), findsOneWidget); }); - testWidgets('switches to devices page', (WidgetTester tester) async { + testWidgets('renders shell pages without overflow at compact viewport', ( + WidgetTester tester, + ) async { + tester.view.physicalSize = const Size(600, 400); + tester.view.devicePixelRatio = 1.0; + addTearDown(tester.view.reset); + await tester.pumpWidget(const AppSokApp()); - await tester.tap(find.text('디바이스')); - await tester.pump(); - - expect(find.text('R5CT90A1B2C'), findsOneWidget); + final navTargets = ['빌드', '디바이스', '콘솔', '설정']; + for (final label in navTargets) { + final navLabel = find.descendant( + of: find.byType(NavigationRail), + matching: find.text(label), + ); + await tester.tap(navLabel); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + } }); + + testWidgets( + 'switches between all shell destinations with status placeholders', + (WidgetTester tester) async { + await tester.pumpWidget(const AppSokApp()); + + final navTargets = >[ + {'label': '빌드', 'marker': 'Jenkins job URL'}, + {'label': '디바이스', 'marker': 'R5CT90A1B2C'}, + {'label': '콘솔', 'marker': '패키지 또는 태그'}, + {'label': '설정', 'marker': 'Base URL'}, + ]; + + for (final target in navTargets) { + final navLabel = find.descendant( + of: find.byType(NavigationRail), + matching: find.text(target['label']!), + ); + + await tester.tap(navLabel); + await tester.pumpAndSettle(); + + expect(find.byKey(const ValueKey('status-jenkins')), findsOneWidget); + expect(find.byKey(const ValueKey('status-adb')), findsOneWidget); + expect(find.text('Jenkins 대기'), findsOneWidget); + expect(find.text('ADB 대기'), findsOneWidget); + expect(find.text(target['marker']!), findsOneWidget); + } + }, + ); }