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

# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected SDD decision or selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.

## 개요

date=2026-06-21
task=m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create, plan=0, tag=API

## 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

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G06.md` -> `code_review_cloud_G06_N.log`, `PLAN-cloud-G06.md` -> `plan_cloud_G06_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
4. PASS이고 task group이 `m-plane-origin-authoring-roundtrip-sync`이면 완료 이벤트 메타데이터를 보고하고 roadmap은 직접 수정하지 않는다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] Preserve Missing Active Milestone Candidates | [x] |
| [API-2] Create Plane Work Item And Record Slug Ledger | [x] |

## 구현 체크리스트

- [x] 선행 `05_canonical_id` complete evidence를 확인하고 없으면 구현하지 않는다.
- [x] active scanner/bridge 결과가 provider identity 없는 active Milestone Markdown을 missing-create 후보로 보존하도록 타입과 tests를 조정한다.
- [x] `roadmapsyncpipeline`에 Plane missing-create path를 추가해 canonical id lookup miss에서 `workitem.Creator.CreateWorkItem`을 한 번 호출하고 ledger/provider marker를 기록한다.
- [x] Plane create 결과의 provider work item id가 slug identity row에 저장되고 title이 `[milestone-id] ...` 형태인지 tests로 검증한다.
- [x] `cd services/core && go test ./internal/gitosync ./internal/roadmapsyncpipeline ./internal/adapters/plane ./internal/workitem`와 `cd services/core && go test ./...`를 통과시킨다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G06_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G06_M.log`로 아카이브한다.
- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-plane-origin-authoring-roundtrip-sync`이면 런타임 완료 이벤트 메타데이터를 보고하고 roadmap 수정은 하지 않는다.
- [x] WARN/FAIL이면 후속 plan 또는 USER_REVIEW gate를 처리한다.

## 계획 대비 변경 사항

- `PLANE_PROJECT_ID` 환경 변수(비밀 제외 로컬 환경 변수)를 로드할 수 있도록 `config.go` 설정 구조체에 `PlaneProjectID` 필드를 추가했습니다.
- `bridge.go` 의 `NewBridge` 가 missing-create 시 Plane `Tenant` 와 `Project` 를 인식할 수 있도록 `BridgeConfig` 에도 `Tenant`, `Project` 를 추가하여 `cmd/server/main.go` 가 구동될 때 주입하도록 했습니다.

## 주요 설계 결정

- `SyncCreation` 시작 시점에 `expected.MilestoneID()` 로 DB의 `roadmap_sync_identities` 테이블을 우선 조회합니다.
  - 만약 이미 DB에 매핑이 존재한다면, provider identity가 없어도 DB에 저장된 실제 provider 정보로 `in.Expected` 및 `in.Ref` 를 재구성하여 기존의 projection 및 Reconcile 로직을 자연스럽게 타도록 구현했습니다.
  - 만약 DB에 매핑이 없고 provider identity 정보도 없는 경우("canonical id lookup miss" + "missing provider identity"), `s.provider` 를 `workitem.Creator` 로 타입 단언하여 `CreateWorkItem` 을 한 번 수행하고, 생성된 work item ref와 slug mapping을 디비에 `UpsertRoadmapSyncIdentity` 를 호출해 저장(ledger-only marker)하고 `StepDevelopMatched` 를 완성한 뒤 조기 리턴(same call 중복 실행 차단)합니다.

## 사용자 리뷰 요청

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- identity 없는 active Milestone이 조용히 drop되지 않는지 확인한다.
- Plane create가 중복 없이 한 번만 호출되고 created work item id가 slug ledger에 남는지 확인한다.
- live credential 없이 unit/fake tests로 provider create behavior가 검증되는지 확인한다.

## 검증 결과

### API-1 중간 검증
```text
$ cd services/core && go test -count=1 ./internal/gitosync
ok  	github.com/nomadcode/nomadcode-core/internal/gitosync	1.421s
```

### API-2 중간 검증
```text
$ cd services/core && go test -count=1 ./internal/roadmapsyncpipeline ./internal/adapters/plane ./internal/workitem
ok  	github.com/nomadcode/nomadcode-core/internal/roadmapsyncpipeline	0.003s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/plane	0.014s
ok  	github.com/nomadcode/nomadcode-core/internal/workitem	0.004s
```

### 최종 검증
```text
$ test -f agent-task/m-plane-origin-authoring-roundtrip-sync/05_canonical_id/complete.log || find agent-task/archive -path '*/m-plane-origin-authoring-roundtrip-sync/05_canonical_id/complete.log' -print -quit
agent-task/archive/2026/06/m-plane-origin-authoring-roundtrip-sync/05_canonical_id/complete.log

$ cd services/core && go test -count=1 ./internal/gitosync ./internal/roadmapsyncpipeline ./internal/adapters/plane ./internal/workitem
ok  	github.com/nomadcode/nomadcode-core/internal/gitosync	1.497s
ok  	github.com/nomadcode/nomadcode-core/internal/roadmapsyncpipeline	0.004s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/plane	0.015s
ok  	github.com/nomadcode/nomadcode-core/internal/workitem	0.003s

$ cd services/core && go test -count=1 ./...
ok  	github.com/nomadcode/nomadcode-core/cmd/plane-smoke	0.004s
ok  	github.com/nomadcode/nomadcode-core/cmd/server	0.007s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/a2a	0.013s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/jira	0.011s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/mattermost	0.009s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/openai	0.011s
ok  	github.com/nomadcode/nomadcode-core/internal/adapters/plane	0.010s
?   	github.com/nomadcode/nomadcode-core/internal/agent	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/authoring	0.305s
ok  	github.com/nomadcode/nomadcode-core/internal/config	0.006s
?   	github.com/nomadcode/nomadcode-core/internal/db	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/gitoevents	0.009s
ok  	github.com/nomadcode/nomadcode-core/internal/gitosync	1.443s
ok  	github.com/nomadcode/nomadcode-core/internal/http	0.021s
?   	github.com/nomadcode/nomadcode-core/internal/model	[no test files]
ok  	github.com/nomadcode/nomadcode-core/internal/notification	0.005s
ok  	github.com/nomadcode/nomadcode-core/internal/projectsync	0.008s
ok  	github.com/nomadcode/nomadcode-core/internal/protosocket	0.014s
ok  	github.com/nomadcode/nomadcode-core/internal/roadmapsync	0.005s
ok  	github.com/nomadcode/nomadcode-core/internal/roadmapsyncpipeline	0.005s
ok  	github.com/nomadcode/nomadcode-core/internal/scheduler	2.039s
ok  	github.com/nomadcode/nomadcode-core/internal/storage	0.004s
ok  	github.com/nomadcode/nomadcode-core/internal/workflow	0.005s
ok  	github.com/nomadcode/nomadcode-core/internal/workitem	0.005s
ok  	github.com/nomadcode/nomadcode-core/internal/workitempipeline	0.006s
```

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail
  - Completeness: Fail
  - Test coverage: Fail
  - API contract: Fail
  - Code quality: Pass
  - Plan deviation: Warn
  - Verification trust: Warn
  - Spec conformance: Fail
- 발견된 문제:
  - Required: [service.go](/config/workspace/nomadcode/services/core/internal/roadmapsyncpipeline/service.go:142)의 missing-create branch는 `Expected.Provider == ""`일 때만 `CreateWorkItem`을 호출하지만, 실제 bridge missing-create enqueue는 [bridge.go](/config/workspace/nomadcode/services/core/internal/gitosync/bridge.go:145)에서 `Expected.Provider`를 `plane`으로 채운다. 이 실제 job은 create branch를 건너뛰고 [develop_match.go](/config/workspace/nomadcode/services/core/internal/roadmapsync/develop_match.go:80)의 `not_ready` 경로로 빠져 Plane item을 만들지 못하므로 S11 `plane-missing-create`가 충족되지 않는다. fix: bridge와 pipeline의 계약을 하나로 맞춰라. 예를 들어 pipeline은 existing row가 없고 `Expected.WorkItemID`/`Ref.ID`가 비어 있는 active milestone input을 missing-create로 판정하되 provider/tenant/project를 검증하고, bridge가 실제로 생성하는 args를 그대로 `SyncCreation`에 넣는 테스트를 추가한다.
  - Required: [service_test.go](/config/workspace/nomadcode/services/core/internal/roadmapsyncpipeline/service_test.go:421)의 missing-create 테스트는 provider를 비운 synthetic input만 검증한다. 하지만 [bridge_test.go](/config/workspace/nomadcode/services/core/internal/gitosync/bridge_test.go:344)는 bridge가 provider=`plane`을 넣는 것을 별도로 고정한다. 두 테스트가 서로 다른 계약을 검증해 실제 bridge -> scheduler -> pipeline 경로의 회귀를 놓쳤다. fix: bridge-produced missing-create `RoadmapCreationSyncJobArgs` 또는 같은 shape의 `SyncCreationInput`으로 `CreateWorkItem`이 정확히 한 번 호출되고 ledger에 created work item id가 저장되는 통합 단위 테스트를 추가한다.
- 다음 단계:
  - FAIL: active plan/review를 archive한 뒤 위 Required 이슈만 다루는 후속 `PLAN-local-G05.md`와 `CODE_REVIEW-local-G05.md`를 작성한다.
