# Plan - API Proto-Socket Task Service ## 이 파일을 읽는 구현 에이전트에게 이 plan의 구현이 끝나면 active `CODE_REVIEW-*-G??.md`에서 구현 에이전트 소유 섹션을 실제 구현 내용, 검증 출력, 계획 대비 변경 사항으로 채워야 한다. 최종 archive, `complete.log`, 리뷰 판정은 code-review-skill 전용이다. 구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경/secret 준비, 또는 안전한 진행을 막는 scope 충돌이 있으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정과 근거를 남기고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 메울 수 있는 검증 공백만으로는 사용자 리뷰 요청을 만들지 않는다. ## 배경 Core task channel은 `task.create`, `task.list`, `task.get`, `task.enqueue`, `task.status.changed`를 proto-socket envelope로 제공한다. Flutter client는 transport가 생긴 뒤에도 task 도메인 모델과 request parsing 경계가 없으면 화면에서 mock data를 대체할 수 없다. 이 작업은 UI를 바꾸지 않고 client service 경계만 만든다. ## 사용자 리뷰 요청 흐름 구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.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-test/local/mobile-smoke.md` - `apps/client/lib/src/integrations/proto_socket/proto_socket_lifecycle.dart` - `apps/client/lib/src/features/workspaces/domain/project_workspace.dart` - `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart` - `apps/client/test/widget_test.dart` - `packages/contracts/notes/flutter-core-api-candidates.md` - `services/core/internal/protosocket/tasks.go` ### 테스트 커버리지 공백 - No existing Flutter test covers task channel request creation. - No existing Flutter test covers Core error envelope mapping to a client exception. - Widget tests use `mockProjectWorkspaces` only; they do not exercise task service consumption. ### 심볼 참조 - No renamed or removed symbols planned. - New service should depend on the `ProtoSocketTransport` produced by `01_client_transport`. ### 분할 판단 Split policy was evaluated before writing files. This subtask depends on `01_client_transport` because it needs `ProtoSocketEnvelope` and `ProtoSocketTransport`. UI integration remains in `03+02_workspace_integration` so service parsing can be reviewed independently. ### 범위 결정 근거 This task does not change `WorkspaceHomePage` rendering or app bootstrap. It does not create Core APIs, new REST endpoints, or persistence. It only creates a Flutter task channel client and tests using fake transport envelopes. ### 빌드 등급 Build lane `cloud-G06`, review lane `cloud-G06`: protocol response parsing and error mapping with moderate blast radius, but no UI or platform runner changes. ## 구현 체크리스트 - [ ] [API-1] Add client task models matching the implemented Core task channel response shape. - [ ] [API-2] Add `ProtoSocketTaskService` that calls `task.list`, `task.get`, `task.enqueue`, and optional `task.create` through `ProtoSocketTransport`. - [ ] [API-3] Add task channel service tests for success, missing payload, and error envelope mapping. - [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. ## [API-1] Task Models ### 문제 The workspace domain model only has `ProjectWorkspace` and a mock count at `apps/client/lib/src/features/workspaces/domain/project_workspace.dart:3-18`. The Core task channel returns full task maps described in `packages/contracts/notes/flutter-core-api-candidates.md:97-108`. ### 해결 방법 Add `apps/client/lib/src/features/workspaces/domain/workspace_task.dart`. Before (`apps/client/lib/src/features/workspaces/domain/project_workspace.dart:3-18`): ```dart class ProjectWorkspace { final String id; final String name; final String description; final ProjectStatus status; final String codeServerUrl; final int tasksCount; } ``` After: ```dart class WorkspaceTask { final String id; final String title; final String status; final String source; final String? error; const WorkspaceTask({ required this.id, required this.title, required this.status, required this.source, this.error, }); factory WorkspaceTask.fromMap(Map map) { return WorkspaceTask( id: map['id'] as String? ?? '', title: map['title'] as String? ?? 'Untitled task', status: map['status'] as String? ?? 'unknown', source: map['source'] as String? ?? '', error: map['error'] as String?, ); } } ``` ### 수정 파일 및 체크리스트 - [ ] Add `apps/client/lib/src/features/workspaces/domain/workspace_task.dart`. - [ ] Keep model tolerant of missing optional fields returned by partial task maps. ### 테스트 작성 Cover `WorkspaceTask.fromMap` in the service test file, using a full map and a minimal map. ### 중간 검증 ```bash cd apps/client && flutter test test/integrations/proto_socket_task_service_test.dart ``` Expected: task model tests pass. ## [API-2] Task Channel Service ### 문제 Core registers task actions at `services/core/internal/protosocket/tasks.go:32-36`, but Flutter has no client API for sending those actions through the transport. ### 해결 방법 Add `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`. Before (`services/core/internal/protosocket/tasks.go:32-36`): ```go dispatcher.Register("task.create", c.handleCreate) dispatcher.Register("task.list", c.handleList) dispatcher.Register("task.get", c.handleGet) dispatcher.Register("task.enqueue", c.handleEnqueue) ``` After: ```dart class ProtoSocketTaskService { final ProtoSocketTransport _transport; ProtoSocketTaskService(this._transport); Future> listTasks({int limit = 20}) async { final response = await _transport.sendRequest( ProtoSocketEnvelope( id: _newMessageId(), type: 'request', channel: 'task', action: 'task.list', payload: {'limit': limit}, ), ); _throwIfError(response); final rawTasks = response.payload['tasks']; if (rawTasks is! List) return const []; return rawTasks .whereType() .map((item) => WorkspaceTask.fromMap(Map.from(item))) .toList(growable: false); } } ``` Also add `getTask`, `enqueueTask`, and `createTask` only if their signatures stay thin wrappers around the implemented Core actions. Do not invent workspace/project metadata APIs. ### 수정 파일 및 체크리스트 - [ ] Add `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart`. - [ ] Add `ProtoSocketTaskException` with `code`, `message`, and `retryable`. - [ ] Use the Core action names exactly: `task.list`, `task.get`, `task.enqueue`, `task.create`. ### 테스트 작성 Write fake transport tests for: - `listTasks` sends `{limit}` and parses `tasks`. - `enqueueTask` returns the updated task from `payload.task`. - error envelope `task.not_found` throws `ProtoSocketTaskException`. - malformed success payload returns empty list or throws a service exception consistently; choose one and document in test name. ### 중간 검증 ```bash cd apps/client && flutter test test/integrations/proto_socket_task_service_test.dart ``` Expected: all task service tests pass. ## [API-3] Service Test Coverage ### 문제 Without service-level tests, later UI work could silently depend on mock data or incorrectly shaped envelopes. ### 해결 방법 Add `apps/client/test/integrations/proto_socket_task_service_test.dart` with a fake `ProtoSocketTransport` that records requests and returns queued `ProtoSocketEnvelope` responses. Do not use network sockets. ### 수정 파일 및 체크리스트 - [ ] Add fake transport fixture. - [ ] Assert outgoing envelope `type`, `channel`, `action`, and `payload`. - [ ] Assert error envelope mapping includes `retryable`. ### 테스트 작성 This item is the test implementation for API-1 and API-2. ### 중간 검증 ```bash cd apps/client && flutter test test/integrations/proto_socket_task_service_test.dart ``` Expected: all task service tests pass. ## 의존 관계 및 구현 순서 Directory dependency `02+01_task_service` means implementation must start only after `agent-task/m-proto-socket-infrastructure-communication-rail/01_client_transport/complete.log` exists. ## 수정 파일 요약 | 파일 | 항목 | |------|------| | `apps/client/lib/src/features/workspaces/domain/workspace_task.dart` | API-1 | | `apps/client/lib/src/integrations/proto_socket/proto_socket_task_service.dart` | API-2 | | `apps/client/test/integrations/proto_socket_task_service_test.dart` | API-1, API-2, API-3 | ## 최종 검증 ```bash test -f agent-task/m-proto-socket-infrastructure-communication-rail/01_client_transport/complete.log cd apps/client && flutter test test/integrations/proto_socket_task_service_test.dart cd apps/client && flutter analyze --no-fatal-infos ``` Expected: predecessor complete.log exists; task service tests pass; analyzer has no fatal issues. Cached Flutter test output is acceptable only if no implementation files changed after the run. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.