update flutter-test config and add m-client-validation task

This commit is contained in:
toki 2026-05-29 11:17:13 +09:00
parent 1336c51adf
commit eb04e93d45
5 changed files with 325 additions and 2 deletions

View file

@ -0,0 +1,135 @@
<!-- task=m-client-validation/04+03_fcm_ack_smoke plan=0 tag=SMOKE_RUN -->
# Code Review Reference - SMOKE_RUN
> **[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-29
task=m-client-validation/04+03_fcm_ack_smoke, plan=0, tag=SMOKE_RUN
## 이 파일을 읽는 리뷰 에이전트에게
> **[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-client-validation/04+03_fcm_ack_smoke/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
---
## 구현 항목별 완료 여부
| 항목 | 완료 여부 |
|------|---------|
| [SMOKE_RUN-1] Android test host 실행 | [ ] |
| [SMOKE_RUN-2] FCM delivery와 ACK evidence 수집 | [ ] |
| [SMOKE_RUN-3] Manual Smoke Checklist evidence 정리 | [ ] |
## 구현 체크리스트
- [ ] [SMOKE_RUN-1] `03_smoke_preflight` 완료를 확인하고 Android target에서 `apps/flutter-test`를 실행한다.
- [ ] [SMOKE_RUN-2] 실제 server push trigger를 실행하고 FCM receipt, signature handling, notification display, ACK response evidence를 수집한다.
- [ ] [SMOKE_RUN-3] opened-routing, inline reply, dismiss, device-token prefix evidence를 redact해서 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 코드리뷰 전용 체크리스트
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
- [ ] `코드리뷰 결과``PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [ ] active `CODE_REVIEW-*-G??.md``code_review_{review_lane}_GNN_N.log`로 아카이브한다.
- [ ] active `PLAN-*-G??.md``plan_{build_lane}_GNN_M.log`로 아카이브한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/``agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md``CODE_REVIEW-{review_lane}-GNN.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`를 작성한다._
- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 재개 조건: 없음
## 리뷰어를 위한 체크포인트
- `03_smoke_preflight/complete.log` 존재 확인이 실제로 수행되었는지 확인한다.
- trigger/logcat/server evidence가 실제 출력 또는 파일 경로로 기록되었는지 확인한다.
- secret/token/signing key/server endpoint 원문이 review stub에 남지 않았는지 확인한다.
- manual checklist 항목이 PASS/BLOCKED/FAIL로 과장 없이 분류되었는지 확인한다.
## 검증 결과
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
### SMOKE_RUN-1 중간 검증
```
$ test -f agent-task/m-client-validation/03_smoke_preflight/complete.log
$ test -n "${NEXO_ANDROID_DEVICE_ID:-}"
$ cd apps/flutter-test && flutter pub get
$ adb -s "$NEXO_ANDROID_DEVICE_ID" logcat -c
$ cd apps/flutter-test && flutter run -d "$NEXO_ANDROID_DEVICE_ID"
(output)
```
### SMOKE_RUN-2 중간 검증
```
$ test -n "${NEXO_SMOKE_TRIGGER_CMD:-}"
$ bash -lc "$NEXO_SMOKE_TRIGGER_CMD" 2>&1 | tee /tmp/nexo-smoke-trigger.log
$ adb -s "$NEXO_ANDROID_DEVICE_ID" logcat -d | rg "NexoFirebaseMessagingService|ReceiptDelivery|NotificationReplyBroadcastReceiver|NotificationDismissService|ACK|signature|opened|Navigate" | tee /tmp/nexo-smoke-logcat.txt
(output)
```
### SMOKE_RUN-3 중간 검증
```
$ rg --sort path -n "FCM payload wakes|ACK request|onNotificationOpened|onNavigateToChannel|onNavigateToThread|Inline reply|Dismiss action|Device token" agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md
(output)
```
### 최종 검증
```
$ rg --sort path -n "FCM payload wakes|ACK request|onNotificationOpened|onNavigateToChannel|onNavigateToThread|Inline reply|Dismiss action|Device token" agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md
(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.

View file

@ -0,0 +1,173 @@
<!-- task=m-client-validation/04+03_fcm_ack_smoke plan=0 tag=SMOKE_RUN -->
# Plan - SMOKE_RUN: Real FCM And ACK Smoke
## 이 파일을 읽는 구현 에이전트에게
이 계획의 구현이 끝나도 작업은 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 내용으로 채우기 전까지 완료가 아니다. 검증 명령을 실행하고 실제 stdout/stderr를 붙이며, active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 사용자만 줄 수 있는 결정, 외부 환경 준비, 또는 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션을 정확한 근거와 재개 조건으로 채우고 멈춘다. `USER_REVIEW.md`, `complete.log`, archive 이동은 code-review 전용이다.
## 배경
`manual-smoke` Epic의 실제 FCM delivery와 server ACK 확인은 headless repository test가 아니라 Android/Firebase/server runtime에 의존한다. `03_smoke_preflight`에서 환경 준비가 확인된 뒤에만 앱 실행, push trigger, logcat/server evidence를 수집한다. 결과는 active `CODE_REVIEW-cloud-G07.md`에 기록하고 roadmap은 수정하지 않는다.
## 사용자 리뷰 요청 흐름
구현 중 차단 사유가 생기면 active `CODE_REVIEW-cloud-G07.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`
- `apps/flutter-test/README.md`
- `packages/messaging_flutter/README.md`
- `packages/messaging_flutter/docs/android-test-environment.md`
- `agent-ops/rules/project/domain/client-app/rules.md`
- `agent-ops/rules/project/domain/messaging-flutter/rules.md`
- `agent-ops/rules/private/testing-env.md`
### 테스트 커버리지 공백
- True FCM delivery, notification display, inline reply, dismiss, and server ACK are intentionally outside deterministic Flutter tests. Evidence must come from device/emulator logs, app UI, screenshots/recordings, and server-side ACK/reply logs.
- Existing integration tests cover method channel, event channel, and opened-routing via debug injection only. They do not prove Firebase delivery.
### 심볼 참조
- none. public symbol rename/remove 없음.
### 분할 판단
분할 정책을 먼저 평가했다. shared task group은 `m-client-validation`이다.
- `03_smoke_preflight`: Firebase/Android/server trigger readiness를 확인하는 선행 작업.
- `04+03_fcm_ack_smoke`: 이 계획이며 `03_smoke_preflight``complete.log`가 필요하다.
### 범위 결정 근거
- source code 수정은 기본 범위가 아니다. smoke 결과에서 제품 결함이 확인되면 이 plan에서 임의 수정하지 말고 review stub에 evidence를 남긴다.
- tracked docs, roadmap, public README에 secret/token/signing key/server endpoint 원문을 쓰지 않는다.
- 실제 smoke evidence는 review stub에 redact해서 기록하고, 민감한 원본 로그나 screenshot은 repository 밖 임시 경로에 둔다.
### 빌드 등급
build=`cloud-G07`, review=`cloud-G07`. long-running `flutter run`, adb/logcat, external push trigger, manual evidence 해석이 성공 조건이라 terminal-agent work다.
## 구현 체크리스트
- [ ] [SMOKE_RUN-1] `03_smoke_preflight` 완료를 확인하고 Android target에서 `apps/flutter-test`를 실행한다.
- [ ] [SMOKE_RUN-2] 실제 server push trigger를 실행하고 FCM receipt, signature handling, notification display, ACK response evidence를 수집한다.
- [ ] [SMOKE_RUN-3] opened-routing, inline reply, dismiss, device-token prefix evidence를 redact해서 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 의존 관계 및 구현 순서
이 task directory 이름 `04+03_fcm_ack_smoke`는 같은 task group의 `03_smoke_preflight` 완료에 의존한다. 시작 전 `agent-task/m-client-validation/03_smoke_preflight/complete.log`가 있어야 한다. 없으면 구현하지 말고 preflight 완료를 요청한다.
## [SMOKE_RUN-1] Android test host 실행
### 문제
`packages/messaging_flutter/README.md:251-288`의 manual smoke checklist는 실제 Android host app이 떠 있고 Firebase config가 준비된 상태를 전제로 한다. 앱이 실행되지 않으면 FCM receipt와 opened-routing evidence를 수집할 수 없다.
### 해결 방법
preflight 완료를 확인하고, `NEXO_ANDROID_DEVICE_ID`로 지정된 device/emulator에서 `apps/flutter-test`를 실행한다. long-running command 출력은 review stub에 요약하지 말고 실제 출력 또는 저장 파일 경로를 기록한다.
### 수정 파일 및 체크리스트
- [ ] `agent-task/m-client-validation/03_smoke_preflight/complete.log` 존재 확인.
- [ ] `apps/flutter-test` dependency 준비.
- [ ] 지정 Android target에서 앱 실행.
### 테스트 작성
실제 device smoke라 테스트 파일은 추가하지 않는다.
### 중간 검증
```sh
test -f agent-task/m-client-validation/03_smoke_preflight/complete.log
test -n "${NEXO_ANDROID_DEVICE_ID:-}"
cd apps/flutter-test && flutter pub get
adb -s "$NEXO_ANDROID_DEVICE_ID" logcat -c
cd apps/flutter-test && flutter run -d "$NEXO_ANDROID_DEVICE_ID"
```
기대 결과: app이 지정 device/emulator에서 실행되고 logcat buffer가 clear된 상태로 smoke를 시작할 수 있다.
## [SMOKE_RUN-2] FCM delivery와 ACK evidence 수집
### 문제
repository test injection path는 true Firebase delivery와 server ACK를 증명하지 못한다. 실제 server push trigger와 device logcat, server-side ACK/reply log를 함께 확인해야 한다.
### 해결 방법
private 환경에서 제공된 `NEXO_SMOKE_TRIGGER_CMD`를 실행해 실제 push를 보낸다. command 자체나 출력에 secret이 포함되면 review stub에는 redacted 형태만 기록한다. device logcat에서는 plugin/service 관련 line만 추출한다.
### 수정 파일 및 체크리스트
- [ ] `NEXO_SMOKE_TRIGGER_CMD` 실행 결과 저장.
- [ ] `NexoFirebaseMessagingService`, signature handling, ACK 관련 logcat evidence 저장.
- [ ] server-side ACK/reply status evidence를 redacted 형태로 기록.
### 테스트 작성
외부 smoke evidence 수집 작업이라 테스트 파일은 추가하지 않는다.
### 중간 검증
```sh
test -n "${NEXO_SMOKE_TRIGGER_CMD:-}"
bash -lc "$NEXO_SMOKE_TRIGGER_CMD" 2>&1 | tee /tmp/nexo-smoke-trigger.log
adb -s "$NEXO_ANDROID_DEVICE_ID" logcat -d | rg "NexoFirebaseMessagingService|ReceiptDelivery|NotificationReplyBroadcastReceiver|NotificationDismissService|ACK|signature|opened|Navigate" | tee /tmp/nexo-smoke-logcat.txt
```
기대 결과: trigger command가 성공하고 logcat extract에 receipt/signature/ACK 관련 evidence가 포함된다.
## [SMOKE_RUN-3] Manual Smoke Checklist evidence 정리
### 문제
`packages/messaging_flutter/README.md:275-288`의 checklist는 FCM, notification, ACK, opened-routing, inline reply, dismiss, token prefix를 모두 확인해야 한다. 일부만 확인하고 PASS로 처리하면 manual-smoke Epic이 과하게 완료된 것으로 보일 수 있다.
### 해결 방법
각 checklist 항목을 review stub의 `주요 설계 결정` 또는 `검증 결과`에 PASS/BLOCKED/FAIL로 기록한다. secret이 포함된 원본 파일은 repository 밖 임시 경로만 남기고 tracked file에는 redact된 설명만 남긴다.
### 수정 파일 및 체크리스트
- [ ] FCM receipt, valid signature, invalid/unsigned drop, notification display evidence 기록.
- [ ] ACK success, opened event, channel/thread routing evidence 기록.
- [ ] inline reply, dismiss, `android_rn-v2:` token prefix evidence 기록.
- [ ] 미확인 항목은 PASS가 아니라 BLOCKED/FAIL로 분류하고 사유 기록.
### 테스트 작성
manual evidence 정리 작업이라 테스트 파일은 추가하지 않는다.
### 중간 검증
```sh
rg --sort path -n "FCM payload wakes|ACK request|onNotificationOpened|onNavigateToChannel|onNavigateToThread|Inline reply|Dismiss action|Device token" agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md
```
기대 결과: review stub에 checklist evidence가 항목별로 기록된다.
## 수정 파일 요약
| 파일 | 항목 |
| --- | --- |
| `agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md` | SMOKE_RUN-1, SMOKE_RUN-2, SMOKE_RUN-3 |
## 최종 검증
```sh
rg --sort path -n "FCM payload wakes|ACK request|onNotificationOpened|onNavigateToChannel|onNavigateToThread|Inline reply|Dismiss action|Device token" agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md
```
기대 결과: 실제 smoke checklist 항목의 PASS/BLOCKED/FAIL evidence가 review stub에 있다.
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.

View file

@ -57,6 +57,9 @@ flutter run
For Android FCM smoke testing, make sure the Android app has the required
Firebase configuration, including `android/app/google-services.json`.
Use the plugin-owned
[`Manual Smoke Checklist`](../../packages/messaging_flutter/README.md#manual-smoke-checklist)
for FCM delivery, ACK, inline reply, dismiss, and routing evidence.
## Verification
@ -99,4 +102,5 @@ integration and native unit verification:
- Do not grow this host into a nexo product app.
- Do not use this path for the original Mattermost app; that belongs under `apps/mattermost/`.
- Add integration tests here when a plugin behavior needs a real Flutter host.
- Keep manual FCM and server checks documented with the owning module.
- Keep manual FCM and server checks documented with the owning module:
[`Manual Smoke Checklist`](../../packages/messaging_flutter/README.md#manual-smoke-checklist).

View file

@ -5,6 +5,15 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
val smokeApplicationId =
providers.gradleProperty("nexoSmokeApplicationId").orNull?.trim()?.takeIf { it.isNotEmpty() }
// Apply Google Services only when a Firebase config is present so plain repo
// builds without google-services.json keep working.
if (file("google-services.json").exists()) {
apply(plugin = "com.google.gms.google-services")
}
android {
namespace = "com.tokilabs.nexo_client"
compileSdk = flutter.compileSdkVersion
@ -21,7 +30,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.tokilabs.nexo_client"
// Default stays com.tokilabs.nexo_client; smoke runs override via -PnexoSmokeApplicationId.
applicationId = smokeApplicationId ?: "com.tokilabs.nexo_client"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion

View file

@ -21,6 +21,7 @@ plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
id("com.google.gms.google-services") version "4.4.2" apply false
}
include(":app")