<!-- task=m-stream-evidence-gate-core/01_event_contract_types plan=4 tag=REVIEW_API -->
<!-- plan-model=GPT-5 -->
<!-- implementation-model=pending -->
<!-- review-model=pending -->

# Code Review Reference - REVIEW_API

> **[IMPLEMENTATION WORKER]**
> Complete the implementation checklist, fill the implementation evidence below, report `review-ready`, and stop.
> Edit only completion checkboxes, `계획 대비 변경 사항`, `주요 설계 결정`, `사용자 리뷰 요청`, Agent UI implementation evidence when present, and command output blocks.
> Do not edit metadata comments or fixed sections, review, archive, create terminal files, or start another agent. Runtime and the later Review model own those actions.
> Do not ask the user directly. Use `사용자 리뷰 요청` only for a selected Milestone `구현 잠금 > 결정 필요`; record other blockers in the implementation evidence.

## 개요

date=2026-07-23
task=m-stream-evidence-gate-core/01_event_contract_types, plan=4, tag=REVIEW_API

## Archive Evidence Snapshot

- 입력 pair:
  - `agent-task/m-stream-evidence-gate-core/01_event_contract_types/plan_local_G05_3.log`
  - `agent-task/m-stream-evidence-gate-core/01_event_contract_types/code_review_local_G06_3.log`
- 판정: `FAIL`
- Required:
  - text/reasoning/tool normalized public constructor가 완성된 값을 `NormalizedEvent.Validate`에 통과시키지 않아 빈 payload를 허용한다.
  - filter outcome/directive의 반대 payload, filter/intent/evidence의 nested stable token, sanitized evidence의 closed enum이 `Validate`에서 거부되지 않는다.
  - failure cause/descriptor의 required stable token과 terminal result의 descriptor/cause chain이 재귀 검증되지 않는다.
  - non-terminal `EvidenceBatch`의 staged response-start가 `ResponseStart.Validate`를 거치지 않아 invalid HTTP status를 보존한다.
- 검증 evidence:
  - formatter, proto generation, package/transport test, vet, import/public-symbol boundary는 PASS했다.
  - public probe는 빈 normalized payload 3개를, same-package overlay probe는 반대 payload·unknown enum·nested token/descriptor/cause·invalid staged status 15개를 재현했다.
- 영향 파일:
  - `packages/go/streamgate/event.go`
  - `packages/go/streamgate/filter_contract.go`
  - `packages/go/streamgate/terminal.go`
- 역할 identity preflight 예외:
  - 입력 review ledger는 두 후속 역할이 미할당된 채 verdict executor와 Plan 역할 identity가 동일한 상태였고 사용자 예외로 진행됐다.
  - 사용자가 미할당/동일 역할 identity 조건을 무시하고 진행하라고 명시했다. 기존 runtime ledger는 수정하지 않으며, 이 pair도 top metadata를 runtime 기본 상태로 유지한 채 같은 예외를 handoff 근거로 기록한다.
- Roadmap carryover: 이 task는 `event-contract` production 타입의 부분 구현이다. persistent unit test write set은 dependent `03+01_event_contract_unit_tests`, codec/host fixture closure는 `04+01,03_codec_contract_fixtures`가 소유하므로 Roadmap Targets를 두지 않는다.

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| REVIEW_API-1 Normalized constructor-to-Validate 단일화 | [ ] |
| REVIEW_API-2 Filter discriminant와 nested token 폐쇄 | [ ] |
| REVIEW_API-3 Terminal nested value 재귀 검증 | [ ] |
| REVIEW_API-4 Staged response-start lifecycle validation | [ ] |

## 구현 체크리스트

- [ ] `REVIEW_API-1` text/reasoning/tool normalized constructor를 assemble-then-`Validate` 경계로 통일한다.
- [ ] `REVIEW_API-2` filter outcome/directive의 반대 payload와 filter/intent/evidence의 nested token·closed enum을 재검증한다.
- [ ] `REVIEW_API-3` failure cause/descriptor/terminal result가 required token과 bounded cause chain 전체를 재귀 검증하게 한다.
- [ ] `REVIEW_API-4` staged response-start validation을 terminal 여부와 무관한 lifecycle 단일 경계로 옮긴다.
- [ ] 계획에 명시한 local 검증 명령과 temporary same-package regression을 실행하고 실제 stdout/stderr를 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채우고 review-ready를 보고한 뒤 현재 실행을 종료한다. code-review를 실행하거나 다음 에이전트를 시작하지 않는다.

## 계획 대비 변경 사항

_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._

## 주요 설계 결정

_구현 에이전트가 주요 설계 결정 사항을 기록한다._

## 사용자 리뷰 요청

> 기본값은 `없음`이다. 선택된 Milestone `구현 잠금 > 결정 필요`가 구현을 막을 때만 `없음`을 `사유 유형: milestone-lock`, `연결 대상`, `결정 필요`, `차단 근거`, `실행한 검증/명령`, `재개 조건`으로 교체한다.

없음

## 리뷰어를 위한 체크포인트

- 세 normalized delta constructor가 완성된 값을 `NormalizedEvent.Validate`에 통과시키고 빈 payload를 거부하는지 확인한다.
- `FilterOutcome`과 `RecoveryDirective`가 kind별 required payload 외 모든 반대 payload를 거부하는지 확인한다.
- filter decision/outcome, recovery directive/intent, sanitized evidence의 required/optional stable token과 event/outcome enum이 constructor와 같은 원본으로 재검증되는지 확인한다.
- failure cause와 external descriptor의 required token, terminal result의 descriptor와 최대 4개 cause 전체가 재귀 검증되는지 확인한다.
- terminal 여부와 무관하게 staged response-start가 `ResponseStart.Validate`를 먼저 통과하고 commit-state 조합이 한 helper에서 검사되는지 확인한다.
- 세 production 파일 외 source/test가 수정되지 않았고 temporary overlay가 삭제됐으며 dependent test sibling의 write set을 침범하지 않았는지 확인한다.
- local profile 명령과 fixed same-package regression의 실제 stdout/stderr가 모두 PASS인지 확인한다.

## 검증 결과

> 구현 에이전트는 각 명령의 실제 stdout/stderr를 아래 코드 블록에 원문으로 기록한다. 명령을 바꾸면 `계획 대비 변경 사항`에 대체 명령과 이유를 적는다. repository package의 `[no test files]`는 허용하지만 temporary same-package regression 실패 또는 미실행은 PASS가 아니다.

### `gofmt -d packages/go/streamgate/event.go packages/go/streamgate/filter_contract.go packages/go/streamgate/terminal.go`

```text
<actual stdout/stderr>
```

### `make proto`

```text
<actual stdout/stderr>
```

### `go test -count=1 ./packages/go/streamgate`

```text
<actual stdout/stderr>
```

### `go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport`

```text
<actual stdout/stderr>
```

### `go vet ./packages/go/streamgate`

```text
<actual stdout/stderr>
```

### `go list -f '{{range .Imports}}{{println .}}{{end}}' ./packages/go/streamgate | LC_ALL=C sort`

```text
<actual stdout/stderr>
```

### forbidden production import search

```bash
if rg -n --sort path '"iop/(apps/|proto/gen/|packages/go/config)"' packages/go/streamgate; then exit 1; else echo "PASS: no forbidden imports"; fi
```

```text
<actual stdout/stderr>
```

### forbidden public constructor search

```bash
if rg -n --sort path '^func NewRelease(ResponseStart|Terminal)Event|^func NewTerminalResult' packages/go/streamgate; then exit 1; else echo "PASS: forbidden public constructors absent"; fi
```

```text
<actual stdout/stderr>
```

### temporary overlay preflight와 source copy

```bash
test ! -e /tmp/iop_streamgate_contract_overlay && mkdir -p /tmp/iop_streamgate_contract_overlay && cp packages/go/streamgate/*.go /tmp/iop_streamgate_contract_overlay/
```

```text
<actual stdout/stderr>
```

### fixed same-package overlay 생성

Plan의 고정 source와 동일한 `/tmp/iop_streamgate_contract_overlay/go.mod`, `/tmp/iop_streamgate_contract_overlay/contract_overlay_test.go`를 모델의 `apply_patch` 도구로 생성한다.

```text
<actual apply_patch result>
```

### `(cd /tmp/iop_streamgate_contract_overlay && go test -count=1 .)`

```text
<actual stdout/stderr>
```

### temporary overlay 정리

모델의 `apply_patch` 도구로 `/tmp/iop_streamgate_contract_overlay/contract_overlay_test.go`, `go.mod`, `event.go`, `filter_contract.go`, `terminal.go`를 삭제한 뒤 아래 명령을 실행한다.

```bash
rmdir /tmp/iop_streamgate_contract_overlay && test ! -e /tmp/iop_streamgate_contract_overlay
```

```text
<actual apply_patch result and stdout/stderr>
```

### `git diff --check -- packages/go/streamgate/event.go packages/go/streamgate/filter_contract.go packages/go/streamgate/terminal.go`

```text
<actual stdout/stderr>
```

### 재개 조건

```text
<없음 또는 차단 사유와 결정적 재개 조건>
```

---

> Before saving, fill every implementation-owned field and command output. Then report `review-ready` and stop.
