<!-- task=m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

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

구현 완료의 마지막 단계는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이다. 선택된 SDD/Milestone 결정이 막을 때만 review stub의 `사용자 리뷰 요청`을 채운다. 직접 사용자에게 묻거나 `USER_REVIEW.md`, `complete.log`, archive 이동을 수행하지 않는다.

## 배경

리뷰 결과, bridge가 identity 없는 active Milestone을 missing-create job으로 enqueue할 때 `Expected.Provider=plane`을 채우지만, pipeline은 `Expected.Provider == ""`인 synthetic input에서만 `CreateWorkItem`을 호출한다. 그 결과 실제 bridge -> scheduler -> pipeline 경로에서는 `MatchDevelopMilestone`의 no scanned identity not-ready 경로로 빠지고 Plane item이 생성되지 않는다.

## 사용자 리뷰 요청 흐름

선택된 SDD 결정 또는 Milestone 잠금 결정만 review stub의 `사용자 리뷰 요청`에 기록한다. 외부 Plane credential 준비나 live service 접근 실패는 사용자 리뷰가 아니라 검증/환경 차단으로 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-origin-authoring-roundtrip-sync.md`
- Task ids:
  - `plane-missing-create`: active Milestone slug가 ledger와 Plane에서 발견되지 않으면 Plane work item을 생성하고, 생성된 Plane work item id를 slug ledger와 Milestone provider identity block 또는 동등한 sync marker에 기록한다.
- Completion mode: check-on-pass

## Spec Targets

- SDD: `agent-roadmap/sdd/agent-ops-mcp-control-plane/plane-origin-authoring-roundtrip-sync/SDD.md`
- Acceptance scenarios:
  - `S11`: task=`plane-missing-create`; evidence=`Plane adapter/reconcile tests or smoke proving missing active Milestone creates one Plane item and records ledger/marker`
- Completion mode: spec-check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/plan_cloud_G06_0.log`
- Archived review: `agent-task/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/code_review_cloud_G06_0.log`
- Verdict: FAIL
- Required summary:
  - `services/core/internal/roadmapsyncpipeline/service.go:142` only enters missing-create when `Expected.Provider == ""`, while `services/core/internal/gitosync/bridge.go:145` sets `Expected.Provider` to `plane` for real identity-less active Milestone jobs.
  - `services/core/internal/roadmapsyncpipeline/service_test.go:421` uses a provider-empty synthetic input, while `services/core/internal/gitosync/bridge_test.go:344` separately asserts provider=`plane`, leaving the actual bridge-produced pipeline contract untested.
- Affected files:
  - `services/core/internal/roadmapsyncpipeline/service.go`
  - `services/core/internal/roadmapsyncpipeline/service_test.go`
  - `services/core/internal/gitosync/bridge.go`
  - `services/core/internal/gitosync/bridge_test.go`
- Verification evidence from review:
  - `cd services/core && go test -count=1 ./internal/gitosync ./internal/roadmapsyncpipeline` passed, demonstrating the current tests do not catch the contract mismatch.
  - `cd services/core && go test -count=1 ./...` passed.
  - `git diff --check` passed.
- Roadmap/spec carryover:
  - Roadmap Task `plane-missing-create` remains incomplete until this follow-up proves actual missing active Milestone input creates one Plane item and records the created id in the slug ledger.
  - SDD Acceptance Scenario `S11` remains incomplete for the same reason.
- Narrow archive reread allowed:
  - `agent-task/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/plan_cloud_G06_0.log`
  - `agent-task/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/code_review_cloud_G06_0.log`

## 분석 결과

### 리뷰 finding

- 실제 bridge missing-create job shape: `Expected.Provider=plane`, `Expected.Tenant=<workspace slug>`, `Expected.Project=<project id>`, `Expected.WorkItemID=""`, `Ref={}`.
- 현재 pipeline create gate: existing row not found and `Expected.Provider == ""`.
- 현재 matcher behavior: provider가 채워진 expected identity는 `Normalize` 이후 scanned provider identity를 요구하고, identity-less scan에서는 `reasonNoScannedIdentity`로 not-ready가 된다.

### 테스트 공백

기존 pipeline 테스트는 provider-empty input만 만들고, bridge 테스트는 enqueue args만 확인한다. 두 레이어를 잇는 job shape를 pipeline에 넣는 테스트가 없어 S11 핵심 경로가 통과하지 못해도 전체 Go test가 통과한다.

### 범위 결정 근거

이번 follow-up은 06 missing-create의 Required 이슈만 닫는다. 기존 05 canonical-id foundation, 07 slug-reconcile active task, live Plane smoke, broader projection/idempotency 확장은 범위 밖이다.

### 빌드 등급

`local-G05`: 수정 범위가 bridge/pipeline 계약과 단위 테스트로 좁고, 외부 live provider 없이 fake creator/store로 결정적으로 검증할 수 있다.

## 구현 체크리스트

- [ ] bridge-produced missing-create input과 pipeline create predicate를 맞춰, existing slug row가 없고 provider work item id가 비어 있는 active Milestone input이 `CreateWorkItem`을 호출하도록 수정한다.
- [ ] provider/tenant/project가 missing-create에 필요한 값인지 검증하고, work item id가 이미 있거나 existing row가 있으면 중복 create를 하지 않는 기존 경로를 유지한다.
- [ ] 실제 bridge missing-create job shape 또는 동등한 `SyncCreationInput`으로 `CreateWorkItem`이 정확히 한 번 호출되고 created work item id와 `develop_matched` ledger가 저장되는 테스트를 추가한다.
- [ ] `cd services/core && go test -count=1 ./internal/gitosync ./internal/roadmapsyncpipeline`와 `cd services/core && go test -count=1 ./...`, `git diff --check`를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

1. `bridge_test.go`의 missing-create args 또는 helper를 기준으로 pipeline input fixture를 만든다.
2. `service.go`의 missing-create predicate를 provider empty가 아니라 missing provider work item id/ref 기준으로 바꾼다.
3. provider/tenant/project validation과 duplicate-prevention branch를 테스트로 고정한다.

### [REVIEW_API-1] Align Missing-Create Contract

문제: bridge와 pipeline이 서로 다른 missing-create input shape를 사용한다. 실제 bridge path는 provider를 채우므로 pipeline create branch가 실행되지 않는다.

해결 방법: `SyncCreation`에서 existing row가 없고 `Expected.WorkItemID`와 `Ref.ID`가 비어 있는 active Milestone을 missing-create로 판정한다. create input에는 `Expected.Provider`가 있으면 그 값을 쓰고, 비어 있으면 `plane`을 사용한다. `Tenant`와 `Project`는 비어 있으면 create 전에 명시 오류를 반환한다. existing row가 있거나 work item id가 있으면 기존 match/reconcile/projection 경로를 유지한다.

수정 파일 및 체크리스트:
- [ ] `services/core/internal/roadmapsyncpipeline/service.go`
- [ ] `services/core/internal/roadmapsyncpipeline/service_test.go`
- [ ] 필요 시 `services/core/internal/gitosync/bridge_test.go`

테스트 작성: 작성한다. provider=`plane`, tenant/project set, work item id empty, ref empty인 실제 bridge-produced shape가 `CreateWorkItem`을 한 번 호출하고, created id를 `roadmap_sync_identities` fake store에 저장하며, `develop_matched`만 mark하는지 검증한다. 기존 provider-empty synthetic test는 실제 path에 맞게 고치거나 보조 케이스로 유지한다.

중간 검증: `cd services/core && go test -count=1 ./internal/gitosync ./internal/roadmapsyncpipeline`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/roadmapsyncpipeline/service.go` | REVIEW_API-1 |
| `services/core/internal/roadmapsyncpipeline/service_test.go` | REVIEW_API-1 |
| `services/core/internal/gitosync/bridge_test.go` | REVIEW_API-1 |

## 최종 검증

`cd services/core && go test -count=1 ./internal/gitosync ./internal/roadmapsyncpipeline`

`cd services/core && go test -count=1 ./...`

`git diff --check`

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