9.9 KiB
9.9 KiB
SDD: Webhook Revision Reconcile
위치
- Milestone:
agent-roadmap/archive/phase/provider-change-request-integration/milestones/webhook-revision-reconcile.md - Phase:
agent-roadmap/phase/provider-change-request-integration/PHASE.md
상태
[승인됨]
SDD 잠금
- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
- 없음
문제 / 비목표
- 문제: NomadCode가 Gito의
branch.updatedevent를 generic HTTP webhook wakeup으로 받을 수 있어야 한다. 동시에 webhook delivery는 최종 source of truth가 아니며, delivery idempotency, retry, signature, revision 재검증 경계를 구현 전에 고정해야 한다. - 비목표:
- NomadCode 전용 필수 payload field를 base schema에 추가하지 않는다.
- provider별 모든 webhook payload를 이 Milestone에서 지원하지 않는다.
- webhook delivery 성공을 NomadCode 또는 다른 consumer의 상태 적용 완료 증거로 취급하지 않는다.
- proto-socket, gRPC, Redis Streams fanout을 external consumer delivery 경로로 추가하지 않는다.
- UI conflict resolution 화면은 이 Milestone에서 다루지 않는다.
Source of Truth
| 영역 | 기준 | 메모 |
|---|---|---|
| Roadmap | agent-roadmap/phase/provider-change-request-integration/milestones/webhook-revision-reconcile.md |
Milestone 목표, 범위, 기능 Task 완료 기준 |
| Code | services/core/internal/controlplane/, services/core/internal/events/, services/core/internal/worker/ |
REST subscription/delivery surface, normalized event, durable delivery/outbox 구현 기준 |
| External Provider | Forgejo push webhook, Gito outbound HTTP webhook consumer | Forgejo push는 wakeup source, consumer delivery는 branch.updated wakeup signal |
| User Decision | 없음 | 기존 gito.forgejo-branch-events.v1 계약을 따른다 |
State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|---|---|---|---|
| webhook-received | Forgejo push callback이 유효 payload로 수신된다 | event-recorded 또는 ignored | POST /callbacks/forgejo/push, watch match 여부 |
| ignored | event가 unsupported 또는 unwatched branch에 해당한다 | 없음 | response accepted=true, matched=false |
| event-recorded | watched branch의 before/after revision이 normalized branch.updated로 기록된다 |
delivery-pending | durable event/outbox record |
| delivery-pending | matching webhook subscription이 존재한다 | delivery-sending | subscription event/repo/branch filter |
| delivery-sending | worker가 pending delivery를 HTTP POST로 전송한다 | delivery-succeeded 또는 delivery-retryable 또는 delivery-failed | target HTTP response, timeout, signature config |
| delivery-succeeded | consumer endpoint가 2xx를 반환한다 | 없음 | delivery attempt record |
| delivery-retryable | consumer endpoint가 429 또는 5xx를 반환하거나 transient network failure가 발생한다 | delivery-pending 또는 delivery-failed | retry/backoff policy |
| delivery-failed | consumer endpoint가 non-retryable response를 반환하거나 retry budget이 소진된다 | 없음 | delivery attempt state |
| reconcile-conflict | expected revision이 현재 source of truth와 맞지 않는다 | dry-run-result 또는 없음 | Git revision scan 또는 provider read API |
| applied | idempotency key로 중복 적용이 차단되고 change apply가 성공한다 | 없음 | apply result and idempotency record |
Interface Contract
- 계약 원문:
agent-contract/provided/gito-forgejo-branch-events-v1.md - 입력:
- Forgejo callback:
POST /callbacks/forgejo/push - branch watch:
POST /api/listeners/branches - webhook subscription:
POST /api/webhook-subscriptions - subscription filter:
events,repo_id,branch - signing reference:
secret_ref - revision identity:
repo_id,branch,before,after
- Forgejo callback:
- 출력:
- normalized event:
branch.updated - webhook headers:
X-Gito-Event,X-Gito-Delivery, optionalX-Gito-Signature - consumer payload:
id,type,provider,delivery_id,repo_id,branch,before,after,changed_files,observed_at,created_at - delivery attempt state: pending, sending, succeeded, retryable, failed
- normalized event:
- 금지:
- raw secret, token, password, credential 값을 tracked docs, event payload, log에 남기지 않는다.
- NomadCode 전용 필수 field를 base
branch.updatedpayload에 추가하지 않는다. - provider callback이 NomadCode, Plane, roadmap source of truth를 직접 변경하지 않는다.
- consumer webhook delivery를 최종 상태 증거로 취급하지 않는다.
- external consumer delivery에 proto-socket을 요구하지 않는다.
Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|---|---|---|---|---|
| S01 | subscription |
consumer endpoint, event type, repo/branch filter, secret_ref가 입력된다 |
webhook subscription을 등록한다 | subscription id와 filter가 저장되고 raw secret은 저장되지 않는다 |
| S02 | deliver |
watched nomadcode/develop branch에서 branch.updated event가 기록되고 matching subscription이 있다 |
delivery worker가 event를 처리한다 | fake consumer가 signed HTTP POST, delivery id, base payload를 수신한다 |
| S03 | nomadcode-wakeup |
NomadCode dev consumer가 generic branch.updated endpoint를 제공한다 |
Gito가 matching delivery를 전송한다 | consumer가 NomadCode 전용 필수 field 없이 wakeup으로 매핑할 수 있다 |
| S04 | retry |
consumer가 429 또는 5xx를 반환한다 | delivery attempt가 기록된다 | retry/backoff 상태가 PostgreSQL 기준으로 남고 delivery idempotency가 유지된다 |
| S05 | webhook-source |
Forgejo push callback이 들어온다 | payload가 watch와 match된다 | provider.webhook.received 또는 equivalent wakeup source event와 normalized branch event가 추적 가능하다 |
| S06 | poll-source |
provider webhook이 누락되거나 지연된다 | polling fallback shape를 사용한다 | 같은 revision identity로 change source를 표현할 수 있다 |
| S07 | git-source |
Git revision scan이 before/after revision을 알고 있다 | provider event와 scan result를 reconcile한다 | 동일 repo/branch/revision identity로 중복 이벤트를 피한다 |
| S08 | expected-rev |
expected revision과 현재 branch revision이 다르다 | reconcile을 시도한다 | apply 없이 conflict로 멈춘다 |
| S09 | dry-run |
event와 현재 Git/provider 상태가 주어진다 | dry-run을 실행한다 | drift, conflict, proposal이 관찰 가능한 결과로 반환된다 |
| S10 | apply |
같은 event 또는 idempotency key가 반복 처리된다 | apply를 두 번 요청한다 | side effect는 한 번만 실행되고 중복은 idempotent 결과로 수렴한다 |
Evidence Map
| Scenario | Required Evidence | agent-task 연결 |
Spec Completion 기대 |
|---|---|---|---|
| S01 | subscription create/list test, secret value grep | agent-task/m-webhook-revision-reconcile/... |
subscription task가 contract filter와 secret_ref storage boundary를 만족했다 |
| S02 | cd services/core && go test -run TestWebhookDeliveryBranchUpdated ./internal/controlplane/ -v -count=1 |
agent-task/m-webhook-revision-reconcile/... |
deliver task가 fake consumer delivery id, headers, payload 수신을 증명했다 |
| S03 | NomadCode interop smoke 또는 fake NomadCode consumer test | agent-task/m-webhook-revision-reconcile/... |
nomadcode-wakeup task가 generic branch.updated만으로 wakeup mapping을 증명했다 |
| S04 | delivery retry/backoff unit or integration test with PostgreSQL store | agent-task/m-webhook-revision-reconcile/... |
retry task가 retryable/non-retryable status와 delivery idempotency를 증명했다 |
| S05 | Forgejo callback test and event inspection evidence | agent-task/m-webhook-revision-reconcile/... |
webhook-source task가 provider callback을 wakeup source로 저장/추적함을 증명했다 |
| S06 | polling fallback shape contract or unit test | agent-task/m-webhook-revision-reconcile/... |
poll-source task가 polling source를 동일 change model로 표현함을 증명했다 |
| S07 | revision identity dedupe/reconcile test | agent-task/m-webhook-revision-reconcile/... |
git-source task가 Git revision scan과 provider event identity 연결을 증명했다 |
| S08 | expected revision mismatch test | agent-task/m-webhook-revision-reconcile/... |
expected-rev task가 mismatch conflict stop을 증명했다 |
| S09 | reconcile dry-run test with drift/conflict/proposal output | agent-task/m-webhook-revision-reconcile/... |
dry-run task가 apply 없이 observable proposal 결과를 증명했다 |
| S10 | idempotent apply test with repeated event/key | agent-task/m-webhook-revision-reconcile/... |
apply task가 repeated processing side effect once를 증명했다 |
Cross-repo Dependencies
- NomadCode는 external consumer로
branch.updateddelivery를 수신한다. 현재 SDD는.agent-roadmap-sync/locks.yamlentry를 요구하지 않는다. - 계약 기준은
agent-contract/provided/gito-forgejo-branch-events-v1.md이다.
Drift Check
- Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- Evidence Map이 plan/code-review/complete.log에서 검증 가능하다.
- agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- 사용자 리뷰가 필요한 항목은
USER_REVIEW.md에만 남겼다.
사용자 리뷰 이력
- 없음
작업 컨텍스트
- 표준선: webhook은 빠른 wakeup signal이며 최종 판정은 Git revision/diff 또는 provider read API로 수행한다. 외부 event delivery는 HTTP webhook/callback이고, proto-socket은 내부 control/runtime 규약으로만 남긴다.
- 후속 SDD: 없음