diff --git a/agent-task/m-client-validation/01_docs_boundary/CODE_REVIEW-local-G04.md b/agent-task/m-client-validation/01_docs_boundary/CODE_REVIEW-local-G04.md deleted file mode 100644 index 75360dc7..00000000 --- a/agent-task/m-client-validation/01_docs_boundary/CODE_REVIEW-local-G04.md +++ /dev/null @@ -1,171 +0,0 @@ - - -# Code Review Reference - DOCS - -> **[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, 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`. -> 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-05-28 -task=m-client-validation/01_docs_boundary, plan=0, tag=DOCS - -## 이 파일을 읽는 리뷰 에이전트에게 - -> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. - -각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. -리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. - -1. 판정을 append한다. -2. `CODE_REVIEW-local-G04.md` -> `code_review_local_G04_0.log`, `PLAN-local-G04.md` -> `plan_local_G04_0.log`로 아카이브한다. -3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-client-validation/01_docs_boundary/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. -4. PASS이고 task group이 `m-client-validation`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. -5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. - ---- - -## 구현 항목별 완료 여부 - -| 항목 | 완료 여부 | -|------|---------| -| [DOCS-1] flutter-test README 경계 보강 | [x] | -| [DOCS-2] plugin README SDK 경계와 smoke runbook 보강 | [x] | -| [DOCS-3] Android test environment guide 보강 | [x] | - -## 구현 체크리스트 - -- [x] [DOCS-1] `apps/flutter-test/README.md`에 thin host 책임, plugin-owned SDK boundary, remote Android guide 연결을 명확히 한다. -- [x] [DOCS-2] `packages/messaging_flutter/README.md`에 nexo-owned embedded SDK 경계와 manual FCM/ACK smoke runbook의 사전조건/증거를 보강한다. -- [x] [DOCS-3] `packages/messaging_flutter/docs/android-test-environment.md`가 local Android SDK 없음, remote emulator, Gradle unit test 실패 조건을 분명히 설명하게 한다. -- [x] `rg --sort path -n "nexo product app|manual Firebase FCM|nexo-owned|android-test-environment|testDebugUnitTest|flutter test integration_test" apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md`로 문서 기준선 문구를 확인한다. -- [x] `git diff --check -- apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md`를 통과시킨다. -- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -## 코드리뷰 전용 체크리스트 - -> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. -> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. - -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G04_0.log`로 아카이브한다. -- [ ] active `PLAN-*-G??.md`를 `plan_local_G04_0.log`로 아카이브한다. -- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. -- [ ] PASS이면 active task 디렉터리 `agent-task/m-client-validation/01_docs_boundary/`를 `agent-task/archive/YYYY/MM/m-client-validation/01_docs_boundary/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. -- [ ] PASS이고 task group이 `m-client-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-client-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. -- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-local-G04.md`와 `CODE_REVIEW-local-G04.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로 이동한다. - -## 계획 대비 변경 사항 - -없음. - -## 주요 설계 결정 - -- `apps/flutter-test` README에는 앱이 plugin API consumer/verification host임을 추가하고, native push/ACK/storage/rendering은 `packages/messaging_flutter` 소유로 남겼다. -- `packages/messaging_flutter` README에는 nexo-owned embedded SDK 경계를 별도 섹션으로 두고, Mattermost mobile app source를 팔로잉하지 않는다는 기준을 명시했다. -- manual FCM/ACK smoke는 자동 테스트가 아니라 외부 Firebase/server 인프라가 필요한 checklist로 유지하고, prerequisites/evidence만 보강했다. - -## 사용자 리뷰 요청 - -_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._ - -- 상태: 없음 -- 사유 유형: 없음 -- 결정 필요: 없음 -- 차단 근거: 없음 -- 실행한 검증/명령: 없음 -- 재개 조건: 없음 - -## 리뷰어를 위한 체크포인트 - -- 문서가 `apps/flutter-test`를 제품 앱으로 확장하지 않는지 확인한다. -- SDK boundary가 Mattermost mobile app code following과 혼동되지 않는지 확인한다. -- manual FCM/ACK smoke가 자동 검증과 명확히 분리되었는지 확인한다. - -## 검증 결과 - -_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ - -### DOCS-1 중간 검증 - -```sh -$ rg --sort path -n "plugin API consumer|source of truth|android-test-environment" apps/flutter-test/README.md -21:The app is a plugin API consumer and verification host. Native push handling, -81:[`../../packages/messaging_flutter/docs/android-test-environment.md`](../../packages/messaging_flutter/docs/android-test-environment.md). -``` - -### DOCS-2 중간 검증 - -```sh -$ rg --sort path -n "nexo-owned|Mattermost mobile|Prerequisites|Evidence" packages/messaging_flutter/README.md -13:This package is the nexo-owned embedded messaging SDK. Its Android -16:the Mattermost mobile app source tree; that app remains under `apps/mattermost/` -252:Prerequisites: -261:Evidence to capture: -``` - -### DOCS-3 중간 검증 - -```sh -$ rg --sort path -n "SDK location not found|ANDROID_HOME|sdk.dir|remote" packages/messaging_flutter/docs/android-test-environment.md -14:Use these environment variables for every remote Android test session: -18:export ANDROID_HOME="$HOME/Library/Android/sdk" -19:export ANDROID_SDK_ROOT="$ANDROID_HOME" -24:Before using the remote host, check whether the local machine already has the -34:`./gradlew testDebugUnitTest` requires either `ANDROID_HOME`/`ANDROID_SDK_ROOT` -35:or a valid `sdk.dir` entry in `apps/flutter-test/android/local.properties`. If -36:the command fails with `SDK location not found`, treat it as a local environment -37:blocker and use the remote flow below instead of changing repository files. -42:remote directory. This is useful when local changes are not committed yet. -69:export ANDROID_HOME="$HOME/Library/Android/sdk" -70:export ANDROID_SDK_ROOT="$ANDROID_HOME" -``` - -### 최종 검증 - -```sh -$ rg --sort path -n "plugin API consumer|source of truth|nexo-owned|Mattermost mobile|Prerequisites|Evidence|SDK location not found|ANDROID_HOME|sdk.dir" apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md -apps/flutter-test/README.md:21:The app is a plugin API consumer and verification host. Native push handling, -apps/flutter-test/README.md:74:Android Gradle tests require a configured Android SDK through `ANDROID_HOME`, -packages/messaging_flutter/README.md:13:This package is the nexo-owned embedded messaging SDK. Its Android -packages/messaging_flutter/README.md:16:the Mattermost mobile app source tree; that app remains under `apps/mattermost/` -packages/messaging_flutter/README.md:194:Android Gradle tests require a configured Android SDK through `ANDROID_HOME` or -packages/messaging_flutter/README.md:232:Use the repository tests as the source of truth for plugin behavior: -packages/messaging_flutter/README.md:252:Prerequisites: -packages/messaging_flutter/README.md:261:Evidence to capture: -packages/messaging_flutter/docs/android-test-environment.md:18:export ANDROID_HOME="$HOME/Library/Android/sdk" -packages/messaging_flutter/docs/android-test-environment.md:19:export ANDROID_SDK_ROOT="$ANDROID_HOME" -packages/messaging_flutter/docs/android-test-environment.md:34:`./gradlew testDebugUnitTest` requires either `ANDROID_HOME`/`ANDROID_SDK_ROOT` -packages/messaging_flutter/docs/android-test-environment.md:35:or a valid `sdk.dir` entry in `apps/flutter-test/android/local.properties`. If -packages/messaging_flutter/docs/android-test-environment.md:36:the command fails with `SDK location not found`, treat it as a local environment -$ git diff --check -- apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md -(no 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. - -Sections and their ownership: - -| Section | Owner | Note | -|---------|-------|------| -| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these | -| 구현 항목별 완료 여부 | Fixed at stub creation | Implementing agent checks `[ ]` -> `[x]` only | -| 구현 체크리스트 | Fixed at stub creation from plan | Implementing agent checks `[ ]` -> `[x]` only | -| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | -| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content | -| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required | -| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan | -| 검증 결과 | Fixed headings and commands | Implementing agent fills command output only | diff --git a/agent-task/m-client-validation/01_docs_boundary/PLAN-local-G04.md b/agent-task/m-client-validation/01_docs_boundary/PLAN-local-G04.md deleted file mode 100644 index 1f34e6c8..00000000 --- a/agent-task/m-client-validation/01_docs_boundary/PLAN-local-G04.md +++ /dev/null @@ -1,188 +0,0 @@ - - -# Plan - DOCS - -## 이 파일을 읽는 구현 에이전트에게 - -`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 변경 내용, 명령 출력, 계획 대비 변경 사항을 채운 뒤 active 파일을 그대로 둔 채 리뷰 준비 상태로 보고한다. 최종 판정, 로그 rename, `complete.log`, archive 이동은 code-review 스킬 전용이다. 사용자만 결정할 수 있는 사항, 외부 환경 준비, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정 필요 사항과 근거를 채우고 멈춘다. - -## 배경 - -현재 Milestone은 `apps/flutter-test`를 제품 앱이 아닌 얇은 플러그인 검증 호스트로 고정해야 한다. README들은 이미 방향을 담고 있지만, SDK ownership boundary와 수동 FCM/ACK smoke runbook이 한눈에 이어지지 않는다. 먼저 문서 기준선을 정리해야 후속 테스트 작업이 범위를 넓히지 않는다. - -## 사용자 리뷰 요청 흐름 - -구현 중 blocker는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 섹션 형식은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`와 같으며, code-review가 검증 후 실제 `USER_REVIEW.md` 작성 여부를 결정한다. - -## 분석 결과 - -### 읽은 파일 - -- `agent-roadmap/current.md` -- `agent-roadmap/phase/product-foundation/PHASE.md` -- `agent-roadmap/phase/product-foundation/milestones/client-validation.md` -- `agent-ops/rules/project/domain/client-app/rules.md` -- `agent-ops/rules/project/domain/messaging-flutter/rules.md` -- `agent-ops/rules/project/domain/workspace-ops/rules.md` -- `apps/flutter-test/README.md` -- `packages/messaging_flutter/README.md` -- `packages/messaging_flutter/docs/android-test-environment.md` -- `apps/flutter-test/lib/main.dart` -- `apps/flutter-test/test/widget_test.dart` -- `apps/flutter-test/integration_test/plugin_integration_test.dart` -- `packages/messaging_flutter/lib/nexo_messaging.dart` -- `packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart` -- `packages/messaging_flutter/lib/src/notification_opened_event.dart` -- `packages/messaging_flutter/lib/src/push_notification_type.dart` -- `packages/messaging_flutter/test/nexo_messaging_test.dart` -- `apps/flutter-test/pubspec.yaml` -- `packages/messaging_flutter/pubspec.yaml` - -### 테스트 커버리지 공백 - -- 문서 기준선 변경: 자동 테스트는 직접 커버하지 않는다. `rg --sort path` 검색과 `git diff --check`로 링크/용어 회귀를 확인한다. -- SDK ownership boundary 설명: 기존 Dart/widget/integration 테스트가 동작 자체를 커버하지만 README 문구 자체는 테스트하지 않는다. -- FCM/ACK manual smoke runbook: 외부 Firebase/server 인프라가 필요해 자동화하지 않는다. - -### 심볼 참조 - -- 변경/삭제할 심볼 없음. -- 확인한 관련 참조: `rg --sort path -n "android-test-environment|google-services|ACK|Manual Smoke|testDebugUnitTest|flutter test integration_test" apps/flutter-test packages/messaging_flutter`. - -### 분할 판단 - -- split decision policy를 plan 파일 선택 전에 평가했다. -- 공유 task group: `agent-task/m-client-validation`. -- 이 subtask: `01_docs_boundary`, 선행 없음. -- 후속: `02+01_flutter_contract_tests`는 문서 기준선 후 테스트 계약을 보강한다. `03+01,02_android_native_validation`은 문서와 Flutter 계약 테스트 후 Android/원격 검증을 정리한다. - -### 범위 결정 근거 - -- `apps/mattermost/**`, `services/**`는 이 Milestone의 client validation 범위가 아니므로 제외한다. -- `apps/flutter-test`를 제품 UI로 확장하지 않는다. README 문구와 문서 링크만 정리한다. -- `docs/` root에는 환경별 host/token/runbook을 두지 않는 private rule 때문에 새 root docs를 만들지 않는다. - -### 빌드 등급 - -- build=`local-G04`, review=`local-G04`. 문서 중심이고 자동 검증은 검색/공백 검사지만, 두 도메인 README를 연결하므로 G04로 둔다. - -## 구현 체크리스트 - -- [ ] [DOCS-1] `apps/flutter-test/README.md`에 thin host 책임, plugin-owned SDK boundary, remote Android guide 연결을 명확히 한다. -- [ ] [DOCS-2] `packages/messaging_flutter/README.md`에 nexo-owned embedded SDK 경계와 manual FCM/ACK smoke runbook의 사전조건/증거를 보강한다. -- [ ] [DOCS-3] `packages/messaging_flutter/docs/android-test-environment.md`가 local Android SDK 없음, remote emulator, Gradle unit test 실패 조건을 분명히 설명하게 한다. -- [ ] `rg --sort path -n "nexo product app|manual Firebase FCM|nexo-owned|android-test-environment|testDebugUnitTest|flutter test integration_test" apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md`로 문서 기준선 문구를 확인한다. -- [ ] `git diff --check -- apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md`를 통과시킨다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -### [DOCS-1] flutter-test README 경계 보강 - -#### 문제 - -[apps/flutter-test/README.md](/config/workspace/nexo/apps/flutter-test/README.md:7)는 test harness 경계를 설명하지만 SDK ownership boundary와 manual smoke owner가 분리되어 보이지 않는다. - -Before: - -```markdown -7 This app is the workspace test harness for `packages/messaging_flutter`. Keep it -8 small and focused on proving the messaging/notification package contract: -``` - -#### 해결 방법 - -`Purpose` 뒤에 이 앱은 plugin API 소비자이며 native push/ACK/storage source of truth는 `packages/messaging_flutter`라는 문장을 추가한다. `Verification`에는 local에서 device가 없으면 integration test가 실패할 수 있고 remote guide를 쓰라는 연결을 더 직접적으로 둔다. - -#### 수정 파일 및 체크리스트 - -- [ ] `apps/flutter-test/README.md` - -#### 테스트 작성 - -문서 변경이라 테스트 파일은 추가하지 않는다. - -#### 중간 검증 - -```sh -rg --sort path -n "plugin API consumer|source of truth|android-test-environment" apps/flutter-test/README.md -``` - -### [DOCS-2] plugin README SDK 경계와 smoke runbook 보강 - -#### 문제 - -[packages/messaging_flutter/README.md](/config/workspace/nexo/packages/messaging_flutter/README.md:6)는 plugin이 push domain을 소유한다고 설명하지만, Mattermost mobile code를 팔로잉하지 않는 nexo-owned embedded SDK 경계는 명시적이지 않다. [packages/messaging_flutter/README.md](/config/workspace/nexo/packages/messaging_flutter/README.md:238)의 manual checklist도 사전조건과 증거 형식이 부족하다. - -Before: - -```markdown -6 The plugin owns the push-notification domain that should not be duplicated in -7 each consuming app: native FCM handling, notification display, notification tap -8 events, inline replies, dismiss handling, ACK delivery, device-token storage, -9 auth-token storage, and server signing-key storage. -``` - -#### 해결 방법 - -`Platform Status` 전후에 SDK ownership boundary를 추가한다. `Manual Smoke Checklist`를 유지하되 `Prerequisites`와 `Evidence to capture`를 더해 Firebase config, server URL/auth/signing key, logcat/notification/ACK evidence를 기록하게 한다. - -#### 수정 파일 및 체크리스트 - -- [ ] `packages/messaging_flutter/README.md` - -#### 테스트 작성 - -문서 변경이라 테스트 파일은 추가하지 않는다. - -#### 중간 검증 - -```sh -rg --sort path -n "nexo-owned|Mattermost mobile|Prerequisites|Evidence" packages/messaging_flutter/README.md -``` - -### [DOCS-3] Android test environment guide 보강 - -#### 문제 - -[packages/messaging_flutter/docs/android-test-environment.md](/config/workspace/nexo/packages/messaging_flutter/docs/android-test-environment.md:73)는 remote check 명령을 제공하지만, local failure mode와 Gradle SDK precondition이 README와 같은 언어로 연결되지 않는다. 현재 local `./gradlew testDebugUnitTest`는 `SDK location not found`로 실패했다. - -Before: - -```markdown -73 ## Run The Checks -75 Run the deterministic Flutter checks: -``` - -#### 해결 방법 - -`Run The Checks` 앞에 local preflight와 expected fallback을 추가한다. `flutter doctor -v`, `ANDROID_HOME`, `local.properties sdk.dir` 확인을 넣고, local Android SDK가 없으면 remote run이 source of truth임을 적는다. - -#### 수정 파일 및 체크리스트 - -- [ ] `packages/messaging_flutter/docs/android-test-environment.md` - -#### 테스트 작성 - -문서 변경이라 테스트 파일은 추가하지 않는다. - -#### 중간 검증 - -```sh -rg --sort path -n "SDK location not found|ANDROID_HOME|sdk.dir|remote" packages/messaging_flutter/docs/android-test-environment.md -``` - -## 수정 파일 요약 - -| 파일 | 항목 | -| --- | --- | -| `apps/flutter-test/README.md` | DOCS-1 | -| `packages/messaging_flutter/README.md` | DOCS-2 | -| `packages/messaging_flutter/docs/android-test-environment.md` | DOCS-3 | - -## 최종 검증 - -```sh -rg --sort path -n "plugin API consumer|source of truth|nexo-owned|Mattermost mobile|Prerequisites|Evidence|SDK location not found|ANDROID_HOME|sdk.dir" apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md -git diff --check -- apps/flutter-test/README.md packages/messaging_flutter/README.md packages/messaging_flutter/docs/android-test-environment.md -``` - -모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-client-validation/02+01_flutter_contract_tests/CODE_REVIEW-cloud-G05.md b/agent-task/m-client-validation/02+01_flutter_contract_tests/CODE_REVIEW-cloud-G05.md deleted file mode 100644 index 9aa2184c..00000000 --- a/agent-task/m-client-validation/02+01_flutter_contract_tests/CODE_REVIEW-cloud-G05.md +++ /dev/null @@ -1,133 +0,0 @@ - - -# Code Review Reference - TEST - -> **[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, 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`. -> 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-05-28 -task=m-client-validation/02+01_flutter_contract_tests, plan=0, tag=TEST - -## 이 파일을 읽는 리뷰 에이전트에게 - -> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. - -각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. -리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. - -1. 판정을 append한다. -2. `CODE_REVIEW-cloud-G05.md` -> `code_review_cloud_G05_0.log`, `PLAN-cloud-G05.md` -> `plan_cloud_G05_0.log`로 아카이브한다. -3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-client-validation/02+01_flutter_contract_tests/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. -4. PASS이고 task group이 `m-client-validation`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. -5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. - ---- - -## 구현 항목별 완료 여부 - -| 항목 | 완료 여부 | -|------|---------| -| [TEST-1] integration test exact contract 보강 | [ ] | -| [TEST-2] local Dart/widget 계약 보강 | [ ] | - -## 구현 체크리스트 - -- [ ] [TEST-1] `apps/flutter-test/integration_test/plugin_integration_test.dart`가 notification/action channel constants와 exact opened-routing UI state를 검증하게 보강한다. -- [ ] [TEST-2] `apps/flutter-test/test/widget_test.dart` 또는 `packages/messaging_flutter/test/nexo_messaging_test.dart`에 integration에서 device 때문에 못 보는 계약을 local test로 보강한다. -- [ ] `flutter test`를 `packages/messaging_flutter`에서 실행해 package Dart contract가 통과함을 기록한다. -- [ ] `flutter test`를 `apps/flutter-test`에서 실행해 thin host widget test가 통과함을 기록한다. -- [ ] `flutter test integration_test`를 `apps/flutter-test`에서 실행한다. device 없음이면 실제 실패 출력을 기록하고, remote/device 검증은 후속 `03+01,02_android_native_validation`에 남긴다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -## 코드리뷰 전용 체크리스트 - -> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. -> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. - -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G05_0.log`로 아카이브한다. -- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G05_0.log`로 아카이브한다. -- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. -- [ ] PASS이면 active task 디렉터리 `agent-task/m-client-validation/02+01_flutter_contract_tests/`를 `agent-task/archive/YYYY/MM/m-client-validation/02+01_flutter_contract_tests/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. -- [ ] PASS이고 task group이 `m-client-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-client-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. -- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G05.md`와 `CODE_REVIEW-cloud-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로 이동한다. - -## 계획 대비 변경 사항 - -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ - -## 주요 설계 결정 - -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ - -## 사용자 리뷰 요청 - -_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._ - -- 상태: 없음 -- 사유 유형: 없음 -- 결정 필요: 없음 -- 차단 근거: 없음 -- 실행한 검증/명령: 없음 -- 재개 조건: 없음 - -## 리뷰어를 위한 체크포인트 - -- `01_docs_boundary` complete dependency가 충족된 뒤 구현되었는지 확인한다. -- integration test가 exact opened state를 검증하되 product UI를 늘리지 않았는지 확인한다. -- local test 보강이 integration gap을 정직하게 줄이는지 확인한다. - -## 검증 결과 - -_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ - -### TEST-1 중간 검증 - -```sh -$ flutter test integration_test -``` - -### TEST-2 중간 검증 - -```sh -$ cd packages/messaging_flutter && flutter test -$ cd apps/flutter-test && flutter test -``` - -### 최종 검증 - -```sh -$ cd packages/messaging_flutter && flutter test -$ cd apps/flutter-test && flutter test -$ cd apps/flutter-test && flutter test integration_test -``` - ---- - -> **[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. - -Sections and their ownership: - -| Section | Owner | Note | -|---------|-------|------| -| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these | -| 구현 항목별 완료 여부 | Fixed at stub creation | Implementing agent checks `[ ]` -> `[x]` only | -| 구현 체크리스트 | Fixed at stub creation from plan | Implementing agent checks `[ ]` -> `[x]` only | -| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | -| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content | -| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required | -| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan | -| 검증 결과 | Fixed headings and commands | Implementing agent fills command output only | diff --git a/agent-task/m-client-validation/02+01_flutter_contract_tests/PLAN-cloud-G05.md b/agent-task/m-client-validation/02+01_flutter_contract_tests/PLAN-cloud-G05.md deleted file mode 100644 index bda1f5b5..00000000 --- a/agent-task/m-client-validation/02+01_flutter_contract_tests/PLAN-cloud-G05.md +++ /dev/null @@ -1,166 +0,0 @@ - - -# Plan - TEST - -## 이 파일을 읽는 구현 에이전트에게 - -`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 변경 내용, 명령 출력, 계획 대비 변경 사항을 채운 뒤 active 파일을 그대로 둔 채 리뷰 준비 상태로 보고한다. 최종 판정, 로그 rename, `complete.log`, archive 이동은 code-review 스킬 전용이다. 사용자만 결정할 수 있는 사항, 외부 환경 준비, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정 필요 사항과 근거를 채우고 멈춘다. - -## 배경 - -`apps/flutter-test`의 Dart widget test와 integration test는 plugin registration, method channel, event channel, opened-routing 경로를 보호해야 한다. 현재 unit/widget 테스트는 통과하지만 integration test는 local Android device/emulator가 없어 실행되지 않는다. 이 subtask는 device-dependent integration test를 더 결정적으로 만들고 local에서 가능한 테스트 계약을 보강한다. - -## 사용자 리뷰 요청 흐름 - -구현 중 blocker는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 섹션 형식은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`와 같으며, code-review가 검증 후 실제 `USER_REVIEW.md` 작성 여부를 결정한다. - -## 분석 결과 - -### 읽은 파일 - -- `apps/flutter-test/lib/main.dart` -- `apps/flutter-test/test/widget_test.dart` -- `apps/flutter-test/integration_test/plugin_integration_test.dart` -- `apps/flutter-test/pubspec.yaml` -- `packages/messaging_flutter/lib/nexo_messaging.dart` -- `packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart` -- `packages/messaging_flutter/lib/src/notification_opened_event.dart` -- `packages/messaging_flutter/lib/src/push_notification_type.dart` -- `packages/messaging_flutter/test/nexo_messaging_test.dart` -- `packages/messaging_flutter/pubspec.yaml` -- `packages/messaging_flutter/README.md` -- `apps/flutter-test/README.md` - -### 테스트 커버리지 공백 - -- plugin registration constant: integration test checks `notificationChannelName` only; `actionChannelName` is only checked in package unit test. -- event-channel opened-routing: integration test checks text contains IDs but not exact `Last opened` state. -- method-channel debug event: package unit test checks Dart method invocation; integration test checks native route via `debugSendNativeEventForTesting`. -- local environment gap: `flutter test integration_test` currently fails with no supported Android device. This is environment-dependent and must be recorded, not hidden. - -### 심볼 참조 - -- 변경/삭제할 심볼 없음. -- 관련 참조는 `rg --sort path -n "debugSendNativeEvent|listenNativeChannelForTesting|notificationChannelName|actionChannelName|handleNativeEvent" apps/flutter-test packages/messaging_flutter`로 확인했다. - -### 분할 판단 - -- split decision policy를 plan 파일 선택 전에 평가했다. -- shared task group: `agent-task/m-client-validation`. -- predecessor: `01_docs_boundary` must produce `complete.log` first because it fixes the test-host scope and verification wording. -- this subtask: `02+01_flutter_contract_tests`. -- dependent sibling: `03+01,02_android_native_validation` waits for this because it will rely on the finalized Flutter contract test shape. - -### 범위 결정 근거 - -- Native Kotlin/JVM test changes are excluded here and handled by `03+01,02_android_native_validation`. -- No public Dart API rename or package identifier change is allowed in this Milestone subtask. -- No product UI is added to `apps/flutter-test`. - -### 빌드 등급 - -- build=`cloud-G05`, review=`cloud-G05`. The code change is small, but integration verification is device-dependent and local tests do not fully cover the behavior. - -## 의존 관계 및 구현 순서 - -- Runtime dependency from directory name: wait for `agent-task/m-client-validation/01_docs_boundary/complete.log`. -- Do not start this subtask until the predecessor has PASS completion. - -## 구현 체크리스트 - -- [ ] [TEST-1] `apps/flutter-test/integration_test/plugin_integration_test.dart`가 notification/action channel constants와 exact opened-routing UI state를 검증하게 보강한다. -- [ ] [TEST-2] `apps/flutter-test/test/widget_test.dart` 또는 `packages/messaging_flutter/test/nexo_messaging_test.dart`에 integration에서 device 때문에 못 보는 계약을 local test로 보강한다. -- [ ] `flutter test`를 `packages/messaging_flutter`에서 실행해 package Dart contract가 통과함을 기록한다. -- [ ] `flutter test`를 `apps/flutter-test`에서 실행해 thin host widget test가 통과함을 기록한다. -- [ ] `flutter test integration_test`를 `apps/flutter-test`에서 실행한다. device 없음이면 실제 실패 출력을 기록하고, remote/device 검증은 후속 `03+01,02_android_native_validation`에 남긴다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -### [TEST-1] integration test exact contract 보강 - -#### 문제 - -[apps/flutter-test/integration_test/plugin_integration_test.dart](/config/workspace/nexo/apps/flutter-test/integration_test/plugin_integration_test.dart:10)는 singleton과 notification channel만 확인한다. [apps/flutter-test/integration_test/plugin_integration_test.dart](/config/workspace/nexo/apps/flutter-test/integration_test/plugin_integration_test.dart:32)는 `find.textContaining` 위주라 opened state regression을 덜 정확히 잡는다. - -Before: - -```dart -10 testWidgets('singleton instance is accessible', (WidgetTester tester) async { -11 expect(NexoMessagingPlugin.instance, isNotNull); -12 expect( -13 NexoMessagingPlugin.notificationChannelName, -14 'com.tokilabs.nexo.messaging/notifications', -15 ); -16 }); -``` - -#### 해결 방법 - -`actionChannelName` assertion을 추가한다. channel/thread integration tests에는 exact `Last opened: ...` assertion을 추가하되 existing navigation assertions는 유지한다. - -#### 수정 파일 및 체크리스트 - -- [ ] `apps/flutter-test/integration_test/plugin_integration_test.dart` - -#### 테스트 작성 - -기존 integration test를 보강한다. 새 파일은 만들지 않는다. - -#### 중간 검증 - -```sh -flutter test integration_test -``` - -예상: local Android device/emulator가 없으면 현재처럼 "No supported devices connected"로 실패할 수 있다. 그 경우 출력 전체를 review stub에 기록한다. - -### [TEST-2] local Dart/widget 계약 보강 - -#### 문제 - -[packages/messaging_flutter/test/nexo_messaging_test.dart](/config/workspace/nexo/packages/messaging_flutter/test/nexo_messaging_test.dart:192)는 channel constants를 커버하지만 `apps/flutter-test/test/widget_test.dart`는 debug/native event path 대신 direct `handleNativeEvent`만 본다. device 없는 환경에서 integration gap을 보완할 local assertion이 더 필요하다. - -Before: - -```dart -192 group('Channel constants', () { -193 test('notification and action channel names are preserved', () { -``` - -#### 해결 방법 - -가능하면 existing local tests에 small assertion을 추가한다. 예: widget test initial state가 product-like controls 없이 status text만 렌더링하는 thin host임을 확인하거나 package unit test에 `message` without `userInteraction` does not navigate assertion을 추가한다. 새 public API는 만들지 않는다. - -#### 수정 파일 및 체크리스트 - -- [ ] `apps/flutter-test/test/widget_test.dart` 또는 `packages/messaging_flutter/test/nexo_messaging_test.dart` - -#### 테스트 작성 - -기존 test file에 regression assertion을 추가한다. - -#### 중간 검증 - -```sh -cd packages/messaging_flutter && flutter test -cd apps/flutter-test && flutter test -``` - -## 수정 파일 요약 - -| 파일 | 항목 | -| --- | --- | -| `apps/flutter-test/integration_test/plugin_integration_test.dart` | TEST-1 | -| `apps/flutter-test/test/widget_test.dart` | TEST-2 | -| `packages/messaging_flutter/test/nexo_messaging_test.dart` | TEST-2 | - -## 최종 검증 - -```sh -cd packages/messaging_flutter && flutter test -cd apps/flutter-test && flutter test -cd apps/flutter-test && flutter test integration_test -``` - -`flutter test integration_test`가 local device 부재로 실패하면 그 실제 출력을 기록하고, remote/device 재검증은 `03+01,02_android_native_validation`에서 수행한다. - -모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/apps/flutter-test/integration_test/plugin_integration_test.dart b/apps/flutter-test/integration_test/plugin_integration_test.dart index 7fb95265..b807dd7f 100644 --- a/apps/flutter-test/integration_test/plugin_integration_test.dart +++ b/apps/flutter-test/integration_test/plugin_integration_test.dart @@ -13,6 +13,10 @@ void main() { NexoMessagingPlugin.notificationChannelName, 'com.tokilabs.nexo.messaging/notifications', ); + expect( + NexoMessagingPlugin.actionChannelName, + 'com.tokilabs.nexo.messaging/notification_actions', + ); }); testWidgets('native debug event reaches harness routing UI (channel)', ( @@ -31,6 +35,12 @@ void main() { expect(find.textContaining('Last notification:'), findsOneWidget); expect(find.textContaining('channel-integration'), findsWidgets); + expect( + find.text( + 'Last opened: server_url: https://nexo.example.com, channel_id: channel-integration, root_id: null, is_crt_enabled: false', + ), + findsOneWidget, + ); expect( find.text( 'Last navigation: channel:https://nexo.example.com/channel-integration', @@ -59,6 +69,12 @@ void main() { expect(find.textContaining('Last notification:'), findsOneWidget); expect(find.textContaining('root-integration'), findsWidgets); + expect( + find.text( + 'Last opened: server_url: https://nexo.example.com, channel_id: channel-integration, root_id: root-integration, is_crt_enabled: true', + ), + findsOneWidget, + ); expect( find.text( 'Last navigation: thread:https://nexo.example.com/root-integration', diff --git a/apps/flutter-test/test/widget_test.dart b/apps/flutter-test/test/widget_test.dart index 016e4246..48263154 100644 --- a/apps/flutter-test/test/widget_test.dart +++ b/apps/flutter-test/test/widget_test.dart @@ -12,6 +12,18 @@ void main() { expect(find.text('Last opened: none'), findsOneWidget); expect(find.text('Last navigation: none'), findsOneWidget); + // Thin host contract: body contains exactly 4 status Text widgets + // and no product UI controls. + final listViewFinder = find.byType(ListView); + expect(listViewFinder, findsOneWidget); + final statusTexts = find.descendant( + of: listViewFinder, + matching: find.byType(Text), + ); + expect(statusTexts, findsNWidgets(4)); + expect(find.byType(ElevatedButton), findsNothing); + expect(find.byType(TextField), findsNothing); + await tester.pumpWidget(const SizedBox.shrink()); }); diff --git a/packages/messaging_flutter/test/nexo_messaging_test.dart b/packages/messaging_flutter/test/nexo_messaging_test.dart index a0712a87..ca9215f5 100644 --- a/packages/messaging_flutter/test/nexo_messaging_test.dart +++ b/packages/messaging_flutter/test/nexo_messaging_test.dart @@ -88,6 +88,33 @@ void main() { }, ); + test( + 'message event without userInteraction does not trigger navigation', + () async { + var channelCalled = false; + var threadCalled = false; + plugin.onNavigateToChannel = (_, _) => channelCalled = true; + plugin.onNavigateToThread = (_, _) => threadCalled = true; + + final openedEvents = []; + final sub = plugin.onNotificationOpened.listen(openedEvents.add); + + plugin.handleNativeEvent({ + 'type': PushNotificationType.message, + 'server_url': 'https://nexo.example.com', + 'channel_id': 'channel-xyz', + }); + + // Allow any pending microtasks to settle. + await Future.delayed(Duration.zero); + await sub.cancel(); + + expect(channelCalled, isFalse); + expect(threadCalled, isFalse); + expect(openedEvents, isEmpty); + }, + ); + test('opened event with CRT and rootId routes to thread callback', () { String? navServer; String? navRoot;