# Milestone: External Integration

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/external-integration/PHASE.md`

## 목표

Work Item Provider Pipeline Design과 workflow core 이후 남은 Plane 확장, Jira-compatible provider 추상화, Mattermost, IOP 실행 연결을 stub 또는 호환 호출 경로에서 실제 통합 흐름으로 확장한다. IOP 호출은 현재 단계에서 OpenAI-compatible Responses API 경로를 기본으로 하며, NomadCode/IOP 고유 실행 문맥은 `metadata` 확장으로 전달한다. NomadCode가 직접 모델 런타임을 호출하거나 IOP native protocol을 외부 호출 표면으로 사용하지 않는다.

## 상태

[완료]

## 구현 잠금

- 상태: 해제
- 결정 필요:
  - [x] Mattermost와 Plane/Jira 결과 발행의 책임 경계를 결정한다.
  - [x] Agent Integrator를 유지할지 IOP/A2A 또는 다른 연결 지점으로 대체할지 결정한다.
  - [x] A2A는 이 Milestone 범위로 들이지 않고, 외부 agent-to-agent delegation 필요가 명확해질 때 후속 Milestone에서 재검토한다.
- 외부 의존 잠금:
  - 충족됨: `iop:agent-roadmap/phase/automation-runtime-bridge/milestones/openai-responses-input-surface.md`가 `[검토중]`으로 전환됐고 workspace lock `nomadcode:external-integration`의 rely-on 상태가 `enable`로 동기화됐다. 남은 잠금은 없다.

## 범위

- provider별 HTTP handler를 늘리지 않아도 `workitem.Reader`/provider registry로 같은 intake pipeline을 탈 수 있는 provider-neutral 연결 구조
- Plane work item 생성 / comment / status update 확장
- Jira live API 구현은 테스트 환경이 생길 때까지 범위에서 제외하고, provider-neutral work item 계약이 Jira issue / comment / status transition 의미를 수용할 수 있게 추상화
- Mattermost 메시지 발송 구현
- IOP Node agent interface를 호출하는 thin execution connector/adapter 경계 추가
- IOP OpenAI-compatible Responses API 호출 구조와 metadata 기반 실행 문맥 전달 추가
- IOP 외부 호출 표면은 OpenAI-compatible API를 기본으로 하고 A2A는 후속 재검토 대상으로 두며, 현재 단계의 기본 호출은 OpenAI-compatible Responses API 경로로 한정
- NomadCode core가 직접 모델 endpoint 또는 Ollama fallback을 기본 실행 경로로 전제하지 않도록 전환 기준 정리

## 기능

### Epic: [external-integration] Provider and execution adapters

외부 work item provider, 협업 도구, 실행 표면을 adapter 경계 안에서 실제 통합 흐름으로 확장한다.

- [x] [provider-intake-registry] Plane 전용 task intake handler를 provider-neutral registry/generic route 또는 동등한 조립 구조로 정리해, 새 provider가 `workitem.Reader`와 필요한 capability facet만 구현하면 같은 work item -> core task pipeline을 재사용할 수 있게 한다. 검증: Plane 기존 경로는 호환되거나 같은 registry를 통해 동작하고, Jira-compatible provider를 붙일 때 provider별 task handler/route를 새로 만들 필요가 없다.
- [x] [plane-adapter-expand] Plane work item 생성, comment, status update adapter 확장. 검증: core가 Plane에 work item, comment, status update를 요청할 수 있다.
- [x] [jira-adapter] Jira live provider 구현은 테스트 환경이 생길 때까지 범위에서 제외하고, provider-neutral work item 계약이 Jira issue 조회, comment, status transition 의미를 수용할 수 있게 추상화한다. 검증: Jira credential 없이도 core의 provider-neutral contract/unit tests가 Jira-compatible state/comment/projection 요구를 설명하고, live Jira API 호출/e2e를 요구하지 않는다.
- [x] [mattermost-adapter] Mattermost 메시지 발송 adapter 구현과 `../nexo/packages/messaging_flutter` host notification boundary 정합성 유지. 검증: core가 Mattermost에 메시지를 발송하고, server-generated signed push smoke가 `agent-test/local/mattermost-server-generated-push-smoke.md` 기준으로 FCM/ACK/opened/reply/dismiss evidence를 남긴다.
- [x] [adapter-boundary] 외부 provider별 구현 경계 점검. 검증: provider 세부 구현이 adapter 경계 밖으로 새지 않는다.
- [x] [agent-integrator] Agent Integrator를 별도 runtime이 아닌 IOP Node agent interface용 thin execution connector/adapter 경계로 재정의한다. 검증: Agent Integrator 또는 그 대체 연결 지점이 명확히 정의되어 있다.
- [x] [iop-responses] IOP OpenAI-compatible Responses API 경로와 metadata 확장을 NomadCode의 기본 실행 호출 경로로 정리. 검증: `services/core/internal/adapters/openai` request가 `model.GenerateInput.Metadata`를 OpenAI-compatible `metadata` field로 전송하고, scheduler가 task/source 실행 문맥을 채우며, OpenAI client/unit tests가 metadata 전송을 검증한다. IOP 쪽 handler/service metadata 수신과 `/v1/responses` smoke는 sibling IOP Milestone에서 PASS했으며, 원격 runner에서 NomadCode Core task enqueue/poll smoke도 완료됐다.
- [x] [model-reclass] direct model endpoint / Ollama fallback 표현과 설정을 IOP 경유 호출 기준으로 재분류. 검증: NomadCode의 기본 실행 경로가 직접 모델 호출이 아니라 IOP 경유 호출임이 로드맵과 운영 문서에서 일관되게 읽힌다.

## 완료 리뷰

- 상태: 완료
- 요청일: 2026-06-08
- 완료 근거:
  - 기존 완료 근거로 Mattermost REST post adapter 및 completed task event sink가 `services/core/internal/adapters/mattermost/`에 구현되어 server wiring에 연결되었다.
  - `agent-test/local/mattermost-server-generated-push-smoke.md`의 redacted evidence checklist가 FCM/ACK/opened/inline reply/dismiss 항목 PASS를 기록한다.
  - `services/core/internal/adapters/openai`가 `model.GenerateInput.Metadata`를 Responses request `metadata` field로 전송하도록 보완했고, scheduler metadata 생성 경로와 OpenAI request body 테스트를 추가했다.
  - sibling IOP `OpenAI Responses Input Surface` Milestone이 `[검토중]`으로 전환됐고, `agent-task/archive/2026/06/m-external-integration/01_iop_responses_surface/complete.log`가 flat metadata alias, four-step `iop-edge smoke openai`, `./scripts/e2e-openai-ollama.sh` PASS를 기록했다.
  - workspace lock `nomadcode:external-integration`의 IOP 선행 조건은 `enable` 상태다.
  - `agent-task/archive/2026/06/m-external-integration/02+01_nomadcode_core_iop_smoke/complete.log` 최종 판정(PASS)에서 `iop-responses` 완결, remote create/enqueue/poll smoke, IOP Edge smoke, openai-adapter unit test가 모두 통과했다.
- 리뷰 필요:
  - [x] `iop-responses` metadata 전송 보완 결과를 확인한다
  - [x] IOP Edge `/v1/responses` non-streaming 표면을 실제 edge service 경로로 구현하고 handler/service tests와 `iop-edge smoke openai` 또는 동등 smoke를 남긴다
  - [x] NomadCode Core를 원격 standard runner에서 실제 IOP Responses listener에 연결해 task create/enqueue/poll smoke를 통과시킨다
  - [x] 사용자가 완료 결과를 다시 확인한다 (2026-06-08)
  - [ ] archive 이동을 다시 승인한다
- 리뷰 코멘트: 2026-06-05 사용자 요청으로 완료/archive 처리했으나, 2026-06-08 코드/작업 히스토리 재검토에서 `model.GenerateInput.Metadata`가 `services/core/internal/adapters/openai`의 Responses request로 직렬화되지 않는 누락을 확인했다. 같은 날 해당 누락을 보완했고 sibling IOP `/v1/responses` 표면과 smoke도 PASS로 닫았다. 현재 `iop-responses`는 IOP 선행 조건을 충족하고 NomadCode Core 원격 task create/enqueue/poll smoke까지 완료되어 리뷰 요청 상태로 전환했다.

## 범위 제외

- Outline / Forgejo / Nextcloud 연동
- Jira live API adapter 완성, 실제 Jira credential 기반 e2e smoke, Jira Cloud 운영 검증
- MCP 서버
- Web Agent UI
- Flutter 앱 기능 구현
- IOP A2A JSON-RPC 기반 외부 agent 추가, task 상태, artifact, cancel 흐름 공유
- NomadCode가 IOP native protocol을 직접 외부 호출 경로로 사용하는 것
- IOP 내부 모델 라우팅, 모델 프로파일, RAG, MCP, output validation, fallback 정책 구현

## 작업 컨텍스트

- 이전 출처: `services/core/README.md`의 `## 단계별 다음 작업`
- 주요 작업 영역: `services/core/internal/adapters/`, `services/core/internal/scheduler/`, `services/core/internal/workflow/`
- 선행 작업: Work Item Provider Pipeline Design, Workflow Core, Mattermost Nexo Messaging Alignment
- 후속 작업: Project Workspace Management UX
- 현재 지점: Workflow Core Phase가 `[완료]`로 archive되었고, External Integration의 기능 Task는 현재 completion 로그 기준으로 완료되었다. `iop-responses` 보완은 `services/core/internal/adapters/openai/client.go`, `services/core/internal/adapters/openai/client_test.go`, `services/core/internal/scheduler/jobs_test.go`에서 마무리됐고, `NomadCode Core 원격 task create/enqueue/poll smoke`는 `agent-task/archive/2026/06/m-external-integration/02+01_nomadcode_core_iop_smoke/complete.log`로 PASS 확인됐다. Jira는 현재 테스트 환경이 없으므로 live provider 구현 완료가 아니라 provider-neutral 추상화 적합성까지만 이 Milestone에서 다뤘다. `구현 잠금`의 직접 결정 항목과 IOP 선행 잠금은 해소되었다.
- 선행 IOP Milestone: `../iop/agent-roadmap/phase/automation-runtime-bridge/milestones/openai-responses-input-surface.md`
- Mattermost signed push smoke 재현 가이드: `agent-test/local/mattermost-server-generated-push-smoke.md`
- private 환경값 router: `agent-test/local/mattermost-server-generated-push-smoke.md` (ignored local file)
- Mattermost 책임 경계: core는 Mattermost REST 메시지 발송과 task notification 발행을 담당하고, `../nexo/packages/messaging_flutter`는 client-side FCM 수신, signature 검증, ACK, notification display, opened-routing, inline reply, dismiss를 담당한다.
- 결과 발행/알림 기준: 자동 알림은 Milestone 내부 모든 작업이 완료 체크되는 시점과 사용자 리뷰 요청 시점에 한정한다. 사용자가 현재 에이전트 대화 화면을 활성으로 보고 있으면 알림을 보내지 않고, 백그라운드 상태이거나 다른 workspace agent로 이동한 상태에서 메시지/작업이 완료되면 알림을 보낸다. 추가 알림 유형은 필요가 생길 때 별도 결정으로 확장한다.
- Nexo host 정합성: NomadCode host는 Firebase 설정, Mattermost credential handoff, signing key, optional server identifier, navigation callback만 `MattermostPushClient` 경계로 전달한다. native push 처리 로직은 `apps/client/android`에 복제하지 않는다.
- 선행 순서: Mattermost 메시지/알림 경계 정합성은 `agent-roadmap/archive/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md`에서 먼저 닫았고, 이후 `[mattermost-adapter]`는 core의 Mattermost REST 메시지 발송 구현에 집중한다.
- Plane 제어 범위: 이 Milestone은 Plane work item 생성, comment, status update adapter 확장까지만 다룬다. Plane 상위 티켓/Milestone, 하위 티켓/Task 제어 흐름과 MCP 기반 agent-ops control plane은 `Agent-Ops MCP Control Plane` Phase로 미룬다.
- Provider pipeline 현재 상태: `services/core/internal/workitem`과 `services/core/internal/workitempipeline`은 provider-neutral 계약을 갖고 있고, HTTP intake wiring은 `[provider-intake-registry]` 완료 근거 기준으로 generic `/api/integrations/{provider}/tasks` route와 provider registry를 통해 조립된다. 새 provider 추가는 adapter/capability facet과 server wiring 중심으로 닫히게 한다.
- Agent runtime 연결 기준: Agent Shell은 실제 agent runtime이 아니라 사용자 대화 UX surface이며, 실제 agent는 IOP Node의 agent interface 뒤에 있다. 따라서 `Agent Integrator`는 별도 runtime이나 A2A 전제 계층으로 키우지 않고, NomadCode core에서 IOP Node agent interface를 호출하는 thin execution connector/adapter 경계로 재정의한다.
- A2A 결정: 이번 Milestone은 A2A JSON-RPC 기반 외부 agent 추가, task 상태, artifact, cancel 흐름 공유를 구현하지 않는다. A2A는 IOP OpenAI-compatible Responses 표면으로 감당하기 어려운 agent-to-agent delegation 요구가 명확해질 때 후속 Milestone에서 재검토한다.
- IOP 호출 계약: 이번 Milestone의 기본 실행 호출 표면은 OpenAI-compatible Responses API로 정식 채택한다. NomadCode/IOP 전용 task/workspace/session/agent/approval/artifact/notification 의미는 별도 `iop` wrapper 필드를 만들지 않고 OpenAI-compatible `metadata` 또는 IOP native endpoint의 명시 필드로 전달한다. 완전 신규 프로토콜은 OpenAI-compatible 표면으로 task lifecycle, artifact, cancel, approval, streaming 요구를 감당하기 어렵다는 근거가 생길 때 재검토한다.
- 현재 반영 근거:
  - `[agent-integrator]`는 별도 runtime이나 A2A 전제 계층이 아니라 NomadCode core에서 IOP Node agent interface를 호출하는 thin execution connector/adapter 경계로 정의했다.
  - `services/core/internal/adapters/mattermost/client.go`는 Mattermost `POST /api/v4/posts` 메시지 발송과 task completion message 포맷을 구현한다.
  - `services/core/internal/adapters/mattermost/task_notification_sink.go`는 `task.completed` 이벤트만 Mattermost post로 fanout한다.
  - `services/core/cmd/server/main.go`는 Mattermost sink와 proto-socket broadcaster를 notification service에 함께 연결한다.
  - `agent-test/local/mattermost-server-generated-push-smoke.md`는 server-generated signed push의 FCM receipt, ACK, notification display, opened, inline reply, dismiss evidence를 redaction 상태로 PASS 기록한다.
  - `services/core/internal/adapters/openai/client.go`는 non-streaming `POST /v1/responses` 호출 경로를 사용하고 `model.GenerateInput.Metadata`를 request `metadata` field로 전송한다.
  - `services/core/cmd/server/main.go`는 `MODEL_BASE_URL`, `MODEL_API_KEY`, `MODEL_NAME`, `MODEL_CONTEXT_SIZE`, `MODEL_TIMEOUT_SEC` 설정으로 OpenAI-compatible model client를 구성해 scheduler에 연결한다.
  - `services/core/internal/scheduler/jobs.go`는 model client 결과를 task completion 결과로 저장한다.
  - `agent-task/archive/2026/06/m-external-integration/01_iop_responses_surface/complete.log`는 sibling IOP Responses surface와 smoke contract 복구 PASS를 기록한다.
  - `../iop/agent-roadmap/phase/automation-runtime-bridge/milestones/openai-responses-input-surface.md`는 `[검토중]`이며, workspace lock의 IOP 선행 조건은 `enable`로 동기화됐다.
  - `README.md`와 `services/core/README.md`는 NomadCode의 기본 실행 호출을 IOP Edge OpenAI-compatible Responses 경로로 정리하고, direct Ollama/model endpoint는 local development compatibility로 재분류한다.
  - sibling IOP repository의 로드맵은 OpenAI-compatible API를 외부 모델 기반 호출 표면으로, A2A를 외부 agent 작업 위임 표면으로, IOP native protocol을 운영 제어 표면으로 분리한다.
  - `cd services/core && go test ./...`가 2026-06-05 현재 checkout에서 통과했다.
- 확인 필요: 사용자 완료 확인과 archive 재승인.
