feat: macos notification support and documentation updates
- Add macos notification smoke evidence template and test guide - Add Fixtures for open_channel and open_thread json - Update roadmaps for messaging runtime phase and macos notification milestone - Update flutter-test and messaging_flutter READMEs
This commit is contained in:
parent
706e1ae6f0
commit
0faa687954
9 changed files with 307 additions and 13 deletions
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## 활성 Milestone
|
||||
|
||||
- [계획] macOS 알림 선행 정리
|
||||
- [검토중] macOS 알림 선행 정리
|
||||
- Phase: `agent-roadmap/phase/messaging-runtime/PHASE.md`
|
||||
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/macos-notification-preflight.md`
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Mattermost server/webapp/push-proxy는 upstream-followable runtime으로 두고,
|
|||
- [완료] iOS 알림 선행 정리
|
||||
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/ios-notification-preflight.md`
|
||||
- 요약: Apple/Firebase/APNs 실자산 없이 iOS token 계약, native bridge 최소 범위, fixture, evidence 기준을 정리했다.
|
||||
- [계획] macOS 알림 선행 정리
|
||||
- [검토중] macOS 알림 선행 정리
|
||||
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/macos-notification-preflight.md`
|
||||
- 요약: Apple signing/장비 없이 macOS native bridge 최소 범위, host placeholder, fixture, evidence 기준을 먼저 정리한다.
|
||||
- [계획] Windows 알림 선행 정리
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ macOS native bridge 최소 범위, host placeholder, payload fixture, evidence
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[검토중]
|
||||
|
||||
## 승격 조건
|
||||
|
||||
|
|
@ -38,21 +38,24 @@ macOS native bridge 최소 범위, host placeholder, payload fixture, evidence
|
|||
|
||||
Apple signing, 장비, OS UI 수동 검증 전에 먼저 확정할 수 있는 계약과 검증 표면을 분리한다.
|
||||
|
||||
- [ ] [bridge-scope] Dart가 기대하는 EventChannel/MethodChannel 이름, macOS 최소 구현 method, no-op/fallback 경계를 정리한다.
|
||||
- [ ] [host-placeholder] entitlement, signing/notarization placeholder, ignored credential 위치, macOS target resource 기준을 정리한다.
|
||||
- [ ] [payload-map] macOS notification userInfo를 `NotificationOpenedEvent`와 channel/thread callback에 매핑하는 기준을 정리한다.
|
||||
- [ ] [fixtures] local notification payload fixture와 click/open routing injection 시나리오를 작성한다.
|
||||
- [ ] [evidence-template] permission, display, click/open routing, lifecycle, ACK/dismiss gap evidence 템플릿을 작성한다.
|
||||
- [x] [bridge-scope] Dart가 기대하는 EventChannel/MethodChannel 이름, macOS 최소 구현 method, no-op/fallback 경계를 정리한다.
|
||||
- [x] [host-placeholder] entitlement, signing/notarization placeholder, ignored credential 위치, macOS target resource 기준을 정리한다.
|
||||
- [x] [payload-map] macOS notification userInfo를 `NotificationOpenedEvent`와 channel/thread callback에 매핑하는 기준을 정리한다.
|
||||
- [x] [fixtures] local notification payload fixture와 click/open routing injection 시나리오를 작성한다.
|
||||
- [x] [evidence-template] permission, display, click/open routing, lifecycle, ACK/dismiss gap evidence 템플릿을 작성한다.
|
||||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: 없음
|
||||
- 상태: 요청됨
|
||||
- 요청일: 2026-06-02
|
||||
- 완료 근거:
|
||||
- `docs/macos-notification-test-guide.md`에 macOS bridge scope, host placeholder, userInfo mapping, fixture 사용 기준을 정리했다.
|
||||
- `apps/flutter-test/macos/Fixtures/*.json`에 channel/thread open routing fixture를 추가했다.
|
||||
- `docs/macos-notification-smoke-evidence-template.md`에 permission, display, click/open routing, lifecycle, ACK/dismiss/inline reply gap evidence 양식을 추가했다.
|
||||
- 리뷰 필요:
|
||||
- [ ] 사용자가 완료 결과를 확인했다
|
||||
- [ ] archive 이동을 승인했다
|
||||
- 리뷰 코멘트: 없음
|
||||
- 리뷰 코멘트: 모든 기능 Task가 문서/fixture/evidence 기준으로 충족되어 완료 후보로 올린다. 실제 macOS native bridge 구현과 OS notification UI smoke는 후속 Milestone 또는 별도 plan 대상이다.
|
||||
|
||||
## 범위 제외
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,15 @@ Local routing fixtures live under `ios/Fixtures/*.apns`; replace
|
|||
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 macOS preflight and smoke 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).
|
||||
|
||||
## Verification
|
||||
|
||||
The integration test uses the plugin's test/debug event injection path for
|
||||
|
|
|
|||
11
apps/flutter-test/macos/Fixtures/open_channel.json
Normal file
11
apps/flutter-test/macos/Fixtures/open_channel.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"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-channel",
|
||||
"post_id": "post-smoke-channel",
|
||||
"userInteraction": true
|
||||
}
|
||||
11
apps/flutter-test/macos/Fixtures/open_thread.json
Normal file
11
apps/flutter-test/macos/Fixtures/open_thread.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "message",
|
||||
"server_url": "https://example.invalid",
|
||||
"server_id": "server-smoke",
|
||||
"channel_id": "channel-smoke",
|
||||
"root_id": "thread-smoke",
|
||||
"is_crt_enabled": "true",
|
||||
"ack_id": "ack-smoke-thread",
|
||||
"post_id": "post-smoke-thread",
|
||||
"userInteraction": true
|
||||
}
|
||||
104
docs/macos-notification-smoke-evidence-template.md
Normal file
104
docs/macos-notification-smoke-evidence-template.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# 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: `<redacted-or-placeholder>`
|
||||
- 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=<authorized|denied|provisional|not_determined|not_implemented>
|
||||
request_path=<FlutterFire|UserNotifications|not_implemented>
|
||||
notes=<redacted-or-placeholder>
|
||||
```
|
||||
|
||||
## 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=<yes|no|not_implemented>
|
||||
notification_title=<redacted-or-placeholder>
|
||||
notification_body=<redacted-or-placeholder>
|
||||
```
|
||||
|
||||
## 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=<foreground|background|terminated|not_tested>
|
||||
app_state_after=<foreground|background|terminated|not_tested>
|
||||
event_type=<message|opened|not_implemented>
|
||||
userInteraction=<true|false|not_set>
|
||||
```
|
||||
|
||||
## 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.
|
||||
143
docs/macos-notification-test-guide.md
Normal file
143
docs/macos-notification-test-guide.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# macOS Notification Test Guide
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Current Repository State
|
||||
|
||||
- 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`.
|
||||
|
||||
The first useful macOS smoke target is:
|
||||
|
||||
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.
|
||||
|
||||
## Minimum Bridge Contract
|
||||
|
||||
The Dart SDK expects the macOS native side to preserve these channel names:
|
||||
|
||||
| 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.
|
||||
|
|
@ -51,7 +51,7 @@ 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, 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) |
|
||||
|
||||
## Repository Layout
|
||||
|
||||
|
|
@ -504,6 +504,19 @@ If the local machine does not have an Android SDK or Android device/emulator,
|
|||
use the separate SSH-based Android test environment documented in
|
||||
[`docs/android-test-environment.md`](docs/android-test-environment.md).
|
||||
|
||||
## macOS Preflight Notes
|
||||
|
||||
macOS remains a scaffold, but its preflight contract is fixed before native
|
||||
implementation work starts. Dart expects the macOS plugin to register
|
||||
`com.tokilabs.nexo.messaging/notifications` and
|
||||
`com.tokilabs.nexo.messaging/notification_actions`, preserve the legacy
|
||||
`nexo_messaging` `getPlatformVersion` compatibility channel, and map
|
||||
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.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
Use deterministic tests for repository-owned behavior. The example integration
|
||||
|
|
|
|||
Loading…
Reference in a new issue