# 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 _stateController = 34 StreamController.broadcast(); ... 43 ProtoSocketConnectionState get state => _state; 44 Stream 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 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.