From eb04e93d45d235f0a08870694a8fbd62905b0b76 Mon Sep 17 00:00:00 2001 From: toki Date: Fri, 29 May 2026 11:17:13 +0900 Subject: [PATCH] update flutter-test config and add m-client-validation task --- .../CODE_REVIEW-cloud-G07.md | 135 ++++++++++++++ .../04+03_fcm_ack_smoke/PLAN-cloud-G07.md | 173 ++++++++++++++++++ apps/flutter-test/README.md | 6 +- .../flutter-test/android/app/build.gradle.kts | 12 +- apps/flutter-test/android/settings.gradle.kts | 1 + 5 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md create mode 100644 agent-task/m-client-validation/04+03_fcm_ack_smoke/PLAN-cloud-G07.md diff --git a/agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md b/agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md new file mode 100644 index 00000000..cec01c9c --- /dev/null +++ b/agent-task/m-client-validation/04+03_fcm_ack_smoke/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,135 @@ + + +# 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-`이면 완료 이벤트 메타데이터를 보고한다. 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-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, 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. diff --git a/agent-task/m-client-validation/04+03_fcm_ack_smoke/PLAN-cloud-G07.md b/agent-task/m-client-validation/04+03_fcm_ack_smoke/PLAN-cloud-G07.md new file mode 100644 index 00000000..740049fe --- /dev/null +++ b/agent-task/m-client-validation/04+03_fcm_ack_smoke/PLAN-cloud-G07.md @@ -0,0 +1,173 @@ + + +# 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/apps/flutter-test/README.md b/apps/flutter-test/README.md index cde7a08d..24f7694f 100644 --- a/apps/flutter-test/README.md +++ b/apps/flutter-test/README.md @@ -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). diff --git a/apps/flutter-test/android/app/build.gradle.kts b/apps/flutter-test/android/app/build.gradle.kts index e29877bd..312ddaaf 100644 --- a/apps/flutter-test/android/app/build.gradle.kts +++ b/apps/flutter-test/android/app/build.gradle.kts @@ -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 diff --git a/apps/flutter-test/android/settings.gradle.kts b/apps/flutter-test/android/settings.gradle.kts index ca7fe065..e4e86fb6 100644 --- a/apps/flutter-test/android/settings.gradle.kts +++ b/apps/flutter-test/android/settings.gradle.kts @@ -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")