# SDD: Gito HTTP Webhook Consumer Readiness

## 위치

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-http-webhook-consumer-readiness.md`
- Phase: `agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md`

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
  - [x] [D01] Gito -> NomadCode 외부 branch event delivery는 `/proto-socket`이 아니라 signed HTTP webhook/callback을 따른다.
  - [x] [D02] NomadCode는 `branch.updated` delivery를 wakeup signal로만 사용하고 target branch revision을 재검증한다.
  - [x] [D03] Gito outbound webhook delivery 구현 완료 전에는 NomadCode 실제 연동 구현/검증을 완료로 닫지 않는다.

## 문제 / 비목표

- 문제: NomadCode의 기존 Gito branch event consumer는 `/proto-socket` wire consumer로 정리되어 있었지만, Gito의 현재 외부/인접 consumer 계약은 signed HTTP webhook delivery다. NomadCode는 Gito 외부 소비 표면을 HTTP callback으로 재정렬하면서도 기존 `branch.updated` wakeup, target branch revision 재검증, creation sync enqueue 의미 로직을 보존해야 한다.
- 비목표:
  - Gito의 Forgejo webhook producer, outbound webhook delivery worker, subscription API 구현
  - Gito 제공 계약 `gito.forgejo-branch-events.v1` 원문 변경
  - Gito event schema를 NomadCode 전용 필수 필드로 확장
  - Plane `Todo -> In Progress` 실행 lifecycle, Milestone Task와 Plane 하위 티켓 동기화, Flutter UI 표시

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-http-webhook-consumer-readiness.md` | NomadCode 구현 범위, Task id, 완료 리뷰 기준 |
| Code | `services/core/internal/http/`, `services/core/internal/gitoevents/`, `services/core/internal/gitosync/`, `services/core/internal/config/` | HTTP callback, payload decode, branch scan/enqueue, config/env source of truth |
| External Provider | Gito `../gito/agent-contract/provided/gito-forgejo-branch-events-v1.md` | `branch.updated` HTTP delivery contract source of truth. SDD는 원문 계약을 복제하지 않는다. |
| User Decision | 없음 | 사용자가 Gito external consumer 표면을 HTTP webhook/callback으로 정렬하라고 지시했다. |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| disabled | webhook callback 설정 또는 signing secret 설정이 부족하다 | listening | config/env가 충족되고 core가 callback route를 등록한다 |
| listening | NomadCode Core가 Gito HTTP delivery를 받을 준비가 됐다 | accepted | `X-Gito-Event=branch.updated`, signature, payload shape가 유효하다 |
| listening | event, signature, payload가 유효하지 않다 | rejected | HTTP 4xx와 side effect 없음 |
| accepted | delivery id 또는 revision key가 이미 처리됐다 | duplicate_ignored | idempotency guard |
| accepted | on-target repo/branch이고 duplicate가 아니다 | scanning | `repo_id`, `branch`, `before`, `after`, `changed_files` wakeup hint |
| scanning | target branch fetch/scan이 아직 delivery revision을 확인하지 못했다 | retry_or_drop | not-ready revision failure policy |
| scanning | target branch revision과 roadmap-related changes가 확인됐다 | enqueued | `EnqueueRoadmapCreationSync` 호출 |
| enqueued | creation sync job enqueue 성공 | complete | job id 또는 enqueue success log |

## Interface Contract

- 계약 원문: `../gito/agent-contract/provided/gito-forgejo-branch-events-v1.md`
- 입력:
  - `X-Gito-Event`: normalized event type. NomadCode는 `branch.updated`만 처리한다.
  - `X-Gito-Delivery`: stable delivery id. NomadCode idempotency key 후보로 사용한다.
  - `X-Gito-Signature`: `secret_ref` 기반 HMAC-SHA256 digest. 설정된 경우 검증해야 한다.
  - JSON body: Gito `branch.updated` base payload. NomadCode는 `repo_id`, `branch`, `before`, `after`, `changed_files`, timestamp/id 계열을 wakeup hint로 소비한다.
- 출력:
  - HTTP response: accepted/rejected 상태. invalid signature나 malformed payload는 side effect 없이 거부한다.
  - Internal event: on-target delivery는 branch revision scan과 creation sync enqueue 후보로 전달한다.
  - Logs/metrics: invalid signature, malformed payload, duplicate delivery, not-ready revision, enqueue failure를 secret 없이 기록한다.
- 금지:
  - Gito 외부 consumer delivery에 `/proto-socket`을 요구하지 않는다.
  - `changed_files` 또는 webhook payload만으로 Plane/roadmap source-of-truth mutation을 확정하지 않는다.
  - duplicate delivery로 중복 Plane mutation이나 중복 creation sync job을 만들지 않는다.
  - raw signing secret, token, webhook body 원문을 tracked docs나 logs에 남기지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `receiver` | NomadCode Core에 callback route와 signing secret이 설정되어 있다 | Gito가 signed `branch.updated` HTTP delivery를 보낸다 | 요청이 수락되고 wrong event/invalid signature는 side effect 없이 거부된다 |
| S02 | `payload-boundary` | Gito base payload에 NomadCode 전용 필드가 없다 | on-target `repo_id`, `branch`, revision fields를 가진 delivery를 받는다 | payload가 decode되고 NomadCode 전용 필드 없이 wakeup 후보가 된다 |
| S03 | `idempotency` | 같은 `X-Gito-Delivery` 또는 같은 revision key delivery가 재전달된다 | consumer가 재시도 delivery를 처리한다 | 중복 enqueue 없이 한 번의 sync 후보로 수렴한다 |
| S04 | `scan-enqueue` | on-target delivery가 accepted 상태다 | target branch fetch/scan이 delivery revision을 확인한다 | `EnqueueRoadmapCreationSync`가 정확히 한 번 호출된다 |
| S05 | `config-docs` | local/dev 환경에서 Gito webhook consumer를 켜야 한다 | operator가 docs/env sample을 따른다 | secret 원문 없이 callback endpoint, signing secret ref, subscription bootstrap 경로를 구성할 수 있다 |
| S06 | `failure-ops` | invalid signature, malformed payload, duplicate delivery, not-ready revision, Gito retry가 들어온다 | consumer가 장애 입력을 처리한다 | core server shutdown이나 중복 Plane mutation 없이 reject/drop/retry 후보로 기록된다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | `Spec Completion` 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | HTTP handler unit/integration test, invalid signature test | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `receiver` Task와 S01 PASS, 검증 명령과 결과 |
| S02 | payload decode/filter tests without NomadCode-specific fields | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `payload-boundary` Task와 S02 PASS, fixture 이름과 결과 |
| S03 | duplicate delivery/revision idempotency test | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `idempotency` Task와 S03 PASS, duplicate enqueue 방지 근거 |
| S04 | bridge test proving accepted HTTP delivery reaches branch scan and one enqueue | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `scan-enqueue` Task와 S04 PASS, enqueue count/result |
| S05 | README/compose/env sample diff plus docs keyword scan | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `config-docs` Task와 S05 PASS, secret 원문 없음 근거 |
| S06 | failure case tests and log assertion without secret leakage | `agent-task/m-gito-http-webhook-consumer-readiness/...` | `failure-ops` Task와 S06 PASS, failure handling evidence |

## Cross-repo Dependencies

- `gito:agent-roadmap/phase/provider-change-request-integration/milestones/webhook-revision-reconcile.md`
- `.agent-roadmap-sync/locks.yaml` entry: `nomadcode:gito-http-webhook-consumer-readiness`
- 현재 lock 상태: `enable`. Gito의 consumer webhook subscription과 signed HTTP delivery 구현이 완료되어 NomadCode Milestone의 구현 잠금 해제 조건을 충족했다.

## Drift Check

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

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: NomadCode는 Gito `branch.updated` delivery를 wakeup signal로만 소비하고, 최종 판단은 target branch fetch/scan과 provider identity 재검증으로 수행한다. Gito external consumer delivery는 HTTP webhook/callback이고 `/proto-socket`은 Gito 내부 control/runtime transport로 본다.
- 후속 SDD: 없음
