<!-- task=m-mattermost-nexo-messaging-alignment plan=0 tag=SMOKE -->

# Plan - SMOKE

## 이 파일을 읽는 구현 에이전트에게

이 계획의 구현 완료는 active `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 내용으로 채우는 것까지 포함한다. 검증 명령을 실행하고 실제 stdout/stderr 또는 저장한 로그 경로를 기록한 뒤 active 파일을 유지한 채 리뷰 준비를 보고한다.

외부 Android runner, private secret, 원격 sibling workspace 준비처럼 사용자 결정이나 환경 준비 없이는 진행할 수 없는 차단점이 있으면 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 정확한 결정 필요 사항, 근거, 실행한 명령과 출력, 재개 조건을 채우고 멈춘다. 구현 에이전트는 `USER_REVIEW.md`, archive log, `complete.log`를 만들지 않는다.

## 배경

`Mattermost Nexo Messaging Alignment`는 Workflow Core 재개 전에 Mattermost 메시지/알림 경계를 `../nexo/packages/messaging_flutter` 기준으로 닫는 선행 마일스톤이다. host identifier와 문서 경계는 이미 반영됐고, 남은 작업은 실제 Android runner에서 server-generated signed push smoke를 재실행해 FCM/ACK/opened/reply/dismiss evidence를 redaction 상태로 남기는 것이다. private 값은 tracked 파일에 기록하지 않는다.

## 사용자 리뷰 요청 흐름

구현 중 차단점은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 그 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/external-integration/PHASE.md`
- `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md`
- `docs/test-environments.md`
- `agent-ops/rules/private/rules.md`
- `apps/client/lib/src/integrations/mattermost/mattermost_push_client.dart`
- `apps/client/lib/src/integrations/mattermost/mattermost_push_plugin_client.dart`
- `apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart`
- `apps/client/lib/src/integrations/mattermost/mattermost_push_host_integration.dart`
- `apps/client/test/integrations/mattermost_push_host_integration_test.dart`
- `apps/client/pubspec.yaml`
- `../nexo/packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart`
- `../nexo/packages/messaging_flutter/lib/src/notification_opened_event.dart`
- `../nexo/packages/messaging_flutter/android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoFirebaseMessagingService.kt`
- `../nexo/packages/messaging_flutter/android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPlugin.kt`
- `../nexo/packages/messaging_flutter/README.md`

### 테스트 커버리지 공백

- Host identifier forwarding: `apps/client` unit tests cover interface compatibility only indirectly; no asset parsing regression test currently asserts `serverId/serverIdentifier` forwarding.
- Signed FCM receipt, valid signature, system notification display, opened event, inline reply, dismiss: not covered by local Flutter tests. This is the purpose of the remote smoke.
- Negative controls for invalid/unsigned push: only executable if direct push-proxy access is available; plan records `N/A` when access is unavailable.

### 심볼 참조

- Changed symbol: `MattermostPushClient.setAuthToken(String, String, {String? identifier})`.
- Call sites found with `rg --sort path`: `mattermost_auth_service.dart`, `mattermost_push_plugin_client.dart`, `mattermost_push_host_integration_test.dart`, docs/contracts references, and `../nexo` plugin `NexoMessagingPlugin.setAuthToken`.

### 분할 판단

Split decision policy was evaluated before choosing plan files. This is a single-plan task because the remaining work is one coherent external smoke/evidence unit, has one primary verification boundary, and splitting setup/run/evidence would create artificial coordination overhead. The task group is `agent-task/m-mattermost-nexo-messaging-alignment/`.

### 범위 결정 근거

Do not implement core Mattermost REST message sending here; that remains in the later `External Integration` `[mattermost-adapter]` task. Do not move native push code into `apps/client/android`; `../nexo/packages/messaging_flutter` remains the owner. Do not implement iOS/macOS push runtime. Do not record raw token, password, signing key, FCM token, or push signature values in roadmap, docs, task logs, final response, or review output.

### 빌드 등급

Build lane: `cloud-G07`. Rationale: success depends on real external Android runner, SSH workflow, private inputs, Mattermost server-generated push delivery, and log/evidence interpretation.

## 구현 체크리스트

- [ ] [SMOKE-1] 원격 Android runner workspace가 `<workspace>/nomadcode`, `<workspace>/nexo`, `<workspace>/proto-socket` sibling shape를 갖고 현재 변경을 포함하는지 확인하고, 없으면 사용자 리뷰 요청에 준비 필요 사항을 기록한다.
- [ ] [SMOKE-2] private rules가 라우팅하는 ignored secret 파일에서 필요한 값만 로드해 `apps/client/assets/mattermost_credentials.json`과 Mattermost trigger 환경을 준비한다. raw secret 값은 출력/기록하지 않는다.
- [ ] [SMOKE-3] `docs/test-environments.md#mattermost-server-generated-push-smoke` 절차로 Android app run과 server-generated signed push를 실행하고 Evidence Checklist를 PASS/PARTIAL/BLOCKED/N/A로 기록한다.
- [ ] [SMOKE-4] evidence 결과에 따라 `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md`의 `[signed-smoke-refresh]` 상태와 완료 리뷰 후보를 갱신하거나, 미충족 항목을 작업 컨텍스트에 남긴다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [SMOKE-1] Remote Workspace Preflight

### 문제

`agent-ops/rules/private/rules.md`는 2026-05-29 기준 standard remote host에 `nomadcode`, `nexo`, `proto-socket` checkout이 없었다고 기록한다. `docs/test-environments.md`는 remote Android runner가 sibling workspace shape를 필요로 한다.

### 해결 방법

Remote host에서 workspace shape와 Flutter/Android 환경을 확인한다. checkout이 없거나 현재 변경이 반영되지 않았으면, 구현을 계속하지 말고 review stub의 사용자 리뷰 요청에 필요한 sync/prep 작업을 기록한다.

```bash
ssh toki@toki-labs.com 'set -e; pwd; ls -ld ~/works/nomadcode /config/workspace/nomadcode 2>/dev/null || true; command -v flutter; command -v adb || true'
```

### 수정 파일 및 체크리스트

- [ ] `CODE_REVIEW-cloud-G07.md`에 remote workspace preflight 실제 출력 또는 redacted 요약을 기록한다.
- [ ] checkout 부재 시 `사용자 리뷰 요청`에 재개 조건을 기록한다.

### 테스트 작성

테스트 추가 없음. 이 항목은 외부 환경 preflight다.

### 중간 검증

```bash
ssh toki@toki-labs.com 'set -e; command -v flutter; command -v adb || true; test -d ~/works/nomadcode -o -d /config/workspace/nomadcode'
```

Expected: `flutter` path is printed, `adb` availability is shown or noted, and at least one NomadCode checkout path exists.

## [SMOKE-2] Private Inputs And Credential Asset

### 문제

Valid server-generated signed push requires Mattermost receiver credentials, bot trigger token, and signing-key handoff. These values are local-only secrets and must not be printed or committed.

### 해결 방법

Read only the needed values from the private secret file routed by `agent-ops/rules/private/rules.md`. Create or update ignored `apps/client/assets/mattermost_credentials.json` on the runner with `serverUrl`, `loginId`, `password`, and optional `serverId` or `serverIdentifier` when available.

```json
{
  "serverUrl": "<redacted>",
  "loginId": "<redacted>",
  "password": "<redacted>",
  "serverId": "<redacted optional>"
}
```

### 수정 파일 및 체크리스트

- [ ] Remote ignored credential asset exists with `0600` or equivalent restrictive permissions.
- [ ] Trigger environment exists only in ignored/private files.
- [ ] No raw secret values are copied into tracked files or task logs.

### 테스트 작성

테스트 추가 없음. Correctness is verified by app-side login/signing-key/device-token logs in SMOKE-3.

### 중간 검증

```bash
ssh toki@toki-labs.com 'set -e; cd <workspace>/nomadcode/apps/client; test -f assets/mattermost_credentials.json; stat -f "%Sp %N" assets/mattermost_credentials.json 2>/dev/null || stat -c "%a %n" assets/mattermost_credentials.json'
```

Expected: credential asset exists; permissions are restrictive. Do not print file contents.

## [SMOKE-3] Server-Generated Signed Push Smoke

### 문제

Local tests cannot prove real FCM receipt, valid signature handling, system notification display, opened event, inline reply, dismiss, or device-token registration against Mattermost.

### 해결 방법

Run the public procedure in `docs/test-environments.md#mattermost-server-generated-push-smoke`. Use a normal Mattermost post through REST so the server and push-proxy generate the push; do not use hand-built placeholder signatures as positive evidence.

```bash
cd <workspace>/nomadcode/apps/client
flutter pub get
flutter analyze --no-fatal-infos
flutter test
flutter run -d emulator-5554 --no-resident
```

Trigger a Mattermost DM or mention using the private bot token as documented. Capture redacted app logs, logcat, and notification evidence.

### 수정 파일 및 체크리스트

- [ ] `CODE_REVIEW-cloud-G07.md` records actual outputs for `flutter pub get`, `flutter analyze --no-fatal-infos`, `flutter test`, and app run/smoke logs.
- [ ] Evidence checklist records FCM receipt, valid signature, invalid/unsigned result or N/A, notification display, ACK, opened event, channel/thread navigation, inline reply, dismiss/clear, device token prefix.
- [ ] Raw secrets and payload signatures are redacted.

### 테스트 작성

테스트 추가 없음. This is the missing external smoke for the roadmap task.

### 중간 검증

```bash
cd apps/client && flutter test
```

Expected: all Flutter tests pass before relying on device smoke. Cached Flutter test output is acceptable for this smoke precheck.

## [SMOKE-4] Roadmap Evidence Update

### 문제

The milestone task `[signed-smoke-refresh]` must not be checked off unless the evidence checklist is actually satisfied or explicitly classified as partial/blocked.

### 해결 방법

Update `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md` only after SMOKE-3. If all required evidence passes, mark `[signed-smoke-refresh]` `[x]` and move the milestone to `[검토중]` with completion review evidence. If some items fail or are blocked, keep status `[진행중]`, leave the task unchecked, and add a short redacted `작업 컨텍스트` note.

### 수정 파일 및 체크리스트

- [ ] `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md` reflects the actual smoke result.
- [ ] Evidence note is short and contains no private values.
- [ ] If milestone reaches `[검토중]`, completion review asks for user confirmation and archive approval.

### 테스트 작성

테스트 추가 없음. Roadmap update is document evidence only.

### 중간 검증

```bash
git diff --check
```

Expected: no whitespace errors.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md` | SMOKE-4 |
| `agent-task/m-mattermost-nexo-messaging-alignment/CODE_REVIEW-cloud-G07.md` | SMOKE-1, SMOKE-2, SMOKE-3, SMOKE-4 |
| ignored remote `apps/client/assets/mattermost_credentials.json` | SMOKE-2 |

## 최종 검증

```bash
cd apps/client && flutter test
cd apps/client && flutter analyze --no-fatal-infos
git diff --check
```

Expected: Flutter tests pass; analyze exits 0 with known `avoid_print` info only; `git diff --check` is clean. All smoke outputs and evidence classification must be pasted into `CODE_REVIEW-cloud-G07.md`.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
