<!-- task=m-policy-history-audit/01_audit_contracts plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

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

`CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 필수 마지막 단계다. 코드를 바꾸고 검증을 실행한 뒤 실제 구현 내용, 계획 대비 변경 사항, 주요 설계 결정, 검증 stdout/stderr를 리뷰 스텁에 기록하고 active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 구현 중 사용자만 결정할 수 있는 제품 선택, 사용자 소유 외부 환경 prerequisite, 또는 범위 충돌이 생기면 리뷰 스텁의 `사용자 리뷰 요청` 섹션에 정확한 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 evidence 공백은 사용자 리뷰 요청이 아니다.

## 배경

첫 리뷰(`code_review_local_G05_0.log`)에서 audit schema 구현 자체와 테스트 실행은 확인되었지만, required event type coverage 테스트가 milestone draft의 정확한 문자열 계약을 고정하지 못했다. 또한 code-review 루프 계약상 계획과 리뷰 스텁의 `구현 체크리스트` item text/order가 정확히 일치해야 하므로, 이번 후속에서는 테스트 assertion을 보강하고 새 리뷰 스텁의 체크리스트 문구를 계획과 동일하게 보존한다.

## 사용자 리뷰 요청 흐름

구현 중 차단 사유가 있으면 active `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청` 섹션을 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 채운다. code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/control-plane-portal-ops/milestones/policy-history-audit.md`
- Task ids:
  - `schema-evidence`: policy/audit 상세 schema는 구현 단계에서 명확한 evidence와 함께 확정한다.
- Completion mode: check-on-pass

## 리뷰 발견사항

- Required: `packages/go/audit/audit_test.go:177`의 `TestRequiredEventTypesMatchMilestoneDraft`가 length/non-empty만 검증해 milestone draft의 정확한 event type 문자열과 중복 여부를 고정하지 못한다.
- Required: 이전 리뷰 스텁의 `구현 체크리스트` 문구가 계획 파일의 체크리스트와 정확히 일치하지 않았다. 이번 루프에서는 새 스텁의 체크리스트 문구를 이 계획과 동일하게 유지하고 체크 상태만 변경한다.

## 범위 결정 근거

- 포함: `packages/go/audit/audit_test.go`의 required event type coverage assertion 보강.
- 포함: `CODE_REVIEW-local-G06.md` 구현 체크리스트 exact-text 보존과 구현 소유 섹션 작성.
- 제외: `packages/go/audit/audit.go` schema 변경. 현 코드의 event type constants는 계획과 일치하며 실패 원인은 테스트 assertion 품질이다.
- 제외: Edge/Control Plane/Node integration, proto/wire 변경, persistence schema.

## 빌드 등급

- `local-G06`: 실패 원인이 명확하고 deterministic test assertion으로 검증 가능하므로 local follow-up을 유지하되, 테스트 의미성/루프 계약 보강을 위해 한 단계 올린다.

## 구현 체크리스트

- [ ] `packages/go/audit/audit_test.go`의 required event type coverage를 expected string table 기반으로 보강해 모든 audit event constant의 정확한 문자열 값과 중복 여부를 검증한다.
- [ ] `go test -count=1 ./packages/go/audit ./packages/go/policy ./packages/go/jobs ./packages/go/events ./packages/go/metadata`를 실행해 보강 테스트와 인접 공통 패키지를 검증한다.
- [ ] `go test -count=1 ./packages/go/...`를 실행해 platform-common 전체 회귀를 검증한다.
- [ ] `CODE_REVIEW-local-G06.md`의 구현 체크리스트 문구를 이 계획과 동일하게 유지하고 체크 상태만 변경한 뒤, 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## [REVIEW_API-1] Event Type Coverage Assertion

### 문제

`TestRequiredEventTypesMatchMilestoneDraft`는 현재 event type slice 길이와 빈 문자열 여부만 확인한다. 이 방식은 다음 회귀를 잡지 못한다.

- 상수 값 typo: 예를 들어 `execution.policy_evaluated`가 다른 문자열로 바뀌어도 non-empty이면 통과한다.
- 중복 값: 두 event type constant가 같은 문자열을 공유해도 slice 길이는 그대로라 통과한다.
- milestone draft 불일치: 테스트 이름과 리뷰 체크포인트가 요구하는 "draft와 일치"를 증명하지 못한다.

### 해결 방법

`packages/go/audit/audit_test.go`에서 expected string table을 사용한다. 각 항목은 constant와 기대 문자열을 함께 둔다.

예시 방향:

```go
expected := []struct {
	name string
	got  EventType
	want string
}{
	{"TypeExecutionRequested", TypeExecutionRequested, "execution.requested"},
	// ...
}

seen := map[string]string{}
for _, tt := range expected {
	if string(tt.got) != tt.want {
		t.Fatalf("%s = %q, want %q", tt.name, tt.got, tt.want)
	}
	if previous, ok := seen[tt.want]; ok {
		t.Fatalf("duplicate event type value %q for %s and %s", tt.want, previous, tt.name)
	}
	seen[tt.want] = tt.name
}
```

17개 event type 모두를 milestone draft 문자열 그대로 나열한다.

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

- [ ] `packages/go/audit/audit_test.go`: `TestRequiredEventTypesMatchMilestoneDraft`가 정확한 문자열 값과 중복 여부를 검증하도록 보강한다.

### 테스트 작성

- 기존 테스트를 보강한다.
- 신규 테스트명을 만들 필요는 없지만, assertion은 정확한 문자열 값과 중복 탐지를 포함해야 한다.

### 중간 검증

```bash
go test -count=1 ./packages/go/audit ./packages/go/policy ./packages/go/jobs ./packages/go/events ./packages/go/metadata
```

기대 결과: audit package와 인접 공통 패키지 테스트가 모두 통과한다.

## [REVIEW_API-2] Review Checklist Contract

### 문제

code-review 루프 계약은 plan `구현 체크리스트`와 review stub `구현 체크리스트`의 item text/order가 일치해야 한다. 이전 루프에서는 의미는 같았지만 어미가 바뀌어 exact-text 비교 계약을 만족하지 못했다.

### 해결 방법

`CODE_REVIEW-local-G06.md`의 `구현 체크리스트`는 이 계획의 `구현 체크리스트`를 그대로 복사한 상태를 유지한다. 구현 에이전트는 `[ ]`를 `[x]`로 바꾸는 것 외에 문구를 바꾸지 않는다.

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

- [ ] `CODE_REVIEW-local-G06.md`: 구현 완료 표시 시 체크리스트 문구를 변경하지 않고 체크 상태만 바꾼다.

### 테스트 작성

- 별도 코드 테스트는 작성하지 않는다. 리뷰 단계에서 plan/review checklist item text/order를 대조한다.

### 중간 검증

```bash
rg --sort path -n "required event type coverage를 expected string table|CODE_REVIEW-local-G06.md의 구현 체크리스트 문구" agent-task/m-policy-history-audit/01_audit_contracts/PLAN-local-G06.md agent-task/m-policy-history-audit/01_audit_contracts/CODE_REVIEW-local-G06.md
```

기대 결과: 동일한 checklist 핵심 문구가 plan과 review stub 양쪽에서 확인된다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/go/audit/audit_test.go` | REVIEW_API-1 |
| `agent-task/m-policy-history-audit/01_audit_contracts/CODE_REVIEW-local-G06.md` | REVIEW_API-2 |

## 최종 검증

```bash
go test -count=1 ./packages/go/...
```

기대 결과: platform-common 전체 Go 테스트가 fresh run으로 통과한다. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
