From a90d15f85eb45ce5f229a2240c5a7b0ea6e750df Mon Sep 17 00:00:00 2001 From: toki Date: Tue, 26 May 2026 18:36:23 +0900 Subject: [PATCH] chore: update roadmap, client README, push plugin integration, and assets --- agent-ops/roadmap/current.md | 5 ++ .../phase/external-integration/PHASE.md | 5 ++ .../mattermost-messaging-android-smoke.md | 67 +++++++++++++++++++ apps/client/README.md | 8 +-- apps/client/assets/.gitkeep | 1 + .../mattermost_push_plugin_client.dart | 16 +++-- .../Flutter/GeneratedPluginRegistrant.swift | 2 +- apps/client/pubspec.lock | 14 ++-- apps/client/pubspec.yaml | 9 ++- apps/client/push-notification-todo.md | 9 +-- 10 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md create mode 100644 apps/client/assets/.gitkeep diff --git a/agent-ops/roadmap/current.md b/agent-ops/roadmap/current.md index 04bad33..4e61e5e 100644 --- a/agent-ops/roadmap/current.md +++ b/agent-ops/roadmap/current.md @@ -4,6 +4,8 @@ - [진행중] Workflow Core - 경로: `agent-ops/roadmap/phase/workflow-core/PHASE.md` +- [계획] External Integration + - 경로: `agent-ops/roadmap/phase/external-integration/PHASE.md` ## 활성 Milestone @@ -13,6 +15,9 @@ - [계획] Workflow Core - Phase: `agent-ops/roadmap/phase/workflow-core/PHASE.md` - 경로: `agent-ops/roadmap/phase/workflow-core/milestones/workflow-core.md` +- [계획] Mattermost Messaging Android Smoke + - Phase: `agent-ops/roadmap/phase/external-integration/PHASE.md` + - 경로: `agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md` ## 선택 규칙 diff --git a/agent-ops/roadmap/phase/external-integration/PHASE.md b/agent-ops/roadmap/phase/external-integration/PHASE.md index 7c5ab8c..7ea8c63 100644 --- a/agent-ops/roadmap/phase/external-integration/PHASE.md +++ b/agent-ops/roadmap/phase/external-integration/PHASE.md @@ -13,6 +13,10 @@ Work Item Provider Pipeline Design과 Workflow Core 이후 Plane/Jira 확장, Ma 완료된 Milestone은 archive 경로를 가리키고, 검토중, 진행중, 계획 또는 보류 Milestone은 이 Phase 하위 `milestones/` 경로를 가리킨다. 완료, 검토중, 진행중, 계획 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다. +- [계획] Mattermost Messaging Android Smoke + - 경로: `agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md` + - 요약: `mattermost_push_plugin`이 `../nexo/packages/messaging_flutter`로 서버와 함께 합쳐지고 메시징 지원 방향으로 확장된 뒤, 원격 Android SDK 환경에서 build와 알림 도착 smoke를 확인한다. + - [계획] External Integration - 경로: `agent-ops/roadmap/phase/external-integration/milestones/external-integration.md` - 요약: Plane/Jira/Mattermost/Agent Integrator/IOP 호출 경계를 실제 통합 adapter 흐름으로 확장한다. @@ -20,5 +24,6 @@ Work Item Provider Pipeline Design과 Workflow Core 이후 Plane/Jira 확장, Ma ## Phase 경계 - 외부 provider 세부 구현은 adapter 경계 밖으로 새지 않게 한다. +- Mattermost messaging Android smoke는 사용자 테스트를 제외하고 원격 Android SDK build와 알림 도착 확인까지만 완료 범위로 둔다. - IOP 내부 모델 라우팅, 모델 프로파일, RAG, MCP, output validation, fallback 정책은 NomadCode 범위에서 제외한다. - A2A 도입 시점과 Agent Integrator 유지 여부는 사용자 결정이 필요하다. diff --git a/agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md b/agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md new file mode 100644 index 0000000..b85efcd --- /dev/null +++ b/agent-ops/roadmap/phase/external-integration/milestones/mattermost-messaging-android-smoke.md @@ -0,0 +1,67 @@ +# Milestone: Mattermost Messaging Android Smoke + +## 위치 + +- Roadmap: `agent-ops/roadmap/ROADMAP.md` +- Phase: `agent-ops/roadmap/phase/external-integration/PHASE.md` + +## 목표 + +`mattermost_push_plugin`이 서버와 같은 `../nexo` repo의 `packages/messaging_flutter` package로 합쳐지고, 알림 전용에서 메시징 지원까지 포함하는 방향으로 전환된 현재 상태를 Android 기준으로 검증한다. 사용자 테스트까지 진행하지 않고, 원격 Android SDK 환경에서 build가 통과하고 Mattermost 메시지/FCM 알림이 실제 Android runtime에 도착하는지 확인하는 데서 완료 범위를 닫는다. + +## 상태 + +[계획] + +## 구현 잠금 + +- 상태: 해제 +- 결정 필요: 없음 + +## 범위 + +- `apps/client` host와 `../nexo/packages/messaging_flutter` package 경계가 현재 messaging 방향성과 충돌하지 않는지 확인 +- 원격 Android SDK runner에서 `apps/client`의 Android debug build 검증 +- Android device 또는 emulator에서 앱 boot, Firebase 초기화, Mattermost auth/token handoff, 알림 callback이 깨지지 않는지 smoke 확인 +- Mattermost 메시지 또는 push trigger를 통해 Android 시스템 알림이 도착하는지 확인 +- 검증 중 드러나는 build/configuration regression은 smoke를 통과시키는 최소 수정 범위에서 처리 + +## 기능 + +### Epic: [android-smoke] Remote Android notification verification + +Mattermost messaging package 전환 이후 Android build와 알림 수신 경로가 실제 환경에서 깨지지 않았는지 확인한다. + +- [x] [scope-check] `apps/client`, `apps/client/push-notification-todo.md`, `../nexo/packages/messaging_flutter` 경계를 확인해 현재 repo 업데이트가 메시징 지원 방향성과 맞는지 정리한다. +- [ ] [remote-build] `docs/test-environments.md#android-sdk-remote-runner` 기준 원격 Android SDK 환경에서 `apps/client`의 `flutter pub get`과 `flutter build apk --debug`를 통과시킨다. 검증: 원격 host, branch/commit, 주요 command 결과를 secret 없이 evidence로 남긴다. +- [ ] [runtime-smoke] Android device 또는 emulator에서 앱을 실행해 앱 boot, Firebase 초기화, Mattermost auth/token handoff가 crash 없이 진행되는지 확인한다. 검증: `flutter run -d ` 또는 동등한 runtime 실행 결과를 남긴다. +- [ ] [notification-arrival] Mattermost 메시지 또는 push trigger로 Android 시스템 알림이 도착하는지 확인한다. 검증: 알림 도착 여부, 앱 callback 관찰 여부, 실패 시 원인 후보를 secret 없이 기록한다. +- [ ] [evidence-note] build와 알림 smoke 결과, 사용자 테스트 미포함 한계, 후속 보완이 필요한 경우를 완료 리뷰 근거로 남긴다. + +## 완료 리뷰 + +- 상태: 없음 +- 요청일: 없음 +- 완료 근거: 없음 +- 리뷰 필요: + - [ ] 사용자가 완료 결과를 확인했다 + - [ ] archive 이동을 승인했다 +- 리뷰 코멘트: 없음 + +## 범위 제외 + +- 사용자 테스트, UAT, 제품 사용성 검증 +- iOS/macOS native push full implementation 또는 runtime smoke +- Mattermost 메시징 제품 UX 구현 +- Mattermost server-side plugin 대규모 기능 확장 +- External Integration Milestone의 Plane/Jira/Agent Integrator/IOP adapter 구현 + +## 작업 컨텍스트 + +- 관련 경로: `apps/client/`, `apps/client/push-notification-todo.md`, `docs/test-environments.md`, `../nexo/packages/messaging_flutter` +- 연관 core 경계: `services/core/internal/adapters/mattermost/`, `services/core/internal/notification/` +- 선행 작업: Mattermost Push Plugin Extraction, Client Integration Standardization +- 후속 작업: External Integration의 Mattermost 메시지 발송 adapter 구현 +- 표준선: Android SDK 의존 검증은 `docs/test-environments.md#android-sdk-remote-runner`를 따른다. +- 보안 주의: Mattermost token, Firebase credential, signing key, device token은 roadmap이나 문서에 기록하지 않는다. +- 2026-05-26 scope-check: `apps/client` host dependency/import 문서를 `../nexo/packages/messaging_flutter`의 현재 Dart package명 `nexo_messaging`과 `NexoMessagingPlugin` API에 맞췄고, ignored `assets/mattermost_credentials.json`을 smoke 때 bundle할 수 있도록 assets 디렉터리를 선언했다. 원격 Android build와 실제 알림 도착 smoke는 환경 의존 작업으로 plan에서 수행한다. diff --git a/apps/client/README.md b/apps/client/README.md index b9a5c10..7e32349 100644 --- a/apps/client/README.md +++ b/apps/client/README.md @@ -38,7 +38,7 @@ We prioritize and validate two primary platforms in this milestone, while ensuri `apps/client/lib/src/integrations/` groups all external-system adapters behind small Dart interfaces: -- `integrations/mattermost/` — `MattermostPushClient` interface, `MattermostPushPluginClient` adapter (the only production reference to `MattermostPushPlugin.instance`), `MattermostPushHostIntegration` (initialize + auth handoff + navigation callbacks), and `MattermostAuthService`. +- `integrations/mattermost/` — `MattermostPushClient` interface, `MattermostPushPluginClient` adapter (the only production reference to `NexoMessagingPlugin.instance`), `MattermostPushHostIntegration` (initialize + auth handoff + navigation callbacks), and `MattermostAuthService`. - `integrations/proto_socket/` — `ProtoSocketEndpointConfig` and `ProtoSocketLifecycle` facade over the local `proto_socket` Dart path dependency. Bootstrap does not auto-connect; enabling and wiring a real connector are deferred to the next milestone. For detailed integration boundaries, platform targets, and clone handoff guidelines, see [push-notification-todo.md](push-notification-todo.md). @@ -47,7 +47,7 @@ For detailed integration boundaries, platform targets, and clone handoff guideli ## Mattermost Push Notification Integration -Mattermost push notification delivery is implemented via the local `mattermost_push_plugin` path dependency. +Mattermost push notification delivery is implemented via the local `nexo_messaging` path dependency at `../nexo/packages/messaging_flutter`. ### Architecture & Responsibility Split @@ -55,11 +55,11 @@ To maintain a clean and maintainable codebase, the responsibilities are split be #### Host Application (`apps/client`) Responsibilities: - **Firebase Configuration**: Contains the standard Firebase configuration files (`android/app/google-services.json`) and performs the standard `Firebase.initializeApp()` call in Dart main. -- **Mattermost Authentication**: Handles login and session lifecycles, and passes the user authentication token and server signing key through the `MattermostPushClient` boundary. The production `MattermostPushPluginClient` adapter owns the plugin singleton calls. +- **Mattermost Authentication**: Handles login and session lifecycles, and passes the user authentication token and server signing key through the `MattermostPushClient` boundary. The production `MattermostPushPluginClient` adapter owns the plugin singleton calls. Local smoke credentials may be provided as ignored `assets/mattermost_credentials.json`. - **Navigation Callbacks**: Registers standard Flutter navigation callbacks `onNavigateToChannel` and `onNavigateToThread` to handle workspace channel and CRT thread routing upon user interaction. - **FCM Token Sync**: Listens to device token registrations via `onDeviceTokenReady` and registers the token with the Mattermost server. -#### Mattermost Push Plugin (`mattermost_push_plugin`) Responsibilities: +#### Nexo Messaging Plugin (`nexo_messaging`) Responsibilities: - **Native Android FCM Service**: Intercepts background/foreground FCM notifications securely via a custom Kotlin FCM service. - **Signature Verification**: Validates incoming push payload signatures against stored public signing keys (using `JJWT`). - **System Notification Builder**: Displays structured notifications with local avatar caches, thread summaries, and action buttons. diff --git a/apps/client/assets/.gitkeep b/apps/client/assets/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/client/assets/.gitkeep @@ -0,0 +1 @@ + diff --git a/apps/client/lib/src/integrations/mattermost/mattermost_push_plugin_client.dart b/apps/client/lib/src/integrations/mattermost/mattermost_push_plugin_client.dart index ce29edb..56dddf3 100644 --- a/apps/client/lib/src/integrations/mattermost/mattermost_push_plugin_client.dart +++ b/apps/client/lib/src/integrations/mattermost/mattermost_push_plugin_client.dart @@ -1,17 +1,19 @@ -import 'package:mattermost_push_plugin/mattermost_push_plugin.dart'; +import 'dart:async'; + +import 'package:nexo_messaging/nexo_messaging.dart'; import 'mattermost_push_client.dart'; /// Production adapter wrapping the platform-channel singleton. /// /// This is the ONLY file in production that should reference -/// `MattermostPushPlugin.instance`. Everything else depends on +/// `NexoMessagingPlugin.instance`. Everything else depends on /// [MattermostPushClient]. class MattermostPushPluginClient implements MattermostPushClient { - final MattermostPushPlugin _plugin; + final NexoMessagingPlugin _plugin; - MattermostPushPluginClient({MattermostPushPlugin? plugin}) - : _plugin = plugin ?? MattermostPushPlugin.instance; + MattermostPushPluginClient({NexoMessagingPlugin? plugin}) + : _plugin = plugin ?? NexoMessagingPlugin.instance; @override Stream> get onNotification => _plugin.onNotification; @@ -32,7 +34,9 @@ class MattermostPushPluginClient implements MattermostPushClient { @override set onDeviceTokenReady(Future Function(String token)? callback) { - _plugin.onDeviceTokenReady = callback; + _plugin.onDeviceTokenReady = callback == null + ? null + : (token) => unawaited(callback(token)); } @override diff --git a/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift index 3ea0f1c..16df218 100644 --- a/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,7 +7,7 @@ import Foundation import firebase_core import firebase_messaging -import mattermost_push_plugin +import nexo_messaging import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { diff --git a/apps/client/pubspec.lock b/apps/client/pubspec.lock index 465f385..edab04f 100644 --- a/apps/client/pubspec.lock +++ b/apps/client/pubspec.lock @@ -208,13 +208,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.0" - mattermost_push_plugin: - dependency: "direct main" - description: - path: "../../../nexo/packages/messaging_flutter" - relative: true - source: path - version: "0.0.1" meta: dependency: transitive description: @@ -223,6 +216,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + nexo_messaging: + dependency: "direct main" + description: + path: "../../../nexo/packages/messaging_flutter" + relative: true + source: path + version: "0.0.1" path: dependency: transitive description: diff --git a/apps/client/pubspec.yaml b/apps/client/pubspec.yaml index 40d0ee2..f19e123 100644 --- a/apps/client/pubspec.yaml +++ b/apps/client/pubspec.yaml @@ -39,8 +39,8 @@ dependencies: # Firebase firebase_core: ^3.13.0 - # Mattermost push plugin - mattermost_push_plugin: + # Nexo messaging / Mattermost-compatible push plugin + nexo_messaging: path: ../../../nexo/packages/messaging_flutter # proto-socket: shared protocol/transport library (local path) @@ -99,3 +99,8 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package + + assets: + # Local Mattermost smoke credentials live at assets/mattermost_credentials.json + # and are ignored by apps/client/.gitignore. + - assets/ diff --git a/apps/client/push-notification-todo.md b/apps/client/push-notification-todo.md index 200b121..9212cb7 100644 --- a/apps/client/push-notification-todo.md +++ b/apps/client/push-notification-todo.md @@ -3,7 +3,7 @@ > [!IMPORTANT] > **Host App Integration Standard** > As of the `mattermost-push-plugin-extraction` milestone, all native FCM services, Room DB structures, JJWT signature verification, inline reply/dismiss receivers, notification builders, and ACK request handling have been successfully extracted into the local Flutter plugin package. -> The package now lives in the `nexo` monorepo at `../nexo/packages/messaging_flutter` while keeping the Dart package name `mattermost_push_plugin` for compatibility. +> The package now lives in the `nexo` monorepo at `../nexo/packages/messaging_flutter` with Dart package name `nexo_messaging`. The host-facing interface keeps Mattermost naming because this app consumes the Mattermost-compatible messaging boundary. > > **Do NOT edit native push files directly inside this host app repository (`apps/client/android`).** All native push logic and platform services now reside in and must be maintained within the plugin repository. @@ -20,6 +20,7 @@ To keep host architectures generic and lightweight, we maintain a strict boundar - Initializing Firebase inside `lib/src/app/bootstrap.dart` via `Firebase.initializeApp()`. 2. **Credential Plumbing**: - Host standardizes all credential synchronization through the `MattermostPushClient` interface rather than accessing the native plugin directly: + - Local smoke credentials may be provided as ignored `assets/mattermost_credentials.json`. - Login: Sync authorization credentials via `pushClient.setAuthToken(serverUrl, token)`. - Logout: Revoke and clear credentials via `pushClient.setAuthToken(serverUrl, '')`. - Asymmetric Signing Keys: Register server public keys via `pushClient.setSigningKey(serverUrl, publicKey)`. @@ -29,8 +30,8 @@ To keep host architectures generic and lightweight, we maintain a strict boundar - `onNavigateToThread = (serverUrl, rootId) => ...`: Handles CRT thread view navigation. 4. **Device Token Sync**: - Listen to token registration callbacks via the `onDeviceTokenReady` callback on `MattermostPushClient` interface, and forward the target device token back to the Mattermost backend server. - - *Note*: Production calls to `MattermostPushPlugin.instance` are strictly isolated and encapsulated within the `MattermostPushPluginClient` adapter implementation, maintaining a generic, testable host bootstrap. -- **Mattermost Push Plugin (`mattermost_push_plugin`) Responsibilities**: + - *Note*: Production calls to `NexoMessagingPlugin.instance` are strictly isolated and encapsulated within the `MattermostPushPluginClient` adapter implementation, maintaining a generic, testable host bootstrap. +- **Nexo Messaging Plugin (`nexo_messaging`) Responsibilities**: - Intercepting incoming FCM pushes via Kotlin services (`MattermostFirebaseMessagingService`). - Parsing payloads and verifying cryptographic signatures with JJWT. - Interacting with local SQLite database (Room DB). @@ -74,7 +75,7 @@ When replicating this integration boundary in sibling repositories (`../iop` and 1. **Firebase Credential Sync**: Provide valid `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) in respective project roots. 2. **Local Path Imports**: Ensure `pubspec.yaml` points correctly to the actual sibling workspace dependency paths: ```yaml - mattermost_push_plugin: + nexo_messaging: path: ../../../nexo/packages/messaging_flutter proto_socket: path: ../../../proto-socket/dart