# SDD: Agent-Origin Milestone Creation Sync

## 위치

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/agent-origin-milestone-creation-sync.md`
- Phase: `agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md`

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
  - 없음

## 문제 / 비목표

- 문제: Plane-origin 흐름은 Plane 요청에서 시작해 pushed develop SHA를 확인한 뒤 Plane Todo projection까지 수렴하지만, agent 또는 사람이 `agent-roadmap` active Milestone을 직접 수정해 push한 경우 provider identity가 없는 Milestone을 Plane work item과 안정적으로 연결하고 그 identity를 repo에 되돌려 쓰는 경로가 부족하다.
- 비목표:
  - Todo 이후 실행 lifecycle과 Plane 하위 티켓 동기화
  - MCP control surface 또는 외부 agent tool permission 정책
  - provider native API schema 일반화 또는 Jira 구현
  - 사용자 승인 없는 archive 이동

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | `agent-roadmap/phase/**/milestones/*.md` on `develop` | active Milestone markdown이 Plane projection의 본문/제목 기준이며 filename slug가 canonical milestone id다. |
| Code | `services/core/internal/gitosync/`, `services/core/internal/roadmapsyncpipeline/`, `services/core/internal/roadmapsync/` | Gito revision scan, identity matching, provider mutation, ledger/retry orchestration을 소유한다. |
| External Provider | Plane | Plane work item id와 state는 provider side effect 결과이며 raw token/secret은 tracked 문서에 기록하지 않는다. |
| User Decision | 없음 | 사용자는 agent-origin identity backfill 방향을 승인했고, 남은 항목은 기존 sync/domain rule로 구현 가능하다. |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `branch_event_received` | Gito `branch.updated` webhook이 target repo/branch로 들어온다. | `revision_verified` 또는 `not_ready` | `agent-contract/outer/gito-branch-webhook-consumer-v1.md` |
| `revision_verified` | local develop checkout fetch 후 `origin/develop`이 event `after` SHA와 일치한다. | `milestone_scanned` | `git fetch`, `git rev-parse`, `git ls-tree`, `git show` |
| `milestone_scanned` | active Milestone diff와 active index를 읽고 identity 유무를 분류한다. | `missing_identity_candidate`, `identity_matched`, `conflict` | roadmap parser, duplicate slug guard |
| `missing_identity_candidate` | provider identity가 없는 active Milestone이 생성 후보로 판별된다. | `plane_created` 또는 `conflict` | filename slug, project sync config, ledger lookup |
| `plane_created` | Plane work item 생성과 DB identity upsert가 성공한다. | `identity_backfilled` | Plane adapter, `roadmap_sync_identities` |
| `identity_backfilled` | Milestone markdown에 `Provider identity` block을 주입한 commit이 `develop`에 push된다. | `self_loop_checked` | Core-authored git commit/push |
| `identity_matched` | provider identity가 있는 Milestone 변경이다. | `plane_projected`, `complete`, `conflict` | roadmap revision guard, completed-step ledger |
| `self_loop_checked` | identity-only backfill commit webhook이 재수신된다. | `complete` 또는 `conflict` | actor/commit marker, markdown identity, ledger |
| `conflict` | slug reuse, identity mismatch, duplicate active slug, destructive overwrite 위험이 감지된다. | `conflict` | no provider mutation, operator/user review evidence |

## Interface Contract

- 계약 원문: `agent-contract/outer/gito-branch-webhook-consumer-v1.md`
- 입력:
  - `repo_id`: target repository id. `GITO_REPO_ID`와 일치해야 한다.
  - `branch`: target branch. 기본값은 `develop`이다.
  - `before`: 이전 git SHA. diff 후보 산정에 사용하되 source-of-truth로 단정하지 않는다.
  - `after`: 새 git SHA. fetch/scan 및 idempotency 기준이다.
  - `changed_files`: wakeup relevance 힌트. 실제 sync는 target revision scan으로 확인한다.
- 출력:
  - Plane work item 생성/갱신 결과
  - `roadmap_sync_identities` identity row와 completed step ledger
  - provider identity block backfill commit SHA
  - conflict/not-ready/complete/projected 로그와 job result
- 금지:
  - `changed_files`만 믿고 Plane 또는 roadmap 파일을 mutation하지 않는다.
  - provider identity가 충돌하는 Milestone을 덮어쓰지 않는다.
  - Core-authored backfill commit 재전달로 Plane work item을 중복 생성하지 않는다.
  - raw secret, token, provider credential을 tracked evidence나 로그에 남기지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `scan-diff` | Gito event가 `before`, `after`, `changed_files`를 포함한다. | Core가 fetch 후 `origin/develop`과 `after` SHA를 비교한다. | SHA가 일치할 때만 active Milestone scan이 진행되고, 불일치하면 not-ready로 남는다. |
| S02 | `missing-create` | active Milestone 파일이 추가됐고 provider identity가 없다. | `after` revision scan에서 생성 후보로 분류된다. | Plane work item이 1개만 생성되고 같은 revision 재처리는 중복 생성하지 않는다. |
| S03 | `identity-ledger` | 생성된 Plane work item id와 milestone slug가 있다. | identity row를 upsert한다. | 같은 slug/provider key는 재시도 가능하고, 다른 provider work item과 충돌하면 conflict로 멈춘다. |
| S04 | `identity-backfill` | Plane 생성과 identity ledger 기록이 완료됐다. | Core가 Milestone markdown에 provider identity block을 추가한다. | identity-only backfill commit이 `develop`에 push되고 unrelated 변경은 포함되지 않는다. |
| S05 | `self-loop` | backfill commit으로 다시 Gito webhook이 들어온다. | Core가 actor/commit marker, markdown identity, ledger를 비교한다. | Plane 생성/갱신이 반복되지 않고 complete/no-op으로 수렴한다. |
| S06 | `identity-update` | provider identity가 있는 Milestone markdown이 agent-origin으로 수정됐다. | Core가 target revision을 scan한다. | Plane 제목/본문 갱신이 ledger step으로 재시도 가능하게 수행된다. |
| S07 | `conflict-policy` | rename, slug reuse, stale provider item, duplicate active slug 중 하나가 발생한다. | Core가 sync를 시도한다. | destructive overwrite 없이 conflict/not-ready/skip 중 하나로 분류하고 provider mutation을 제한한다. |
| S08 | `field-smoke` | dev Plane/Gito/NomadCode/IOP 환경이 준비되어 있다. | agent가 identity 없는 active Milestone을 직접 push한다. | Plane ticket 생성, identity backfill commit, 재전달 no-op, secret-redacted evidence가 확인된다. |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | gitosync scanner/bridge unit test and webhook handler test | `agent-task/m-agent-origin-milestone-creation-sync/...` | `scan-diff` Roadmap Completion, test output, revision mismatch/not-ready evidence |
| S02 | roadmapsyncpipeline missing-create idempotency test | `agent-task/m-agent-origin-milestone-creation-sync/...` | `missing-create` Roadmap Completion, duplicate retry evidence |
| S03 | storage identity conflict/upsert test | `agent-task/m-agent-origin-milestone-creation-sync/...` | `identity-ledger` Roadmap Completion, conflict evidence |
| S04 | identity backfill writer test and git commit scope check | `agent-task/m-agent-origin-milestone-creation-sync/...` | `identity-backfill` Roadmap Completion, commit diff evidence |
| S05 | self-loop/redelivery test across process restart or durable ledger | `agent-task/m-agent-origin-milestone-creation-sync/...` | `self-loop` Roadmap Completion, no-op/complete evidence |
| S06 | provider identity update projection test | `agent-task/m-agent-origin-milestone-creation-sync/...` | `identity-update` Roadmap Completion, Plane body/title projection evidence |
| S07 | conflict classification tests | `agent-task/m-agent-origin-milestone-creation-sync/...` | `conflict-policy` Roadmap Completion, no destructive provider mutation evidence |
| S08 | dev field smoke with redacted logs/DB/Plane/git evidence | `agent-task/m-agent-origin-milestone-creation-sync/...` | `field-smoke` Roadmap Completion, final dev cycle evidence without secrets |

## Cross-repo Dependencies

- Gito provides `gito.forgejo-branch-events.v1`; NomadCode consumes it through `agent-contract/outer/gito-branch-webhook-consumer-v1.md`.

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰가 필요한 항목은 `USER_REVIEW.md`에만 남겼다.

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: `develop` branch의 active Milestone markdown이 roadmap source of truth이며, Gito webhook은 wakeup으로만 사용한다. Provider identity block은 repo portable anchor이고, DB ledger는 runtime idempotency/retry state다.
- 후속 SDD: Milestone Execution Lifecycle Sync에서 하위 티켓/agent-task lifecycle을 다룰 때 별도 SDD 필요 여부를 다시 판정한다.
