<!-- task=m-forgejo-branch-event-mvp/02+01_watch_store plan=2 tag=REVIEW_REVIEW_API -->

# Plan - REVIEW_REVIEW_API

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

구현이 끝났다고 판단하기 전에 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 설계 결정, 검증 출력으로 채운다. 검증 명령을 실행하고 출력 원문을 붙인 뒤 active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 최종 판정, log archive, `complete.log`, task directory archive 이동은 code-review-skill 전용이다.

구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경/secret/service 준비, 또는 계획 충돌이 발생하면 active review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거를 채우고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과/계획 대비 변경 사항에 기록한다.

## 배경

두 번째 리뷰에서 duplicate response의 event id 보완은 확인됐지만, 실제 PostgreSQL 경로에서는 `TIMESTAMPTZ` microsecond precision 때문에 첫 응답과 duplicate 응답의 `created_at` 문자열이 달라질 수 있는 위험이 남았다. 또한 Postgres integration test가 여전히 skip되어 roadmap target 완료 근거가 부족하다. 이 follow-up은 timestamp precision을 deterministic하게 만들고 실제 DB evidence를 확보하는 데만 집중한다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사유는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`에서 복사한 형식을 사용하며, 구현 에이전트는 직접 사용자 prompt를 띄우지 않는다. code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-foundation/milestones/forgejo-branch-event-mvp.md`
- Task ids:
  - `watch-store`: branch watch와 revision cursor를 Postgres store로 옮긴다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/plan_cloud_G07_1.log`
- `agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/code_review_cloud_G07_1.log`
- `services/core/internal/controlplane/runtime.go`
- `services/core/internal/controlplane/runtime_test.go`
- `services/core/internal/controlplane/router.go`
- `services/core/internal/controlplane/router_test.go`
- `services/core/internal/storage/storage.go`
- `services/core/internal/storage/storage_test.go`
- `services/core/internal/storage/postgres.go`
- `services/core/internal/storage/postgres_test.go`
- `services/core/cmd/server/main.go`
- `services/core/migrations/00001_initial.sql`
- `agent-contract/provided/gito-forgejo-branch-events-v1.md`

### 테스트 환경 규칙

- `test_env=local`.
- 이번 review turn에서 재확인한 검증: `(cd services/core && go test -count=1 ./internal/controlplane ./internal/storage)`, `(cd services/core && go test -count=1 ./cmd/server ./internal/storage)`, `(cd services/core && go test -count=1 ./...)`, `(cd services/core && go vet ./...)`, `./bin/build`, `git diff --check` 모두 PASS.
- 현재 환경에서는 `command -v docker` exit 1, `command -v podman` exit 1, `GITO_TEST_DATABASE_URL=unset`이다. raw private DB URL은 사용하지 않는다.

### 테스트 커버리지 공백

- fake store tests는 duplicate response의 `created_at` equality를 확인하지만, PostgreSQL의 microsecond timestamp truncation을 시뮬레이션하지 않아 실제 PG 경로의 mismatch를 잡지 못한다.
- `TestPostgresBranchEventStorePersistsWatchCursorAndDedupesDelivery`는 여전히 skip 상태라 실제 schema/migration/store round-trip evidence가 없다.

### 심볼 참조

- renamed/removed symbol은 없다.
- 변경 대상 주요 호출부:
  - `Runtime.HandleRevision`: `services/core/internal/controlplane/router.go`, `services/core/internal/controlplane/runtime_test.go`
  - `ProviderDeliveryStore.RecordOnce`: `services/core/internal/controlplane/runtime.go`, `services/core/internal/storage/postgres.go`, fake stores
  - `eventRecordPayload`: `services/core/internal/controlplane/router.go`

### 분할 판단

기존 task group은 `m-forgejo-branch-event-mvp`이고 현재 subtask는 `02+01_watch_store`다. predecessor `01_consumer_smoke`는 이미 archived `complete.log`로 충족됐다. 이번 follow-up은 같은 storage/runtime idempotency fix와 evidence recovery라 기존 subtask에서 이어간다.

### 범위 결정 근거

- persistent event outbox 전체 구현은 범위 제외다.
- Docker helper, compose baseline, private DSN 문서화는 범위 제외다.
- schema 변경은 필요한 경우에만 최소화한다. 우선은 timestamp 정규화와 test strengthening으로 해결한다.

### 빌드 등급

build lane은 `cloud-G07`, review lane은 `cloud-G07`이다. storage/migration, REST idempotency contract, verification trust가 얽힌 후속 이슈다.

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_API-1] duplicate response의 `created_at`이 실제 PostgreSQL precision에서도 첫 delivery와 동일하도록 timestamp 정규화와 regression test를 보완한다.
- [ ] [REVIEW_REVIEW_API-2] 실제 PostgreSQL against integration test와 focused/full 검증을 실행해 skip 없는 stdout/stderr evidence를 남긴다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_API-1] Postgres Precision-Safe Duplicate Timestamp

#### 문제

`services/core/internal/controlplane/runtime.go:160`은 첫 delivery response에 Go `time.Now().UTC()` 값을 그대로 사용한다. `services/core/internal/storage/postgres.go:176`-`203`은 같은 값을 PostgreSQL `TIMESTAMPTZ`에 저장하고 duplicate 때 다시 읽어 반환한다. PostgreSQL timestamp precision은 microsecond라 첫 응답의 RFC3339Nano 문자열과 duplicate 응답의 문자열이 실제 PG 경로에서 달라질 수 있다.

#### 해결 방법

event `CreatedAt`을 provider delivery에 저장하기 전부터 PostgreSQL-compatible precision으로 정규화한다. 예: `time.Now().UTC().Truncate(time.Microsecond)` 같은 helper를 runtime에서 사용해 첫 response와 stored duplicate response가 같은 값을 공유하게 한다. fake delivery store test는 duplicate lookup 시 `ExistingCreatedAt`을 microsecond로 truncate해 현재 버그가 재현되도록 만들고, 수정 후 첫/duplicate `created_at` equality를 확인한다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/controlplane/runtime.go`에서 event `CreatedAt` 생성 시 precision-safe helper를 사용한다.
- [ ] `services/core/internal/controlplane/runtime_test.go`의 fake store가 Postgres precision truncation을 시뮬레이션하도록 조정한다.
- [ ] router-level duplicate response test도 `created_at` equality를 유지하는지 확인한다.
- [ ] 필요하면 `services/core/internal/storage/postgres_test.go`에서 duplicate `ExistingCreatedAt` 검증을 추가한다.

#### 중간 검증

```bash
(cd services/core && go test -count=1 ./internal/controlplane ./internal/storage)
```

기대 결과: duplicate event id와 `created_at` equality tests가 PASS한다.

### [REVIEW_REVIEW_API-2] Skip-Free Postgres Evidence

#### 문제

`agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/code_review_cloud_G07_1.log`의 검증 기록은 Postgres integration test가 `GITO_TEST_DATABASE_URL` 미설정으로 skip됐음을 보여준다. Roadmap target `watch-store`는 branch watch/cursor/delivery를 PostgreSQL source of truth로 옮기는 작업이므로 skip evidence로 PASS할 수 없다.

#### 해결 방법

Docker 또는 준비된 `GITO_TEST_DATABASE_URL`이 있는 환경에서 `TestPostgresBranchEventStorePersistsWatchCursorAndDedupesDelivery`를 실제 실행한다. 현재 환경에 Docker/Podman/DSN이 없으면 구현 완료 표시를 하지 말고 active review stub의 `사용자 리뷰 요청`에 exact command output과 재개 조건을 남긴다.

#### 수정 파일 및 체크리스트

- [ ] `command -v docker` 또는 대체 DB 준비 상태를 실제 출력으로 기록한다.
- [ ] `GITO_TEST_DATABASE_URL`이 있는 상태에서 storage integration test가 skip 없이 PASS한 출력 원문을 남긴다.
- [ ] focused/full test, vet, build, diff check 출력을 다시 남긴다.

#### 중간 검증

```bash
(cd services/core && GITO_TEST_DATABASE_URL='postgres://postgres:postgres@127.0.0.1:15460/gito_test?sslmode=disable' go test -count=1 ./internal/storage)
```

기대 결과: Postgres integration test가 `t.Skip` 없이 PASS한다.

## 수정 파일 요약

| 파일 | 항목 |
| --- | --- |
| `services/core/internal/controlplane/runtime.go` | REVIEW_REVIEW_API-1 |
| `services/core/internal/controlplane/runtime_test.go` | REVIEW_REVIEW_API-1 |
| `services/core/internal/controlplane/router_test.go` | REVIEW_REVIEW_API-1 |
| `services/core/internal/storage/postgres_test.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2 |
| `agent-task/m-forgejo-branch-event-mvp/02+01_watch_store/CODE_REVIEW-cloud-G07.md` | REVIEW_REVIEW_API-2 |

## 최종 검증

```bash
command -v docker
docker rm -f gito-watch-store-test >/dev/null 2>&1 || true
docker run --name gito-watch-store-test -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=gito_test -p 15460:5432 -d postgres:16
for i in $(seq 1 30); do docker exec gito-watch-store-test pg_isready -U postgres && break; sleep 1; done
(cd services/core && GITO_TEST_DATABASE_URL='postgres://postgres:postgres@127.0.0.1:15460/gito_test?sslmode=disable' go test -count=1 ./internal/storage ./internal/controlplane ./internal/provider/forgejo ./internal/protosocket)
(cd services/core && go test -count=1 ./...)
(cd services/core && go vet ./...)
./bin/build
git diff --check
docker rm -f gito-watch-store-test
```

기대 결과: Docker availability check PASS, disposable PostgreSQL-backed focused tests PASS without skip, full Go test PASS, go vet PASS, root build PASS, diff whitespace check PASS. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
