nomadcode/packages/contracts/notes/flutter-core-api-candidates.md
toki 88674917de feat: proto socket infrastructure communication rail implementation
- 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
2026-05-30 21:12:37 +09:00

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/clientservices/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.Struct semantic envelope를 사용한다.
  • Heartbeat: proto-socket 기본 heartbeat를 사용하며 Core 설정 기본값은 interval 30s, wait 10s다.

1.5 Task Channel Actions (구현됨)

  • 설명: services/core/internal/protosocket가 REST task API와 같은 의미를 task channel의 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/clientservices/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": "ready"
    }
    
    (Status 503 Service Unavailable):
    {
      "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: 연동 플랫폼 측 프로젝트 ID
    • status: 프로젝트 상태 (예: 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_socket Dart 패키지(local path)를 끌어 쓰며 bootstrap에서 자동 연결하지 않는다.
  • 후보 필드:
    • host: 대상 서버 hostname
    • port: 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/client host가 plugin adapter에게 위임/공급해야 하는 데이터와 콜백. plugin adapter는 platform 측 plugin singleton에 격리되어 있고, 그 외 코드는 모두 MattermostPushClient 인터페이스에만 의존한다.
  • 호스트 소유 필드/콜백:
    • server_url: Mattermost 서버 base URL (auth 핸드오프, signing key 저장에 함께 사용)
    • server_identifier: 선택 필드. FCM payload의 server_idserver_url로 역해석해야 하는 multi-server host에서 plugin의 setAuthToken(..., identifier:)로 함께 전달
    • auth_token: 로그인 응답에서 받은 인증 토큰 (plugin의 setAuthToken으로 전달)
    • signing_key: 서버 config의 AsymmetricSigningPublicKey (plugin의 setSigningKey로 전달)
    • device_token: FCM 디바이스 토큰 (plugin의 getDeviceTokenonDeviceTokenReady 콜백으로 동기화)
    • 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