- Add proto socket client and envelope implementations - Add proto socket lifecycle integration tests - Add task service PLAN and CODE_REVIEW documents - Add workspace integration PLAN and CODE_REVIEW documents - Update bootstrap with proto socket integration - Update pubspec dependencies and lock file - Update Flutter core API candidates documentation - Update workflow core milestone tracking
15 KiB
Flutter/Core API Contract Candidates
이 문서는 source schema가 아니라 Flutter-first 전환 중 공유 계약 후보를 모으는 compatibility note다.
실제 Go core 구현(services/core)과 향후 작성될 Flutter 클라이언트(apps/client) 간의 인터페이스 정합성을 보장하기 위한 임시 계약 후보이며, 단일 source-of-truth 스키마가 정의되기 전까지의 과도기적 스펙을 정의한다.
API 안정성 레벨 정의
- 안정 (Stable): 인터페이스와 데이터 포맷이 고정되어 있어 즉시 연동에 사용할 수 있음.
- 후보 (Candidate): 연동 후보 스펙으로, 실연동 과정에서 필드가 추가되거나 변경될 수 있음.
1. 내부 통신 원칙
1.1 기본 표준
- 설명: NomadCode 내부 런타임 간 신규 통신은
proto-socket을 기본 표준으로 사용한다. - 안정성 수준: 후보 (Candidate)
- 적용 대상:
apps/client↔services/core- NomadCode Core ↔ 내부 control/runtime adapter
- 향후 NomadCode가 직접 소유하는 MCP/action runtime 표면
- 예외 대상:
- health/readiness처럼 외부 운영 도구와 curl smoke가 직접 확인해야 하는 endpoint
- Plane/Jira/Mattermost/IOP/OpenAI-compatible Responses/A2A처럼 외부 provider 또는 외부 실행 표면이 요구하는 native protocol
- public callback/webhook
- 단순 정적 조회처럼
proto-socket연결 비용이 과한 read-only endpoint
- 예외 기록 규칙: REST/HTTP를 새로 추가하거나 유지할 때는 이 문서 또는 관련 domain rule에 예외 사유를 남긴다.
1.2 proto-socket Semantic Envelope 후보
- 설명: 아래 JSON은 wire schema가 아니라 proto-socket message가 가져야 할 의미 필드 후보다. 실제 source schema는 proto-socket 계약이 안정화되면 별도 schema로 승격한다.
- Envelope Shape 후보:
{ "protocol_version": "nomadcode.proto-socket.v1", "id": "msg-uuid", "correlation_id": "request-or-flow-id", "type": "request", "channel": "task", "action": "task.list", "actor": { "kind": "client", "id": "apps/client" }, "auth": { "scheme": "basic", "token_ref": "runtime-secret-ref" }, "payload": {}, "error": null, "meta": { "connection_id": "conn-id", "timestamp": "2026-05-30T00:00:00Z" } } - type 후보:
request: 요청 메시지response: 요청 성공 응답event: 구독 또는 서버 발행 이벤트error: 요청 실패 또는 channel-level 오류heartbeat: 연결 생존 확인auth: 연결 인증 또는 인증 갱신
- error 후보 필드:
{ "code": "task.not_found", "message": "task not found", "retryable": false, "details": {} }
1.3 REST Compatibility Map 후보
- 설명: 현재 REST API는 proto-socket rail 구현 전까지의 호환 표면이다. task 관련 신규 내부 기능은 proto-socket channel을 우선한다.
| 현재 REST/HTTP 표면 | proto-socket 후보 | 유지/전환 기준 |
|---|---|---|
GET /healthz |
없음 | Public 운영 health check로 REST 유지 |
GET /readyz |
system.ready event 또는 request 후보 |
DB readiness와 curl smoke 때문에 REST 유지, 내부 UI에는 proto-socket 상태 이벤트를 추가 가능 |
POST /api/tasks |
task.create request |
client-core 내부 호출은 proto-socket으로 전환, REST는 smoke/compat 유지 |
GET /api/tasks |
task.list request |
client-core 내부 조회는 proto-socket으로 전환, REST는 smoke/compat 유지 |
GET /api/tasks/{id} |
task.get request |
client-core 내부 조회는 proto-socket으로 전환, REST는 smoke/compat 유지 |
POST /api/tasks/{id}/enqueue |
task.enqueue request + task.status.changed event |
client-core 실행 요청과 상태 반영은 proto-socket으로 전환, REST는 smoke/compat 유지 |
POST /api/integrations/plane/tasks |
workitem.plane.import 후보 |
Plane API 자체는 외부 provider REST이며, 내부 호출 표면은 proto-socket으로 추가 가능 |
1.4 Core Endpoint 후보
- 기본 경로:
/proto-socket - 인증: 기존 Core Basic Auth middleware 적용
- Wire message: 초기 구현은
google.protobuf.Structsemantic envelope를 사용한다. - Heartbeat: proto-socket 기본 heartbeat를 사용하며 Core 설정 기본값은 interval 30s, wait 10s다.
1.5 Task Channel Actions (구현됨)
- 설명:
services/core/internal/protosocket가 REST task API와 같은 의미를taskchannel의 proto-socket action으로 제공한다. REST는 smoke/compat로 유지한다. - 안정성 수준: 후보 (Candidate)
- 채널:
task - 요청/응답 action 표:
| action | request payload | response type | response payload |
|---|---|---|---|
task.create |
POST /api/tasks JSON shape (title, source, payload, metadata?, external?) |
response |
{ "id", "status", "external_provider", "external_id", "task": { …full task } } |
task.list |
{ "limit": 20 } (생략 시 0 전달, 기본 20/최대 100 bounding은 core workflow service가 담당) |
response |
{ "tasks": [ { …full task } ] } |
task.get |
{ "id": "task-id" } |
response |
{ "task": { …full task } } |
task.enqueue |
{ "id": "task-id" } |
response |
{ "id", "status": "queued", "task": { …full task } } |
- task status event (서버 발행):
| action | type | channel | payload |
|---|---|---|---|
task.status.changed |
event |
task |
{ "id", "type": "task.running|task.completed|task.failed|task.canceled", "status", "title", "message" } |
-
scheduler가 내는 모든 task lifecycle event(running/completed/failed/canceled)가 notification fanout을 통해 broadcast된다. completed event의 Mattermost 알림 동작은 그대로 유지된다.
-
error envelope code 표 (
type: "error"):
| code | 의미 | retryable | REST 대응 status |
|---|---|---|---|
task.invalid_input |
잘못된 task 입력(ErrInvalidTaskInput) |
false | 400 |
task.conflict |
현재 상태에서 enqueue 불가(ErrTaskCannotBeEnqueued) |
false | 409 |
task.not_found |
task 없음(pgx.ErrNoRows) |
false | 404 |
task.invalid_payload |
envelope payload 디코드 실패 | false | (REST 없음) |
internal.error |
그 외 내부 오류 | true | 500 |
UNSUPPORTED_ACTION |
dispatcher에 등록되지 않은 action | false | (REST 없음) |
1.6 새 내부 통신 추가 운영 체크
- 신규
apps/client↔services/core또는 내부 runtime 간 통신은 먼저 proto-socket channel/action으로 표현 가능한지 확인한다. - REST/HTTP를 새로 추가하거나 유지해야 하면 health/readiness, 외부 provider 호환 API, 운영 smoke/curl, public callback, 단순 정적 조회 중 어느 예외인지 이 문서 또는 관련 domain rule에 기록한다.
- 새 channel/action은 envelope의
protocol_version,type,channel,action,correlation_id,payload,error.retryable의미를 이 문서에 추가한다. - 클라이언트가 소비하는 request/response/event shape는 구현 전에 후보 표와 테스트 fixture 기준을 남긴다.
- Core 구현은 기존 REST smoke/compat 표면을 제거하지 않고, 내부 UI/런타임 호출만 proto-socket 우선 경로로 전환한다.
- 검증은 최소한 channel/action dispatch, auth boundary, error envelope, event broadcast 또는 reconnect 영향 중 해당되는 항목을 테스트로 고정한다.
2. 공통 및 인프라 API
2.1 GET /healthz
- 설명: 서비스 헬스 체크.
- 인증: 없음 (Public)
- 안정성 수준: 안정 (Stable)
- Response Shape (Status 200 OK):
{ "status": "ok" }
2.2 GET /readyz
- 설명: 서비스 및 데이터베이스 준비 상태 체크.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Response Shape (Status 200 OK):
(Status 503 Service Unavailable):{ "status": "ready" }{ "error": "database is not ready" }
3. 태스크 관리 API (Tasks API)
3.1 POST /api/tasks
- 설명: 새로운 태스크 생성.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Request Shape:
{ "title": "Task Title", "source": "source_identifier", "payload": {}, "metadata": {}, "external": { "provider": "plane", "id": "external-task-id", "url": "https://...", "metadata": {} } } - Response Shape (Status 201 Created):
{ "id": "task-uuid-string", "status": "pending", "external_provider": "plane", "external_id": "external-task-id" }
3.2 GET /api/tasks
- 설명: 태스크 목록 조회.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Query Parameters:
limit(Optional, 기본값 20, 양의 정수)
- Response Shape (Status 200 OK):
[ { "id": "task-uuid-string", "title": "Task Title", "source": "source_identifier", "status": "pending", "payload": {}, "result": {}, "error": null, "created_at": "2026-05-24T12:00:00Z", "updated_at": "2026-05-24T12:00:00Z", "external_provider": "plane", "external_id": "external-task-id", "external_url": "https://...", "external_metadata": {}, "metadata": {} } ]
3.3 GET /api/tasks/{id}
- 설명: 단일 태스크 세부 정보 조회.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Response Shape (Status 200 OK):
{ "id": "task-uuid-string", "title": "Task Title", "source": "source_identifier", "status": "pending", "payload": {}, "result": {}, "error": null, "created_at": "2026-05-24T12:00:00Z", "updated_at": "2026-05-24T12:00:00Z", "external_provider": "plane", "external_id": "external-task-id", "external_url": "https://...", "external_metadata": {}, "metadata": {} }
3.4 POST /api/tasks/{id}/enqueue
- 설명: 태스크를 큐에 진입시켜 실행 대기 상태로 변경.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Response Shape (Status 200 OK):
{ "id": "task-uuid-string", "status": "queued" }
4. 외부 연동 API (Integrations API)
4.1 POST /api/integrations/plane/tasks
- 설명: Plane 워크아이템 정보를 기반으로 새로운 태스크 생성.
- 인증: Basic Auth
- 안정성 수준: 안정 (Stable)
- Request Shape:
{ "workspace_slug": "my-workspace", "project_id": "project-uuid-string", "work_item_id": "work-item-id-string", "state_id": "state-uuid-string", "external_url": "https://...", "comment": "Optional comment" } - Response Shape (Status 201 Created):
{ "id": "task-uuid-string", "status": "pending", "external_provider": "plane", "external_id": "work-item-id-string" }
5. Workspace / Project Metadata 계약 후보
Important
Workspace/Project Metadata 상태: 현재 Go core 백엔드에 이와 관련한 별도의 Source-of-Truth 저장소나 전용 테이블 구조가 존재하지 않으며, API 명세 또한 완전히 확정되지 않았습니다. 아래는 Flutter 앱과 코어 간 향후 연동을 대비한 계약 후보 필드 목록입니다.
5.1 Workspace Metadata 후보 스펙
- 설명: 에이전트 작업 공간(Workspace)에 대한 설정 및 상태 메타데이터.
- 후보 필드:
workspace_id: 작업 공간 고유 식별자 (UUID)workspace_slug: 작업 공간의 URL 친화적 식별자 (예:nomadcode-dev)name: 작업 공간 표시 이름 (예:NomadCode Dev Workspace)owner_id: 소유자 고유 식별자created_at: 생성 시각 (ISO 8601)updated_at: 최종 변경 시각 (ISO 8601)status: 현재 상태 (예:active,archived,suspended)settings: 작업 공간에 특화된 동적 설정 JSON 오브젝트 (예: 자동 스케줄링 옵션, 알림 채널 정보 등)
5.2 Project Metadata 후보 스펙
- 설명: 작업 공간 내 세부 프로젝트(Project) 정보.
- 후보 필드:
project_id: 프로젝트 고유 식별자 (UUID)workspace_id: 해당 프로젝트가 속한 Workspace 식별자name: 프로젝트 표시 이름 (예:Flutter client consolidation)description: 프로젝트 세부 설명provider: 연동 플랫폼 식별자 (예:plane,github)external_project_id: 연동 플랫폼 측 프로젝트 IDstatus: 프로젝트 상태 (예:active,paused)
6. 클라이언트 Integration 설정 계약 후보
Important
이 절은
apps/client의 integration boundary에서 host/plugin/transport 사이에 교환되는 설정값을 후보로 모은 것이다. 실제 source schema는 소비하는 core/client 경계가 안정화된 뒤로 미룬다. 관련 코드 경계:apps/client/lib/src/integrations/proto_socket/와apps/client/lib/src/integrations/mattermost/.
6.1 proto-socket Endpoint 설정 후보
- 설명:
apps/client가 proto-socket transport에 의존할 때 사용하는 연결/하트비트 설정. 현재 구현은proto_socketDart 패키지(local path)를 끌어 쓰며 bootstrap에서 자동 연결하지 않는다. - 후보 필드:
host: 대상 서버 hostnameport: TCP/WebSocket 포트 (기본은secure값에 따라 443 또는 80)secure: TLS 사용 여부 (boolean)path: WebSocket path (기본/)heartbeat_interval_seconds: heartbeat 송신 주기 (기본 30)heartbeat_wait_seconds: heartbeat 응답 대기 한계 (기본 60)enabled: bootstrap 시 자동 연결 활성화 여부 (기본 false)
6.2 Mattermost Push Host 책임 경계 후보
- 설명: Mattermost push 통합에서
apps/clienthost가 plugin adapter에게 위임/공급해야 하는 데이터와 콜백. plugin adapter는 platform 측 plugin singleton에 격리되어 있고, 그 외 코드는 모두MattermostPushClient인터페이스에만 의존한다. - 호스트 소유 필드/콜백:
server_url: Mattermost 서버 base URL (auth 핸드오프, signing key 저장에 함께 사용)server_identifier: 선택 필드. FCM payload의server_id를server_url로 역해석해야 하는 multi-server host에서 plugin의setAuthToken(..., identifier:)로 함께 전달auth_token: 로그인 응답에서 받은 인증 토큰 (plugin의setAuthToken으로 전달)signing_key: 서버 config의AsymmetricSigningPublicKey(plugin의setSigningKey로 전달)device_token: FCM 디바이스 토큰 (plugin의getDeviceToken및onDeviceTokenReady콜백으로 동기화)on_navigate_to_channel(server_url, channel_id): notification open → 채널 라우팅 콜백on_navigate_to_thread(server_url, root_id): notification open → CRT 스레드 라우팅 콜백notification_stream: app-level UI(snackbar 등) 소비를 위한 broadcast stream