docs: Linux 알림 테스트 가이드 및 evidence 템플릿 추가, Windows/Linux 선행 정리 아카이브

- Windows 및 Linux 알림 선행 정리 마일스톤을 아카이브로 이동
- iOS 알림 테스트 마일스톤을 활성 milestone로 변경
- Linux 알림 테스트 가이드 및 smoke evidence 템플릿 추가
- Flutter Test Linux fixture 파일 추가 (open_channel, open_thread)
This commit is contained in:
toki 2026-06-02 10:50:27 +09:00
parent cc9f99c54a
commit ee2e79b334
11 changed files with 377 additions and 25 deletions

View file

@ -12,7 +12,7 @@ Linux plugin 등록, notification daemon 연동 후보, bridge 계약, fixture,
## 상태
[계획]
[완료]
## 승격 조건
@ -38,21 +38,24 @@ Linux plugin 등록, notification daemon 연동 후보, bridge 계약, fixture,
Linux 장비, 배포판, desktop environment 결정을 기다리지 않고 먼저 확정할 수 있는 계약과 검증 표면을 분리한다.
- [ ] [platform-contract] Linux plugin 등록 방식, Dart channel 이름, host scaffold 필요 범위를 정리한다.
- [ ] [bridge-scope] Linux notification daemon click/open event를 `NotificationOpenedEvent`와 channel/thread callback에 연결하는 최소 bridge 계약을 정리한다.
- [ ] [api-matrix] D-Bus notification daemon, desktop portal, package/runtime dependency 후보와 제약을 비교한다.
- [ ] [fixtures] local/debug notification payload fixture와 opened-routing injection 시나리오를 작성한다.
- [ ] [evidence-template] display, click/open routing, lifecycle, ACK/dismiss gap evidence 템플릿을 작성한다.
- [x] [platform-contract] Linux plugin 등록 방식, Dart channel 이름, host scaffold 필요 범위를 정리한다.
- [x] [bridge-scope] Linux notification daemon click/open event를 `NotificationOpenedEvent`와 channel/thread callback에 연결하는 최소 bridge 계약을 정리한다.
- [x] [api-matrix] D-Bus notification daemon, desktop portal, package/runtime dependency 후보와 제약을 비교한다.
- [x] [fixtures] local/debug notification payload fixture와 opened-routing injection 시나리오를 작성한다.
- [x] [evidence-template] display, click/open routing, lifecycle, ACK/dismiss gap evidence 템플릿을 작성한다.
## 완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 없음
- 상태: 승인됨
- 요청일: 2026-06-02
- 완료 근거:
- `docs/linux-notification-test-guide.md`에 Linux platform registration, bridge scope, API matrix, runtime dependency boundary, payload mapping, fixture 사용 기준을 정리했다.
- `apps/flutter-test/linux/Fixtures/*.json`에 channel/thread open routing fixture를 추가했다.
- `docs/linux-notification-smoke-evidence-template.md`에 display, click/open routing, lifecycle, ACK/dismiss/inline reply gap evidence 양식을 추가했다.
- 리뷰 필요:
- [ ] 사용자가 완료 결과를 확인했다
- [ ] archive 이동을 승인했다
- 리뷰 코멘트: 없음
- [x] 사용자가 완료 결과를 확인했다
- [x] archive 이동을 승인했다
- 리뷰 코멘트: 모든 기능 Task가 문서/fixture/evidence 기준으로 충족되어 완료 승인 및 archive 대상으로 전환한다. 실제 Linux plugin scaffold, platform registration, native bridge 구현과 OS notification UI smoke는 후속 Milestone 또는 별도 plan 대상이다.
## 범위 제외

View file

@ -12,7 +12,7 @@ Windows plugin 등록, native notification API 후보, bridge 계약, fixture, e
## 상태
[검토중]
[완료]
## 승격 조건
@ -46,16 +46,16 @@ Windows 장비, 계정, packaging 결정을 기다리지 않고 먼저 확정할
## 완료 리뷰
- 상태: 요청
- 상태: 승인
- 요청일: 2026-06-02
- 완료 근거:
- `docs/windows-notification-test-guide.md`에 Windows platform registration, bridge scope, API matrix, payload mapping, fixture 사용 기준을 정리했다.
- `apps/flutter-test/windows/Fixtures/*.json`에 channel/thread open routing fixture를 추가했다.
- `docs/windows-notification-smoke-evidence-template.md`에 display, click/open routing, lifecycle, ACK/dismiss/inline reply gap evidence 양식을 추가했다.
- 리뷰 필요:
- [ ] 사용자가 완료 결과를 확인했다
- [ ] archive 이동을 승인했다
- 리뷰 코멘트: 모든 기능 Task가 문서/fixture/evidence 기준으로 충족되어 완료 후보로 올린다. 실제 Windows plugin scaffold, platform registration, native bridge 구현과 OS notification UI smoke는 후속 Milestone 또는 별도 plan 대상이다.
- [x] 사용자가 완료 결과를 확인했다
- [x] archive 이동을 승인했다
- 리뷰 코멘트: 모든 기능 Task가 문서/fixture/evidence 기준으로 충족되어 완료 승인 및 archive 대상으로 전환한다. 실제 Windows plugin scaffold, platform registration, native bridge 구현과 OS notification UI smoke는 후속 Milestone 또는 별도 plan 대상이다.
## 범위 제외

View file

@ -7,9 +7,9 @@
## 활성 Milestone
- [검토중] Windows 알림 선행 정리
- [계획] iOS 알림 테스트
- Phase: `agent-roadmap/phase/messaging-runtime/PHASE.md`
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/windows-notification-preflight.md`
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/ios-notification-test.md`
## 선택 규칙

View file

@ -28,12 +28,12 @@ Mattermost server/webapp/push-proxy는 upstream-followable runtime으로 두고,
- [완료] macOS 알림 선행 정리
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/macos-notification-preflight.md`
- 요약: Apple signing/장비 없이 macOS native bridge 최소 범위, host placeholder, fixture, evidence 기준을 먼저 정리한다.
- [검토중] Windows 알림 선행 정리
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/windows-notification-preflight.md`
- 요약: Windows 장비/packaging 결정 없이 plugin 등록, native API 후보, bridge 계약, fixture, evidence 기준을 먼저 정리다.
- [계획] Linux 알림 선행 정리
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/linux-notification-preflight.md`
- 요약: Linux 배포판/desktop environment 결정 없이 plugin 등록, notification daemon 후보, bridge 계약, fixture, evidence 기준을 먼저 정리다.
- [완료] Windows 알림 선행 정리
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/windows-notification-preflight.md`
- 요약: Windows 장비/packaging 결정 없이 plugin 등록, native API 후보, bridge 계약, fixture, evidence 기준을 먼저 정리다.
- [완료] Linux 알림 선행 정리
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/linux-notification-preflight.md`
- 요약: Linux 배포판/desktop environment 결정 없이 plugin 등록, notification daemon 후보, bridge 계약, fixture, evidence 기준을 먼저 정리다.
- [계획] iOS 알림 테스트
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/ios-notification-test.md`
- 요약: 선행 정리 후 iOS/APNs 권한, token, open routing, ACK/dismiss 계약을 실제 설정과 smoke 기준으로 검증한다.

View 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
}

View 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
}

View file

@ -0,0 +1,11 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}

View file

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View file

@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -0,0 +1,109 @@
# 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=<redacted-or-placeholder>
desktop_environment=<GNOME|KDE|XFCE|other|not_tested>
session_type=<X11|Wayland|headless|not_tested>
notification_daemon=<redacted-or-placeholder|not_detected|not_tested>
portal_status=<available|missing|not_tested>
```
## 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=<yes|no|not_implemented>
notification_title=<redacted-or-placeholder>
notification_body=<redacted-or-placeholder>
action_data_present=<yes|no|not_implemented>
```
## 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=<foreground|background|not_running|not_tested>
app_state_after=<foreground|background|not_running|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
- [ ] 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.

View file

@ -0,0 +1,169 @@
# Linux Notification Test Guide
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.
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.
## Current Repository State
- `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`.
The first useful Linux smoke target is:
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.
## Platform Registration Contract
Do not add a partial Linux platform entry without the matching source and host
scaffold. A future implementation plan should update these together:
```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.