<!-- task=m-plane-origin-authoring-roundtrip-sync/07+05,06_slug_reconcile plan=0 tag=REFACTOR -->

# Plan - REFACTOR

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

`05_canonical_id`와 `06+05_plane_missing_create`의 `complete.log`가 없으면 구현을 시작하지 않는다. 구현 완료의 마지막 단계는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이다. finalization은 code-review 전용이며, 사용자 결정이 필요한 SDD/Milestone lock만 review stub의 `사용자 리뷰 요청`에 기록한다.

## 배경

같은 filename slug 재처리는 path/provider work item id/revision/step ledger만 갱신하거나 남은 step을 재개해야 한다. 동시에 active index 안에 같은 slug가 둘 이상 있거나 filename 재사용/rename 의심이 있으면 자동 병합하면 안 된다. SDD S12는 duplicate Plane item 없이 resume하고 slug conflict/reuse는 명시 failure로 멈추라고 요구한다.

## 사용자 리뷰 요청 흐름

구현 중 새 결정이 필요해 보여도 직접 질문하지 않는다. 선택된 SDD/Milestone lock 결정만 review stub의 `사용자 리뷰 요청`에 기록한다. 충돌 처리 구현 중 환경/검증 공백은 후속 plan 또는 검증 결과로 남긴다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-origin-authoring-roundtrip-sync.md`
- Task ids:
  - `slug-reconcile`: 같은 Milestone slug 재처리는 중복 Plane item을 만들지 않고 path, provider work item id, revision, 완료 step만 갱신한다.
- 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:
  - `S12`: task=`slug-reconcile`; evidence=`retry/idempotency tests for same slug reprocess, partial failure resume, and slug conflict/reuse failure`
- Completion mode: spec-check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/workspace-ops-smoke.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-origin-authoring-roundtrip-sync.md`
- `agent-roadmap/sdd/agent-ops-mcp-control-plane/plane-origin-authoring-roundtrip-sync/SDD.md`
- `services/core/internal/gitosync/scanner.go`
- `services/core/internal/gitosync/scanner_test.go`
- `services/core/internal/roadmapsync/identity.go`
- `services/core/internal/roadmapsync/identity_test.go`
- `services/core/internal/roadmapsync/retry.go`
- `services/core/internal/roadmapsync/retry_test.go`
- `services/core/internal/roadmapsyncpipeline/service.go`
- `services/core/internal/roadmapsyncpipeline/service_test.go`
- `services/core/internal/storage/roadmap_sync_identities.go`
- `services/core/internal/storage/roadmap_sync_identities_test.go`

### 테스트 환경 규칙

test_env=`local`. core 변경 최소 검증은 `cd services/core && go test ./...`다. 이 task는 live Plane smoke가 아니라 retry/idempotency unit tests로 닫는다.

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

- same slug + same revision 재처리에서 completed step ledger가 중복 provider call을 막는 테스트가 부족하다.
- same slug + partial failure resume이 path/provider work item id 속성 갱신과 충돌하지 않는 테스트가 부족하다.
- duplicate active slug 또는 filename reuse/rename 의심을 명시 failure로 반환하는 테스트가 없다.

### 심볼 참조

- 변경 후보: `ReconcileCreationCycle`, `ExistingIdentity`, `SyncCreation`, storage conflict mapping, scanner active index duplicate detection.
- removed/renamed symbol은 계획 단계에서는 없음.

### 분할 판단

split decision policy를 적용했다. 이 subtask는 `07+05,06_slug_reconcile`이며 predecessor `05_canonical_id`와 `06+05_plane_missing_create`가 필요하다. 현재 active/complete predecessor는 없으므로 구현 전 두 predecessor의 active 또는 archive `complete.log`를 확인해야 한다. slug conflict/retry behavior는 05/06 위에서만 의미가 있으므로 별도 dependent plan이 맞다.

### 범위 결정 근거

이 subtask는 duplicate prevention과 conflict/resume semantics만 다룬다. Plane live UI/API evidence, slot finalizer, full-cycle smoke는 제외한다. `gito-delivery`, `scan-enqueue`, `plane-todo`, `slot-finalize` Task는 후속 Epic 작업이다.

### 빌드 등급

`cloud-G05`: retry/idempotency behavior가 cross-module이지만 05/06 foundation 뒤에는 unit/fake tests로 닫을 수 있다.

## 구현 체크리스트

- [ ] 선행 `05_canonical_id`와 `06+05_plane_missing_create` complete evidence를 확인하고 없으면 구현하지 않는다.
- [ ] scanner 또는 roadmapsync active index layer에서 duplicate active Milestone slug를 감지해 명시 failure로 반환한다.
- [ ] `ReconcileCreationCycle`/pipeline tests를 같은 slug same revision, partial failure resume, changed path/provider work item attribute update 케이스로 보강한다.
- [ ] slug conflict/reuse/rename 의심은 자동 병합하지 않고 typed conflict action/reason으로 멈추게 한다.
- [ ] `cd services/core && go test ./internal/gitosync ./internal/roadmapsync ./internal/storage ./internal/roadmapsyncpipeline`와 `cd services/core && go test ./...`를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

`07+05,06_slug_reconcile`는 디렉터리명 기준으로 predecessor `05_canonical_id`, `06+05_plane_missing_create`에 의존한다. 구현 전 두 predecessor의 `complete.log`가 active 또는 archive에 있어야 한다. 누락이면 구현을 시작하지 않는다.

### [REFACTOR-1] Duplicate Slug Detection

문제: active scanner는 현재 active paths를 나열하지만 같은 filename slug가 두 active phase에 동시에 있는 경우를 명시 conflict로 모델링하지 않는다. SDD S12는 slug 충돌 또는 파일명 재사용을 자동 병합하지 말라고 한다.

해결 방법: active index layer에서 `MilestoneID`별 path set을 만들고 같은 id가 둘 이상이면 typed error/result를 반환한다. archive candidate는 충돌 대상이 아니라 history lookup 후보로만 남긴다.

수정 파일 및 체크리스트:
- [ ] `services/core/internal/gitosync/scanner.go`
- [ ] `services/core/internal/gitosync/scanner_test.go`
- [ ] 필요 시 `services/core/internal/roadmapsync/identity.go`

테스트 작성: 작성한다. 두 active path가 같은 `m1.md`를 가질 때 scanner가 enqueue 없이 명시 error를 반환하고, active+archive 같은 slug는 error가 아닌 archive candidate로 남는 테스트를 추가한다.

중간 검증: `cd services/core && go test ./internal/gitosync`.

### [REFACTOR-2] Same Slug Retry And Conflict Semantics

문제: [retry.go](/config/workspace/nomadcode/services/core/internal/roadmapsync/retry.go:106)는 persisted identity와 trigger가 strict match하지 않으면 conflict를 반환한다. 05 이후 strict match는 provider/work item/path가 아니라 canonical slug 중심이어야 하며, revision mismatch와 duplicate slug는 여전히 conflict여야 한다.

해결 방법: `ExistingIdentity` 비교를 canonical id 중심으로 바꾸고, path/provider work item id는 safe attribute update로 허용한다. same slug + same revision + completed steps는 complete/resume하고, same slug + different roadmap revision은 기존 guard처럼 conflict한다. filename reuse 의심은 scanner/pipeline의 typed conflict reason으로 전달한다.

수정 파일 및 체크리스트:
- [ ] `services/core/internal/roadmapsync/retry.go`
- [ ] `services/core/internal/roadmapsync/retry_test.go`
- [ ] `services/core/internal/storage/roadmap_sync_identities.go`
- [ ] `services/core/internal/storage/roadmap_sync_identities_test.go`
- [ ] `services/core/internal/roadmapsyncpipeline/service.go`
- [ ] `services/core/internal/roadmapsyncpipeline/service_test.go`

테스트 작성: 작성한다. same slug/same revision no duplicate provider call, partial completed ledger resumes at first incomplete step, changed provider work item id updates row without creating a second Plane item, different roadmap revision conflicts.

중간 검증: `cd services/core && go test ./internal/roadmapsync ./internal/storage ./internal/roadmapsyncpipeline`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/gitosync/scanner.go` | REFACTOR-1 |
| `services/core/internal/gitosync/scanner_test.go` | REFACTOR-1 |
| `services/core/internal/roadmapsync/identity.go` | REFACTOR-1 |
| `services/core/internal/roadmapsync/retry.go` | REFACTOR-2 |
| `services/core/internal/roadmapsync/retry_test.go` | REFACTOR-2 |
| `services/core/internal/storage/roadmap_sync_identities.go` | REFACTOR-2 |
| `services/core/internal/storage/roadmap_sync_identities_test.go` | REFACTOR-2 |
| `services/core/internal/roadmapsyncpipeline/service.go` | REFACTOR-2 |
| `services/core/internal/roadmapsyncpipeline/service_test.go` | REFACTOR-2 |

## 최종 검증

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

`test -f agent-task/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/complete.log || find agent-task/archive -path '*/m-plane-origin-authoring-roundtrip-sync/06+05_plane_missing_create/complete.log' -print -quit`

`cd services/core && go test ./internal/gitosync ./internal/roadmapsync ./internal/storage ./internal/roadmapsyncpipeline`

`cd services/core && go test ./...`

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