<!-- task=m-external-integration/04+01,02,03_adapter_boundary plan=1 tag=REVIEW_BOUNDARY -->

# Code Review Reference - REVIEW_BOUNDARY

> **[IMPLEMENTING AGENT - READ FIRST] This plan closes Required items from code_review_local_G06_0.log FAIL.**

## 개요

date=2026-06-03
task=m-external-integration/04+01,02,03_adapter_boundary, plan=1, tag=REVIEW_BOUNDARY

## Roadmap Targets

- Milestone: `agent-roadmap/phase/external-integration/milestones/external-integration.md`
- Task ids:
  - `adapter-boundary`: 외부 provider별 구현 경계 점검
- Completion mode: check-on-pass

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

> **[REVIEW AGENT ONLY]** Verify boundary fix evidence before PASS.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|------|
| [REVIEW_BOUNDARY-1] Evidence recovery | [x] Predecessor archive complete logs confirmed (01, 02, 03) |
| [REVIEW_BOUNDARY-2] Mattermost notification boundary | [x] notification package no longer imports mattermost; sink moved to adapter |
| [REVIEW_BOUNDARY-3] Docs and final verification | [x] no docs changes needed |

## 구현 체크리스트

- [x] [REVIEW_BOUNDARY-1] 선행 complete.log와 audit 검색 증거를 실제 출력으로 재수집한다.
- [x] [REVIEW_BOUNDARY-2] `internal/notification`의 Mattermost adapter 직접 의존을 provider-neutral sink 경계 뒤로 이동한다.
- [x] [REVIEW_BOUNDARY-3] README/domain consistency와 최종 검증을 수행한다.
- [x] `cd services/core && go test ./...`를 실행한다.
- [x] `cd services/core && go vet ./...`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

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

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] active files를 `.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
- [x] PASS이면 `complete.log` 작성 후 `agent-task/archive/YYYY/MM/m-external-integration/04+01,02,03_adapter_boundary/`로 이동한다.
- [x] PASS이면 runtime roadmap completion metadata를 보고하고 직접 roadmap을 수정하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-external-integration/`를 제거했다.

## 계획 대비 변경 사항

- Expected files per plan: `notification/service.go`, `notification/service_test.go`, `adapters/mattermost/task_notification_sink.go`+tests, `cmd/server/main.go`
- No README changes needed (docs consistent with code).
- No roadmap modified.

## 주요 설계 결정

- `notification.Service` constructor changed from `NewService(mattermostClient, logger, sinks...)` to `NewService(logger, sinks...)`
- `TaskNotificationSink` moved into `internal/adapters/mattermost/` so it can import both `mattermost.Client` and `notification.TaskEventSink` without cycle
- `main.go` wires mattermost sink: `NewTaskNotificationSink(mattermostClient)` as first provider-neutral sink

## 수정 파일 목록

| 파일 | 변경 내용 |
|------|------|
| `services/core/internal/notification/service.go` | Removed mattermost import, `*mattermost.Client` field; constructor simplified |
| `services/core/internal/notification/service_test.go` | Removed mattermost import; tests use only fake recording sinks |
| `services/core/internal/adapters/mattermost/task_notification_sink.go` | NEW - provides `TaskNotificationSink` implementing `TaskEventSink` |
| `services/core/internal/adapters/mattermost/task_notification_sink_test.go` | NEW - tests for `TaskNotificationSink` |
| `services/core/cmd/server/main.go` | Wiring updated: `NewService(logger, mattermost.NewTaskNotificationSink(client), ...)` |
| `services/core/internal/scheduler/jobs_test.go` | Updated `NewService` call signatures |

## 검증 결과

### B1-1 predecessor evidence

```text
$ test -f agent-task/archive/2026/06/m-external-integration/01_plane_adapter/complete.log
(exited 0)
$ test -f agent-task/archive/2026/06/m-external-integration/02_jira_adapter/complete.log
(exited 0)
$ test -f agent-task/archive/2026/06/m-external-integration/03_mattermost_adapter/complete.log
(exited 0)
```

3개 모두 archive에 존재 - 선행 조건 충족.

### B1-2 strict boundary search (post-fix)

```text
$ rg --sort path -n "internal/adapters/(plane|jira|mattermost)|mattermost\.TaskNotificationInput|\*mattermost\.Client|plane\.WorkItemRef|jira\.IssueRef" services/core/internal/notification services/core/internal/workitem services/core/internal/workitempipeline services/core/internal/http services/core/internal/scheduler
(no output)
```

Search exits 1 (no results) - no adapter imports or DTO references in non-adapter packages. Boundary violation resolved.

### B1-3 broad leakage search (post-fix)

```text
$ rg --sort path -n "plane\\.|jira\\.|mattermost\\.|WorkItemRef|IssueRef|comment_html|/api/v4/posts|/rest/api/3" services/core/internal --glob '!**/adapters/**'
services/core/internal/config/config_test.go:97:	t.Setenv("JIRA_BASE_URL", "https://jira.example.com")
services/core/internal/config/config_test.go:103:	if cfg.JiraBaseURL != "https://jira.example.com" {
services/core/internal/config/config_test.go:115:	t.Setenv("MATTERMOST_BASE_URL", "https://mattermost.example.com")
services/core/internal/config/config_test.go:121:	if cfg.MattermostBaseURL != "https://mattermost.example.com" {
services/core/internal/http/handlers_test.go:32:		ExternalURL:   "  https://plane.example/work-1  ",
services/core/internal/http/handlers_test.go:50:	if ref.URL != "https://plane.example/work-1" {
services/core/internal/workflow/service_test.go:16:		URL:      " https://plane.example/work-item-1 ",
services/core/internal/workflow/service_test.go:27:	if ref.URL == nil || *ref.URL != "https://plane.example/work-item-1" {
```

Classification: all intentional config/test data only. No boundary violations.

### B3-1 final verification

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

$ cd services/core && go vet ./...
(no output)

$ git diff --name-only
agent-task/m-external-integration/04+01,02,03_adapter_boundary/CODE_REVIEW-local-G06.md
services/core/cmd/server/main.go
services/core/internal/notification/service.go
services/core/internal/notification/service_test.go
services/core/internal/scheduler/jobs_test.go
services/core/internal/adapters/mattermost/task_notification_sink.go
services/core/internal/adapters/mattermost/task_notification_sink_test.go
```

All changes within follow-up scope. No roadmap files modified.

## 구현 에이전트 자체 점검

**PASS** - Required boundary violation (notification → mattermost direct import) resolved by `REVIEW_BOUNDARY-2`. `notification` package is now fully provider-neutral via `TaskEventSink` sink pattern. All tests pass, no leakage in strict search.

| 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
- 발견된 문제:
  - Nit: `services/core/internal/adapters/mattermost/task_notification_sink.go` had a gofmt-only trailing blank line; reviewer ran `gofmt -w services/core/internal/adapters/mattermost/task_notification_sink.go` and rechecked `gofmt -l` with no output.
- 다음 단계: PASS terminal completion으로 `complete.log`를 작성하고 task directory를 archive로 이동한다.
