# SDD: Plane-Origin Authoring Roundtrip Sync

## 위치

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

## 상태

[승인됨]

## SDD 잠금

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

## 문제 / 비목표

- 문제: Plane `Backlog + AGENT assignee`에서 시작한 Milestone authoring은 사용자 prompt, IOP workspace 실행, git push, Gito webhook, provider identity, Plane projection을 한 사이클로 묶기 때문에 source of truth와 terminal 조건이 분산될 수 있다.
- 비목표:
  - Plane `Todo -> In Progress` 이후 실행 lifecycle은 다루지 않는다.
  - Milestone Task를 Plane 하위 티켓으로 생성하거나 plan/code-review 루프와 연결하지 않는다.
  - Agent-origin Milestone 생성 뒤 Plane 티켓을 만드는 역방향 생성 sync는 다루지 않는다.
  - IOP 내부 모델 라우팅, queue 구현, RAG, output validation은 다루지 않는다.
  - 외부 agent 제어 표면 또는 MCP tool policy는 확정하지 않는다.

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/plane-origin-authoring-roundtrip-sync.md` | Milestone 목표, 기능 Task, 범위 제외, 완료 리뷰의 기준이다. |
| Code | `services/core/internal/authoring/`, `services/core/internal/scheduler/`, `services/core/internal/gitosync/`, `services/core/internal/roadmapsync/`, `services/core/internal/roadmapsyncpipeline/`, `services/core/internal/projectsync/`, `services/core/internal/workitempipeline/` | authoring input, state metadata, Gito scan, identity match, Plane projection, slot finalization의 구현 기준이다. |
| External Provider | Plane, IOP OpenAI-compatible Responses, Forgejo, Gito | Plane work item id/state, IOP response lifecycle, remote `develop` revision, signed Gito delivery가 외부 관찰 기준이다. |
| User Decision | 없음 | 현재 Milestone 범위와 실패 처리 기준은 기존 roadmap/domain rule 표준선으로 확정 가능하다. |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `plane_backlog_assigned` | Plane work item이 `Backlog + AGENT assignee` 조건을 만족한다. | `core_task_created`, `ignored` | Plane webhook 또는 intake scan, work item payload |
| `core_task_created` | Core task와 checkout metadata가 생성된다. | `authoring_in_progress`, `authoring_blocked` | `workitempipeline`, task metadata `checkout` |
| `authoring_in_progress` | IOP Responses authoring 요청을 시작한다. | `authoring_generated`, `authoring_waiting`, `authoring_failed` | `authoring_run_state`, `authoring_run_updated_at`, IOP response result |
| `authoring_generated` | authoring 결과가 workspace checkout에 반영된다. | `push_requested`, `authoring_failed` | model response, workspace dirty state |
| `push_requested` | Core/runtime이 push-only workspace agent 요청을 보낸다. | `develop_pushed`, `push_failed`, `authoring_waiting` | push-only task metadata, remote `develop` verification |
| `develop_pushed` | remote `develop` revision이 변경된다. | `gito_delivered`, `scan_waiting` | Forgejo revision, Gito delivery |
| `gito_delivered` | Gito signed HTTP webhook이 Core에 도착한다. | `develop_scan_enqueued`, `delivery_duplicate`, `delivery_failed` | signed delivery 202, Core gito webhook log |
| `develop_scan_enqueued` | Core가 local develop checkout을 fetch/scan하고 creation sync job을 enqueue한다. | `develop_matched`, `identity_missing`, `identity_mismatch`, `milestone_path_missing` | `gitosync` scan, `roadmap_sync_identities`, job enqueue |
| `develop_matched` | pushed Milestone path와 provider identity가 원래 Plane 티켓과 일치한다. | `plane_projected`, `projection_failed` | `roadmap_sync_steps`, provider identity ledger |
| `plane_projected` | Plane 본문/제목/comment/status projection이 완료된다. | `slot_available` | Plane API/UI, `roadmap_sync_steps` |
| `slot_available` | 성공 terminal 상태를 기록한다. | 없음 | workspace slot state `available`, task metadata |
| `authoring_failed` | timeout, queue timeout, bridge failure, identity failure 등 terminal failure가 확정된다. | `slot_error`, `slot_dirty` | failure category, slot finalizer |
| `slot_dirty` | 수동 복구가 필요한 git dirty 또는 push failure 상태다. | 없음 | slot state `dirty`, task metadata |
| `slot_error` | bridge/provider 오류 또는 복구 불가 failure 상태다. | 없음 | slot state `error`, task metadata |

## Interface Contract

- 계약 원문: 없음
- 입력:
  - Plane work item: title, description/comment body, assignee, state, provider work item id, external URL.
  - Core task external ref: provider, external id, external metadata, payload `work_item`.
  - checkout metadata: slot id, slot path, source branch, project sync context.
  - IOP Responses request: thin authoring instruction, user-origin message, workspace metadata, optional push-only request.
  - Gito webhook: signed push delivery, repository/ref/revision metadata.
- 출력:
  - workspace changes on local checkout and remote `develop` commit.
  - `roadmap_sync_identities` and `roadmap_sync_steps` records for identity, revision, projection, retry/idempotency state.
  - Plane original comment preservation, body/title projection, `Todo` state move.
  - task metadata and workspace slot terminal state.
- 금지:
  - Plane/provider identity나 sync 계약 문구를 기본 authoring 사용자 prompt에 섞지 않는다.
  - 첫 authoring 응답 성공만으로 Plane projection 또는 task completion을 단정하지 않는다.
  - Gito webhook 202 또는 remote commit만으로 full-cycle 완료를 단정하지 않는다.
  - identity missing, identity mismatch, milestone path missing, push failure를 Plane `Todo` projection으로 넘기지 않는다.
  - tracked docs, roadmap, task logs, 최종 응답에 secret 원문을 기록하지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `thin-prompt` | Plane-origin authoring task에 explicit instruction override가 없다. | `BuildAuthoringGenerateInput`이 기본 instruction을 만든다. | instruction은 `다음 메시지를 바탕으로 마일스톤을 작성해.` 수준으로 얇고 Plane/provider 계약 문구를 포함하지 않는다. |
| S02 | `iop-long-run` | IOP Responses authoring이 10분 이상 걸리거나 queue 대기 중이다. | scheduler가 timeout, retry, stale 판단을 수행한다. | 작업은 조기 `failed`로 확정되지 않고 관찰 가능한 in-progress/stale metadata를 남긴다. |
| S03 | `iop-progress` | dev IOP가 streaming을 지원하거나 지원하지 않는다. | Core가 `/v1/responses` 진행 상태를 기록한다. | stream 미지원은 명시 사유로 기록되고, stream 지원 시 진행 갱신 중단을 감지할 수 있다. |
| S04 | `push-command` | authoring 결과 작성은 성공했지만 remote `develop` 확인은 아직 없다. | Core/runtime이 push-only workspace agent 요청을 보낸다. | push-only 단계와 remote `develop` 확인 전에는 다음 projection 단계로 넘어가지 않는다. |
| S05 | `identity-source` | Plane work item identity가 task external ref, payload, checkout metadata, project sync 설정에 있다. | authoring input과 projection input을 만든다. | 사용자 작성 문구에 의존하지 않고 identity를 유지하며, 누락 시 명시 오류로 멈춘다. |
| S06 | `identity-write` | generated Milestone에 provider identity block이 없다. | Gito callback 이후 scan/match가 실행된다. | Core/runtime 주입 또는 sync ledger로 match 가능 상태가 되거나 명시 failure로 수렴한다. |
| S07 | `develop-match` | remote `develop` revision에 Milestone path 변경 후보가 있다. | Core가 provider identity와 원래 Plane 티켓을 비교한다. | path와 identity가 모두 일치할 때만 Plane projection을 실행한다. |
| S08 | `idempotency-retry` | 같은 Plane 티켓, Milestone path, Gito revision이 재처리된다. | creation sync가 재실행된다. | 중복 Milestone/Plane mutation 없이 남은 단계만 재시도하고 충돌은 중단한다. |
| S09 | `gito-delivery` | Forgejo push가 Gito subscription 대상 branch에 발생한다. | Gito가 Core signed HTTP webhook을 보낸다. | Core는 delivery를 202로 받고 duplicate 처리를 secret 없이 확인할 수 있다. |
| S10 | `scan-enqueue` | Gito wakeup 후 local develop checkout이 fetch/scan된다. | provider identity가 있는 Milestone 변경을 발견한다. | 정확히 하나의 `roadmap_creation_sync` job을 enqueue하고 identity 없는 변경은 명시 사유를 남긴다. |
| S11 | `plane-todo` | develop match가 완료된 creation sync job이 있다. | Plane projection을 실행한다. | 원문 댓글 보존, 본문 projection, `[milestone-id]` 제목 갱신, `Todo` 이동이 기록된다. |
| S12 | `slot-finalize` | success/failure/timeout/push failed/identity missing 결과가 확정된다. | slot finalizer가 task metadata와 slot state를 갱신한다. | 성공은 `available`, 수동 git 복구는 `dirty`, bridge/provider 오류는 `error`로 수렴한다. |
| S13 | `remote-baseline` | standard remote runner와 dev services 상태가 stale/dirty일 수 있다. | reset/clean/reclone 또는 rebuild로 테스트 기준을 맞춘다. | dirty runner 상태가 차단 사유가 되지 않고 현재 요청 범위와 같은 검증 환경이 된다. |
| S14 | `full-cycle` | 새 Plane Backlog 티켓이 AGENT에 assign된다. | Core task, IOP authoring, push-only, Gito callback, sync projection까지 실행한다. | Plane `Todo` projection과 `roadmap_sync_steps` full-cycle evidence가 남는다. |
| S15 | `regression-doc` | 회귀 원인과 완료 오인 조건이 확인된다. | 운영/테스트 문서를 갱신한다. | identity 누락, IOP queue timeout, push-only 누락, delivery-only 성공을 완료로 오인하지 않는 기준이 문서화된다. |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | `Spec Completion` 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | `services/core/internal/authoring` unit test showing thin default instruction and forbidden provider terms absent | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `thin-prompt`: authoring request test passed and default instruction text/provider-term absence recorded |
| S02 | core config/scheduler test or smoke evidence for long-running timeout/retry/stale metadata behavior | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `iop-long-run`: timeout/retry/stale thresholds and non-premature failure evidence recorded |
| S03 | IOP streaming capability check and fallback stale-monitor evidence | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `iop-progress`: stream supported path or stream unsupported explicit reason plus stale monitor evidence recorded |
| S04 | scheduler/runtime test or smoke showing push-only request and remote `develop` verification gate | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `push-command`: authoring success does not complete without push-only and remote develop check |
| S05 | authoring/workitempipeline test showing workspace metadata identity retention and missing identity failure | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `identity-source`: external ref/payload/checkout/project sync identity source evidence recorded |
| S06 | gitosync/roadmapsync test or smoke showing identity block absence handled by deterministic injection/ledger or explicit failure | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `identity-write`: identity-less generated Milestone does not silently drop |
| S07 | develop match test covering dirty slot, push failure, mismatch, missing path, and successful match | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `develop-match`: only path+identity match triggers Plane projection |
| S08 | retry/idempotency tests for repeated Plane ticket, Milestone path, Gito revision, and partial failure recovery | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `idempotency-retry`: duplicate mutation prevention and retry/abort distinction recorded |
| S09 | secret-redacted live evidence for Forgejo -> Gito -> Core signed delivery and duplicate handling | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `gito-delivery`: signed delivery 202, Core log, duplicate result recorded without secret |
| S10 | gitosync/roadmapsyncpipeline test or smoke proving one creation sync job enqueue and identity-missing reason | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `scan-enqueue`: scan-to-enqueue and identity-missing evidence recorded |
| S11 | Plane UI/API plus `roadmap_sync_steps` evidence for comment/body/title/Todo projection | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `plane-todo`: original comment, body update, title update, Todo move recorded |
| S12 | scheduler/projectsync finalizer tests or smoke for `available`, `dirty`, `error` terminal mapping | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `slot-finalize`: terminal task metadata and slot state evidence recorded |
| S13 | standard remote runner setup log with branch/revision and clean/reclone/rebuild actions as needed | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `remote-baseline`: runner baseline evidence recorded |
| S14 | `agent-test/live-cycle-smoke.md` run evidence with secret redaction from Plane Backlog to Plane Todo | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `full-cycle`: full-cycle live evidence recorded |
| S15 | doc diff in `agent-test/live-cycle-smoke.md` or core README covering regression prevention criteria | `agent-task/m-plane-origin-authoring-roundtrip-sync/...` | `regression-doc`: regression criteria documented |

## Cross-repo Dependencies

- 없음

## Drift Check

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

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: Plane 티켓 본문과 authoring prompt는 사용자 요청 원문 중심으로 유지하고, provider identity, project sync 설정, push 검증, Gito delivery, Plane projection은 Core/runtime의 결정적 책임으로 둔다.
- 후속 SDD: 없음
