From e49fb45dcbe6aaa12d863812f1e81a99616f0fca Mon Sep 17 00:00:00 2001 From: toki Date: Tue, 2 Jun 2026 18:51:58 +0900 Subject: [PATCH] docs: update notification test guides and remove smoke evidence templates --- apps/flutter-test/README.md | 45 +- docs/README.md | 11 +- ...os-notification-smoke-evidence-template.md | 96 --- docs/ios-notification-test-guide.md | 598 ++---------------- ...ux-notification-smoke-evidence-template.md | 109 ---- docs/linux-notification-test-guide.md | 185 +----- ...os-notification-smoke-evidence-template.md | 104 --- docs/macos-notification-test-guide.md | 159 +---- ...ws-notification-smoke-evidence-template.md | 95 --- docs/windows-notification-test-guide.md | 171 +---- packages/messaging_flutter/README.md | 14 +- 11 files changed, 152 insertions(+), 1435 deletions(-) delete mode 100644 docs/ios-notification-smoke-evidence-template.md delete mode 100644 docs/linux-notification-smoke-evidence-template.md delete mode 100644 docs/macos-notification-smoke-evidence-template.md delete mode 100644 docs/windows-notification-smoke-evidence-template.md diff --git a/apps/flutter-test/README.md b/apps/flutter-test/README.md index 55d1e23a..187337f3 100644 --- a/apps/flutter-test/README.md +++ b/apps/flutter-test/README.md @@ -74,35 +74,28 @@ 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. -For iOS preflight and smoke preparation, keep real Firebase and Apple files out -of git. Copy `ios/Runner/GoogleService-Info.example.plist` to -`ios/Runner/GoogleService-Info.plist` locally, replace placeholder values, and -make sure the real plist is added to the Runner target resources in Xcode. -Use `ios/Runner/Runner.example.entitlements` as the minimal Push Notifications -entitlement reference, then configure the real Runner target through Xcode. -Local routing fixtures live under `ios/Fixtures/*.apns`; replace -`REPLACE_WITH_IOS_BUNDLE_ID` before simulator injection. -See [`../../docs/ios-notification-test-guide.md`](../../docs/ios-notification-test-guide.md) -and [`../../docs/ios-notification-smoke-evidence-template.md`](../../docs/ios-notification-smoke-evidence-template.md). +For iOS notification preparation, keep real Firebase and Apple files out of +git. The user must provide the Apple Team/signing path, Bundle ID, APNs +credential choice, Firebase iOS app, local `GoogleService-Info.plist`, physical +device path, and smoke send path before an agent can complete a real APNs/FCM +smoke. Use `ios/Runner/GoogleService-Info.example.plist` and +`ios/Runner/Runner.example.entitlements` only as placeholder references. See +[`../../docs/ios-notification-test-guide.md`](../../docs/ios-notification-test-guide.md). -For macOS preflight and smoke preparation, keep real signing, provisioning, +For macOS notification preparation, keep real signing, provisioning, notarization, Firebase, APNs, device token, and private host values out of git. -The macOS host scaffold keeps placeholder entitlements only; configure real -signing and notification capability locally when a smoke run requires it. Local -routing fixtures live under `macos/Fixtures/*.json`. See -[`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) -and -[`../../docs/macos-notification-smoke-evidence-template.md`](../../docs/macos-notification-smoke-evidence-template.md). +The user must provide the macOS runner, signing mode, Bundle ID if needed, +APNs/Firebase scope, UI prompt responsibility, and evidence destination before +an agent can complete visible notification smoke. See +[`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md). -For Windows preflight and smoke preparation, keep real Microsoft account, -package identity, signing, push credentials, device token, and private host -values out of git. This repository does not currently include a Windows host -scaffold; add one only through a focused implementation plan that also registers -the plugin platform. Local routing fixtures live under -`windows/Fixtures/*.json`. See -[`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) -and -[`../../docs/windows-notification-smoke-evidence-template.md`](../../docs/windows-notification-smoke-evidence-template.md). +For Windows notification preparation, keep real Microsoft account, package +identity, signing, push credentials, device token, and private host values out +of git. The user must provide the Windows runner, packaging mode, notification +API path, package identity/signing details if needed, UI prompt responsibility, +and evidence destination before an agent can complete visible notification +smoke. See +[`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md). ## Verification diff --git a/docs/README.md b/docs/README.md index 8af00b26..a29e28e8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,12 +14,11 @@ Module-specific instructions stay with the module that owns them. - Mattermost mobile app upstream snapshot baseline: `../apps/mattermost/UPSTREAM.md` - Mattermost push-proxy upstream snapshot baseline: `../services/push-proxy/UPSTREAM.md` - Runtime Image Validation details: `runtime-image-validation.md` -- iOS notification smoke prerequisites and runbook: `ios-notification-test-guide.md` -- iOS notification smoke evidence template: `ios-notification-smoke-evidence-template.md` -- macOS notification smoke prerequisites and runbook: `macos-notification-test-guide.md` -- macOS notification smoke evidence template: `macos-notification-smoke-evidence-template.md` -- Windows notification smoke prerequisites and runbook: `windows-notification-test-guide.md` -- Windows notification smoke evidence template: `windows-notification-smoke-evidence-template.md` +- Notification access preparation: + `ios-notification-test-guide.md`, + `macos-notification-test-guide.md`, + `windows-notification-test-guide.md`, + `linux-notification-test-guide.md` ## Upstream Following Policy diff --git a/docs/ios-notification-smoke-evidence-template.md b/docs/ios-notification-smoke-evidence-template.md deleted file mode 100644 index 6f353b47..00000000 --- a/docs/ios-notification-smoke-evidence-template.md +++ /dev/null @@ -1,96 +0,0 @@ -# iOS Notification Smoke Evidence Template - -Use this template when collecting iOS notification smoke evidence. Replace every -private value with a placeholder before storing output in tracked files. - -## Run Metadata - -- Date: -- Operator: -- Device model: -- iOS version: -- Xcode version: -- Flutter version: -- Build mode: Debug | Profile | Release -- Bundle ID: `` -- Firebase project/app alias: `` -- APNs auth path: key | certificate | Firebase-managed | direct APNs console - -## Preflight Inputs - -- Local `GoogleService-Info.plist` present in `apps/flutter-test/ios/Runner/`: yes | no -- Example plist matched: yes | no -- Entitlements include `aps-environment`: yes | no -- Push Notifications capability enabled: yes | no -- Background Modes > Remote notifications enabled: yes | no | not tested -- iOS token contract used: `apple_rn-v2:` -- FCM smoke token used only for Firebase direct send: yes | no | not tested - -## Permission And Token Evidence - -Paste sanitized logs only. - -```text -permission_status= -apns_registration= reason= -apns_token_present= -fcm_token_present= -formatted_server_token_prefix=apple_rn-v2 -``` - -## Payload Evidence - -Payload file or sanitized JSON: - -```json -{ - "type": "message", - "server_url": "https://example.invalid", - "server_id": "server-smoke", - "channel_id": "channel-smoke", - "root_id": "", - "is_crt_enabled": "false", - "ack_id": "ack-smoke", - "post_id": "post-smoke" -} -``` - -Send path: - -- Firebase direct FCM: -- Apple Push Notification Console: -- nexo push-proxy/server: -- Local simulator fixture: - -Response ID or log reference: - -```text - -``` - -## Foreground / Background / Open Routing - -| Scenario | Expected | Actual | Evidence path | -|---|---|---|---| -| Foreground receipt | Dart/native event logged | | | -| Background display | System notification shown | | | -| Channel tap | `onNavigateToChannel` with server/channel | | | -| Thread tap | `onNavigateToThread` with server/root | | | -| Terminated tap | Launch payload forwarded | | | - -## ACK, Dismiss, Inline Reply Boundary - -| Feature | Expected for this run | Actual | Follow-up | -|---|---|---|---| -| ACK | implemented | not tested | pending gap | -| Dismiss | implemented | not tested | pending gap | -| Inline reply | implemented | not tested | pending gap | - -## Sanitization Checklist - -- [ ] Full APNs device token removed. -- [ ] Full FCM registration token removed. -- [ ] Apple Team ID, Key ID, certificates, provisioning profile details removed. -- [ ] Firebase API key, app ID, sender ID, service account details removed. -- [ ] Private server URL, auth token, signing key, username, channel names removed. -- [ ] Screenshots or recordings contain no private message/user data. diff --git a/docs/ios-notification-test-guide.md b/docs/ios-notification-test-guide.md index fb5abab6..1de28cc4 100644 --- a/docs/ios-notification-test-guide.md +++ b/docs/ios-notification-test-guide.md @@ -1,564 +1,44 @@ -# iOS Notification Test Guide +# iOS Notification Credential Preparation -Last verified: 2026-06-01 +Last verified: 2026-06-02 -This guide lists what is needed to run iOS notification smoke tests for -`packages/messaging_flutter` through `apps/flutter-test`. It separates external -Apple/Firebase setup, local host-app setup, current implementation gaps, and -repeatable evidence to collect. +Read this when you need to provide Apple/Firebase access so an agent can finish +iOS notification implementation and smoke verification. You do not need to run +the test from this document. -Do not commit real Apple, APNs, Firebase, service-account, device-token, auth -token, private endpoint, Team ID, or provisioning values into tracked docs. -Use placeholders in examples and keep credential files in the approved local or -secret-store location. +## Provide -## Current Repository State - -- The iOS plugin now registers the minimum debug/action bridge. - `packages/messaging_flutter/ios/Classes/NexoMessagingPlugin.swift` registers - the legacy `nexo_messaging` method channel (for `getPlatformVersion` - compatibility), the - `com.tokilabs.nexo.messaging/notification_actions` method channel, and the - `com.tokilabs.nexo.messaging/notifications` event channel. -- The iOS bridge implements `debugSendNativeEvent`, `saveDeviceToken`, - `getDeviceToken`, `setAuthToken`, `clearAuthToken`, `setSigningKey`, and - `getPlatformVersion`. `saveDeviceToken`/`getDeviceToken` persist only the - formatted token string via `UserDefaults`; `setAuthToken`, `clearAuthToken`, - and `setSigningKey` are validation-only no-ops that do not persist auth tokens - or signing secrets on iOS yet. -- This is a debug/local-routing bridge only. APNs registration, Firebase token - refresh handoff, notification center delegate, ACK, dismiss, and inline reply - are not implemented and remain future smoke/implementation work. -- The Dart plugin expects native notification and action channels: - `com.tokilabs.nexo.messaging/notifications` and - `com.tokilabs.nexo.messaging/notification_actions`. -- `apps/flutter-test/lib/main.dart` already calls `Firebase.initializeApp()` - before starting the app. -- `apps/flutter-test/ios/Runner/AppDelegate.swift` does not currently register - for remote notifications or install a `UNUserNotificationCenter` delegate. -- `apps/flutter-test/ios/Runner/Info.plist` has no notification-specific keys. -- `GoogleService-Info.plist` is intentionally ignored by `.gitignore`. - -The first meaningful iOS smoke target is therefore: - -1. Build and install the signed test app on a physical iOS device. -2. Initialize Firebase successfully with a local `GoogleService-Info.plist`. -3. Request notification permission. -4. Obtain an APNs token and an FCM registration token. -5. Deliver one visible FCM notification through APNs. -6. Map a notification open into the Dart `NotificationOpenedEvent` contract. - -The minimum iOS debug/action bridge (channel registration plus the method -contract above) now exists, so Level 0 contract tests and local injection can -exercise the Dart routing path. APNs registration, FCM token handoff, ACK, -dismiss, inline reply, and native notification persistence are still follow-up -implementation work. - -## Decisions Needed Before Testing - -Record the answers in a private run note or task evidence, not in tracked docs. - -| Decision | Why it matters | +| Needed | What to prepare | |---|---| -| Apple Developer Team | Determines signing identity, App ID ownership, and provisioning profile. | -| Bundle ID | Must match Xcode, Apple App ID, Firebase iOS app, and APNs topic. | -| Provisioning flow | Automatic signing is easiest for smoke; manual profiles are better for controlled evidence. | -| APNs auth style | Prefer token key `.p8` for Firebase/FCM; certificate `.p12` is a fallback if the project requires it. | -| Firebase project/app | Existing project vs new smoke-only project affects data isolation and credential access. | -| Firebase config storage | This repo ignores `GoogleService-Info.plist`; decide who provides it and where local copies live. | -| Physical device | FCM via APNs requires a real iOS device for meaningful delivery testing. | -| Evidence path | Decide where screenshots, logs, payloads, and run notes are stored. | -| CI boundary | Keep real APNs/FCM delivery manual unless a signed device farm is explicitly available. | - -## Apple Setup Checklist - -### Account And App ID - -- Active Apple Developer Program membership. -- Access to the target Apple Developer Team. -- Bundle ID reserved for the iOS smoke app. -- App ID with Push Notifications enabled. -- Registered physical device if manual provisioning is used. - -The Bundle ID must be consistent everywhere: - -- Xcode `PRODUCT_BUNDLE_IDENTIFIER`. -- Apple Developer App ID. -- Firebase iOS app bundle ID. -- APNs `apns-topic` when sending directly to APNs. - -### Xcode Capabilities - -Open `apps/flutter-test/ios/Runner.xcworkspace` in Xcode and configure the -`Runner` target. - -- `Signing & Capabilities > Team`: selected smoke Team. -- `Signing & Capabilities > Bundle Identifier`: selected smoke Bundle ID. -- `Signing & Capabilities > Push Notifications`: enabled. -- `Signing & Capabilities > Background Modes`: enable `Remote notifications` - if the smoke includes background/silent delivery or background FCM callbacks. - -Expected local file outcome: - -- A Runner entitlements file exists or Xcode build settings point to one. -- The signed app includes `aps-environment` as `development` for debug smoke. -- Provisioning profile includes the App ID and push entitlement. - -Useful verification commands after building: - -```sh -codesign -d --entitlements :- build/ios/iphoneos/Runner.app -``` - -Look for: - -```xml -aps-environment -development -``` - -### APNs Auth Key Or Certificate - -Preferred path for Firebase/FCM: - -- Create or reuse an APNs Authentication Key in Apple Developer. -- Download the `.p8` file once. -- Record the Key ID privately. -- Record the Team ID privately. -- Upload the `.p8`, Key ID, and Team ID to Firebase Cloud Messaging settings - for the matching iOS app. - -Fallback path: - -- Create an APNs provider certificate for the App ID. -- Export it as `.p12` if Firebase or a provider server requires certificate - auth. -- Track expiry and renewal ownership. - -Do not confuse these with iOS code-signing certificates. Code signing lets the -app install and carry the push entitlement. APNs provider credentials let a -server, Firebase, or Apple Push Notification Console send pushes. - -## Firebase Setup Checklist - -### Project And iOS App - -- Firebase project exists. -- iOS app is registered with the exact smoke Bundle ID. -- `GoogleService-Info.plist` is downloaded for that iOS app. -- The plist is copied locally to `apps/flutter-test/ios/Runner/GoogleService-Info.plist`. -- The plist is added to the Xcode `Runner` target resources. -- The plist remains untracked because `.gitignore` ignores - `**/GoogleService-Info.plist`. - -The test app already calls: - -```dart -WidgetsFlutterBinding.ensureInitialized(); -await Firebase.initializeApp(); -``` - -If Firebase initialization fails on iOS, check that the plist is present in the -Runner target and that the bundle ID in the plist matches the signed app. - -### Cloud Messaging Settings - -In Firebase Console: - -- Open Project Settings. -- Open Cloud Messaging. -- Find the matching iOS app configuration. -- Upload APNs auth key `.p8`, or configure APNs certificates if that is the - selected path. -- Confirm Firebase Cloud Messaging API is enabled for HTTP v1 sends. - -For server-side sends: - -- Use Firebase Admin SDK, or -- Use FCM HTTP v1 with a service account or Application Default Credentials. - -Do not place service-account JSON in the repo. - -## Native Bridge Work Needed For A Full Smoke - -The minimum debug/action bridge is in place, but the iOS native side still -cannot complete a full APNs/FCM delivery smoke. Already done: - -- Register the expected EventChannel: - `com.tokilabs.nexo.messaging/notifications`. -- Register the expected MethodChannel: - `com.tokilabs.nexo.messaging/notification_actions`. -- Implement `saveDeviceToken`, `getDeviceToken`, `setAuthToken`, - `clearAuthToken`, `setSigningKey`, and `debugSendNativeEvent` for iOS, with - auth token/signing key kept as validation-only no-ops for now. - -Remaining native bridge work: - -- Request user notification authorization or rely consistently on - FlutterFire's `FirebaseMessaging.requestPermission`. -- Call `UIApplication.shared.registerForRemoteNotifications()`. -- Handle `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`. -- Handle `application(_:didFailToRegisterForRemoteNotificationsWithError:)`. -- Decide whether Firebase Messaging method swizzling stays enabled. -- If swizzling is disabled, set `Messaging.messaging().apnsToken` manually. -- Subscribe to FCM token refresh and forward token changes to Dart. -- Set `UNUserNotificationCenter.current().delegate`. -- Forward foreground receipt, notification tap, and launch-from-notification - payloads into Dart. -- Map APNs/FCM userInfo fields to `NotificationOpenedEvent`. - -## iOS Token Contract - -The current Dart implementation stores Android tokens as -`android_rn-v2:`. For iOS preflight, reserve the -Mattermost-compatible APNs server-registration format: - -```text -apple_rn-v2: -``` - -Use this distinction during testing: - -- APNs device token: server registration candidate for the existing - Mattermost-compatible Apple push-proxy path. -- FCM registration token: Firebase direct-send smoke token. Do not register it - with the nexo server unless a future server/push-proxy path explicitly - supports FCM-backed iOS delivery. -- Direct APNs sends through Apple Push Notification Console also require the raw - APNs token, not the FCM registration token. - -Follow-up implementation should make token formatting platform-aware instead of -using the current Android-oriented Dart constant. - -## Payload Contract - -For nexo routing, keep custom fields outside the APNs `aps` dictionary. FCM -`data` values must be strings. - -Required or high-value fields: - -| Field | Purpose | -|---|---| -| `type` | `message`, `opened`, `clear`, `session`, or `token_refresh` equivalent in the Dart contract. | -| `server_url` | Preferred direct routing target when available. | -| `server_id` | Fallback key if the app resolves server IDs locally. | -| `channel_id` | Channel routing. | -| `root_id` | Thread routing when CRT is enabled. | -| `is_crt_enabled` | Determines channel vs thread navigation behavior. | -| `ack_id` | ACK contract and signed push validation. | -| `post_id` | Notification identity and ACK enrichment. | - -Visible FCM HTTP v1 smoke payload shape: - -```json -{ - "message": { - "token": "", - "notification": { - "title": "nexo smoke", - "body": "iOS notification smoke" - }, - "data": { - "type": "message", - "server_url": "https://", - "channel_id": "", - "root_id": "", - "is_crt_enabled": "false", - "ack_id": "", - "post_id": "" - }, - "apns": { - "headers": { - "apns-push-type": "alert", - "apns-priority": "10", - "apns-topic": "" - }, - "payload": { - "aps": { - "sound": "default" - } - } - } - } -} -``` - -Silent/background smoke payload shape: - -```json -{ - "message": { - "token": "", - "data": { - "type": "message", - "server_url": "https://", - "channel_id": "" - }, - "apns": { - "headers": { - "apns-push-type": "background", - "apns-priority": "5", - "apns-topic": "" - }, - "payload": { - "aps": { - "content-available": 1 - } - } - } - } -} -``` - -Use visible alert smoke first. Silent pushes are best-effort and easier to -misread during early setup. - -## Test Levels - -### Level 0: Repo Contract Tests - -No Apple/Firebase credentials required. - -- Dart widget tests. -- Dart event mapping tests using `handleNativeEvent`. -- Integration tests using `debugSendNativeEvent` when native debug bridge exists. - -This level validates the Dart routing contract only. It does not prove APNs or -FCM delivery. - -### Level 1: Simulator / Local Injection - -No real FCM via APNs. - -- Use simulator/local notification payload injection only to exercise - userInfo parsing and open routing. -- Do not mark FCM delivery, APNs token registration, or background delivery as - passed from simulator evidence. - -Example local `.apns` fixture shape: - -```json -{ - "Simulator Target Bundle": "", - "aps": { - "alert": { - "title": "nexo smoke", - "body": "local routing fixture" - }, - "sound": "default" - }, - "type": "message", - "server_url": "https://", - "channel_id": "", - "root_id": "", - "is_crt_enabled": "false" -} -``` - -Tracked fixture files live in `apps/flutter-test/ios/Fixtures/`: - -- `open_channel.apns`: channel routing payload. -- `open_thread.apns`: CRT thread routing payload. - -Before simulator injection, replace `REPLACE_WITH_IOS_BUNDLE_ID` with the -signed Runner bundle identifier. These fixtures prove local payload shape and -open-routing parsing only; they do not prove APNs registration, FCM delivery, or -server ACK behavior. - -### Level 2: Physical Device FCM Smoke - -Required for meaningful iOS delivery. - -- Signed debug or release app installed on a real iPhone/iPad. -- Notification permission requested and accepted. -- APNs registration succeeds. -- FCM registration token is displayed or logged. -- A visible FCM message arrives. -- Tapping the notification opens or resumes the app. -- Dart receives a notification-open event with routing fields. - -### Level 3: Nexo Server End-To-End Smoke - -Required before claiming server contract compatibility. - -- FCM token is registered with the nexo server using the agreed iOS token - prefix. -- Server can send a nexo-compatible push payload. -- Signed payload validation behavior is known. -- ACK request behavior is observed. -- Open routing reaches the host app callback. - -## Manual Smoke Runbook - -### 1. Preflight - -- Confirm Xcode version and iOS device OS version. -- Confirm Bundle ID in Xcode, Apple Developer, and Firebase match. -- Confirm `GoogleService-Info.plist` exists locally and is part of Runner - target resources. -- Confirm APNs auth key or certificate is configured in Firebase. -- Confirm the app target has Push Notifications capability. -- Confirm `aps-environment` is present in the built app entitlements. -- Confirm no real credential values are staged in git. - -### 2. Build And Install - -From the Flutter test app: - -```sh -cd apps/flutter-test -flutter pub get -flutter run -d -``` - -If Xcode signing configuration is not already set, open: - -```sh -open ios/Runner.xcworkspace -``` - -Build from Xcode once to resolve signing and provisioning issues. - -### 3. Permission And Token - -Expected evidence: - -- App launches without Firebase initialization error. -- iOS notification permission prompt appears. -- Permission state is logged. -- APNs registration success is logged. -- FCM token is logged or displayed in the app UI. -- `Device token:` in the Flutter test app changes from `pending` to a formatted - token once the Dart/native bridge supports iOS token handoff. - -Important distinction: - -- FCM sends to the FCM registration token returned by Firebase Messaging. -- Apple Push Notification Console or direct APNs sends require the APNs device - token from `didRegisterForRemoteNotificationsWithDeviceToken`. - -### 4. Send A Visible FCM Message - -Use Firebase Admin SDK or FCM HTTP v1. Keep payload, response ID, timestamp, -device state, and app logs as evidence. - -Expected result: - -- Foreground: app receives callback or foreground presentation according to the - native delegate policy. -- Background: system displays a notification. -- Terminated: tapping the notification starts the app and exposes the launch - payload. - -### 5. Verify Routing - -For channel routing: - -- Payload has `is_crt_enabled=false`. -- Payload has `server_url` and `channel_id`. -- App UI updates `Last opened`. -- App UI updates `Last navigation` with `channel:/`. - -For thread routing: - -- Payload has `is_crt_enabled=true`. -- Payload has `server_url` and `root_id`. -- App UI updates `Last opened`. -- App UI updates `Last navigation` with `thread:/`. - -### 6. Verify ACK, Dismiss, Inline Reply Boundaries - -Current expected state: - -- ACK is not passed unless an iOS ACK client/native bridge exists. -- Dismiss is not passed unless notification category/delete handling exists. -- Inline reply is not passed unless `UNTextInputNotificationAction` and reply - forwarding exist. - -If these are not implemented, record them as known iOS gaps instead of smoke -failures. - -## Evidence To Keep - -Do keep: - -- Date, operator, device model, iOS version, Xcode version. -- Bundle ID shape, but not private Team ID if policy treats it as sensitive. -- Build configuration: Debug/Profile/Release. -- Entitlement proof with secrets redacted. -- Firebase project/app alias, not raw private IDs if policy forbids them. -- Permission status. -- APNs registration success/failure log. -- FCM token presence, redacted. -- FCM message ID or API response ID. -- Payload shape with private values replaced by placeholders. -- Screenshot or screen recording of notification display and app routing UI. -- App logs around receipt, tap, routing, ACK/dismiss/reply if implemented. - -Use `docs/ios-notification-smoke-evidence-template.md` as the repeatable -evidence record when real device smoke begins. - -Do not keep: - -- `.p8`, `.p12`, provisioning profile, private key, or service-account JSON. -- Full APNs device token or FCM registration token. -- Full auth token, signing key, private server URL, or user data. - -## Troubleshooting Matrix - -| Symptom | Likely cause | Check | -|---|---|---| -| Firebase initialization fails | Missing or wrong `GoogleService-Info.plist` | Confirm file is in Runner target and Bundle ID matches. | -| Permission prompt never appears | Permission already decided, request not called, or initialization failed | Check iOS Settings and app logs. | -| APNs registration fails | Missing entitlement or bad signing profile | Inspect built entitlements and provisioning profile. | -| FCM token is null | APNs token not linked, Firebase app mismatch, network issue, or swizzling/delegate conflict | Check Firebase logs, APNs token callback, and Messaging setup. | -| FCM send succeeds but no notification | Wrong token, wrong Firebase project, APNs key not uploaded, device offline, notification disabled | Confirm token source, Firebase app, APNs config, device notification settings. | -| Direct APNs send fails | Using FCM token instead of APNs token, wrong topic, wrong environment | Use APNs device token and matching Bundle ID/environment. | -| Foreground receives nothing | `UNUserNotificationCenter` delegate missing or not forwarding | Confirm delegate setup and callback implementation. | -| Tap opens app but no routing | Launch/tap payload not forwarded to Dart | Check native open handler and EventChannel emission. | -| Background/silent unreliable | iOS best-effort delivery, wrong headers, app force-quit, missing background mode | Start with visible alert smoke and test silent separately. | - -## Completion Criteria For This Milestone - -Minimum pass for `ios-notification-test` planning smoke: - -- Apple/Firebase ownership and credential flow are decided. -- Bundle ID and provisioning approach are documented privately. -- Firebase iOS app and local plist path are confirmed. -- Physical-device smoke path is confirmed. -- Current native bridge gaps are listed. -- A visible FCM/APNs smoke checklist exists with evidence fields. -- CI/headless boundary is explicit. - -Minimum pass for a future implementation smoke: - -- iOS native bridge emits Dart notification events. -- FCM token is obtained and handed to Dart with an iOS-compatible prefix. -- Visible FCM notification arrives on a physical device. -- Tapping the notification triggers `NotificationOpenedEvent`. -- Channel and thread routing callbacks are exercised. -- ACK/dismiss/inline reply are either implemented and verified or explicitly - tracked as remaining gaps. - -## Official References - -- Apple: Registering your app with APNs - https://developer.apple.com/documentation/usernotifications/registering-your-app-with-apns -- Apple: Generating a remote notification - https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification -- Apple: Sending notification requests to APNs - https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns -- Apple: Testing notifications using the Push Notification Console - https://developer.apple.com/documentation/usernotifications/testing-notifications-using-the-push-notification-console -- Firebase: Add Firebase to your Apple project - https://firebase.google.com/docs/ios/setup -- Firebase: Set up a Firebase Cloud Messaging client app on Apple platforms - https://firebase.google.com/docs/cloud-messaging/get-started?platform=ios -- Firebase: Receive messages in Apple platform apps - https://firebase.google.com/docs/cloud-messaging/ios/receive-messages -- Firebase: Build app server send requests - https://firebase.google.com/docs/cloud-messaging/send-message -- FlutterFire: FCM via APNs Integration - https://firebase.flutter.dev/docs/messaging/apple-integration/ -- FlutterFire: Cloud Messaging usage - https://firebase.flutter.dev/docs/messaging/usage +| Apple Team | The Apple Developer Team that owns the smoke app. | +| Bundle ID | A Bundle ID reserved for the iOS smoke app. | +| Signing path | Either allow automatic signing or provide the provisioning profile path privately. | +| Push capability | Enable Push Notifications for the App ID. | +| APNs credential | Prefer APNs auth key `.p8` plus Key ID and Team ID; certificate is a fallback. | +| Firebase app | Create/select the Firebase iOS app with the exact same Bundle ID. | +| Firebase plist | Provide `GoogleService-Info.plist` privately for `apps/flutter-test/ios/Runner/`. | +| Physical device | Provide an installable iPhone/iPad or define who operates it remotely. | +| Send path | Choose Firebase Console, FCM HTTP v1, or an existing private sender. | +| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. | + +## Do Not Put In Tracked Files + +- Apple Team ID, private Bundle ID, provisioning profile, `.p8`, `.p12`, Key ID, + certificate, or Apple account values. +- `GoogleService-Info.plist` or service-account JSON. +- APNs device token, FCM token, auth token, private endpoint, channel name, user + name, or server URL. + +## Agent Can Then + +- configure the local iOS host app with the selected signing and Bundle ID; +- place the private Firebase plist in the ignored local path; +- implement or verify APNs registration, token handoff, and open routing; +- run or coordinate the smoke through the approved send path; +- record sanitized evidence in the task or private run note. + +## Current Repo Note + +The iOS plugin has a minimum debug/action bridge, but real APNs/FCM smoke still +needs the private Apple/Firebase/device setup above plus native APNs/token/open +routing work. diff --git a/docs/linux-notification-smoke-evidence-template.md b/docs/linux-notification-smoke-evidence-template.md deleted file mode 100644 index 1cc10765..00000000 --- a/docs/linux-notification-smoke-evidence-template.md +++ /dev/null @@ -1,109 +0,0 @@ -# Linux Notification Smoke Evidence Template - -Use this template when collecting Linux notification smoke evidence. Replace -every private value with a placeholder before storing output in tracked files. - -## Run Metadata - -- Date: -- Operator: -- Hardware or VM: -- Distribution and version: -- Desktop environment: -- Session type: X11 | Wayland | headless | other -- Notification daemon: -- Desktop portal available: yes | no | not tested -- Flutter version: -- Build mode: Debug | Profile | Release -- Package/runtime dependency path: D-Bus | desktop portal | libnotify | debug injection | other - -## Preflight Inputs - -- Linux platform registered in `packages/messaging_flutter/pubspec.yaml`: yes | no -- Linux plugin scaffold present: yes | no -- Linux test host scaffold present: yes | no -- Linux bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no -- Linux bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no -- Local fixture used: `open_channel.json` | `open_thread.json` | other -- Real account, private host, token, and machine-identifying values kept out of tracked files: yes | no - -## Environment Evidence - -Paste sanitized logs only. - -```text -distribution= -desktop_environment= -session_type= -notification_daemon= -portal_status= -``` - -## Display Evidence - -Payload file or sanitized JSON: - -```json -{ - "type": "message", - "server_url": "https://example.invalid", - "server_id": "server-smoke", - "channel_id": "channel-smoke", - "root_id": "", - "is_crt_enabled": "false", - "ack_id": "ack-smoke", - "post_id": "post-smoke" -} -``` - -Display path: - -- Local debug injection: -- D-Bus notification daemon: -- Desktop portal notification: -- libnotify wrapper: -- Not tested: - -Observed result: - -```text -displayed= -notification_title= -notification_body= -action_data_present= -``` - -## Click / Open Routing - -| Scenario | Expected | Actual | Evidence path | -|---|---|---|---| -| Channel click | `onNavigateToChannel` with server/channel | | | -| Thread click | `onNavigateToThread` with server/root | | | -| Foreground receipt | Raw `onNotification` event logged | | | -| Background click | App resumes and emits opened event | | | -| Cold start click | Launch payload forwarded | | | - -## Lifecycle Evidence - -```text -app_state_before= -app_state_after= -event_type= -userInteraction= -``` - -## ACK, Dismiss, Inline Reply Boundary - -| Feature | Expected for this run | Actual | Follow-up | -|---|---|---|---| -| ACK | gap documented unless implemented by a later plan | | | -| Dismiss | gap documented unless implemented by a later plan | | | -| Inline reply | gap documented unless implemented by a later plan | | | - -## Sanitization Checklist - -- [ ] Private host, username, channel name, and machine-identifying values removed. -- [ ] Full device token or remote push token removed. -- [ ] Private server URL, auth token, signing key, username, channel names removed. -- [ ] Screenshots or recordings contain no private message/user data. -- [ ] Distribution, daemon, and desktop details are sanitized when they identify a private environment. diff --git a/docs/linux-notification-test-guide.md b/docs/linux-notification-test-guide.md index d6760983..fa47acad 100644 --- a/docs/linux-notification-test-guide.md +++ b/docs/linux-notification-test-guide.md @@ -1,169 +1,38 @@ -# Linux Notification Test Guide +# Linux Notification Environment Preparation Last verified: 2026-06-02 -This guide fixes the preflight contract for Linux notification work before a -Linux test machine, distribution, desktop environment, notification daemon, or -real OS notification UI evidence is available. +Read this when you need to provide Linux runner and desktop notification +environment choices so an agent can finish Linux notification implementation and +smoke verification. You do not need to run the test from this document. -Do not commit private host values, device tokens, auth tokens, signing keys, -runtime endpoints, user names, channel names, desktop session details that -identify a private machine, or private package repository values into tracked -docs. Use placeholders in examples and keep credential files in approved local -or secret-store locations. +## Provide -## Current Repository State +| Needed | What to prepare | +|---|---| +| Linux runner | Local machine, VM, remote desktop, headless-only runner, or deferral. | +| Distribution | Distribution and version, such as Ubuntu, Debian, or Fedora. | +| Desktop session | GNOME/KDE/XFCE/other and X11/Wayland/headless. | +| Notification path | D-Bus notifications, XDG Desktop Portal, libnotify, or debug injection only. | +| Dependencies | Whether the agent may install required system packages. | +| Operator path | Who handles UI prompts/clicks if the runner is remote. | +| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. | -- `packages/messaging_flutter/pubspec.yaml` does not currently register a - Linux plugin platform. -- There is no `packages/messaging_flutter/linux/` plugin scaffold and no - `apps/flutter-test/linux/` host scaffold in the current repository. -- Dart already expects native notification and action channels: - `com.tokilabs.nexo.messaging/notifications` and - `com.tokilabs.nexo.messaging/notification_actions`. -- The Linux bridge is therefore not implemented yet. Until it exists, local - routing can only be tested through Dart unit tests or a future debug injection - bridge. -- Local routing fixtures live under `apps/flutter-test/linux/Fixtures/*.json`. +## Do Not Put In Tracked Files -The first useful Linux smoke target is: +- Private host names, user names, private endpoints, auth tokens, channel names, + server URLs, machine-identifying session details, private package repository + credentials, device tokens, or signing keys. -1. Add a Linux plugin platform registration and generated host scaffold in a - controlled implementation plan. -2. Register the expected EventChannel and MethodChannel in the Linux plugin. -3. Implement a debug-only local injection path compatible with - `debugSendNativeEvent`. -4. Choose a notification API path and runtime dependency strategy for the smoke. -5. Map notification daemon action data or portal activation data into the Dart - `NotificationOpenedEvent` contract. -6. Collect display, click/open routing, lifecycle, and known ACK, dismiss, and - inline reply gap evidence. +## Agent Can Then -## Platform Registration Contract +- add Linux platform registration and host scaffold if selected; +- implement the native notification/action bridge; +- configure the selected desktop notification path; +- run routing checks and, if allowed, visible notification smoke; +- record sanitized evidence in the task or private run note. -Do not add a partial Linux platform entry without the matching source and host -scaffold. A future implementation plan should update these together: +## Current Repo Note -```yaml -flutter: - plugin: - platforms: - linux: - pluginClass: NexoMessagingPlugin -``` - -Expected source/host areas for that plan: - -- `packages/messaging_flutter/linux/` for the Linux plugin implementation. -- `apps/flutter-test/linux/` for the test host scaffold generated by Flutter. -- `packages/messaging_flutter/pubspec.yaml` for platform registration. -- `apps/flutter-test/README.md` and package docs for smoke instructions. - -The current preflight milestone documents this contract only. It does not claim -Linux runtime support. - -## Minimum Bridge Contract - -The Dart SDK expects the Linux native side to preserve these channel names: - -| Channel | Name | Direction | Minimum Linux behavior | -|---|---|---|---| -| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. | -| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. | - -Minimum methods for the action channel: - -| Method | Minimum preflight result | Production expectation | -|---|---|---| -| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. | -| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the Linux server-registration token once the platform token source is chosen. | -| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted Linux server-registration token. | -| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. | -| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. | -| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. | - -Fallback boundaries: - -- Missing bridge methods should return the Flutter Linux equivalent of "not - implemented", not a successful placeholder. -- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not - implied by this preflight milestone. -- Real notification display and activation handling belong to `Linux 알림 테스트` - unless a smaller bridge implementation plan explicitly handles them first. - -## Notification API Matrix - -| Candidate | Environment fit | Strength | Preflight constraint | -|---|---|---|---| -| D-Bus `org.freedesktop.Notifications` | Common desktop notification daemon path | Broad support across GNOME, KDE, XFCE, and other desktops | Action callbacks and capabilities vary by daemon and session. | -| XDG Desktop Portal notifications | Flatpak/sandbox-friendly desktops | Better fit for portal-mediated desktop environments | Portal availability and notification support vary by distribution and session. | -| libnotify wrapper | Thin client around the freedesktop notification spec | Simple display smoke for common desktops | Callback/action support still depends on the underlying daemon. | -| Local debug injection only | No daemon, package, or desktop requirement | Deterministic EventChannel/opened-routing verification | Does not prove OS display or activation. | - -Preflight default: use local debug injection for contract tests, and defer the -D-Bus vs desktop portal vs libnotify runtime decision to the `Linux 알림 테스트` -milestone or a dedicated implementation plan. - -## Runtime Dependency Boundaries - -Future Linux notification implementation should record the runtime assumptions -before claiming smoke support: - -- Distribution and version, such as Ubuntu, Fedora, Debian, or another target. -- Desktop environment and session type, such as GNOME/KDE/XFCE and X11/Wayland. -- Notification daemon or portal availability. -- Required build packages and runtime libraries. -- Whether action callbacks are supported by the chosen daemon or portal path. - -Tracked docs should keep these as sanitized evidence fields, not private machine -configuration dumps. - -## Payload Mapping - -Linux notification action data maps into Dart using the same stable keys as the -Android, iOS, macOS, and Windows contracts: - -| Payload key | Dart event field | Notes | -|---|---|---| -| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. | -| `server_url` | `server_url` | Required before host navigation callbacks can run. | -| `server_id` | `server_id` | Diagnostic and future server URL fallback input. | -| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. | -| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. | -| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. | -| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. | -| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. | -| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. | - -Routing expectation: - -- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty - and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`. -- Thread fixture: `server_url` and `root_id` are present and - `is_crt_enabled` is true, so Dart calls `onNavigateToThread`. - -## Local Fixtures - -Use the fixture files as sanitized payloads for a future debug injection method -or for manual D-Bus/portal action data construction during smoke testing: - -- `apps/flutter-test/linux/Fixtures/open_channel.json` -- `apps/flutter-test/linux/Fixtures/open_thread.json` - -The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace -private runtime values only in local run notes or sanitized evidence. - -## Evidence - -Use [`linux-notification-smoke-evidence-template.md`](linux-notification-smoke-evidence-template.md) -for Linux runs. Preflight evidence should explicitly separate: - -- distribution, desktop environment, session, and daemon or portal state -- visible display state -- click/open routing result -- app lifecycle state -- ACK, dismiss, and inline reply implementation gaps - -Until the scaffold, bridge, and real OS smoke exist, a valid preflight result -may be "not implemented yet" with a concrete follow-up plan, rather than a -failed runtime claim. +Linux is not registered yet and has no plugin or test-host scaffold. Smoke work +first needs platform scaffold plus the environment choices above. diff --git a/docs/macos-notification-smoke-evidence-template.md b/docs/macos-notification-smoke-evidence-template.md deleted file mode 100644 index 0c8609c7..00000000 --- a/docs/macos-notification-smoke-evidence-template.md +++ /dev/null @@ -1,104 +0,0 @@ -# macOS Notification Smoke Evidence Template - -Use this template when collecting macOS notification smoke evidence. Replace -every private value with a placeholder before storing output in tracked files. - -## Run Metadata - -- Date: -- Operator: -- Hardware: -- macOS version: -- Xcode version: -- Flutter version: -- Build mode: Debug | Profile | Release -- Bundle ID: `` -- Signing mode: local ad hoc | Apple Developer | not signed for notification smoke -- Notarization state: not tested | local placeholder | submitted | accepted | failed - -## Preflight Inputs - -- macOS bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no -- macOS bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no -- Legacy `nexo_messaging` channel retained for `getPlatformVersion`: yes | no -- Local fixture used: `open_channel.json` | `open_thread.json` | other -- Entitlements inspected: yes | no -- Notification capability present in built app: yes | no | not tested -- Real signing values kept out of tracked files: yes | no - -## Permission Evidence - -Paste sanitized logs only. - -```text -permission_status= -request_path= -notes= -``` - -## Display Evidence - -Payload file or sanitized JSON: - -```json -{ - "type": "message", - "server_url": "https://example.invalid", - "server_id": "server-smoke", - "channel_id": "channel-smoke", - "root_id": "", - "is_crt_enabled": "false", - "ack_id": "ack-smoke", - "post_id": "post-smoke" -} -``` - -Display path: - -- Local debug injection: -- Local notification API: -- Remote push provider: -- Not tested: - -Observed result: - -```text -displayed= -notification_title= -notification_body= -``` - -## Click / Open Routing - -| Scenario | Expected | Actual | Evidence path | -|---|---|---|---| -| Channel click | `onNavigateToChannel` with server/channel | | | -| Thread click | `onNavigateToThread` with server/root | | | -| Foreground receipt | Raw `onNotification` event logged | | | -| Background click | App resumes and emits opened event | | | -| Terminated click | Launch payload forwarded | | | - -## Lifecycle Evidence - -```text -app_state_before= -app_state_after= -event_type= -userInteraction= -``` - -## ACK, Dismiss, Inline Reply Boundary - -| Feature | Expected for this run | Actual | Follow-up | -|---|---|---|---| -| ACK | gap documented unless implemented by a later plan | | | -| Dismiss | gap documented unless implemented by a later plan | | | -| Inline reply | gap documented unless implemented by a later plan | | | - -## Sanitization Checklist - -- [ ] Apple Team ID, signing identity, provisioning profile, certificate, and notarization values removed. -- [ ] Full device token or remote push token removed. -- [ ] Firebase API key, app ID, sender ID, service account details removed if Firebase was used. -- [ ] Private server URL, auth token, signing key, username, channel names removed. -- [ ] Screenshots or recordings contain no private message/user data. diff --git a/docs/macos-notification-test-guide.md b/docs/macos-notification-test-guide.md index 550af521..212cb37f 100644 --- a/docs/macos-notification-test-guide.md +++ b/docs/macos-notification-test-guide.md @@ -1,143 +1,38 @@ -# macOS Notification Test Guide +# macOS Notification Access Preparation Last verified: 2026-06-02 -This guide fixes the preflight contract for macOS notification work before -Apple signing, a macOS test device, or real OS notification UI evidence is -available. +Read this when you need to provide macOS runner/signing access so an agent can +finish macOS notification implementation and smoke verification. You do not need +to run the test from this document. -Do not commit real Apple, APNs, Firebase, service-account, device-token, auth -token, private endpoint, Team ID, signing identity, provisioning, notarization, -or private host values into tracked docs. Use placeholders in examples and keep -credential files in approved local or secret-store locations. +## Provide -## Current Repository State +| Needed | What to prepare | +|---|---| +| macOS runner | Local Mac, remote Mac, or a decision that visible smoke is deferred. | +| Operator path | Who handles UI prompts if the runner is remote. | +| Signing mode | Local ad hoc, Apple Developer signing, or distribution/notarization path. | +| Bundle ID | Required if signing beyond ad hoc is needed. | +| Apple access | Team/provisioning access if notification entitlement or APNs is in scope. | +| Push scope | Decide local notification only, APNs/Firebase-backed smoke, or deferred push. | +| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. | -- The macOS plugin is still a scaffold. `packages/messaging_flutter/macos/Classes/NexoMessagingPlugin.swift` - currently registers only the legacy `nexo_messaging` method channel and - implements `getPlatformVersion`. -- Dart expects native notification and action channels: - `com.tokilabs.nexo.messaging/notifications` and - `com.tokilabs.nexo.messaging/notification_actions`. -- The minimum macOS bridge is therefore not implemented yet. Until it exists, - local routing can only be tested through Dart unit tests or a future debug - injection bridge. -- `apps/flutter-test/macos` is a host scaffold. It carries sandbox - entitlements, but no notification entitlement, signing team, provisioning - profile, hardened runtime, or notarization value is tracked in this repo. -- Local routing fixtures live under `apps/flutter-test/macos/Fixtures/*.json`. +## Do Not Put In Tracked Files -The first useful macOS smoke target is: +- Team ID, signing identity, provisioning profile, notarization profile, Apple + ID, APNs/Firebase secrets, device token, private endpoint, auth token, channel + name, user name, or server URL. -1. Register the expected EventChannel and MethodChannel in the macOS plugin. -2. Implement a debug-only local injection path compatible with - `debugSendNativeEvent`. -3. Map notification `userInfo` fields into the Dart - `NotificationOpenedEvent` contract. -4. Build and run the test host with local placeholder signing. -5. Collect permission, display, click/open routing, lifecycle, and known ACK, - dismiss, and inline reply gap evidence. +## Agent Can Then -## Minimum Bridge Contract +- configure the macOS host app for the selected signing mode; +- implement or verify the native notification/action bridge; +- run local routing checks and, if allowed, visible notification smoke; +- record sanitized evidence in the task or private run note. -The Dart SDK expects the macOS native side to preserve these channel names: +## Current Repo Note -| Channel | Name | Direction | Minimum macOS behavior | -|---|---|---|---| -| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. | -| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. | -| Legacy MethodChannel | `nexo_messaging` | Dart/test compatibility | Keep `getPlatformVersion` until the generated example compatibility path is intentionally removed. | - -Minimum methods for the action channel: - -| Method | Minimum preflight result | Production expectation | -|---|---|---| -| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. | -| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the macOS server-registration token once the platform token source is chosen. | -| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted macOS server-registration token. | -| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. | -| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. | -| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. | - -Fallback boundaries: - -- Missing bridge methods should return `FlutterMethodNotImplemented`, not a - successful placeholder. -- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not - implied by this preflight milestone. -- Real notification permission prompts and visible notification display belong - to `macOS 알림 테스트` unless a small debug bridge task explicitly implements - them first. - -## Host Placeholder Criteria - -Tracked macOS host files must stay generic: - -- Keep real signing Team ID, identity, provisioning profile, notarization - profile, Apple ID, and private host values out of tracked files. -- Keep `apps/flutter-test/macos/Runner/DebugProfile.entitlements` and - `Release.entitlements` as placeholder entitlements until a smoke run requires - a specific capability. -- Add notification capability through Xcode or local build settings for a smoke - run, then record sanitized evidence instead of committing private signing - state. -- Do not add real credential files under `apps/flutter-test/macos`. - -Expected local placeholder checks: - -```sh -codesign -d --entitlements :- build/macos/Build/Products/Debug/flutter_test.app -``` - -Look for sandbox values and any locally added notification-related capability. -Record private identifiers as placeholders in evidence. - -## Payload Mapping - -macOS notification `userInfo` maps into Dart using the same stable keys as the -Android and iOS contracts: - -| `userInfo` key | Dart event field | Notes | -|---|---|---| -| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. | -| `server_url` | `server_url` | Required before host navigation callbacks can run. | -| `server_id` | `server_id` | Diagnostic and future server URL fallback input. | -| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. | -| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. | -| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. | -| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. | -| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. | -| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. | - -Routing expectation: - -- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty - and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`. -- Thread fixture: `server_url` and `root_id` are present and - `is_crt_enabled` is true, so Dart calls `onNavigateToThread`. - -## Local Fixtures - -Use the fixture files as sanitized payloads for a future debug injection method -or for manual `userInfo` construction during smoke testing: - -- `apps/flutter-test/macos/Fixtures/open_channel.json` -- `apps/flutter-test/macos/Fixtures/open_thread.json` - -The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace -private runtime values only in local run notes or sanitized evidence. - -## Evidence - -Use [`macos-notification-smoke-evidence-template.md`](macos-notification-smoke-evidence-template.md) -for macOS runs. Preflight evidence should explicitly separate: - -- permission state -- visible display state -- click/open routing result -- app lifecycle state -- ACK, dismiss, and inline reply implementation gaps - -Until the bridge and real OS smoke exist, a valid preflight result may be -"not implemented yet" with a concrete follow-up plan, rather than a failed -runtime claim. +The macOS plugin is still a scaffold. It currently has only the legacy +`nexo_messaging` channel, so visible notification smoke also needs bridge +implementation work. diff --git a/docs/windows-notification-smoke-evidence-template.md b/docs/windows-notification-smoke-evidence-template.md deleted file mode 100644 index fd8ccb4e..00000000 --- a/docs/windows-notification-smoke-evidence-template.md +++ /dev/null @@ -1,95 +0,0 @@ -# Windows Notification Smoke Evidence Template - -Use this template when collecting Windows notification smoke evidence. Replace -every private value with a placeholder before storing output in tracked files. - -## Run Metadata - -- Date: -- Operator: -- Hardware or VM: -- Windows version: -- Visual Studio version: -- Windows SDK version: -- Flutter version: -- Build mode: Debug | Profile | Release -- Packaging mode: unpackaged | MSIX | sparse package | not tested -- Package identity/AppUserModelID: `` -- Notification API path: Windows App SDK | WinRT toast | debug injection | other - -## Preflight Inputs - -- Windows platform registered in `packages/messaging_flutter/pubspec.yaml`: yes | no -- Windows plugin scaffold present: yes | no -- Windows test host scaffold present: yes | no -- Windows bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no -- Windows bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no -- Local fixture used: `open_channel.json` | `open_thread.json` | other -- Real account, package, signing, and private host values kept out of tracked files: yes | no - -## Display Evidence - -Payload file or sanitized JSON: - -```json -{ - "type": "message", - "server_url": "https://example.invalid", - "server_id": "server-smoke", - "channel_id": "channel-smoke", - "root_id": "", - "is_crt_enabled": "false", - "ack_id": "ack-smoke", - "post_id": "post-smoke" -} -``` - -Display path: - -- Local debug injection: -- Windows App SDK notification: -- WinRT toast notification: -- Not tested: - -Observed result: - -```text -displayed= -notification_title= -notification_body= -activation_arguments_present= -``` - -## Click / Open Routing - -| Scenario | Expected | Actual | Evidence path | -|---|---|---|---| -| Channel click | `onNavigateToChannel` with server/channel | | | -| Thread click | `onNavigateToThread` with server/root | | | -| Foreground receipt | Raw `onNotification` event logged | | | -| Background click | App resumes and emits opened event | | | -| Cold start click | Launch payload forwarded | | | - -## Lifecycle Evidence - -```text -app_state_before= -app_state_after= -event_type= -userInteraction= -``` - -## ACK, Dismiss, Inline Reply Boundary - -| Feature | Expected for this run | Actual | Follow-up | -|---|---|---|---| -| ACK | gap documented unless implemented by a later plan | | | -| Dismiss | gap documented unless implemented by a later plan | | | -| Inline reply | gap documented unless implemented by a later plan | | | - -## Sanitization Checklist - -- [ ] Microsoft account, package identity, certificate, Store association, and AppUserModelID values removed. -- [ ] Full device token or remote push token removed. -- [ ] Private server URL, auth token, signing key, username, channel names removed. -- [ ] Screenshots or recordings contain no private message/user data. diff --git a/docs/windows-notification-test-guide.md b/docs/windows-notification-test-guide.md index 83290ebd..81de2fa9 100644 --- a/docs/windows-notification-test-guide.md +++ b/docs/windows-notification-test-guide.md @@ -1,155 +1,38 @@ -# Windows Notification Test Guide +# Windows Notification Environment Preparation Last verified: 2026-06-02 -This guide fixes the preflight contract for Windows notification work before a -Windows test machine, Microsoft account, packaging decision, or real OS -notification UI evidence is available. +Read this when you need to provide Windows runner, packaging, and signing +choices so an agent can finish Windows notification implementation and smoke +verification. You do not need to run the test from this document. -Do not commit Microsoft account values, package identity values, signing -certificates, store association files, push credentials, device tokens, auth -tokens, private endpoints, or private host values into tracked docs. Use -placeholders in examples and keep credential files in approved local or -secret-store locations. +## Provide -## Current Repository State +| Needed | What to prepare | +|---|---| +| Windows runner | Local PC, VM, remote machine, or a decision that visible smoke is deferred. | +| Toolchain | Windows version plus Visual Studio/C++ desktop workload/Windows SDK availability. | +| Packaging mode | Unpackaged, MSIX, sparse package, or debug injection only. | +| Notification API | Windows App SDK, WinRT toast, desktop AppUserModelID fallback, or debug injection only. | +| Identity | Package identity or AppUserModelID if visible toast activation is in scope. | +| Signing | Test certificate, organization cert, Store association, or no signing. | +| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. | -- `packages/messaging_flutter/pubspec.yaml` does not currently register a - Windows plugin platform. -- There is no `packages/messaging_flutter/windows/` plugin scaffold and no - `apps/flutter-test/windows/` host scaffold in the current repository. -- Dart already expects native notification and action channels: - `com.tokilabs.nexo.messaging/notifications` and - `com.tokilabs.nexo.messaging/notification_actions`. -- The Windows bridge is therefore not implemented yet. Until it exists, local - routing can only be tested through Dart unit tests or a future debug injection - bridge. -- Local routing fixtures live under `apps/flutter-test/windows/Fixtures/*.json`. +## Do Not Put In Tracked Files -The first useful Windows smoke target is: +- Microsoft account values, package identity if private, signing certificate + contents/passwords, Store association files, push credentials, device token, + private endpoint, auth token, channel name, user name, or server URL. -1. Add a Windows plugin platform registration and generated host scaffold in a - controlled implementation plan. -2. Register the expected EventChannel and MethodChannel in the Windows plugin. -3. Implement a debug-only local injection path compatible with - `debugSendNativeEvent`. -4. Choose a notification API path and package identity strategy for the smoke. -5. Map notification activation arguments or user data into the Dart - `NotificationOpenedEvent` contract. -6. Collect display, click/open routing, lifecycle, and known ACK, dismiss, and - inline reply gap evidence. +## Agent Can Then -## Platform Registration Contract +- add Windows platform registration and host scaffold if selected; +- implement the native notification/action bridge; +- configure the selected notification API and packaging path; +- run routing checks and, if allowed, visible toast smoke; +- record sanitized evidence in the task or private run note. -Do not add a partial Windows platform entry without the matching source and host -scaffold. A future implementation plan should update these together: +## Current Repo Note -```yaml -flutter: - plugin: - platforms: - windows: - pluginClass: NexoMessagingPlugin -``` - -Expected source/host areas for that plan: - -- `packages/messaging_flutter/windows/` for the Windows plugin implementation. -- `apps/flutter-test/windows/` for the test host scaffold generated by Flutter. -- `packages/messaging_flutter/pubspec.yaml` for platform registration. -- `apps/flutter-test/README.md` and package docs for smoke instructions. - -The current preflight milestone documents this contract only. It does not claim -Windows runtime support. - -## Minimum Bridge Contract - -The Dart SDK expects the Windows native side to preserve these channel names: - -| Channel | Name | Direction | Minimum Windows behavior | -|---|---|---|---| -| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. | -| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. | - -Minimum methods for the action channel: - -| Method | Minimum preflight result | Production expectation | -|---|---|---| -| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. | -| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the Windows server-registration token once the platform token source is chosen. | -| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted Windows server-registration token. | -| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. | -| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. | -| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. | - -Fallback boundaries: - -- Missing bridge methods should return the Flutter Windows equivalent of - "not implemented", not a successful placeholder. -- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not - implied by this preflight milestone. -- Real notification display and activation handling belong to `Windows 알림 테스트` - unless a smaller bridge implementation plan explicitly handles them first. - -## Notification API Matrix - -| Candidate | Packaging fit | Strength | Preflight constraint | -|---|---|---|---| -| Windows App SDK notifications | Best with package identity or sparse package | Modern toast activation path and current Microsoft guidance | Requires Windows App SDK dependency and packaging decision. | -| WinRT toast notifications | Best with MSIX/package identity | Direct OS toast APIs and activation arguments | Packaging/AppUserModelID details must be decided before reliable smoke. | -| Desktop bridge / AppUserModelID shortcuts | Unpackaged desktop fallback | Can support classic desktop notification experiments | More local setup variance and weaker repeatability. | -| Local debug injection only | No packaging requirement | Deterministic EventChannel/opened-routing verification | Does not prove OS display or activation. | - -Preflight default: use local debug injection for contract tests, and defer the -Windows App SDK vs WinRT packaging decision to the `Windows 알림 테스트` milestone -or a dedicated implementation plan. - -## Payload Mapping - -Windows activation payloads map into Dart using the same stable keys as the -Android, iOS, and macOS contracts: - -| Payload key | Dart event field | Notes | -|---|---|---| -| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. | -| `server_url` | `server_url` | Required before host navigation callbacks can run. | -| `server_id` | `server_id` | Diagnostic and future server URL fallback input. | -| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. | -| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. | -| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. | -| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. | -| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. | -| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. | - -Routing expectation: - -- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty - and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`. -- Thread fixture: `server_url` and `root_id` are present and - `is_crt_enabled` is true, so Dart calls `onNavigateToThread`. - -## Local Fixtures - -Use the fixture files as sanitized payloads for a future debug injection method -or for manual activation/user-data construction during smoke testing: - -- `apps/flutter-test/windows/Fixtures/open_channel.json` -- `apps/flutter-test/windows/Fixtures/open_thread.json` - -The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace -private runtime values only in local run notes or sanitized evidence. - -## Evidence - -Use [`windows-notification-smoke-evidence-template.md`](windows-notification-smoke-evidence-template.md) -for Windows runs. Preflight evidence should explicitly separate: - -- packaging and identity state -- visible display state -- click/open routing result -- app lifecycle state -- ACK, dismiss, and inline reply implementation gaps - -Until the scaffold, bridge, and real OS smoke exist, a valid preflight result -may be "not implemented yet" with a concrete follow-up plan, rather than a -failed runtime claim. +Windows is not registered yet and has no plugin or test-host scaffold. Smoke +work first needs platform scaffold plus the environment choices above. diff --git a/packages/messaging_flutter/README.md b/packages/messaging_flutter/README.md index 2eb4284f..44a804f1 100644 --- a/packages/messaging_flutter/README.md +++ b/packages/messaging_flutter/README.md @@ -51,8 +51,8 @@ copying upstream mobile code. | --- | --- | --- | | Android | Production target | Native FCM service, Room storage, JJWT verification, OkHttp ACK client, notification builder, inline reply receiver, dismiss receiver | | iOS | Stub | Plugin scaffold exists, behavior is not implemented yet | -| macOS | Stub | Plugin scaffold exists; preflight bridge, fixture, host placeholder, and evidence criteria are documented in [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) | -| Windows | Not registered | No Flutter plugin scaffold yet; platform registration, bridge scope, API matrix, fixture, and evidence criteria are documented in [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) | +| macOS | Stub | Plugin scaffold exists; user-provided signing, machine, APNs/Firebase scope, and evidence handoff requirements are documented in [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) | +| Windows | Not registered | No Flutter plugin scaffold yet; user-provided Windows runner, packaging, identity, signing, and evidence handoff requirements are documented in [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) | ## Repository Layout @@ -516,8 +516,9 @@ implementation work starts. Dart expects the macOS plugin to register notification `userInfo` fields to `NotificationOpenedEvent`. Use [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) -for the bridge scope, no-op/fallback boundaries, host placeholder criteria, -payload mapping, fixture paths, and evidence expectations. +to identify the Apple signing, macOS runner, APNs/Firebase scope, and private +handoff values the user must provide before an agent can complete macOS +notification smoke work. ## Windows Preflight Notes @@ -532,8 +533,9 @@ Dart still expects any future Windows bridge to use or debug injection payloads to `NotificationOpenedEvent`. Use [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) -for platform registration scope, bridge boundaries, notification API candidates, -fixture paths, and evidence expectations. +to identify the Windows runner, packaging mode, notification API path, package +identity, signing, and private handoff values the user must provide before an +agent can complete Windows notification smoke work. ## Test Strategy