From 58dce3d7a365ec742580ec8205b5810edba7ab7f Mon Sep 17 00:00:00 2001 From: toki Date: Tue, 16 Jun 2026 11:01:17 +0900 Subject: [PATCH] feat: agent-ops MCP control plane phase updates and core service refinements - Update roadmap and phase documentation for agent-ops MCP control plane - Update milestone work-item creation/sync documentation - Refine core server command and HTTP Plane webhook handling - Update Plane webhook tests --- agent-roadmap/ROADMAP.md | 2 +- .../agent-ops-mcp-control-plane/PHASE.md | 4 +- .../milestone-work-item-creation-sync.md | 39 ++++++++++--------- services/core/cmd/server/main.go | 6 +-- services/core/internal/http/plane_webhook.go | 3 ++ .../core/internal/http/plane_webhook_test.go | 11 +++++- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/agent-roadmap/ROADMAP.md b/agent-roadmap/ROADMAP.md index c0f1466..9829a6f 100644 --- a/agent-roadmap/ROADMAP.md +++ b/agent-roadmap/ROADMAP.md @@ -37,7 +37,7 @@ IOP 외부 실행 호출은 OpenAI-compatible Responses API 방식을 기본 계 - 요약: Plane 확장, Jira-compatible provider 추상화, Mattermost, Agent Integrator, IOP OpenAI-compatible Responses 호출 경계를 실제 통합 adapter 흐름으로 확장했고, metadata 실행 문맥 전달, IOP Edge `/v1/responses`, NomadCode Core 원격 create/enqueue/poll smoke를 완료했다. - [진행중] Agent-Ops MCP Control Plane - 경로: `agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md` - - 요약: 로드맵 기반 agent-ops 운영 자동화, Plane/Jira 같은 work item provider와 Milestone item의 양방향 동기화 도메인, 외부 agent용 MCP 제어 표면을 다루며, Plane-origin Milestone Work Item Creation Sync를 진행 후보로 둔다. + - 요약: 로드맵 기반 agent-ops 운영 자동화, Plane/Jira 같은 work item provider와 Milestone item의 양방향 동기화 도메인, 외부 agent용 MCP 제어 표면을 다루며, Plane-origin Milestone Work Item Creation Sync를 검토 후보로 둔다. - [계획] Project Workspace Management UX - 경로: `agent-roadmap/phase/project-workspace-management-ux/PHASE.md` - 요약: client integration 표준화, core workflow, 외부 통합 기준 이후 프로젝트 단위 앱 UX를 다루며, provider slot 기반 외부 console composition은 계획 후보로 둔다. diff --git a/agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md b/agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md index 0e96780..7457a5a 100644 --- a/agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md +++ b/agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md @@ -18,9 +18,9 @@ - 경로: `agent-roadmap/archive/phase/agent-ops-mcp-control-plane/milestones/workspace-port-env-standardization.md` - 요약: NomadCode client/core/compose/test runner 포트와 IOP Edge 연동 endpoint를 workspace 공통 대역과 remote runner 문서 기준으로 정리하고, remote code-server preview 정상 노출 확인 후 archive했다. -- [진행중] Milestone Work Item Creation Sync +- [검토중] Milestone Work Item Creation Sync - 경로: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md` - - 요약: Plane-origin 방식으로 Milestone을 생성하고 Plane Todo 티켓까지 동기화한 뒤 멈추는 마일스톤이다. `creation-sync-orchestrator` 단위/fake 기반 근거는 있으나 원격 live 흐름은 webhook task 생성까지만 즉시 검증되어, 자동 enqueue, develop push gate, Todo projection, slot terminal state 보완 후 다시 검토한다. + - 요약: Plane-origin 방식으로 Milestone을 생성하고 Plane Todo 티켓까지 동기화한 뒤 멈추는 마일스톤이다. 12/13/14번 task group의 `Roadmap Completion` 근거로 live-cycle gate 보완 Task까지 완료되어, 사용자 최종 확인과 archive 승인만 남은 검토 후보로 둔다. - [계획] Gito Proto-Socket Consumer Wire Readiness - 경로: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-protosocket-consumer-wire-readiness.md` diff --git a/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md b/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md index 66106b8..8d93d7c 100644 --- a/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md +++ b/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md @@ -11,7 +11,7 @@ Plane-origin 경로로 Milestone을 생성하고, `develop` branch의 agent-road ## 상태 -[진행중] +[검토중] ## 구현 잠금 @@ -108,31 +108,30 @@ Plane-origin 생성 경로를 `develop` agent-roadmap 기준의 `Todo` 검토 검토 결과, 단위 테스트와 fake provider/store 기반 runtime seam 검증만으로는 이 Milestone의 완료 지점인 Plane `Todo` 검토 상태까지 실제 원격 흐름이 수렴한다고 보기 어렵다. Archive 전에는 최소한 live smoke 또는 runtime-equivalent smoke로 webhook, authoring, develop 반영, Plane projection, slot 회수 경계를 확인한다. -- [ ] [live-webhook-task] 원격 dev Plane에서 `Backlog + AGENT assignee` 상위 티켓을 만들거나 갱신하면 webhook만으로 Core task가 생성되고 checkout metadata가 남는다. 검증: Plane webhook log 202, Core task external ref, `checkout.develop_branch_path`/`checkout.slot_path`, workspace slot reservation을 secret redaction 상태로 확인한다. -- [ ] [webhook-auto-enqueue] webhook-dispatched creation task가 별도 수동 API 호출 없이 authoring queue로 이어진다. 검증: 같은 Plane work item event에서 task 생성 이후 `queued`/`running` 또는 authoring run 시작 로그가 확인되고, 중복 webhook은 중복 task나 중복 queue를 만들지 않는다. -- [ ] [authoring-develop-gate] IOP authoring 응답만으로 task 완료를 단정하지 않고, `develop` Milestone push와 provider identity match가 확인될 때 authoring 성공 또는 sync-ready로 판정된다. 검증: model response-only, slot-local dirty, push 실패는 Plane `Todo` projection으로 넘어가지 않고, `develop` match가 있을 때만 다음 단계로 수렴한다. -- [ ] [live-creation-projection] matched develop scan/job 입력이 `roadmapsyncpipeline.Service`와 `RoadmapCreationSyncWorker`를 통해 실제 Plane 원문 댓글 보존, 본문/제목 갱신, `Todo` 이동으로 수렴한다. 검증: `roadmap_sync_steps`에 `develop_matched`, `original_comment_preserved`, `plane_body_updated`, `plane_todo_moved`가 기록되고 Plane UI에서 `Todo` 상태와 `[milestone-id]` 제목을 확인한다. -- [ ] [slot-terminal-state] authoring 또는 projection이 terminal outcome에 도달하면 reserved workspace slot이 정책에 따라 `available`, `dirty`, `error` 중 하나로 전환된다. 검증: 성공 smoke 이후 다음 Plane-origin smoke가 수동 DB reset 없이 slot을 확보할 수 있고, 실패 smoke는 원인에 맞는 slot state를 남긴다. +- [x] [live-webhook-task] 원격 dev Plane에서 `Backlog + AGENT assignee` 상위 티켓을 만들거나 갱신하면 webhook만으로 Core task가 생성되고 checkout metadata가 남는다. 검증: Plane webhook log 202, Core task external ref, `checkout.develop_branch_path`/`checkout.slot_path`, workspace slot reservation을 secret redaction 상태로 확인한다. +- [x] [webhook-auto-enqueue] webhook-dispatched creation task가 별도 수동 API 호출 없이 authoring queue로 이어진다. 검증: 같은 Plane work item event에서 task 생성 이후 `queued`/`running` 또는 authoring run 시작 로그가 확인되고, 중복 webhook은 중복 task나 중복 queue를 만들지 않는다. +- [x] [authoring-develop-gate] IOP authoring 응답만으로 task 완료를 단정하지 않고, `develop` Milestone push와 provider identity match가 확인될 때 authoring 성공 또는 sync-ready로 판정된다. 검증: model response-only, slot-local dirty, push 실패는 Plane `Todo` projection으로 넘어가지 않고, `develop` match가 있을 때만 다음 단계로 수렴한다. +- [x] [live-creation-projection] matched develop scan/job 입력이 `roadmapsyncpipeline.Service`와 `RoadmapCreationSyncWorker`를 통해 실제 Plane 원문 댓글 보존, 본문/제목 갱신, `Todo` 이동으로 수렴한다. 검증: `roadmap_sync_steps`에 `develop_matched`, `original_comment_preserved`, `plane_body_updated`, `plane_todo_moved`가 기록되고 Plane UI에서 `Todo` 상태와 `[milestone-id]` 제목을 확인한다. +- [x] [slot-terminal-state] authoring 또는 projection이 terminal outcome에 도달하면 reserved workspace slot이 정책에 따라 `available`, `dirty`, `error` 중 하나로 전환된다. 검증: 성공 smoke 이후 다음 Plane-origin smoke가 수동 DB reset 없이 slot을 확보할 수 있고, 실패 smoke는 원인에 맞는 slot state를 남긴다. ## 완료 리뷰 -- 상태: 보완 필요 -- 요청일: 2026-06-14 +- 상태: 요청됨 +- 요청일: 2026-06-16 - 보완 전 완료 근거: - `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/11_creation_sync_orchestrator/complete.log`의 `Roadmap Completion` 근거로 `[creation-sync-orchestrator]`를 완료 처리했다. - 검증 근거는 `roadmapsyncpipeline.Service`의 develop match -> identity/step ledger -> Plane projection 연결, optional `RoadmapCreationSyncWorker`, `EnqueueRoadmapCreationSync` seam, `cd services/core && go test -count=1 ./...`, `cd services/core && go vet ./...`, `git diff --check` PASS다. - Gito proto-socket `branch.updated` backend bridge는 후속 `Gito Branch Event Creation Sync Bridge` Milestone으로 분리했다. -- 보완 필요 근거: - - 2026-06-15 원격 preflight 결과, Core는 `healthz`/`readyz`, Plane webhook secret/API env, project sync 설정, smoke workspace checkout, available slot까지 준비할 수 있으나 현재 live로 확인 가능한 범위는 webhook 수신과 Core task 생성까지다. - - 현재 webhook receiver는 task 생성 후 `202 accepted`로 종료하며, 생성 task를 자동 enqueue해 authoring run으로 넘기는 runtime 연결은 확인되지 않았다. - - authoring worker는 IOP model response를 받으면 task를 complete 처리할 수 있으나, `develop` push/provider identity match를 runtime success gate로 닫는 live 경로와 slot terminal state 회수 정책은 아직 검증되지 않았다. - - `RoadmapCreationSyncWorker`는 존재하지만 public trigger가 없고, Gito/develop event consumer runtime env와 wire smoke는 후속 Milestone에 남아 있어 Plane `Todo` projection까지 실제 원격 한 사이클로 증명되지 않았다. +- 보완 완료 근거: + - `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/12+11_authoring_develop_gate/complete.log`의 `Roadmap Completion` 근거로 `[authoring-develop-gate]`를 완료 처리했다. + - `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/13+12_slot_terminal_state/complete.log`의 `Roadmap Completion` 근거로 `[slot-terminal-state]`를 완료 처리했다. + - `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/14+12,13_live_cycle_smoke/complete.log`의 `Roadmap Completion` 근거로 `[live-webhook-task]`, `[webhook-auto-enqueue]`, `[live-creation-projection]`을 완료 처리했다. - 리뷰 필요: - [x] `creation-sync-orchestrator` 계획이 PASS되고 runtime caller가 검증되었다 - - [ ] `[live-cycle-gate]` 보완 Task가 live smoke 또는 runtime-equivalent smoke evidence로 완료되었다 + - [x] `[live-cycle-gate]` 보완 Task가 live smoke 또는 runtime-equivalent smoke evidence로 완료되었다 - [ ] 사용자가 보완 결과를 확인했다 - [ ] archive 이동을 승인했다 -- 리뷰 코멘트: 기존 unit/fake 기반 완료 근거는 보존하되, 실제 원격 흐름이 Plane `Todo` projection까지 자동 수렴한다는 증거가 부족해 `[진행중]`으로 되돌린다. Archive는 `[live-cycle-gate]` 보완 Task 완료 전까지 승인하지 않는다. +- 리뷰 코멘트: 기존 unit/fake 기반 완료 근거에 12/13/14번 task group의 PASS 근거가 더해져 기능 Task와 보완 Task는 모두 완료 후보가 되었다. Milestone은 `[검토중]`으로 두고, 사용자의 보완 결과 확인과 archive 승인을 기다린다. ## 범위 제외 @@ -179,9 +178,11 @@ Plane-origin 생성 경로를 `develop` agent-roadmap 기준의 `Todo` 검토 - 완료 보완 리뷰(2026-06-14): 현재 Core 코드에는 `roadmapsync` helper와 저장소는 있으나 이를 호출해 develop scan 결과를 identity/step ledger와 Plane `Todo` projection에 연결하는 runtime sync orchestrator가 없다. 보완 계획: `agent-task/m-milestone-work-item-creation-sync/11_creation_sync_orchestrator/`. - Creation sync orchestrator 동기화(2026-06-14): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/11_creation_sync_orchestrator/complete.log`의 `Roadmap Completion` 근거로 `[creation-sync-orchestrator]`를 완료 처리했다. 검증 근거는 `roadmapsyncpipeline.Service`, optional `RoadmapCreationSyncWorker`, `EnqueueRoadmapCreationSync` seam, `cd services/core && go test -count=1 ./...`, `cd services/core && go vet ./...`, `git diff --check` PASS다. - 보완 리뷰(2026-06-15): 원격 Core dev 환경은 `healthz`/`readyz`, Plane secret/API env, active project sync 설정, `branches/main`, `branches/develop`, `slots/000`, available slot까지 준비 가능함을 확인했다. 그러나 현재 live로 바로 검증 가능한 범위는 Plane webhook 수신, trigger gate, Core task 생성, checkout metadata 기록까지다. Webhook task 자동 enqueue, authoring result와 `develop` push match의 runtime 연결, Plane `Todo` projection live smoke, slot terminal state 회수는 완료 전 보강 Task로 남긴다. -- 현황 동기화(2026-06-15): 활성 작업은 `agent-task/m-milestone-work-item-creation-sync/13+12_slot_terminal_state/`와 `agent-task/m-milestone-work-item-creation-sync/14+12,13_live_cycle_smoke/` 두 개다. 두 작업 모두 active `PLAN`/`CODE_REVIEW` stub 단계이며 `complete.log`와 `Roadmap Completion` 근거가 없어 마일스톤 기능 Task를 새로 완료 처리하지 않는다. `slot-terminal-state`는 12번 선행 작업 PASS 이후 slot terminal policy 구현/검증이 필요하고, live smoke 작업은 12번과 13번 PASS 이후 redacted live 또는 runtime-equivalent evidence를 모아야 한다. -- 검토 지점: project sync 설정, workspace topology, IOP Edge HTTP authoring run 경계, authoring run 성공/실패 판정, pushed Milestone identity match, Plane 원문 댓글 보존/본문·제목 갱신/`Todo` 이동 projection, identity map 기반 idempotency/retry, runtime orchestrator 연결은 unit/fake 기반 근거가 있으나, live/runtime-equivalent smoke evidence가 부족하다. `[live-cycle-gate]` 보완 전까지 `[진행중]`으로 유지한다. -- 루프 목표: Plane `Backlog + AGENT assignee` -> `slots/` workspace 예약/준비 -> IOP Edge HTTP로 workspace agent authoring run 실행 -> agent가 Milestone 파일 작성/갱신 후 `develop` push -> sync layer가 `branches/develop` 또는 remote `develop`에서 push 완료/roadmap scan을 감지 -> Plane 원문 댓글 보존/본문 치환/제목 변경/`Todo` 이동 -> 사용자가 확인 후 `In Progress`로 이동하는 지점까지다. 최상위 `plane-nomad-iop-cycle` 실행 단위는 아직 live completion 후보가 아니며, `In Progress` 이후 실행은 후속 잠금 범위다. +- 현황 동기화(2026-06-16): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/12+11_authoring_develop_gate/complete.log`의 `Roadmap Completion` 근거로 `[authoring-develop-gate]`를 완료 처리했다. 검증 근거는 scheduler/workflow/authoring/roadmapsyncpipeline/gitosync focused test, `go test ./...`, `go vet ./...`, `git diff --check` PASS다. +- 현황 동기화(2026-06-16): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/13+12_slot_terminal_state/complete.log`의 `Roadmap Completion` 근거로 `[slot-terminal-state]`를 완료 처리했다. 검증 근거는 slot terminal policy helper, scheduler/roadmap sync worker finalizer wiring, focused core package tests, `go test ./...`, `go vet ./...`, `git diff --check` PASS다. +- 현황 동기화(2026-06-16): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/14+12,13_live_cycle_smoke/complete.log`의 `Roadmap Completion` 근거로 `[live-webhook-task]`, `[webhook-auto-enqueue]`, `[live-creation-projection]`을 완료 처리했다. 검증 근거는 live evidence 보강, duplicate/already-complete slot finalization guard, scheduler/workflow/roadmapsyncpipeline tests, `go test ./...`, `git diff --check` PASS다. +- 검토 지점: project sync 설정, workspace topology, IOP Edge HTTP authoring run 경계, authoring run 성공/실패 판정, pushed Milestone identity match, Plane 원문 댓글 보존/본문·제목 갱신/`Todo` 이동 projection, identity map 기반 idempotency/retry, runtime orchestrator 연결, live-cycle gate 보완 Task가 모두 완료 후보가 되었다. 사용자 보완 결과 확인과 archive 승인 전까지 `[검토중]`으로 유지한다. +- 루프 목표: Plane `Backlog + AGENT assignee` -> `slots/` workspace 예약/준비 -> IOP Edge HTTP로 workspace agent authoring run 실행 -> agent가 Milestone 파일 작성/갱신 후 `develop` push -> sync layer가 `branches/develop` 또는 remote `develop`에서 push 완료/roadmap scan을 감지 -> Plane 원문 댓글 보존/본문 치환/제목 변경/`Todo` 이동 -> 사용자가 확인 후 `In Progress`로 이동하는 지점까지 완료 후보로 둔다. `In Progress` 이후 실행은 후속 잠금 범위다. - 후속 잠금: `Todo -> In Progress` 이후 실행 lifecycle은 사용자가 명시적으로 잠금을 해제할 때까지 진행하지 않는다. - Plane work item 경계: 이 마일스톤에서 말하는 Plane Milestone은 Plane native milestone object가 아니라 1상위 티켓 = 1 agent-roadmap Milestone으로 보는 work item mapping이다. -- 확인 필요: 없음 +- 확인 필요: 사용자의 보완 결과 확인과 archive 이동 승인 diff --git a/services/core/cmd/server/main.go b/services/core/cmd/server/main.go index 521d2e4..c29d45e 100644 --- a/services/core/cmd/server/main.go +++ b/services/core/cmd/server/main.go @@ -99,10 +99,8 @@ func run(logger *slog.Logger) error { // Plane-origin Milestone creation sync orchestrator. Jira is intentionally // not wired here; this slice handles the Plane projection path only. The - // self-actor is left empty until a dedicated NomadCode Plane account id is - // configured, so the reconcile's self-mutation guard stays a no-op rather - // than mis-skipping a real trigger. - creationSync := roadmapsyncpipeline.NewService(store, planeClient, "") + // self-actor guard is active only when PLANE_SELF_ACTOR_ID is configured. + creationSync := roadmapsyncpipeline.NewService(store, planeClient, cfg.PlaneSelfActorID) workflowService := workflow.NewService(store, nil, logger) diff --git a/services/core/internal/http/plane_webhook.go b/services/core/internal/http/plane_webhook.go index a664b1e..ea84386 100644 --- a/services/core/internal/http/plane_webhook.go +++ b/services/core/internal/http/plane_webhook.go @@ -182,6 +182,9 @@ func (h *Handler) dispatchPlaneWebhookWorkItem(ctx context.Context, event planeW if h.planeDispatch.workspaceID != "" && event.WorkspaceID != h.planeDispatch.workspaceID { return storage.Task{}, false, nil } + if h.planeDispatch.selfActorID != "" && event.ActorID == h.planeDispatch.selfActorID { + return storage.Task{}, false, workitempipeline.ErrCreationTriggerIgnored + } ref, err := event.WorkItemRef(h.planeDispatch.workspaceSlug) if err != nil { diff --git a/services/core/internal/http/plane_webhook_test.go b/services/core/internal/http/plane_webhook_test.go index 5ae4f75..ae855d1 100644 --- a/services/core/internal/http/plane_webhook_test.go +++ b/services/core/internal/http/plane_webhook_test.go @@ -624,9 +624,10 @@ func TestReceivePlaneWebhookDuplicateSignedEvent(t *testing.T) { func TestReceivePlaneWebhookSelfActorIgnored(t *testing.T) { creator := &fakeWorkItemTaskCreator{ - err: workitempipeline.ErrCreationTriggerIgnored, + task: storage.Task{ID: "task-1", Status: "pending"}, } - h := newHandlerForTest(withPlaneCreator(creator)) + enqueuer := &fakeWebhookEnqueuer{task: storage.Task{ID: "task-1", Status: "queued"}} + h := newHandlerForTest(withPlaneCreator(creator), withWebhookEnqueuer(enqueuer)) h.SetPlaneWebhookSecret("secret") cfg := planeDispatchTestConfig() @@ -645,4 +646,10 @@ func TestReceivePlaneWebhookSelfActorIgnored(t *testing.T) { if resp["status"] != "ignored" { t.Fatalf("expected ignored status, got %v", resp) } + if creator.calledWith != nil { + t.Fatalf("expected self actor webhook to stop before creator, got %+v", creator.calledWith) + } + if len(enqueuer.enqueued) != 0 { + t.Fatalf("expected self actor webhook not to enqueue existing pending task, got %v", enqueuer.enqueued) + } }