<!-- task=m-milestone-work-item-creation-sync/04+03_plane_todo_projection plan=0 tag=API -->

# Plan - API Plane Todo Projection

## 이 파일을 읽는 구현 에이전트에게

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 반드시 채운다. 검증 명령을 실행하고 실제 출력과 구현 메모를 남긴 뒤 active 파일을 유지한 채 리뷰 준비를 보고한다. 사용자 전용 결정이나 외부 환경 차단은 review stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다.

## 배경

Develop match가 완료된 뒤 Plane은 사용자 검토 UI로만 갱신된다. 원본 Plane 본문은 `사용자 요청:` 댓글로 먼저 보존해야 하며, develop 반영 전에는 본문 치환이나 Todo 이동을 하면 안 된다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청`에 기록한다. 직접 사용자 프롬프트는 금지다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md`
- Task ids:
  - `cycle-plane-todo`: 원본 Plane 본문을 `사용자 요청:` 댓글로 보존하고, `develop`의 Milestone 내용으로 Plane 본문/제목을 갱신한 뒤 `Todo`로 이동한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/domain/core/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md`
- `services/core/internal/adapters/plane/client.go`
- `services/core/internal/adapters/plane/client_test.go`
- `services/core/internal/workitem/provider.go`
- `services/core/internal/http/handlers.go`
- `packages/contracts/notes/flutter-core-api-candidates.md`

### 테스트 환경 규칙

- test_env: `local`.
- `agent-test/local/core-smoke.md` 기준으로 `cd services/core && go test ./...` 실행.
- Plane token이 필요한 real API smoke는 unit test 대체로 충분하다. raw token은 기록하지 않는다.

### 테스트 커버리지 공백

- 실제 Plane dev server Todo 이동 smoke는 이 plan의 필수 검증에서 제외한다. adapter httptest와 projection service fake로 보존 순서와 body/status patch를 검증한다.

### 심볼 참조

- renamed/removed symbols: none.

### 분할 판단

- 이 plan은 `04+03_plane_todo_projection`이며 predecessor `03_develop_match` complete.log가 필요하다. 현재 missing.
- idempotency/retry는 05에서 분리한다.

### 범위 결정 근거

- Plane `Todo -> In Progress`, child tickets, completion states는 Milestone 범위 제외이므로 구현하지 않는다.
- provider custom fields는 free/self-hosted 전제와 맞지 않아 사용하지 않는다.

### 빌드 등급

- build: `cloud-G07`. 외부 adapter mutation 순서와 failure semantics가 중요하다.

## 구현 체크리스트

- [ ] Plane adapter에 work item body/title update capability를 additive로 추가한다.
- [ ] Projection service가 댓글 보존 -> body/title patch -> Todo status 이동 순서로 실행하고 중간 실패 시 다음 단계를 건너뛰게 한다.
- [ ] Milestone 내용을 Plane body로 변환하는 작은 formatter를 추가하고 제목은 `[milestone-id] 제목`으로 만든다.
- [ ] develop projection candidate가 없으면 no-op 또는 explicit not-ready error를 반환한다.
- [ ] `cd services/core && go test ./...`와 `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- `03_develop_match`의 `complete.log`가 필요하다. 현재 missing.

### [API-1] Plane Body And Title Patch

#### 문제

[services/core/internal/adapters/plane/client.go](/config/workspace/nomadcode/services/core/internal/adapters/plane/client.go:226)는 comment 추가와 status update만 제공한다. Milestone 내용으로 body/title을 치환할 adapter capability가 없다.

#### 해결 방법

`UpdateWorkItem` 또는 `UpdateIssue` input에 `Title`, `DescriptionHTML`을 추가하고 PATCH body에 non-empty field만 담는다. 기존 `UpdateIssueStatus`는 새 method를 재사용하거나 wrapper로 유지한다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/adapters/plane/client.go`
- [ ] `services/core/internal/adapters/plane/client_test.go`
- [ ] `services/core/internal/workitem/provider.go`

#### 테스트 작성

- PATCH body가 `name`, `description_html`, `state`를 정확히 보내는지 httptest로 검증한다.
- blank title/body가 의도치 않게 삭제되지 않도록 non-empty field만 patch한다.

#### 중간 검증

```bash
cd services/core && go test ./internal/adapters/plane ./internal/workitem
```

### [API-2] Projection Service Ordering

#### 문제

Milestone은 댓글 보존 실패 시 본문 치환으로 넘어가지 말라고 한다([milestone-work-item-creation-sync.md](/config/workspace/nomadcode/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md:102)). 현재 이 순서를 소유하는 service가 없다.

#### 해결 방법

`roadmapsync.ProjectPlaneTodo`를 추가한다. 입력은 develop match candidate, fetched Plane original body, Todo state id, provider client facets다. 순서는 comment -> update body/title -> project status다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/roadmapsync/plane_projection.go`
- [ ] `services/core/internal/roadmapsync/plane_projection_test.go`

#### 테스트 작성

- comment 실패 시 update/status 미호출.
- body update 실패 시 status 미호출.
- success 시 comment body가 `사용자 요청:` prefix를 포함하고 status가 Todo state로 이동.

#### 중간 검증

```bash
cd services/core && go test ./internal/roadmapsync ./internal/adapters/plane
```

### [API-3] Plane Body Formatter

#### 문제

Develop Milestone Markdown을 Plane 본문으로 옮기는 formatter가 없다. 제목도 `[milestone-id] 제목` 형식으로 만들어야 한다([milestone-work-item-creation-sync.md](/config/workspace/nomadcode/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md:57)).

#### 해결 방법

Milestone title, 목표, 범위, 기능 요약을 HTML-safe Markdown/HTML로 변환하는 최소 formatter를 둔다. raw token/secret은 포함하지 않는다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/roadmapsync/plane_format.go`
- [ ] `services/core/internal/roadmapsync/plane_format_test.go`

#### 테스트 작성

- title format `[milestone-slug] Milestone Title`.
- body includes Milestone goal and excludes provider secret-like metadata.

#### 중간 검증

```bash
cd services/core && go test ./internal/roadmapsync
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/adapters/plane/client.go` | API-1 |
| `services/core/internal/adapters/plane/client_test.go` | API-1 |
| `services/core/internal/workitem/provider.go` | API-1 |
| `services/core/internal/roadmapsync/plane_projection.go` | API-2 |
| `services/core/internal/roadmapsync/plane_projection_test.go` | API-2 |
| `services/core/internal/roadmapsync/plane_format.go` | API-3 |
| `services/core/internal/roadmapsync/plane_format_test.go` | API-3 |

## 최종 검증

```bash
cd services/core && go test ./...
git diff --check
```

Go test cache output is acceptable for unchanged packages; changed packages must appear in output or be rerun directly as 중간 검증.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
