<!-- task=m-stream-evidence-gate-core/21+14,19_filter_observation_contract plan=2 tag=REVIEW_FILTER_OBSERVATION_CONTRACT -->

# Code Review Reference - REVIEW_FILTER_OBSERVATION_CONTRACT

> **[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.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-27
task=m-stream-evidence-gate-core/21+14,19_filter_observation_contract, plan=2, tag=REVIEW_FILTER_OBSERVATION_CONTRACT

## Archive Evidence Snapshot

- 이전 task path: `agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/`
- 종료 pair: `plan_local_G08_1.log`, `code_review_cloud_G08_1.log`
- 판정: FAIL; Required 4, Suggested 1, Nit 0
- 문제 요약:
  - `arbitration_decided`가 action/base disposition 없이 유효해 결정 종류를 복원할 수 없다.
  - observation failure disposition이 canonical outcome/enforcement truth table과 달라도 허용된다.
  - filter epoch와 response/release/terminal commit state의 불가능 조합이 허용된다.
  - closed lifecycle vocabulary에 병렬 filter evaluation start가 없다.
  - raw-free 구조 검사가 직접 포함된 carrier graph 일부를 누락한다.
- 영향 파일: `packages/go/streamgate/filter_observation.go`, `packages/go/streamgate/filter_observation_test.go`
- 실제 검증: host Go `1.26.2`; `go test -count=1 ./packages/go/streamgate`, `go test -race -count=1 ./packages/go/streamgate`, `go build`, `go vet`, `gofmt -l`, `git diff --check` 모두 PASS. 테스트 통과는 위 semantic gap을 검출하지 못했다.
- Roadmap carryover: approved SDD S08의 contract foundation만 보완한다. lifecycle hook/full timeline은 sibling `22`/`23` 범위이며 이 pair에는 `Roadmap Targets`가 없다.
- 좁은 재열람 허용: `agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/plan_local_G08_1.log`, `agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/code_review_cloud_G08_1.log`

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

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

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

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G06.md` → `code_review_cloud_G06_2.log`, `PLAN-local-G06.md` → `plan_local_G06_2.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/`로 이동한다. WARN/FAIL이면 code-review skill이 요구하는 다음 filesystem state를 완전히 작성한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| CONTRACT-1 lifecycle/arbitration | [x] |
| CONTRACT-2 canonical failure policy | [x] |
| CONTRACT-3 raw-free/immutable closure | [x] |

## 구현 체크리스트

- [x] [REVIEW_FILTER_OBSERVATION_CONTRACT-1] arbitration summary, filter evaluation start와 kind별 epoch/commit-state 불변식을 구현하고 정상/불가능 조합 테스트를 추가한다.
- [x] [REVIEW_FILTER_OBSERVATION_CONTRACT-2] observation failure disposition을 canonical outcome/decision/enforcement truth table에서 파생하고 전체 matrix 테스트를 추가한다.
- [x] [REVIEW_FILTER_OBSERVATION_CONTRACT-3] raw-free carrier graph와 모든 pointer/slice accessor defensive copy를 검증하고 local proto/unit/race/format/diff 검증을 수행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

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

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

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G06_2.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_local_G06_2.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` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/`를 `agent-task/archive/YYYY/MM/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-stream-evidence-gate-core/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이면 code-review skill의 판정에 맞는 다음 filesystem state를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

계획과 달리 변경 없음. 모든 구현은 PLAN-local-G06.md의 범위 결정 근거에 명시된 두 파일(`filter_observation.go`, `filter_observation_test.go`)에 국한하며, agent-contract/agent-spec/roadmap은 외부/wire/living spec 변경이 아니므로 갱신하지 않았다.

## 주요 설계 결정

1. `ObservationDecisionPolicy`에서 `failureDisposition`을 필드에서 제거하고 `deriveObservationDecisionPolicyDisposition()`으로 온전히 파생하도록 했다. 이는 `evaluation_contract.go`의 truth table과 observation contract의 불일치를 원천 차단한다.
2. `ObservationArbitrationInfo`를 new immutable value type으로 추가하여 `arbitration_decided` observation이 action + base disposition을 반드시 보존하도록 했다. `cloneObservationArbitrationInfo`로 defensive copy를 적용했다.
3. `ObservationKindFilterEvaluationStarted`를 closed kind set에 추가하여 parallel filter evaluation의 lifecycle vocabulary를 완성했다. attribution + non-zero epoch를 필수로 요구한다.
4. kind별 commit-state 조합을 `Validate()`에서 switch-case로 명시적 허용 행렬을 구성했다. `response_staged`→`transport_uncommitted`, `release_committed`→`stream_open`, `terminal_committed`→`terminal_committed`, recovery resume mode에 따른 coupling을 모두 포함한다.
5. carrier graph 검사에 `ObservationArbitrationInfo`, `SanitizedEvidence`, `FailureCauseChain`, `FailureCause`를 명시 allowlist에 추가하여 direct nested carrier를 누락 없이 검증한다.

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

- `arbitration_decided`가 action/base disposition 없이 생성되지 않고 다섯 action을 정확히 보존하는가.
- filter evaluation start/complete와 response/release/terminal/recovery event의 epoch/commit-state 조합이 lifecycle과 일치하는가.
- failure disposition이 기존 `EpochFilterOutcome` truth table과 완전히 동일하게 파생되는가.
- raw-free carrier graph와 모든 pointer/slice accessor가 회귀 테스트로 보호되는가.

## 검증 결과

- `command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT`
- `make proto`
- `go test -count=1 ./packages/go/streamgate -run '^(TestFilterObservation_(ArbitrationInfoRequiredAndClosed|FilterEvaluationStartedContract|KindStateMatrix|RawFreeCarrierGraph|AllAccessorsDefensive)|TestObservationDecisionPolicy_CanonicalDispositionMatrix)$'`
- `go test -count=1 ./packages/go/streamgate`
- `go test -race -count=1 ./packages/go/streamgate`
- `test -z "$(gofmt -l packages/go/streamgate/filter_observation.go packages/go/streamgate/filter_observation_test.go)"`
- `git diff --check`

_실제 출력:_

```
$ command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
/config/.local/bin/go
/config/opt/go/bin/go
go version go1.26.2 linux/arm64
/config/opt/go

$ make proto
protoc --go_out=. --go_opt=module=iop --proto_path=. proto/iop/runtime.proto proto/iop/node.proto proto/iop/control.proto proto/iop/job.proto
(no output, success)

$ go test -count=1 ./packages/go/streamgate -run '^(TestFilterObservation_(ArbitrationInfoRequiredAndClosed|FilterEvaluationStartedContract|KindStateMatrix|RawFreeCarrierGraph|AllAccessorsDefensive)|TestObservationDecisionPolicy_CanonicalDispositionMatrix)$'
ok  	iop/packages/go/streamgate	0.003s

$ go test -count=1 ./packages/go/streamgate
ok  	iop/packages/go/streamgate	0.869s

$ go test -race -count=1 ./packages/go/streamgate
ok  	iop/packages/go/streamgate	1.917s

$ gofmt -l packages/go/streamgate/filter_observation.go packages/go/streamgate/filter_observation_test.go
(no output)

$ git diff --check
(no output)

$ go vet ./packages/go/streamgate/
(no output)
```

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| 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 |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 리뷰어를 위한 체크포인트 | 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
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - implementation deviation: Fail
  - verification trust: Pass
- 발견된 문제:
  - Required — `packages/go/streamgate/filter_observation.go:362`: `ObservationRecoveryInfo.Validate`가 strategy와 resume mode를 각각 known enum으로만 검사해 `exact_replay`/`schema_repair + continue_stream`과 `continuation_repair`/`managed_continuation + replace_attempt`를 허용한다. 이 조합은 `packages/go/streamgate/recovery_plan.go:820`의 canonical `RecoveryPlan.Validate`와 SDD의 exact/schema post-commit 금지 계약에 정면으로 모순되므로, observation summary도 동일한 strategy→resume-mode matrix를 재사용해 거부하고 네 strategy의 정상 조합과 반대 resume mode 및 observation commit-state 결합을 표 기반 테스트로 고정한다.
- 다음 단계: `plan` 스킬로 위 Required를 반영한 후속 pair를 독립 재라우팅해 구현·검증한다.
