<!-- task=m-gito-http-webhook-consumer-readiness/02+01_http_receiver_idempotency plan=1 tag=REVIEW_API -->

# PLAN local-G06 - REVIEW_API

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

이 작업은 직전 code-review FAIL의 Required issue만 좁게 해결한다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채워야 완료된다. 구현 에이전트는 검증을 실행하고 active 파일을 유지한 채 리뷰 준비를 보고한다. 종결, 로그 rename, `complete.log`, task directory archive는 code-review 스킬 전용이다. 선택된 SDD 결정이나 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션을 채우고 멈춘다. 구현 중 사용자에게 직접 묻거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-http-webhook-consumer-readiness.md`
- Task ids:
  - `receiver`: Gito `branch.updated` HTTP callback endpoint를 추가한다.
  - `idempotency`: `X-Gito-Delivery`와 revision key 기준으로 duplicate delivery를 중복 enqueue 없이 처리한다.
- Completion mode: check-on-pass

## Spec Targets

- SDD: `agent-roadmap/sdd/agent-ops-mcp-control-plane/gito-http-webhook-consumer-readiness/SDD.md`
- Acceptance scenarios:
  - `S01`: task=`receiver`; evidence=`HTTP handler unit/integration test, invalid signature test`
  - `S03`: task=`idempotency`; evidence=`duplicate delivery/revision idempotency test`
- Completion mode: spec-check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-gito-http-webhook-consumer-readiness/02+01_http_receiver_idempotency/plan_local_G06_0.log`
- Archived review: `agent-task/m-gito-http-webhook-consumer-readiness/02+01_http_receiver_idempotency/code_review_local_G06_0.log`
- Verdict: FAIL
- Issue summary:
  - Required: `services/core/internal/http/gito_webhook.go:97` local JSON decode lost `gitoevents.DecodeBranchUpdatedPayload` validation for body `type` and malformed `changed_files`, allowing contract-invalid `branch.updated` payloads into accepted/handler path.
  - Suggested: 없음
  - Nit: 없음
- Affected files:
  - `services/core/internal/http/gito_webhook.go`
  - `services/core/internal/http/gito_webhook_test.go`
- Verification evidence:
  - `git diff --check` - PASS, no output.
  - `cd services/core && go test -count=1 ./internal/http ./internal/config ./cmd/server` - PASS.
  - `cd services/core && go test -count=1 ./...` - PASS.
  - Existing tests are insufficient because they do not cover wrong body `type` or malformed `changed_files` through the HTTP receiver.
- Roadmap/spec carryover:
  - Roadmap Targets remain `receiver` and `idempotency`.
  - Spec Targets remain S01 and S03. This follow-up directly repairs S01; S03 should remain green.
- Narrow reread allowed if needed:
  - `agent-task/m-gito-http-webhook-consumer-readiness/02+01_http_receiver_idempotency/plan_local_G06_0.log`
  - `agent-task/m-gito-http-webhook-consumer-readiness/02+01_http_receiver_idempotency/code_review_local_G06_0.log`

## 범위 결정 근거

- 포함: HTTP receiver body payload validation and receiver tests for contract-invalid payloads.
- 제외: Gito outbound producer/subscription, full scan-enqueue proof, docs/env samples, and unrelated idempotency behavior changes.
- 새 package는 가능하면 만들지 말고 `services/core/internal/http/gito_webhook.go` 안에서 dependency-light local validation을 보강한다. import cycle 없이 공통화가 더 단순하다고 판단되는 경우에만 최소 파일 추가를 선택하고 이유를 review stub에 남긴다.

## 구현 체크리스트

- [ ] Gito HTTP webhook body validation이 `repo_id`/`branch` 필수 검증을 유지하면서 body `type`이 있으면 `branch.updated`만 허용하고, malformed `changed_files`를 400으로 reject하도록 보강한다.
- [ ] wrong body `type`과 malformed `changed_files`가 handler를 호출하지 않음을 검증하는 HTTP receiver tests를 추가하고 기존 accepted/duplicate tests가 계속 통과하게 한다.
- [ ] `cd services/core && go test -count=1 ./internal/http ./internal/config ./cmd/server`를 실행한다.
- [ ] `cd services/core && go test -count=1 ./...`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_API-1] HTTP payload contract validation 복구

### 문제

직전 구현은 import cycle을 피하려고 HTTP receiver에서 로컬 `gitoWebhookPayload` struct를 사용했다. 하지만 현재 struct는 `repo_id`, `branch`, `before`, `after`만 decode하므로, 이전 subtask가 만든 `gitoevents.DecodeBranchUpdatedPayload`의 다음 검증이 HTTP path에서 빠졌다.

- body `type`이 present이면 `branch.updated`여야 한다.
- `changed_files`가 present이면 list of objects여야 한다.

이 때문에 header `X-Gito-Event: branch.updated`가 맞는 malformed body가 accepted path로 들어갈 수 있다.

### 해결 방법

- `services/core/internal/http/gito_webhook.go`의 local decoder를 보강한다.
- `gitoWebhookPayload`에 `Type`과 `ChangedFiles` validation path를 추가하거나, 별도 local `decodeGitoWebhookPayload` helper를 만든다.
- 검증 의미:
  - invalid JSON: 400, handler call 없음.
  - missing `repo_id` or `branch`: 400, handler call 없음.
  - body `type` present and not `branch.updated`: 400, handler call 없음.
  - `changed_files` present but not a list of objects: 400, handler call 없음.
  - valid minimal base payload without NomadCode-specific fields: accepted behavior 유지.
- import cycle을 다시 만들지 않는다.

### 테스트 결정

- `services/core/internal/http/gito_webhook_test.go`에 다음 테스트를 추가한다.
  - `TestReceiveGitoWebhookRejectsWrongBodyTypeWithoutHandlerCall`
  - `TestReceiveGitoWebhookRejectsMalformedChangedFilesWithoutHandlerCall`
- 기존 accepted, wrong event, invalid signature, delivery duplicate, revision duplicate tests가 계속 통과해야 한다.

### 중간 검증

```bash
cd services/core && go test -count=1 ./internal/http
```

예상: PASS. Go test cache output은 허용하지 않는다.

## 최종 검증

```bash
cd services/core && go test -count=1 ./internal/http ./internal/config ./cmd/server
cd services/core && go test -count=1 ./...
```

예상: 두 명령 모두 PASS. Go test cache output은 허용하지 않는다.
