<!-- task=m-forgejo-branch-event-mvp/01_consumer_smoke plan=1 tag=REVIEW_TEST -->

# Plan - REVIEW_TEST

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

구현이 끝났다고 판단하기 전에 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 채운다. 검증 명령을 실행하고 출력 원문을 붙인 뒤 active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 최종 판정, log archive, `complete.log`, task directory archive 이동은 code-review-skill 전용이다.

구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경/secret/service 준비, 또는 계획 충돌이 발생하면 active review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 채우고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과/계획 대비 변경 사항에 기록한다.

## 배경

1차 구현은 NomadCode branch event consumer fixture를 추가했지만 review에서 세 가지 Required 문제가 발견되었다. `event.subscribe` error envelope가 성공처럼 처리되고, subscribed repo/branch와 다른 event가 emit될 수 있으며, malformed `changed_files` 항목이 조용히 누락될 수 있다. 이 follow-up은 기존 consumer smoke 범위 안에서 해당 실패만 보완한다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사유는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`에서 복사한 형식을 사용하며, 구현 에이전트는 직접 사용자 prompt를 띄우지 않는다. code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-foundation/milestones/forgejo-branch-event-mvp.md`
- Task ids:
  - `consumer-smoke`: `../nomadcode`의 proto-socket client가 `branch.updated`를 수신하는 smoke 또는 fixture를 추가한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-task/m-forgejo-branch-event-mvp/01_consumer_smoke/plan_cloud_G06_0.log`
- `agent-task/m-forgejo-branch-event-mvp/01_consumer_smoke/code_review_cloud_G06_0.log`
- `agent-contract/index.md`
- `agent-contract/provided/gito-forgejo-branch-events-v1.md`
- `services/core/internal/controlplane/runtime.go`
- `services/core/internal/controlplane/router.go`
- `services/core/internal/protosocket/envelope.go`
- `services/core/internal/protosocket/subscription.go`
- `services/core/internal/protosocket/server.go`
- `services/core/internal/protosocket/dispatcher.go`
- `../nomadcode/AGENTS.md`
- `../nomadcode/agent-ops/rules/project/rules.md`
- `../nomadcode/agent-ops/rules/private/rules.md`
- `../nomadcode/agent-ops/rules/common/rules-roadmap.md`
- `../nomadcode/agent-ops/rules/project/domain/mobile/rules.md`
- `../nomadcode/agent-ops/rules/project/domain/contracts/rules.md`
- `../nomadcode/agent-test/local/rules.md`
- `../nomadcode/agent-test/local/mobile-smoke.md`
- `../nomadcode/agent-test/local/contracts-smoke.md`
- `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart`
- `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_client.dart`
- `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_envelope.dart`
- `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart`
- `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`
- `../nomadcode/apps/client/test/integrations/proto_socket_branch_event_service_test.dart`
- `../nomadcode/apps/client/test/integrations/proto_socket_task_service_test.dart`
- `../nomadcode/packages/contracts/notes/flutter-core-api-candidates.md`

### 테스트 환경 규칙

- `test_env=local`.
- Gito local rules와 workspace-ops smoke를 읽었다. 이 task artifact 갱신은 `git diff --check`로 whitespace를 확인한다.
- NomadCode local rules, mobile-smoke, contracts-smoke를 읽었다. `apps/client/**` 변경은 focused `flutter test`와 `flutter analyze --no-fatal-infos`를 실행한다. `packages/contracts/**`는 자동 test가 없으므로 경로/링크 일관성을 수동 확인하고 `git diff --check`를 실행한다.
- 구현자는 현재 checkout에서 같은 명령을 실행해 실제 stdout/stderr를 review stub에 붙인다.

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

- `event.subscribe` error envelope handling: 기존 테스트 없음. 새 regression test가 필요하다.
- subscribed repo/branch mismatch filtering: 기존 테스트는 channel/action/type mismatch만 검증한다. repo/branch payload mismatch test가 필요하다.
- malformed `changed_files` list item handling: 기존 테스트는 top-level missing `repo_id`만 검증한다. list 내부 invalid item test가 필요하다.

### 심볼 참조

- 이름 변경은 계획하지 않는다.
- `ProtoSocketBranchEventService` 신규 참조는 focused test뿐이다.
- `ProtoSocketTransport.sendRequest` error envelope 패턴은 `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`와 해당 tests에서 확인했다.

### 분할 판단

split decision policy를 평가했다. 이 follow-up은 기존 split subtask `01_consumer_smoke` 내부의 review 실패 보완이며, 새 ownership boundary나 선행 작업을 만들지 않는다. sibling `02+01_watch_store`는 이 subtask PASS 이후 진행되는 별도 작업이므로 현재 follow-up에 포함하지 않는다.

### 범위 결정 근거

- Gito core/server 구현은 수정하지 않는다. server-side subscription filtering은 이미 `EventSubscription.Allows`와 broadcast path에서 확인했다.
- NomadCode UI, Plane/roadmap mutation, bootstrap auto-connect는 수정하지 않는다.
- contracts note는 본문 복제 없이 현재 pointer를 유지하고, 후속 구현에서 필요한 경우 오탈자/경로만 보정한다.

### 빌드 등급

- build lane은 `cloud-G06`, review lane은 `cloud-G06`이다. 수정 범위는 작지만 cross-repo proto-socket contract와 event consumer semantics를 함께 판단해야 한다.

## 구현 체크리스트

- [ ] [REVIEW_TEST-1] `event.subscribe` error envelope를 실패로 처리하고 regression test를 추가한다.
- [ ] [REVIEW_TEST-2] subscribed repo/branch와 다른 `branch.updated` event를 emit하지 않도록 필터링하고 test로 고정한다.
- [ ] [REVIEW_TEST-3] malformed `changed_files` 원소를 조용히 버리지 않도록 처리하고 test로 고정한다.
- [ ] NomadCode focused Flutter tests, analyzer, NomadCode/Gito diff check를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_TEST-1] Subscribe Error Envelope Handling

#### 문제

`../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart:116-127`은 `sendRequest` 응답을 무시한다. Gito dispatcher는 `event.subscribe` 실패를 error envelope로 반환할 수 있으므로, 현재 코드는 구독 실패를 성공처럼 숨긴다.

#### 해결 방법

기존 `ProtoSocketTaskService` 패턴처럼 branch event service에 typed exception을 추가하거나 명확한 exception mapping을 둔다. `subscribe`는 response를 받은 뒤 `response.error`가 있으면 throw하고, 성공 응답일 때만 subscription filter state를 갱신한다.

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

- [ ] `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart`에 subscribe response error handling을 추가한다.
- [ ] `../nomadcode/apps/client/test/integrations/proto_socket_branch_event_service_test.dart`에 error envelope regression test를 추가한다.
- [ ] 실패 응답을 받은 subscribe가 필터를 등록하지 않는지 확인한다.

#### 테스트 작성

`subscribe throws when Core returns an error envelope` test를 추가한다. fake transport가 `ProtoSocketEnvelopeError(code: 'event.missing_connection', retryable: false)`를 응답하면 `subscribe`가 typed exception 또는 명확한 service exception을 throw해야 한다.

#### 중간 검증

```bash
cd ../nomadcode/apps/client && flutter test test/integrations/proto_socket_branch_event_service_test.dart
```

기대 결과: branch event service focused tests가 PASS한다.

### [REVIEW_TEST-2] Repo/Branch Mismatch Filtering

#### 문제

`../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart:135-143`은 envelope의 `type/channel/action`만 확인한다. plan의 TEST-3 세부 체크리스트는 mismatched repo/branch filtering을 요구했지만, 현재 테스트는 `branch.created`, `task` channel, `response` type만 검증한다.

#### 해결 방법

`subscribe(repoId, branch)` 성공 후 구독 filter를 service 내부에 기록한다. `_handleEnvelope`는 `BranchUpdatedEvent`를 parse한 뒤 등록된 filter가 있으면 `event.repoId`와 `event.branch`가 하나라도 일치할 때만 emit한다. 여러 번 subscribe할 수 있으므로 filter collection으로 처리한다. 아직 성공한 subscribe가 없을 때의 동작은 test로 고정한다.

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

- [ ] `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart`에 subscribed repo/branch filter를 추가한다.
- [ ] `../nomadcode/apps/client/test/integrations/proto_socket_branch_event_service_test.dart`에 matching event는 emit, non-matching repo/branch event는 ignore하는 test를 추가한다.
- [ ] error subscribe는 filter를 추가하지 않는다.

#### 테스트 작성

`emits only subscribed repo and branch events` test를 추가한다. `subscribe(repoId: 'nomadcode', branch: 'develop')` 이후 `repo_id: 'other'` 또는 `branch: 'main'` payload는 emit되지 않고, matching payload만 emit되어야 한다.

#### 중간 검증

```bash
cd ../nomadcode/apps/client && flutter test test/integrations/proto_socket_branch_event_service_test.dart
```

기대 결과: branch event service focused tests가 PASS한다.

### [REVIEW_TEST-3] Strict Changed Files Parsing

#### 문제

`../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart:79-82`는 `rawFiles.whereType<Map>()`로 list 내부의 잘못된 원소를 조용히 버린다. `changed_files`는 후속 sync 관련성을 판단하는 근거라서 부분 손실을 성공 event로 emit하면 안 된다.

#### 해결 방법

`changed_files`를 순회할 때 모든 원소가 map인지 확인하고, 아니면 `FormatException`으로 처리한다. map 원소 안의 `path` 또는 `change_type`이 잘못된 경우도 기존 `ChangedFile.fromMap` 예외가 stream error로 전달되는지 test로 고정한다.

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

- [ ] `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart`의 `changed_files` parsing을 strict하게 바꾼다.
- [ ] `../nomadcode/apps/client/test/integrations/proto_socket_branch_event_service_test.dart`에 invalid `changed_files` item test를 추가한다.
- [ ] malformed payload는 event emit 없이 stream error로 전달되는 정책을 유지한다.

#### 테스트 작성

`emits error when changed_files contains a malformed item` test를 추가한다. `changed_files: ['not-a-map']` 또는 `{'path': 'README.md'}`를 넣었을 때 event는 emit되지 않고 `FormatException`이 전달되어야 한다.

#### 중간 검증

```bash
cd ../nomadcode/apps/client && flutter test test/integrations/proto_socket_branch_event_service_test.dart
```

기대 결과: branch event service focused tests가 PASS한다.

## 수정 파일 요약

| 파일 | 항목 |
| --- | --- |
| `../nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_branch_event_service.dart` | REVIEW_TEST-1, REVIEW_TEST-2, REVIEW_TEST-3 |
| `../nomadcode/apps/client/test/integrations/proto_socket_branch_event_service_test.dart` | REVIEW_TEST-1, REVIEW_TEST-2, REVIEW_TEST-3 |

## 최종 검증

```bash
cd ../nomadcode/apps/client && flutter test test/integrations/proto_socket_branch_event_service_test.dart test/integrations/proto_socket_envelope_test.dart test/integrations/proto_socket_lifecycle_test.dart
cd ../nomadcode/apps/client && flutter analyze --no-fatal-infos
cd ../nomadcode && git diff --check
git diff --check
```

기대 결과: focused Flutter tests PASS, analyzer PASS 또는 기존 Mattermost `avoid_print` non-fatal info만 유지, NomadCode/Gito diff whitespace check PASS. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
