<!-- task=m-stream-evidence-gate-core/01_event_contract_types plan=0 tag=API -->


# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> This implementation model must differ from the plan and review models. Fill implementation-owned sections, report `review-ready`, and stop. Do not review or start another agent.
> If a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation, record it in `사용자 리뷰 요청`. Record other blockers in the implementation-owned evidence; they are not user-facing stopping states.
> A later review model consumes this file state.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) belongs only to the review model.
> `review-ready` ends the current invocation. `USER_REVIEW.md` stops the loop for a decision; `complete.log` ends the task.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

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

## 역할 모델

- Plan: `GPT-5`
- Implementation: `GPT-5o`
- Review: `pending`

> Runtime만 최초 역할 시작 시 해당 identity를 기록한다. 기록된 identity는 이 pair에서 변경하지 않으며, 재시도는 같은 model identity를 사용한다. 세 identity는 모두 달라야 한다.

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
> Review 모델은 plan/implementation 모델과 달라야 한다. product source/test를 수정하거나 plan을 실행하지 않는다.
> Review는 후속 PLAN/CODE_REVIEW pair, `USER_REVIEW.md`, `complete.log` 중 정확히 하나를 남긴 뒤 종료한다. archive log만 남기고 종료하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-local-G05.md` → `code_review_local_G05_0.log`, `PLAN-local-G04.md` → `plan_local_G04_0.log`로 아카이브한다.
3. PASS이면 final archive/event metadata가 포함된 `complete.log`를 작성한다. WARN/FAIL이면 `USER_REVIEW.md`를 쓰거나 다른 Plan 모델을 호출해 후속 pair를 만든다.
4. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 active task의 archived review log에서 체크한다.
5. PASS이면 task archive 이동을 review 모델의 마지막 filesystem mutation으로 수행한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| API-1 Transport-agnostic event와 terminal 계약 | [x] |

## 구현 체크리스트

- [x] `API-1` normalized event/base disposition, immutable evidence/filter decision, terminal/failure/release 공개 계약을 transport-agnostic 타입과 validation/defensive-copy helper로 구현한다.
- [x] 계획에 명시한 local compile 검증 명령을 fresh cache 조건으로 실행하고 stdout/stderr를 review stub에 기록한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채우고 review-ready를 보고한 뒤 현재 실행을 종료한다. code-review를 실행하거나 다음 에이전트를 시작하지 않는다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] 모델 identity preflight는 사용자 명시 예외로 적용 제외했다. archived ledger의 Plan=`GPT-5`, Implementation=`GPT-5o`, Review=`pending`은 수정하지 않았다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G05_0.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_local_G04_0.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 exact final archive path와 완료 이벤트 metadata가 포함된 `complete.log`를 작성하고 검증한다.
- [ ] PASS이면 모든 checklist/file mutation을 끝낸 뒤 active task 디렉터리 `agent-task/m-stream-evidence-gate-core/01_event_contract_types/`를 `complete.log`의 exact final archive path로 review 모델의 마지막 filesystem mutation에서 이동하도록 준비한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 `complete.log`에 런타임이 재생 가능한 완료 이벤트 메타데이터를 기록하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [x] WARN/FAIL이고 user-review gate가 아니면 active pair를 archive하고 다른 Plan 역할이 같은 task path에 후속 pair를 쓰게 한 뒤 `implementation-ready` 보고 정보를 준비한다. Review 역할은 직접 plan이나 구현을 하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 exact final path가 포함된 `complete.log`를 검증한 뒤 task archive 이동을 마지막 filesystem mutation으로 준비한다.
- [ ] USER_REVIEW 해소가 새 구현을 요구하면 `USER_REVIEW.md`를 archive하고 다른 Plan 모델이 후속 pair를 쓰게 한 뒤 `implementation-ready` 보고 정보를 준비한다.

## 계획 대비 변경 사항

계획에 명시된 Write set(`event.go`, `filter_contract.go`, `terminal.go`)과 심볼 범위를 그대로 따랐다. 범위外인 `apps/edge/**`, `apps/node/**`, `proto/**`, `packages/go/config/**` 수정은 수행하지 않았다. 단위 테스트 파일은 dependent child `03+01_event_contract_unit_tests`가 담당하므로 이 child에서는 작성하지 않았다.

interface 메서드 시그니처는 Go 문법에 맞게 파라미터 이름을 명시했다(`ResponseStart` → `rs ResponseStart` 등). 이는 계획의 public shape 예시에서 간결하게 표기한 부분을 Go 컴파일 가능한 형태로 수정한 것이다.

`ReleaseEvent`는 계획에서 `terminal.go`의 "release payload"로 명시했으나, 이벤트 타입의 일관성을 위해 `event.go`에 배치했다. 같은 패키지 내이므로 기능적 영향은 없다.

`NewEvidenceBatch` 생성자와 `ResponseStart()` 접근자에서 `*ResponseStart` 입력을 defensive-copy 하여 내부 backing mutable map이 외부로 노출되지 않도록 수정했다.

## 주요 설계 결정

1. **defensive-copy 관례**: `audit.go`와 `events.go`의 기존 관례를 그대로 따랐다. map/slice/string을 받는 constructor는 입력을 복사하고, accessor도 복사본을 반환한다. mutable map/slice을 직접 노출하지 않는다.
2. **validation 위치**: 모든 constructor(`NewResponseStart`, `NewNormalizedEvent`, `NewReleaseEvent`, `NewTerminalResult`, `NewFailureCause`, `NewExternalDescriptor`, `NewSanitizedEvidence`, `NewRecoveryIntent`, `NewToolCall`)에서 입력을 검증하고, 對應 struct는 `Validate()` 메서드를 제공한다.
3. **bounded FailureCauseChain**: `MaxFailureCauses = 4`로 고정하고, `Append` 시 oldest entry를 drop한다. `TerminalResult` constructor에서 인풋 chain을 `Copy()`하여 immutable하게 보존한다.
4. **Transport-agnostic**: `packages/go/streamgate` package는 `apps/`, `proto/`, `packages/go/config/`를 import하지 않는다. stdlib(`context`, `errors`, `time`)만 사용한다.
5. **BaseDispositionOf 함수**: EventKind를 BaseEventDisposition으로 매핑하는 테이블 함수를 제공하여 codec adapter가 공통 lifecycle disposition을 참조할 수 있게 했다.
6. **ReleaseSink interface**: 실제 상태 머신 구현은 후속 Task로 남기고, interface 정의만 제공한다.

## 사용자 리뷰 요청

_선택된 Milestone `구현 잠금 > 결정 필요`가 구현을 막을 때만 채운다. 이 섹션을 포함한 모든 구현 소유 섹션을 채운 뒤 `review-ready`를 보고하고 종료한다. code-review를 실행하거나 review model을 시작하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 후속으로 해결할 수 없는 이유: 없음
- 재개 조건: 없음

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

- enum/constructor validation이 SDD event/base-disposition table을 정확히 고정하는지 확인한다.
- `EvidenceBatch`, response-start headers, cause chain accessor가 mutable backing을 노출하지 않는지 확인한다.
- `FailureCause`와 `TerminalResult`에 raw stack/provider body/prompt/output/auth를 담을 필드나 우회 경로가 없는지 확인한다.
- 이 child가 test 파일이나 실제 buffering/commit/retry/config 로직으로 범위를 확장하지 않았는지 확인한다.

## 검증 결과

> 구현 에이전트는 각 명령의 실제 stdout/stderr를 아래에 원문으로 기록한다. 명령을 바꾸면 `계획 대비 변경 사항`에 대체 명령과 이유를 적는다.

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

```text
(no output)
```

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

```text
?    	iop/packages/go/streamgate	[no test files]
```

### `git diff --check -- packages/go/streamgate`

```text
(no output)
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-only sections unchanged, report `review-ready`, and stop. Do not execute code-review or start another agent.

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed | Implementation model fills its sections, reports `review-ready`, and stops |
| 역할 모델 | Mixed | Plan identity is fixed; runtime replaces each pending role once. Filled identities are immutable, and duplicate identity blocks that role |
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only; final checkbox is mandatory before saving |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation; do not ask the user directly during implementation; environment/secret/service blockers, generic scope changes, and evidence gaps are not user-review requests |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 리뷰 시작 예외: 사용자가 `Review: pending` identity preflight를 무시하고 진행하라고 명시했다. 런타임 소유 identity 값은 수정하지 않았으며, 아래 판정은 구현·계약·검증 증거만을 기준으로 한다.
- 차원별 평가:
  - Correctness: Fail
  - Completeness: Fail
  - Test coverage: Pass — 직접 단위 테스트는 dependent child `03+01_event_contract_unit_tests`의 명시 범위다.
  - API contract: Fail
  - Code quality: Fail
  - Implementation deviation: Fail
  - Verification trust: Fail
  - Spec conformance: Fail
- 발견된 문제:
  - Required — `packages/go/streamgate/event.go:26`, `packages/go/streamgate/event.go:57`, `packages/go/streamgate/event.go:78`, `packages/go/streamgate/terminal.go:11`: 승인된 SDD의 event/base-disposition/commit-state 계약과 공개 상수가 다르다. SDD는 `response_start|text_delta|reasoning_delta|tool_call_fragment|terminal|provider_error`, `hold|release_candidate|terminal_success_candidate|terminal_error_candidate`, `transport_uncommitted|stream_open|terminal_committed`를 요구하지만 구현은 tool result/host error를 별도 event kind로 만들고 disposition을 `hold|staged|committed|abandoned`, commit state를 `hold|committed|abandoned`로 정의한다. text/reasoning/tool release 후보도 모두 `hold`로 매핑되어 codec과 Arbiter가 S09의 공통 lifecycle table을 사용할 수 없다. SDD의 정확한 typed event 종류와 상태값으로 계약을 다시 정의하고 unknown kind/state 및 kind-disposition 불일치를 validation에서 거부해야 한다.
  - Required — `packages/go/streamgate/event.go:93`, `packages/go/streamgate/event.go:144`: normalized response-start가 status code를 보존하지 않고, event payload가 kind별 typed response-start/tool fragment/terminal/provider-error를 표현하지 못한다. 또한 `Disposition`이 공개 필드라 호출자가 base table을 우회할 수 있고 `Validate`는 unknown kind나 derived disposition 일치를 검사하지 않는다. response status와 allowlisted headers를 가진 typed response-start 및 kind별 typed payload를 제공하고, base disposition은 kind에서만 파생되도록 비공개화하거나 validation으로 고정해야 한다.
  - Required — `packages/go/streamgate/filter_contract.go:8`, `packages/go/streamgate/filter_contract.go:26`: `FilterDecision`과 `FilterOutcome`의 의미가 SDD와 뒤바뀌고 필수 variant가 빠졌다. SDD의 decision은 `pass|observe|violation|fatal|replacement`와 consumer/filter/rule id, sanitized evidence, optional typed intent를 가져야 하며 outcome은 `evaluated(decision)|evaluation_error|not_applicable_for_epoch|deferred_by_requirement`를 표현해야 한다. 현재 `evaluated/not_applicable/deferred` decision과 `pass/fail` outcome만으로는 all-complete barrier와 deterministic Arbiter 입력을 표현할 수 없다. 두 타입을 SDD 모델대로 분리하고 variant별 필수 필드를 검증해야 한다.
  - Required — `packages/go/streamgate/filter_contract.go:39`, `packages/go/streamgate/filter_contract.go:76`: `RecoveryIntent`의 strategy/directive와 sanitized evidence reason이 임의 문자열이라 typed directive와 raw-free 경계를 제공하지 못한다. `exact_replay|continuation_repair|schema_repair` strategy enum과 strategy별 typed directive를 정의하고, raw prompt/output/provider body/auth를 담을 수 없는 bounded sanitized descriptor/fingerprint/count/offset 타입으로 교체해야 한다.
  - Required — `packages/go/streamgate/event.go:95`, `packages/go/streamgate/filter_contract.go:129`, `packages/go/streamgate/filter_contract.go:170`, `packages/go/streamgate/filter_contract.go:236`: immutable/defensive-copy 보장이 실제 코드와 다르다. `ResponseStart.Headers`가 공개 map이고 `NewEvidenceBatch`와 `StagedResponseStart`가 struct만 얕게 복사하므로 constructor 입력 또는 accessor 결과의 header를 수정하면 batch 내부가 함께 바뀐다. `EvidenceBatch`에는 SDD가 요구한 channel별 bounded committed look-behind도 없다. mutable 필드는 private storage로 바꾸고 모든 입출력 경계에서 deep copy하며 pending과 committed look-behind를 분리해 제공해야 한다.
  - Required — `packages/go/streamgate/terminal.go:75`, `packages/go/streamgate/terminal.go:80`, `packages/go/streamgate/terminal.go:150`, `packages/go/streamgate/terminal.go:161`: 최대 4단계 cause 불변조건이 강제되지 않는다. 길이 5 이상인 외부 slice는 `Append`가 한 항목만 제거해 여전히 4를 초과할 수 있고, invalid cause와 oversized chain을 `TerminalResult` constructor가 그대로 수용하며 공개 `FailureCauses` slice로 다시 변경할 수 있다. cause validation과 arbitrary-length 입력의 마지막 4개 cap을 모든 constructor/append 경계에서 적용하고 private chain/accessor copy로 변경해야 한다.
  - Required — `packages/go/streamgate/terminal.go:105`, `packages/go/streamgate/terminal.go:147`: 외부 terminal descriptor가 SDD/외부 계약의 error `type`/`code`/safe message template/`param` 경계를 표현하지 않고 임의 `Message`와 내부 `Stage`를 외부 descriptor에 둔다. 성공 result도 descriptor와 failure cause를 함께 가질 수 있어 success/error mutual exclusion이 불완전하다. endpoint host가 단일 오류로 직렬화할 정확한 safe descriptor shape를 정의하고 success에는 error descriptor/cause를 금지하며 error descriptor와 bounded cause를 함께 검증해야 한다.
- 검증 재실행:
  - `gofmt -d packages/go/streamgate/event.go packages/go/streamgate/filter_contract.go packages/go/streamgate/terminal.go`: PASS, 출력 없음
  - `go test -count=1 ./packages/go/streamgate`: PASS, `[no test files]`
  - `git diff --check -- packages/go/streamgate`: PASS, 출력 없음. 단, 현재 package가 untracked라 이 명령은 whitespace 증거로 유효하지 않고 `gofmt -d`만 유효하다.
  - `go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport`: PASS
  - `go list -f '{{join .Imports "\n"}}' ./packages/go/streamgate`: `context`, `errors`, `time`만 확인
- 다음 단계: active pair를 archive한 뒤 다른 Plan 모델이 같은 task path에 위 Required 항목을 해결하는 후속 PLAN/CODE_REVIEW pair를 작성한다.
