<!-- task=m-stream-evidence-gate-core/09_decision_arbiter plan=1 tag=API -->
# Stream Evidence Gate Core — Decision Arbiter 구현 계획

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

- 아래 체크리스트 순서와 범위를 지키고, 범위를 넓혀야 하면 구현을 멈추고 `CODE_REVIEW-cloud-G07.md`에 정확한 근거를 남긴다.
- 구현 전 `agent-ops/rules/project/rules.md`, `agent-ops/rules/project/domain/platform-common/rules.md`, `agent-test/local/rules.md`를 다시 확인한다.
- 각 항목을 완료할 때 review stub의 구현 에이전트 소유 섹션을 실제 변경, 판단, 명령, 출력으로 채운다.
- 구현 완료 후 active 파일을 유지하고 review-ready로 보고한다. 사용자 질문, commit/push, PR, 배포, recovery 실행은 범위 밖이다.

## 배경

`GateCoordinator`는 complete barrier 뒤 `EpochArbiter`를 한 번 호출하지만 결과를 반환하지 않는다. `ResolvedFilter`의 effective priority도 epoch outcome에 보존되지 않고, `EvidenceBatch`에는 base disposition의 공식 accessor가 없다.

이 작업은 승인된 SDD S13의 출력 이름과 닫힌 action 집합을 그대로 사용한다. `ArbitrationResult`는 `release`, `hold`, `terminal`, `recover`, `replacement` 중 하나만 반환한다. filter는 decision과 선택적 `RecoveryIntent`만 제공하며 mutation/retry/rebuild/dispatch와 budget/side-effect eligibility는 후속 coordinator에 남긴다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md)
- Task ids:
  - `decision-arbiter`: base disposition과 모든 active filter outcome을 deterministic하게 합성해 single action을 반환한다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- `agent-task/m-stream-evidence-gate-core/09_decision_arbiter/plan_local_G06_0.log`: verdict 없는 최초 계획. 별도 `GateAction`과 `terminal_success|terminal_error` action을 정의해 승인된 계약과 충돌했고, batch base disposition과 recovery priority 검증이 불충분해 대체했다.
- `agent-task/m-stream-evidence-gate-core/09_decision_arbiter/code_review_cloud_G07_0.log`: 구현 증거와 판정이 없는 최초 review stub. 위 계획과 함께 이력으로만 보존한다.

## Analysis Result

### 읽은 파일

- `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `packages/go/streamgate/event.go`
- `packages/go/streamgate/filter_contract.go`
- `packages/go/streamgate/filter_contract_test.go`
- `packages/go/streamgate/evaluation_contract.go`
- `packages/go/streamgate/evaluation_contract_test.go`
- `packages/go/streamgate/filter_registry.go`
- `packages/go/streamgate/filter_registry_test.go`
- `packages/go/streamgate/parallel_evaluation.go`
- `packages/go/streamgate/parallel_evaluation_test.go`
- `packages/go/streamgate/evidence_tail.go`
- `packages/go/streamgate/consumer_contract_test.go`
- `go.mod`

### 계약·코드·테스트 분석

- SDD는 `[승인됨]`, 구현 잠금은 `해제`이며 S13 출력은 `ArbitrationResult`와 정확히 다섯 action이다.
- `BaseEventDisposition`은 `hold`, `release_candidate`, `terminal_success_candidate`, `terminal_error_candidate`를 이미 정의한다. terminal 성공/오류는 result의 base disposition으로 구분하고 새 action kind를 만들지 않는다.
- terminal batch는 terminal/provider-error control event를 정확히 하나 포함하지만 데이터 event도 함께 포함할 수 있다. non-terminal batch는 event 없이 staged response-start만 가질 수도 있다.
- `deriveEvaluationFailureDisposition`은 blocking violation과 fatal을 모두 `blocking_fatal`로 접는다. Arbiter가 이 값만 보면 intent가 있는 matched violation을 terminal로 잘못 처리하므로 evaluated decision kind와 intent를 먼저 검사해야 한다.
- `ResolvedFilter.Priority()`는 effective priority를 제공하지만 `BindEpoch`, `EpochFilter`, `EpochFilterOutcome`으로 전달하지 않는다.
- 생산 `Arbitrate` 호출은 `parallel_evaluation.go` 한 곳이고 관련 constructor/signature test seam은 세 contract test 파일에 있다.
- 기준선 `make proto`, fresh streamgate package test, Edge/Node transport test는 통과했다.
- 공백은 canonical base derivation, matched/unmatched error, forged intent priority, priority/id conflict, 입력 순서 독립성, coordinator result exact-once다.

### 분할·범위 판단

- result API, priority snapshot, 같은 패키지의 단일 coordinator call-site는 하나의 원자적 계약이다. 더 나누면 컴파일 불가능한 중간 상태나 임시 adapter가 필요해 분할하지 않는다.
- policy schedule은 `10+09_policy_hook`, consumer closure는 `11+09,10_policy_hook_contract`가 담당한다.
- recovery 실행/eligibility, host/provider wiring, codec/config/wire schema, policy composition과 consumer fixture는 범위 밖이다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`.
- finalizer/mode: `finalize-task-policy.sh`, `pair`.
- Build: local-fit=true, scope=2, state=2, blast=1, evidence=1, verification=1 → `local G07`.
- Review: official-review, scope=2, state=2, blast=1, evidence=1, verification=1 → `cloud G07`.
- Review adapter/model/effort: `codex`, `gpt-5.6-sol`, `xhigh`.
- Loop-risk는 disposition × outcome × enforcement × priority/id와 all-complete 상태 전이다. matrix와 race 검증으로 닫으며 capability gap/unknown은 없다.

## 구현 체크리스트

- [x] [API-1] `EvidenceBatch`의 canonical base disposition과 불변 `ArbitrationResult` 닫힌 계약을 구현한다.
- [x] [API-2] registry-effective priority를 epoch outcome까지 보존하고 deterministic arbitration 및 coordinator result 전파를 구현한다.
- [x] [TEST-3] S13 조합표, matched recovery, terminal 보존, conflict, 입력 순서 독립성, exact-once 경계를 자동화한다.
- [x] [VERIFY-4] local 집중/전체/race/생성물/transport 검증을 실행하고 Roadmap Completion 근거를 정리한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [API-1] canonical base disposition과 `ArbitrationResult`

- `EvidenceBatch.BaseDisposition() (BaseEventDisposition, error)` 또는 동등 accessor를 `filter_contract.go`에 둔다.
- terminal batch는 event 순서와 무관하게 정확히 하나인 terminal/provider-error control event의 disposition을 사용한다.
- non-terminal batch는 ordered `Events()`의 마지막 event를 현재 epoch trigger로 사용한다. response-start와 delta가 함께 있으면 마지막 delta의 disposition이 기준이다.
- event가 없고 staged response-start만 있으면 `hold`, 둘 다 없으면 추측하지 않고 오류다.
- `ArbitrationAction`은 `release|hold|terminal|recover|replacement`만 허용한다. `ArbitrationResult`는 action, 원래 base disposition, 선택 filter/rule/intent 또는 replacement를 불변으로 보존한다.
- 기본 매핑은 hold→hold, release candidate→release, 두 terminal candidate→terminal이다. action별 필수/금지 필드, zero/unknown/id mismatch를 검증하고 nested 값은 방어적으로 복사한다.

수정 파일:

- `packages/go/streamgate/filter_contract.go`
- `packages/go/streamgate/filter_contract_test.go`
- `packages/go/streamgate/decision_arbiter.go` 신규
- `packages/go/streamgate/decision_arbiter_test.go` 신규

### [API-2] effective priority, arbitration, coordinator 전파

- `EpochFilter`와 `EpochFilterOutcome`에 nonnegative effective priority를 추가하고 constructor/accessor/`Validate`를 갱신한다. `ResolvedFilter.BindEpoch`가 snapshot priority를 ready/non-ready outcome 모두에 전달한다.
- blocking evaluated outcome은 failure disposition보다 decision을 먼저 분류한다: violation+valid intent는 recover 후보, violation without intent/fatal/evaluation error는 terminal 실패, replacement는 replacement 후보다.
- observe-only violation/fatal/error/replacement는 blocking action 후보가 아니다.
- intent priority는 epoch effective priority와 정확히 같아야 한다. 불일치는 deterministic validation error이며 intent 값으로 registry priority를 덮거나 정렬하지 않는다.
- terminal 실패가 하나라도 있으면 recover/replacement보다 terminal이다. 그 외 recover/replacement 후보는 effective priority 내림차순, stable filter id 오름차순으로 하나를 고른다.
- blocking deferred는 terminal batch에서 terminal, non-terminal에서 hold다. not-applicable은 release를 막지 않는다. hard-bound epoch에서 deferred를 만들지 않는 책임은 후속 policy hook에 둔다.
- override가 없으면 canonical base action을 유지해 unmatched provider error와 terminal candidate가 pass로 release되지 않게 한다.
- `EpochArbiter.Arbitrate`는 `(ArbitrationResult, error)`, `GateCoordinator.Evaluate`는 `(EvaluationSet, ArbitrationResult, error)`, `Submit`은 `(ArbitrationResult, error)`를 반환한다.
- cancellation/validation/barrier 실패는 유효 result를 가장하지 않는다. complete barrier, arbiter at-most-once, FIFO/backpressure/close는 유지한다.

수정 파일:

- `packages/go/streamgate/evaluation_contract.go`
- `packages/go/streamgate/evaluation_contract_test.go`
- `packages/go/streamgate/filter_registry.go`
- `packages/go/streamgate/filter_registry_test.go`
- `packages/go/streamgate/parallel_evaluation.go`
- `packages/go/streamgate/parallel_evaluation_test.go`
- `packages/go/streamgate/decision_arbiter.go`

### [TEST-3] S13 matrix와 결정성

- release+pass, hold+not-applicable, pass+blocking-deferred, unmatched error+pass, matched error+recovery, deferred+violation, fatal+recovery/replacement를 표로 고정한다.
- terminal success/error base가 pass/observe-only outcome으로 약화되지 않는지, violation intent 유무와 priority mismatch가 올바르게 갈리는지 확인한다.
- recover/replacement 후보 순열이 priority desc/filter id asc와 같은 결과인지 확인한다.
- effective priority snapshot의 ready/non-ready 보존과 방어적 복사를 확인한다.
- arbiter가 complete barrier 뒤 한 번만 호출되고 `Evaluate`/`Submit`이 같은 result를 전달하며 cancel 경로에서는 호출되지 않는지 확인한다.

### [VERIFY-4] 최종 검증

- fresh test만 사용하고 실제 출력을 review stub에 기록한다.
- 전부 통과한 뒤에만 `decision-arbiter` 완료 근거를 준비하고 active 문서를 review-ready 상태로 유지한다.

## 수정 파일 요약

| 파일 | 변경 |
|---|---|
| `filter_contract.go`, `filter_contract_test.go` | canonical batch base disposition |
| `decision_arbiter.go`, `decision_arbiter_test.go` | S13 result와 deterministic arbitration |
| `evaluation_contract.go`, `evaluation_contract_test.go` | effective priority snapshot |
| `filter_registry.go`, `filter_registry_test.go` | registry priority propagation |
| `parallel_evaluation.go`, `parallel_evaluation_test.go` | result 반환과 exact-once 전파 |
| `CODE_REVIEW-cloud-G07.md` | 실제 구현·검증 증거 |

## 최종 검증

```bash
command -v go
readlink -f "$(command -v go)"
go version
go env GOROOT
gofmt -w packages/go/streamgate/filter_contract.go packages/go/streamgate/filter_contract_test.go packages/go/streamgate/decision_arbiter.go packages/go/streamgate/decision_arbiter_test.go packages/go/streamgate/evaluation_contract.go packages/go/streamgate/evaluation_contract_test.go packages/go/streamgate/filter_registry.go packages/go/streamgate/filter_registry_test.go packages/go/streamgate/parallel_evaluation.go packages/go/streamgate/parallel_evaluation_test.go
go test -count=1 ./packages/go/streamgate -run 'Test(EvidenceBatchBaseDisposition|ArbitrationResult|DecisionArbiter|EpochFilter|EpochFilterOutcome|ResolvedFilter|GateCoordinator)'
go test -count=1 ./packages/go/streamgate
go test -race -count=1 ./packages/go/streamgate
make proto
go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport
git diff --check
```

예상 결과: 모든 명령이 exit 0이고 race report, 생성물 drift, whitespace error가 없으며 S13 matrix가 정확히 하나의 닫힌 action을 반환한다.

**모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다.**
