<!-- task=m-external-integration/03_mattermost_adapter plan=1 tag=REVIEW_MM -->

# Code Review Reference - REVIEW_MM

> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Fill implementation-owned sections and paste real command output. Never paste raw tokens, FCM tokens, signing keys, or push payload signatures.

## 개요

date=2026-06-03
task=m-external-integration/03_mattermost_adapter, plan=1, tag=REVIEW_MM

## Roadmap Targets

- Milestone: `agent-roadmap/phase/external-integration/milestones/external-integration.md`
- Task ids:
  - `mattermost-adapter`: Mattermost 메시지 발송 adapter 구현과 Nexo host notification boundary 정합성 유지
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** Validate the redacted Mattermost server-generated signed push smoke evidence before PASS.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_MM-1] Mattermost server-generated signed push smoke evidence | [x] |

## 구현 체크리스트

- [x] [REVIEW_MM-1] Mattermost server-generated signed push smoke를 실행하고 redacted evidence checklist를 기록한다.
- [x] `cd services/core && go test ./...`를 실행한다.
- [x] `cd services/core && go vet ./...`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G08_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G08_N.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
- [x] PASS이면 `complete.log`를 작성하고 active task directory를 archive로 이동한다.
- [x] PASS라서 WARN/FAIL follow-up 또는 `USER_REVIEW.md`를 작성하지 않고 `complete.log`만 남겼다.
- [x] PASS이고 task group이 `m-external-integration`이므로 런타임 완료 이벤트 메타데이터를 최종 보고에 포함한다.
- [x] PASS split 작업이며 active parent `agent-task/m-external-integration/`에는 sibling active task가 남아 있어 유지한다.

## 계획 대비 변경 사항

- 계획대로 코드 변경 없이 Mattermost server-generated signed push smoke evidence만 수집했다.
- remote checkout HEAD 일치는 이번 follow-up의 차단 조건이 아니므로, remote runner의 `main` / `55e0139cca82d9c88308207bcece0c151d4c2da9` 상태에서 Android smoke를 실행했다.

## 주요 설계 결정

- Positive path는 local-only `agent-test/local/testing-secrets.md`의 Mattermost bot token으로 REST `POST /api/v4/posts`를 실행해 server-generated DM을 만들었다.
- remote app setup은 기존 ignored `apps/client/assets/mattermost_credentials.json`와 `google-services.json`을 사용했다. Android notification permission prompt로 bootstrap이 지연되어 `POST_NOTIFICATIONS` permission을 허용한 뒤 login/signing-key/FCM registration을 확인했다.
- Evidence는 remote `/tmp/nomadcode-mm-smoke-evidence-20260603.redacted.txt`에 redacted 상태로 남겼고, raw token/password/FCM token/signing key/push signature는 tracked 파일이나 review output에 기록하지 않았다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- Positive path가 Mattermost REST server-generated DM 또는 mention인지 확인한다.
- Evidence가 raw token, FCM token, signing key, push payload signature를 포함하지 않는지 확인한다.
- FCM receipt, valid signature 처리, notification display, ACK, opened event, channel navigation, inline reply, dismiss/clear, device token prefix 결과가 checklist로 기록됐는지 확인한다.

## 검증 결과

### REVIEW_MM-1 중간 검증

```text
$ ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'zsh -lc "cd $HOME/agent-work/nomadcode && git rev-parse --abbrev-ref HEAD && git rev-parse HEAD && flutter devices"'
main
55e0139cca82d9c88308207bcece0c151d4c2da9

Found 3 connected devices:
  sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64  • Android 14 (API 34) (emulator)
  macOS (desktop)             • macos         • darwin-arm64   • macOS 26.0.1 25A362 darwin-arm64
  Chrome (web)                • chrome        • web-javascript • Google Chrome 148.0.7778.216
```

### Mattermost signed push smoke

```text
$ agent-test/local/mattermost-server-generated-push-smoke.md 절차에 따른 Mattermost REST positive push 및 redacted evidence collection
Redacted evidence path on runner:
/tmp/nomadcode-mm-smoke-evidence-20260603.redacted.txt

App setup:
- FirebaseApp initialization successful
- [MattermostAuth] Login OK - userId=<redacted> sessionId=<redacted>
- [PushNotification] Auth token saved for <url>
- [MattermostAuth] Signing key stored.
- [MattermostAuth] FCM device token registered successfully.
- Notification token registered: android_rn-v2:<redacted-fcm-token>

Positive path:
- Mattermost REST server-generated DM posted:
  `nomadcode signed push smoke REVIEW_MM 2026-06-03T09:06:53Z`
- FCM receipt observed: `NexoFCM >>> onMessageReceived ENTER`
- ACK observed: `ReceiptDelivery Send ACK=<redacted> TYPE=message`
- Flutter event observed: `NexoFCM Sending notification event to Flutter: type=message`
- Positive path signature failure was not observed.
- Notification display observed in `dumpsys notification` with importance HIGH and text matching the smoke message.
- Opened event observed: `[PushNotification] Opened: channelId=dii4...nkfo rootId=null`
- Channel navigation observed: `[MattermostHost] Navigate to channel: dii4...nkfo on <url>`
- Inline reply observed: `NotifReplyReceiver Reply SUCCESS`
- Mattermost latest channel post after inline reply: `nomadcode-inline-reply-smoke-review-mm`
- Dismiss/clear observed: `NotifDismissService Dismiss notification id=<id>` and remaining `com.tokilabs.mattermost` notifications = 0.

Evidence checklist:
- FCM payload wakes / receipt: PASS
- valid server-generated signature handling: PASS
- invalid / unsigned drop: N/A (direct push-proxy negative control not run)
- notification display: PASS
- ACK request: PASS
- opened event: PASS
- channel navigation: PASS
- thread navigation: N/A (rootId=null)
- inline reply: PASS
- dismiss / clear: PASS
- device token prefix: PASS

Redaction check:
- PASS. Evidence file did not include raw bearer token, raw FCM token, raw ACK, raw signature, password, or receiver email.
```

### 최종 검증

```text
$ cd services/core && go test ./...
ok  	github.com/nomadcode/nomadcode-core/cmd/plane-smoke	(cached)
?   	github.com/nomadcode/nomadcode-core/cmd/server	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/a2a	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/jira	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/mattermost	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/openai	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/plane	(cached)
?   	github.com/nomadcode/nomadcode-core/internal/agent	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/config	(cached)
?   	github.com/nomadcode/nomadcode-core/internal/db	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/http	(cached)
?   	github.com/nomadcode/nomadcode-core/internal/model	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/notification	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/protosocket	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/scheduler	(cached)
?   	github.com/nomadcode/nomadcode-core/internal/storage	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/workflow	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/workitem	(cached)
ok  	github.com/nomadcode/nomadcode-core/internal/workitempipeline	(cached)

$ cd services/core && go vet ./...
(no output; exit 0)
```

## Section Ownership

| Section | Owner | Note |
|---------|-------|------|
| 구현 항목별 완료 여부, 구현 체크리스트, 계획 대비 변경 사항, 주요 설계 결정, 사용자 리뷰 요청, 검증 결과 | Implementing agent | Fill before review |
| 코드리뷰 전용 체크리스트 | Review agent only | Do not modify during implementation |

## 코드리뷰 결과

- 종합 판정: PASS
- 차원별 평가:
  - correctness: Pass
  - completeness: Pass
  - test coverage: Pass
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Pass
- 발견된 문제: 없음
- 다음 단계: PASS이므로 `complete.log`를 작성하고 `agent-task/archive/2026/06/m-external-integration/03_mattermost_adapter/`로 이동한다.
