35 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.4.1 Workspace port와 endpoint compatibility 후보
- 설명: 이 표는 source schema가 아니라 NomadCode workspace/remote runner에서 core, Flutter preview, provider endpoint를 같은 언어로 다루기 위한 compatibility note다.
| 표면 | 후보 기준 | compatibility note |
|---|---|---|
| Flutter web preview | code-server /proxy/<port>/ 아래 remote local 13010, remote dev 13011 host port |
기존 local smoke의 ad hoc 8081 계열 포트는 계속 허용한다. |
| code-server workspace entry | http://localhost:8080/?folder=... mock URL 유지 |
이 URL은 code-server entry 호환성이고 Flutter web preview 포트가 아니다. |
| Core HTTP/API | process/container 내부 8080, remote local host publish 18010:8080, remote dev host publish 18011:8080 |
HTTP_ADDR=:8080, Docker EXPOSE 8080, local curl localhost:8080은 migration compatibility로 유지하고, compose는 NOMADCODE_CORE_HOST_PORT로 host port를 override한다. |
| PostgreSQL | compose/service DNS code-server-postgres:5432; host 필요 시 local 15410:5432, dev 15411:5432 후보 |
기본 내부 통신은 service DNS를 우선하고 host publish는 추가하지 않는다. |
| Redis | compose/service DNS code-server-redis:6379; host 필요 시 local 16310:6379, dev 16311:6379 후보 |
기본 내부 통신은 service DNS를 우선하고 host publish는 추가하지 않는다. |
| proto-socket client/core endpoint | client는 PROTO_SOCKET_*, core는 /proto-socket과 heartbeat env를 사용 |
client PROTO_SOCKET_HOST가 없으면 connector는 비활성화된다. |
| IOP Edge/provider endpoints | MODEL_*, A2A_*, PLANE_*, JIRA_*, MATTERMOST_* env group |
raw token, password, API key 값은 tracked contract note에 기록하지 않는다. |
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", "attempt?", "reason?", "occurred_at?" } |
-
scheduler가 내는 모든 task lifecycle event(running/completed/failed/canceled)가 notification fanout을 통해 broadcast된다. completed event의 Mattermost 알림 동작은 그대로 유지된다.
-
attempt(int),reason(string),occurred_at(ISO8601 string) 등의 workflow context가 payload에 포함된다. -
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 Diagnostics 필드 후보
- 설명: proto-socket 로그와 클라이언트 디버그 표면에서 연결과 메시지 흐름을 추적하기 위한 공통 diagnostics 필드 후보다. 확정 schema가 아니라 Core/Client 구현을 맞추기 위한 compatibility note로만 사용한다.
- 안정성 수준: 후보 (Candidate)
- 최소 공통 필드:
| 필드 | 위치 | 필수성 | 소비자 책임 |
|---|---|---|---|
connection_id |
envelope meta.connection_id, Core log attribute, Client debug surface |
연결이 확립된 response/event/error에서 가능하면 포함 | Core는 연결별 안정 식별자를 부여하고, Client는 표시/필터링에만 사용한다. |
protocol_version |
envelope top-level, Core log attribute, Client debug surface | 모든 proto-socket envelope에 포함 | Core와 Client는 지원하지 않는 version을 compatibility 문제로 보고하되, secret이나 payload 원문을 함께 출력하지 않는다. |
channel |
envelope top-level, Core log attribute, Client debug surface | request/response/event/error에서 포함 | dispatcher와 Client debug surface는 channel name을 그대로 노출하되 business payload를 함께 노출하지 않는다. |
action |
envelope top-level, Core log attribute, Client debug surface | request/response/event/error에서 포함 | request 흐름과 최근 event/error를 추적하는 용도로만 사용한다. |
error.code / error_code |
envelope error.code, Core log attribute, Client debug surface |
error envelope에서 포함, 성공 envelope에서는 생략 또는 null | Core는 error code를 안정적인 문자열로 기록하고, Client는 최근 오류 요약에만 사용한다. |
timestamp |
envelope meta.timestamp, Core log timestamp, Client debug surface |
Core가 발행하는 response/event/error에서 가능하면 포함 | 소비자는 시간 정렬과 최근 상태 표시에 사용하고 clock 차이를 프로토콜 오류로 단정하지 않는다. |
- Core log attributes:
connection_id,protocol_version,channel,action,error_code,timestamp를 structured log attribute로 남긴다. error가 없으면error_code는 생략하거나 null로 둔다. - Envelope meta: response/event/error는 가능한 경우
meta.connection_id,meta.timestamp를 포함하고, top-levelprotocol_version,channel,action과 함께 diagnostics 기준으로 소비된다. - Client debug surface: 연결 상태, connection id, protocol version, 최근 channel/action/error code/timestamp만 표시한다.
- 노출 금지: auth token, Basic Auth header, secret,
token_ref가 가리키는 실제 값, payload/result 원문, 외부 provider credential은 Core log와 Client debug surface에 기록하거나 표시하지 않는다.
1.7 새 내부 통신 추가 운영 체크
- 신규
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 영향 중 해당되는 항목을 테스트로 고정한다.
1.8 외부 Gito Branch Event Consumer
- 설명: NomadCode는 Gito의 branch event를 HTTP webhook으로 수신하는 소비자입니다. 소비 표면은 Core backend bridge가 canonical이며, Flutter client wakeup은 후속 UI projection 책임으로 분리합니다.
- NomadCode 소비 계약:
agent-contract/outer/gito-branch-webhook-consumer-v1.md(계약 ID:nomadcode.gito-branch-webhook-consumer.v1) - Gito 원문 제공 계약: Gito
agent-contract/provided/gito-forgejo-branch-events-v1.md(계약 ID:gito.forgejo-branch-events.v1) - 소비 표면:
POST /api/integrations/gito/webhook및branch.updatedpayload. - 포트 profile: remote local은 Gito
18060-> NomadCode18010, remote dev는 Gito18061-> NomadCode18011을 기준으로 한다. - 공통 책임 경계: NomadCode는
branch.updated이벤트를 단순 wakeup signal로만 해석하고, Plane/roadmap mutation 전 target branch revision에 대한 재검증 및 후속 동기화 여부를 직접 확인해야 합니다.GITO_PROTO_SOCKET_URL은 이전 wire consumer 호환 경로가 필요할 때만 사용합니다.
1.8.1 Flutter client wakeup
- 소비자:
apps/clientFlutter 앱. - 용도: Core가 처리한 branch event projection을 UI wakeup 힌트로만 사용합니다. 화면 갱신/알림 트리거에 쓰며, 자체적으로 source-of-truth 상태를 변경하지 않습니다.
1.8.2 Core backend bridge
- 소비자:
services/coreHTTP Gito webhook consumer (POST /api/integrations/gito/webhook) 및 내부 Gito branch event bridge. - 용도: Core는 Gito 제공 계약
gito.forgejo-branch-events.v1을 소비해branch.updated를 backend sync enqueue wakeup으로만 사용합니다. changed_files취급:changed_files는 roadmap Milestone 관련성 판단을 위한 wakeup 힌트이며, 그 자체를 신뢰해 동기화하지 않습니다. 실제 target branch revision 확인/스캔은 backend bridge(후속 revision-scan 단계)의 책임입니다.- 구독 필터:
repo_id,branch로 구독하며, 다른 repo/branch event는 enqueue 후보로 넘기지 않습니다.
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. Roadmap / Work Item Sync 계약 후보
Important
이 절은 source schema가 아니라 Plane/Jira 같은 provider work item과
agent-roadmapMilestone item을 같은 구조로 유지하기 위한 sync domain 계약 후보입니다. 실제 구현 전까지는 compatibility note로만 사용합니다.
5.1 Sync Domain 책임 후보
- 설명: Core 안의 sync domain은 provider adapter 위에서 동작하며, provider native API 세부가 아니라 Milestone/work item identity, revision, 변경 감지, 수렴 적용, conflict review gate를 소유한다. Milestone sync의 source of truth는
developbranch의agent-roadmap이다. - 적용 대상:
- Plane/Jira 상위 work item ↔
developbranch의agent-roadmapMilestone - Plane/Jira 하위 work item ↔
developbranch의 Milestone기능Task item agent-task/m-<milestone-slug>plan/code-review/completion event ↔ Milestone Task ↔ provider child work item
- Plane/Jira 상위 work item ↔
- 비소유 대상:
- Plane/Jira native DTO와 endpoint 세부
- 사용자 승인 없는 archive/delete 강제 적용
- provider별 custom field 전제 설계
5.2 Sync Identity 후보
- 설명: 어느 쪽에서 변경이 발생해도 같은 Milestone item 구조로 찾고 비교하기 위한 provider-neutral identity shape다.
- 후보 필드:
| 필드 | 설명 |
|---|---|
shape |
milestone 또는 task |
roadmap_milestone_path |
agent-roadmap/phase/<phase-slug>/milestones/<milestone-slug>.md |
roadmap_item_id |
Milestone 자체이면 파일 slug, Task이면 Milestone 안의 item-id |
provider |
plane, jira 등 provider id |
tenant |
provider workspace/site/account 식별자 |
project |
provider project 식별자 |
work_item_id |
provider work item id 또는 issue key |
parent_work_item_id |
Task shape일 때 provider 상위 work item id |
roadmap_revision |
roadmap 문서 또는 Core index가 본 revision |
roadmap_branch |
source of truth branch. Milestone sync에서는 기본값 develop |
provider_revision |
provider가 제공하는 updated timestamp/version/hash 후보 |
observed_at |
Core가 변경을 관측한 시각 |
5.3 Project Sync Configuration 후보
- 설명: Milestone sync는 Plane/Jira provider project 단위로 묶이며, Core DB에 저장된 project sync 설정을 통해 provider project, git repository, 실제 작업 workspace를 해석한다.
- 저장 위치: Core DB. source schema와 migration은 구현 시점에 확정한다.
- 후보 필드:
| 필드 | 설명 |
|---|---|
sync_project_id |
Core 내부 project sync 설정 id |
provider |
plane, jira 등 provider id |
provider_tenant |
Plane workspace slug 또는 Jira site/account 식별자 |
provider_project_id |
Plane project id 또는 Jira project id/key |
provider_project_name |
표시용 provider project 이름 |
git_remote_url |
source-of-truth roadmap이 있는 git repository URL |
git_default_branch |
source-of-truth branch. 기본값 develop |
roadmap_root_path |
repository 안의 roadmap root. 기본값 agent-roadmap |
workspace_id |
실제 작업 workspace 식별자 |
workspace_path |
local/runner에서 실제 작업이 돌아갈 workspace path 후보 |
enabled |
sync 활성화 여부 |
settings |
provider별 확장 설정 JSON |
created_at / updated_at |
생성/수정 시각 |
- 운영 원칙:
- Plane/Jira work item intake와 projection은 반드시 project sync 설정을 통해 대상 git/workspace를 확정한 뒤 실행한다.
- 같은 provider project의 Milestone/work item sync는 같은 active project sync 설정을 사용한다.
- project sync 설정이 없거나 둘 이상이면 Milestone 생성 동기화를 진행하지 않고 사용자 설정 또는 운영자 조치가 필요하다고 보고한다.
- 이 설정은 이후 Project settings UI/UX에서 확인/수정할 수 있어야 한다.
5.4 Sync Change Event 후보
- 설명: provider webhook, provider polling, roadmap file/index scan, agent-task completion event를 같은 변경 입력으로 정규화한다.
- proto-socket event 후보:
| action | type | channel | payload |
|---|---|---|---|
roadmap_sync.changed |
event |
roadmap_sync |
{ "source", "change_type", "identity", "fields", "actor?", "reason?", "observed_at" } |
- payload 후보 필드:
source:provider_webhook,provider_poll,roadmap_scan,agent_task_completion,manualchange_type:created,updated,status_changed,parent_changed,completed,archived,deletedidentity: 5.2의 Sync Identity shapefields: title, description, status, labels, parent-child, order, review state 같은 변경 후보actor: provider user, NomadCode actor, external agent 등 변경 주체reason: 사용자 요청, completion event, scheduled reconciliation 등 적용 사유
5.5 Sync Action 후보
- 설명: 외부 agent, Flutter UI, scheduler가 sync domain에 검사와 적용을 요청할 때 사용하는 action 후보다.
| action | request payload | response payload |
|---|---|---|
roadmap_sync.inspect |
{ "identity?", "scope?", "source?", "dry_run": true } |
{ "matches", "drift", "conflicts" } |
roadmap_sync.apply |
{ "identity", "change", "expected_revision?", "idempotency_key", "actor", "reason", "dry_run": false } |
{ "applied", "identity", "roadmap_revision", "provider_revision", "conflict?" } |
roadmap_sync.reconcile |
{ "scope", "provider?", "since?", "dry_run": true } |
{ "changes", "proposals", "conflicts" } |
- 공통 입력 원칙:
- destructive 변경, archive, delete, 사용자 승인 상태 변경은
dry_run또는 review gate를 우선한다. expected_revision이 맞지 않으면 조용히 덮어쓰지 않고 conflict로 반환한다.- 같은 변경 재시도는
idempotency_key로 중복 적용을 피한다. - provider webhook이 없거나 누락될 수 있으므로 scheduler polling과 같은 action shape를 공유한다.
- feature branch 또는 로컬 초안은 Plane/Jira
Todoprojection 대상이 아니며,developbranch에 반영된 roadmap만 sync 확정본으로 적용한다.
- destructive 변경, archive, delete, 사용자 승인 상태 변경은
5.6 Sync Convergence 정책 후보
developbranch의 Milestone과 provider parent work item은 같은 목표, 범위 요약, 상태, review gate를 표현한다.- Milestone
기능Task와 provider child work item은 같은 제목/상태/parent 관계를 표현한다. - roadmap 쪽 변경과 provider 쪽 변경은 모두 sync change event로 정규화한 뒤 Core action을 통해 적용한다.
- 동시에 수정된 항목은 revision mismatch로 멈추고, 사용자 검토 또는 dry-run proposal로 올린다.
- provider 상태는 가볍게 유지하고, agent 내부 실행 상태는 metadata/labels/comment로 투영한다.
Done/archive/delete처럼 되돌리기 어려운 변경은 사용자 승인 또는 명시 action 없이 자동 적용하지 않는다.
6. Roadmap Operations Control Plane 계약 후보
Important
이 절은 Roadmap Operations Control Plane의 후보 계약입니다. 실제 source schema가 아니며, Core가 roadmap/action side effect를 소유하도록 만들 때의 compatibility note로만 사용합니다.
6.1 Roadmap State Model 후보
- 설명: Core는
agent-roadmap파일을 직접 덮어쓰는 도구가 아니라, roadmap state를 읽고 revision/idempotency 조건을 확인한 뒤 action 결과를 제안하거나 적용하는 소유자다. - 상태 단위:
roadmap: 전체 Phase 흐름, source branch, current revision.phase: Phase 상태, Phase 문서 경로, Milestone 흐름, archive link.milestone: Milestone 상태, 구현 잠금, SDD gate, 기능 Task 상태, 완료 리뷰.task_artifact:agent-task/m-<milestone-slug>의 plan, code-review,USER_REVIEW.md,complete.log.review_gate: 사용자 입력, 출력 검토, 완료 승인, archive 승인처럼 자동 적용 전에 멈춰야 하는 판단 지점.
- 공통 revision 필드 후보:
| 필드 | 설명 |
|---|---|
roadmap_revision |
Core가 읽은 git commit SHA 또는 file/index revision |
document_path |
ROADMAP.md, PHASE.md, Milestone 문서, SDD 문서, task artifact 경로 |
document_hash |
같은 commit 안에서 파일 단위 충돌을 확인하기 위한 content hash 후보 |
expected_revision |
caller가 적용을 기대하는 revision |
idempotency_key |
같은 command 재시도 중복 적용 방지 키 |
actor |
사용자, provider webhook, scheduler, agent, runtime event 같은 변경 주체 |
reason |
사용자 요청, provider status change, completion event, reconciliation 등 적용 사유 |
6.2 Roadmap Action 후보
- 설명: Flutter UI, provider webhook, scheduler, agent runtime은 같은 Core action shape를 통해 roadmap 변경을 검사하거나 적용한다.
| action | request payload | response payload |
|---|---|---|
roadmap.validate |
{ "scope?", "paths?", "strict": true } |
{ "valid", "diagnostics", "revision" } |
roadmap.get_position |
{ "current_path?", "include_flow": true } |
{ "roadmap", "phase", "milestones", "current_candidates" } |
roadmap.propose_change |
{ "target", "change", "actor", "reason", "dry_run": true } |
{ "proposal", "affected_paths", "review_gate?" } |
roadmap.apply_change |
{ "proposal_id?", "target", "change", "expected_revision", "idempotency_key", "actor", "reason" } |
{ "applied", "revision", "affected_paths", "review_gate?" } |
roadmap.transition |
{ "target", "to_status", "expected_revision", "idempotency_key", "actor", "reason" } |
{ "transitioned", "from_status", "to_status", "revision", "review_gate?" } |
roadmap.archive |
{ "target", "expected_revision", "idempotency_key", "actor", "reason", "approved": false } |
{ "archived", "archive_paths", "revision", "review_gate?" } |
roadmap.dependency_check |
{ "target", "direction": "both" } |
{ "locks", "ready", "blocking_reasons" } |
roadmap.ingest_completion_event |
{ "origin_task", "complete_log", "roadmap_completion", "expected_revision?", "idempotency_key" } |
{ "applied_tasks", "milestone_status?", "revision", "review_gate?" } |
- 공통 입력 원칙:
- 상태 변경, archive, dependency lock 갱신, completion event ingest는
expected_revision과idempotency_key를 받는다. - destructive 변경과 archive는 사용자 승인 또는
review_gate해소 없이 자동 적용하지 않는다. roadmap.propose_change는 문서 초안이나 dry-run proposal을 만들 수 있지만, 적용은roadmap.apply_change또는 더 구체적인 transition/archive action이 담당한다.roadmap.ingest_completion_event는complete.log의Roadmap Completion과 Milestone 기능 Task id가 매칭될 때만 Task 체크 후보를 만든다.
- 상태 변경, archive, dependency lock 갱신, completion event ingest는
6.3 Review Gate 후보
- 설명: Core는 자동 판단이 위험한 지점을 review gate로 올리고, gate가 해소되기 전까지 apply/archive side effect를 멈춘다.
| gate | trigger | resolution |
|---|---|---|
user_input_required |
제품 방향, 범위, 책임 경계처럼 에이전트가 확정할 수 없는 결정 | Milestone 구현 잠금 > 결정 필요 또는 SDD USER_REVIEW.md |
task_user_review |
plan/code-review 루프가 사용자 결정 없이는 이어갈 수 없음 | agent-task/.../USER_REVIEW.md 해소 후 후속 completion event |
completion_review |
모든 기능 Task가 충족되어 [검토중] 전환 후보 |
사용자 완료 확인 |
archive_approval |
[완료] 전환과 archive 이동 후보 |
사용자 archive 승인 |
conflict_review |
revision mismatch, provider/roadmap 동시 수정, destructive change | dry-run proposal 확인 후 명시 apply |
- Plane
User Review는 provider board state이고,USER_REVIEW.md는 agent-task 또는 SDD의 stop artifact다. Core는 둘을 같은 사용자 판단 신호로 정규화하되 저장 위치와 해소 조건은 분리한다.
6.4 HTTP 역할과 첫 구현 순서 후보
- HTTP API는 Flutter UI, provider webhook, internal integration, smoke/curl compatibility 표면으로 유지한다.
- HTTP handler는 roadmap 문서를 직접 변경하지 않고 Core action을 호출하며, action request에
expected_revision,idempotency_key,actor,reason,dry_run을 전달한다. - 외부 provider webhook은 wakeup/input channel이고, roadmap mutation 여부는 Core action의 validation과 revision check 결과에 따른다.
- 첫 구현 순서는
roadmap.validate->roadmap.get_position->roadmap.ingest_completion_event순서를 기본 후보로 둔다.roadmap.validate: side effect 없이 문서 구조와 lock/gate 상태를 확인한다.roadmap.get_position: Flutter UI와 agent runtime이 같은 current/flow 해석을 공유한다.roadmap.ingest_completion_event: 기존 plan/code-review 완료 이벤트를 Core action 경계로 끌어와 Milestone Task 체크 후보를 만든다.
7. Workspace / Project Metadata 계약 후보
Important
Workspace/Project Metadata 상태: 일반 Workspace/Project metadata는 아직 완전한 source schema가 없으며 아래는 후보 필드 목록입니다. 단, Milestone sync에 필요한 provider project, git remote, workspace 설정은 5.3의 Project Sync Configuration 후보로 분리하고 Core DB 저장 대상으로 본다.
7.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 오브젝트 (예: 자동 스케줄링 옵션, 알림 채널 정보 등)
7.2 Project Metadata 후보 스펙
- 설명: 작업 공간 내 세부 프로젝트(Project) 정보.
- 후보 필드:
project_id: 프로젝트 고유 식별자 (UUID)workspace_id: 해당 프로젝트가 속한 Workspace 식별자name: 프로젝트 표시 이름 (예:Flutter client consolidation)description: 프로젝트 세부 설명provider: 연동 플랫폼 식별자 (예:plane,github)external_project_id: 연동 플랫폼 측 프로젝트 IDstatus: 프로젝트 상태 (예:active,paused)
8. 클라이언트 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/.
8.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)
8.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