<!-- task=m-proto-socket-infrastructure-communication-rail/13+11,12_client_diagnostics plan=0 tag=API -->

# Plan - API Client Proto-Socket Diagnostics

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 반드시 채운다. 구현 후 검증을 실행하고 active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거와 재개 조건을 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

마일스톤의 마지막 남은 기능 Task는 connection id, protocol version, channel name, error code를 로그와 디버그 표면에서 확인 가능하게 하는 것이다. Core가 diagnostics meta를 보내면 Flutter client는 연결 상태와 최근 channel/error 정보를 비밀 없이 보여줘야 한다. 이 subtask는 contracts와 core diagnostics가 완료된 뒤 client lifecycle, task service, workspace 화면, Flutter tests를 닫는다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사유는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`를 기준으로 하며, code-review가 검증 후 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/workflow-core/milestones/proto-socket-infrastructure-communication-rail.md`
- Task ids:
  - `diagnostics`: connection id, protocol version, channel name, error code를 로그와 디버그 표면에서 확인할 수 있게 한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/workflow-core/PHASE.md`
- `agent-roadmap/phase/workflow-core/milestones/proto-socket-infrastructure-communication-rail.md`
- `agent-ops/rules/project/domain/mobile/rules.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/mobile-smoke.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `apps/client/pubspec.yaml`
- `apps/client/analysis_options.yaml`
- `apps/client/lib/src/app/bootstrap.dart`
- `apps/client/lib/src/app/nomadcode_client_app.dart`
- `apps/client/lib/src/integrations/proto_socket/proto_socket_client.dart`
- `apps/client/lib/src/integrations/proto_socket/proto_socket_endpoint_config.dart`
- `apps/client/lib/src/integrations/proto_socket/proto_socket_envelope.dart`
- `apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart`
- `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`
- `apps/client/lib/src/features/workspaces/domain/project_workspace.dart`
- `apps/client/lib/src/features/workspaces/domain/workspace_task.dart`
- `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart`
- `apps/client/test/integrations/proto_socket_endpoint_config_test.dart`
- `apps/client/test/integrations/proto_socket_envelope_test.dart`
- `apps/client/test/integrations/proto_socket_lifecycle_test.dart`
- `apps/client/test/integrations/proto_socket_task_service_test.dart`
- `apps/client/test/widget_test.dart`
- `packages/contracts/notes/flutter-core-api-candidates.md`
- `services/core/internal/protosocket/server.go`
- `services/core/internal/protosocket/events.go`
- `services/core/internal/protosocket/tasks_test.go`

### 테스트 환경 규칙

`test_env=local`로 판단했고 `agent-test/local/rules.md`를 읽었다. 이 파일은 로컬 테스트 금지를 명시하므로 현 세션에서는 검증 명령을 실행하지 않는다. 적용 profile은 `mobile-smoke`이며 mobile 변경의 필수 검증은 `cd apps/client && flutter test`, analyzer 영향이 있으므로 `cd apps/client && flutter analyze --no-fatal-infos`도 필요하다. `apps/client`는 `../../../proto-socket/dart`와 `../../../nexo/packages/messaging_flutter` path dependency가 있어 후속 환경에 sibling workspace가 필요하다.

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

- lifecycle diagnostics stream/snapshot: 기존 `proto_socket_lifecycle_test.dart`는 state와 lastError만 검증한다. diagnostics snapshot 테스트 추가 필요.
- task response/error diagnostics capture: 기존 `proto_socket_task_service_test.dart`는 error mapping만 검증한다. response meta/error code를 diagnostics sink에 기록하는 테스트 추가 필요.
- UI debug surface: 기존 `widget_test.dart`는 service task rendering과 connection lifecycle만 검증한다. connection id/protocol/channel/error code display 테스트 추가 필요.

### 심볼 참조

none. 기존 public symbol rename/remove 없이 optional diagnostics API를 추가한다.

### 분할 판단

split decision policy를 평가했다. 이 subtask는 `13+11,12_client_diagnostics`이며 predecessor는 `11_contracts_diagnostics`, `12+11_core_diagnostics`다. 현재 두 predecessor 모두 아직 `complete.log`가 없으므로 구현은 둘 다 PASS된 뒤 시작해야 한다.

### 범위 결정 근거

Flutter client diagnostics, debug surface, Flutter tests만 수정한다. Core response/event meta 구현은 `12+11_core_diagnostics` 범위다. Project Workspace Management UX 전체 완성, workspace metadata API, external provider integration, Mattermost push native 로직은 범위에서 제외한다.

### 빌드 등급

`cloud-G07`: client lifecycle, service boundary, UI rendering, protocol diagnostics를 함께 다루고 predecessor output에 의존하므로 cloud lane이 적합하다.

## 구현 체크리스트

- [ ] `11_contracts_diagnostics`와 `12+11_core_diagnostics`가 PASS되어 각 `complete.log`가 생겼는지 확인하고 경로를 review stub에 기록한다.
- [ ] `ProtoSocketLifecycle` 또는 dedicated diagnostics controller가 connection state, connection id, protocol version, recent channel/action/error code를 비밀 없이 유지하게 한다.
- [ ] `ProtoSocketTaskService`가 response/error envelope meta를 diagnostics sink에 전달하고 기존 error mapping을 유지한다.
- [ ] `WorkspaceHomePage` 또는 app shell에 compact debug surface를 추가해 connection id, protocol version, channel, error code를 확인할 수 있게 한다.
- [ ] Flutter tests로 endpoint 설정, reconnect/failure lifecycle, task service diagnostics, widget debug surface를 검증한다.
- [ ] `cd apps/client && flutter test`를 허용된 원격/검증 환경에서 실행한다.
- [ ] `cd apps/client && flutter analyze --no-fatal-infos`를 허용된 원격/검증 환경에서 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

이 directory name은 `13+11,12_client_diagnostics`이므로 predecessor는 같은 task group의 `11_*`, `12+11_*` subtasks다. `agent-task/m-proto-socket-infrastructure-communication-rail/11_contracts_diagnostics/complete.log`와 `agent-task/m-proto-socket-infrastructure-communication-rail/12+11_core_diagnostics/complete.log` 또는 matching archive `complete.log`가 모두 생긴 뒤 구현을 시작한다.

### [API-1] Client Diagnostics State

#### 문제

[apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart](/config/workspace/nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart:31)는 state와 lastError만 보관하고, connection id/protocol/channel/error code를 소비할 diagnostics state가 없다. [apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart](/config/workspace/nomadcode/apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart:45)는 response/error envelope를 받은 뒤 diagnostics sink에 전달하지 않는다.

Before:

```dart
31	class ProtoSocketLifecycle {
32	  final ProtoSocketConnector _connector;
33	  final StreamController<ProtoSocketConnectionState> _stateController =
34	      StreamController<ProtoSocketConnectionState>.broadcast();
...
43	  ProtoSocketConnectionState get state => _state;
44	  Stream<ProtoSocketConnectionState> get stateStream => _stateController.stream;
45	  Object? get lastError => _lastError;
46	  ProtoSocketTransport? get transport => _transport;
```

#### 해결 방법

작은 value object를 추가한다. 새 파일을 만들 경우 `apps/client/lib/src/integrations/proto_socket/proto_socket_diagnostics.dart`에 두고, lifecycle은 `diagnostics` getter와 broadcast stream을 제공한다. Task service에는 optional `void Function(ProtoSocketEnvelope envelope) onEnvelope` 또는 diagnostics sink를 주입해 response/error meta를 기록한다.

After:

```dart
class ProtoSocketDiagnostics {
  final ProtoSocketConnectionState state;
  final String? connectionId;
  final String? protocolVersion;
  final String? channel;
  final String? action;
  final String? errorCode;
}

Stream<ProtoSocketDiagnostics> get diagnosticsStream => _diagnosticsController.stream;
void recordEnvelope(ProtoSocketEnvelope envelope) { ... }
```

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

- [ ] `apps/client/lib/src/integrations/proto_socket/proto_socket_diagnostics.dart`: diagnostics value/sanitizer 추가.
- [ ] `apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart`: diagnostics state/stream/recording 추가.
- [ ] `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`: response/error envelope를 diagnostics sink에 전달.
- [ ] `apps/client/test/integrations/proto_socket_lifecycle_test.dart`: state/failure/envelope diagnostics 테스트 추가.
- [ ] `apps/client/test/integrations/proto_socket_task_service_test.dart`: meta/error code recording 테스트 추가.

#### 테스트 작성

`proto_socket_lifecycle_test.dart`는 connect/fail/disconnect 시 diagnostics state가 바뀌는지 검증한다. `proto_socket_task_service_test.dart`는 response meta의 `connection_id`, `protocol_version`, `channel`, `action`과 error envelope의 `error.code`가 sink에 전달되는지 검증한다.

#### 중간 검증

```bash
cd apps/client && flutter test test/integrations/proto_socket_lifecycle_test.dart test/integrations/proto_socket_task_service_test.dart
```

기대 결과: 관련 Flutter tests PASS. 명령은 허용된 원격/검증 환경에서 실행한다.

### [API-2] Workspace Debug Surface

#### 문제

[apps/client/lib/src/app/nomadcode_client_app.dart](/config/workspace/nomadcode/apps/client/lib/src/app/nomadcode_client_app.dart:121)는 task service만 page에 연결하고 diagnostics는 전달하지 않는다. [apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart](/config/workspace/nomadcode/apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart:374)는 task section과 error UI만 있고 connection diagnostics surface가 없다.

Before:

```dart
121	      final taskService = ProtoSocketTaskService(transport);
122	      setState(() {
123	        _taskLoader = (_) => taskService.listTasks();
124	        _connectedByThisWidget = startedHere;
125	      });
```

#### 해결 방법

App state가 lifecycle diagnostics stream을 구독해 `WorkspaceHomePage`에 snapshot을 넘긴다. Workspace details 상단 또는 ACTIVE TASKS 근처에 compact diagnostics row를 추가한다. 화면에는 `connected/failed`, `conn-*`, protocol version, recent `channel/action`, optional `error_code`만 표시하고 payload/auth/error message 원문은 표시하지 않는다.

After:

```dart
_diagnosticsSub = lifecycle.diagnosticsStream.listen((next) {
  if (mounted) setState(() => _protoSocketDiagnostics = next);
});

return WorkspaceHomePage(
  loadTasks: _taskLoader,
  protoSocketDiagnostics: _protoSocketDiagnostics,
);
```

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

- [ ] `apps/client/lib/src/app/nomadcode_client_app.dart`: diagnostics stream 구독/해제와 page 전달 추가.
- [ ] `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart`: compact debug surface 추가.
- [ ] `apps/client/test/widget_test.dart`: connected diagnostics와 error code surface 테스트 추가.

#### 테스트 작성

`widget_test.dart`에 lifecycle diagnostics fake 또는 direct diagnostics prop을 사용해 connection id/protocol/channel/error code가 보이고 secret/payload text는 보이지 않는지 검증한다.

#### 중간 검증

```bash
cd apps/client && flutter test test/widget_test.dart
```

기대 결과: widget tests PASS. 명령은 허용된 원격/검증 환경에서 실행한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/client/lib/src/integrations/proto_socket/proto_socket_diagnostics.dart` | API-1 |
| `apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart` | API-1 |
| `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart` | API-1 |
| `apps/client/lib/src/app/nomadcode_client_app.dart` | API-2 |
| `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart` | API-2 |
| `apps/client/test/integrations/proto_socket_lifecycle_test.dart` | API-1 |
| `apps/client/test/integrations/proto_socket_task_service_test.dart` | API-1 |
| `apps/client/test/widget_test.dart` | API-2 |

## 최종 검증

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

기대 결과: Flutter tests PASS, analyzer는 fatal 없이 종료한다. path dependency가 없으면 review stub에 실제 차단 출력을 기록한다. 명령은 허용된 원격/검증 환경에서 실행한다.

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