<!-- task=m-external-integration/03_mattermost_adapter plan=2 tag=REVIEW_REVIEW_MM -->

# Plan - REVIEW_REVIEW_MM

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션 작성은 필수다. 검증을 실행하고 실제 내용과 출력, 계획 대비 변경 사항을 채운 뒤 active 파일을 그대로 둔 채 리뷰 준비 완료를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 스킬 전용이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정, 근거, 실행한 명령과 출력을 남기고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 evidence 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 계획 대비 변경 사항으로 기록한다.

## 배경

두 번째 리뷰(`code_review_cloud_G07_1.log`)도 `FAIL`이다. 이전 stale secret blocker와 대부분의 Mattermost signed push e2e evidence는 해소되었지만, `device token prefix`가 `PARTIAL`로 남았고, remote smoke checkout이 리뷰 시점에는 tracked dirty 상태라 exact current-code parity를 신뢰할 수 없다. 이 계획은 소스 범위를 넓히지 않고 두 evidence gap만 닫는다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사항은 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 요청의 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

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

## 분석 결과

### 읽은/확인한 파일과 상태

- `agent-task/m-external-integration/03_mattermost_adapter/plan_cloud_G07_0.log`
- `agent-task/m-external-integration/03_mattermost_adapter/code_review_cloud_G07_0.log`
- `agent-task/m-external-integration/03_mattermost_adapter/plan_cloud_G07_1.log`
- `agent-task/m-external-integration/03_mattermost_adapter/code_review_cloud_G07_1.log`
- `agent-test/local/mattermost-server-generated-push-smoke.md`
- `apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart`
- `../nexo/packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart`
- `../nexo/packages/messaging_flutter/test/nexo_messaging_test.dart`

### 리뷰 판정 근거

- Most signed push evidence is good: FCM receipt, server-generated signature path, notification display, ACK, opened, channel navigation, inline reply, and dismiss/clear were recorded as PASS.
- `CODE_REVIEW-cloud-G07.md` recorded `device token prefix` as `PARTIAL`, with no observed `android_rn-v2:` literal prefix. The smoke profile expects this boundary evidence.
- The remote smoke checkout path `/Users/toki/agent-work/nomadcode-smoke-20260605083500` was rechecked during review and had tracked changes:
  - `apps/client/android/gradle.properties`: Flutter migrator flags added.
  - `apps/client/pubspec.lock`: `meta` and `test_api` versions changed.
- Because the smoke ran from a tree that became tracked-dirty, the evidence does not yet cleanly prove current local HEAD behavior unless the dirty state is explained and eliminated, or the necessary tracked changes are intentionally brought into the reviewed tree.

### 범위 결정 근거

- Do not rerun the whole Mattermost smoke blindly unless needed. Reuse prior PASS evidence where it remains valid, but rerun the minimal parts needed to prove clean parity and token-prefix behavior.
- Do not print raw token, password, FCM token, signing key, or push payload signature.
- Existing dirty remote checkout(s) may contain environment/user changes. Prefer a new fresh smoke checkout. Do not reset/delete the old checkouts.
- If Flutter tooling necessarily mutates tracked project files before a valid Android run, stop and either:
  - find a command path that preserves clean HEAD, or
  - make the tracked changes locally as an intentional source change after reading the relevant mobile domain rule and rerunning tests.

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_MM-1] Remote smoke checkout parity를 clean tracked tree 기준으로 재검증하고, Flutter/pub가 tracked files를 바꾸는지 build 전후로 기록한다.
- [ ] [REVIEW_REVIEW_MM-2] raw token 없이 `android_rn-v2:` device token prefix evidence를 `PASS`로 닫는다.
- [ ] [REVIEW_REVIEW_MM-3] 필요 시 최소 signed push 확인을 재실행해 prefix evidence가 실제 app/server registration path와 연결됨을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_MM-1] Clean Remote Parity

#### 문제

이전 smoke path는 HEAD가 맞았지만 이후 tracked dirty 상태가 되었다. `flutter run`이나 `flutter pub get`이 lockfile/Gradle 설정을 바꾸면 smoke evidence가 정확히 local HEAD를 대상으로 한 것인지 흐려진다.

#### 해결 방법

새 remote smoke checkout을 만들고, command 단계마다 `git status --short`와 필요 시 `git diff --stat`를 기록한다. `flutter pub get --enforce-lockfile`, `flutter test`, `flutter run --no-pub`처럼 lockfile mutation을 막을 수 있는 command path를 먼저 시도한다. 그래도 Gradle migrator가 tracked files를 바꾸면, 그 상태로 PASS하지 말고 변경 필요성을 명확히 기록한다.

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

- [ ] `CODE_REVIEW-cloud-G07.md`: fresh checkout path, branch, HEAD, pre-command clean status, post-command status를 기록한다.
- [ ] remote fresh checkout: tracked dirty 상태가 되면 diff stat과 원인을 기록한다.
- [ ] local source files: 변경하지 않는 것이 기본이다. 변경이 필요하면 mobile domain rule을 읽고 범위를 명확히 기록한다.

#### 테스트 작성

새 repository test는 필요 없다. Verification trust recovery다.

#### 중간 검증

```bash
ssh toki@toki-labs.com 'zsh -lc '"'"'SMOKE_DIR="$HOME/agent-work/nomadcode-smoke-prefix-$(date +%Y%m%d%H%M%S)"; echo "SMOKE_DIR=$SMOKE_DIR"; git clone --branch main --single-branch https://git.toki-labs.com/toki/nomadcode.git "$SMOKE_DIR"; cd "$SMOKE_DIR"; git rev-parse --abbrev-ref HEAD; git rev-parse HEAD; git status --short; test -d ../nexo/packages/messaging_flutter; test -d ../proto-socket/dart; if test -f "$HOME/agent-work/nomadcode/apps/client/assets/mattermost_credentials.json"; then cp "$HOME/agent-work/nomadcode/apps/client/assets/mattermost_credentials.json" apps/client/assets/mattermost_credentials.json; fi; test -f apps/client/assets/mattermost_credentials.json; git status --short'"'"''
```

Expected: branch/head match current `origin/main`; tracked status is clean before and after ignored credential copy.

```bash
ssh toki@toki-labs.com 'cd "<SMOKE_DIR_FROM_OUTPUT>" && zsh -lc '"'"'cd apps/client && flutter pub get --enforce-lockfile && git -C ../.. status --short && flutter test && git -C ../.. status --short'"'"''
```

Expected: tests pass and tracked status remains clean. If not, record exact diff and stop before claiming PASS.

### [REVIEW_REVIEW_MM-2] Device Token Prefix Evidence

#### 문제

`device token prefix` was left `PARTIAL`. The smoke profile expects Android registration to use `android_rn-v2:` before raw FCM token registration. Registration success and push delivery prove a valid registered token, but they do not by themselves prove the formatted prefix boundary.

#### 해결 방법

Close this with non-secret evidence. Prefer evidence that directly observes the formatted token prefix while redacting the token body. Acceptable examples:

- A targeted Nexo package test output that proves token refresh formats `android_rn-v2:<raw-token>` before native save/callback, plus NomadCode host evidence that it uses `MattermostPushClient.onDeviceTokenReady` and sends that token to Mattermost.
- A smoke log or server/API observation that shows only the literal prefix and a redacted token body, for example `android_rn-v2:<redacted>`.
- A small, intentional host/plugin log improvement that prints only the token prefix and never the raw token, if source change is unavoidable. If this path is used, read mobile domain rules first and run the mobile/core checks affected by the change.

Do not use a raw FCM token in task logs.

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

- [ ] `CODE_REVIEW-cloud-G07.md`: record prefix evidence and mark `device token prefix` as `PASS`.
- [ ] `../nexo/packages/messaging_flutter/test/nexo_messaging_test.dart`: read/use the existing test; change only if a real gap is found in sibling repo and the user scope permits it.
- [ ] `apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart`: change only if a redacted prefix log is necessary and approved by scope/rules.

#### 테스트 작성

No new test is expected if the existing Nexo package test is enough. If code changes are made, add/adjust the smallest relevant test.

#### 중간 검증

```bash
cd ../nexo/packages/messaging_flutter && flutter test test/nexo_messaging_test.dart --plain-name 'token_refresh event saves formatted device token and invokes callback'
```

Expected: test passes and output/reference proves `android_rn-v2:` formatting without exposing a real token.

```bash
rg -n "android_rn-v2|onDeviceTokenReady|registerDevice|saveDeviceToken|FCM device token" apps/client ../nexo/packages/messaging_flutter -g '!build' -g '!.dart_tool'
```

Expected: source path from formatted token callback to NomadCode registration is clear.

### [REVIEW_REVIEW_MM-3] Minimal Push Link Check

#### 문제

If prefix evidence is source/test-only, it still needs to connect to the actual app/server registration path used by the smoke.

#### 해결 방법

Rerun only the minimal launch/registration path, then optionally send one server-generated Mattermost post to confirm FCM receipt/ACK still works. The full interaction suite does not need to be repeated if prior evidence is reused and the remote tree is clean; if you do repeat it, keep outputs redacted.

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

- [ ] `CODE_REVIEW-cloud-G07.md`: record app launch, registration, optional FCM receipt/ACK evidence, and final checklist.
- [ ] remote `/tmp` evidence path: keep only redacted output if used.

#### 테스트 작성

No repository test file is expected.

#### 중간 검증

```bash
ssh toki@toki-labs.com 'cd "<SMOKE_DIR_FROM_OUTPUT>" && zsh -lc '"'"'cd apps/client && adb -s emulator-5554 logcat -c && flutter run -d emulator-5554 --no-resident --no-pub'"'"''
```

Expected: app launches from clean tracked checkout and registration path is visible without raw token leakage.

## 최종 검증

```bash
cd services/core && go test -count=1 ./internal/adapters/mattermost ./internal/notification ./internal/scheduler
```

Expected: pass.

```bash
cd services/core && go test ./...
```

Expected: pass or `[no test files]`.

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

Expected: no whitespace errors.

```bash
test -f agent-test/local/testing-secrets.md && git check-ignore -v agent-test/local/testing-secrets.md
```

Expected: local secret exists and is ignored; no raw secret values are printed.

```bash
ssh toki@toki-labs.com 'cd "<SMOKE_DIR_FROM_OUTPUT>" && zsh -lc '"'"'git rev-parse --abbrev-ref HEAD && git rev-parse HEAD && git status --short && test -d ../nexo/packages/messaging_flutter && test -d ../proto-socket/dart && flutter devices'"'"''
```

Expected: HEAD matches current target and tracked status is clean after the evidence collection path, or any required tracked changes are explicitly handled in local source scope.

Final pass condition: `device token prefix` is `PASS`, remote parity is trustworthy, and the final Mattermost evidence checklist has no `PARTIAL` or `BLOCKED` items except justified `N/A` for optional negative control.
