<!-- task=m-stream-evidence-gate-core/10+09_policy_hook plan=3 tag=REVIEW_REVIEW_API -->

# Policy Hook 필수 회귀 행렬 후속 계획

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

`CODE_REVIEW-cloud-G04.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수 최종 단계다. 테스트를 수정하고 아래 검증을 그대로 실행한 뒤 실제 notes/stdout/stderr/exit status를 기록하고 active 파일을 유지한 채 리뷰 준비 완료를 보고한다. 차단되면 구현 소유 evidence 필드에 정확한 blocker, 시도한 명령/출력, 재개 조건만 기록한다. 사용자에게 질문하거나 user-input 도구를 호출하거나 control-plane stop 파일을 만들거나 다음 상태를 분류하거나 로그 archive/`complete.log` 작성을 수행하지 않는다. 최종화는 code-review skill 전용이다.

## 배경

직전 follow-up은 세 생산 코드 결함을 수정했고 fresh package/race/proto/transport 검증도 통과했다. 그러나 계획이 필수로 지정한 exact-set malformed applicability 행렬과 mixed storage-envelope runtime lifecycle은 이름이 대응되는 테스트에 모두 구현되지 않았다. 이 후속은 생산 코드를 바꾸지 않고 `evidence_tail_test.go`의 두 회귀 공백만 닫는다.

## Archive Evidence Snapshot

- `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/plan_local_G08_2.log`, `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/code_review_cloud_G08_2.log`: `FAIL`(Required 2, Suggested 0, Nit 0). exact-set test에 resolved empty와 applicability missing/extra/duplicate가 없고, storage-envelope test가 실제 tail buffer/prepare/confirm/look-behind lifecycle을 검증하지 않는다.
- 영향 범위는 `packages/go/streamgate/evidence_tail_test.go`다. 직전 생산 변경의 focused/package/race와 `make proto`, Edge/Node transport, stale-symbol search, `git diff --check`는 fresh PASS했다.
- 선행 의존성은 `agent-task/archive/2026/07/m-stream-evidence-gate-core/09_decision_arbiter/complete.log` 하나로 충족된다. Roadmap Targets는 없고 `policy-hook` completion은 후속 `11+09,10_policy_hook_contract`에 유지된다.

## 분석 결과

### 읽은 파일

- `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/plan_local_G08_2.log`
- `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/code_review_cloud_G08_2.log`
- `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/plan_local_G08_1.log`
- `agent-task/m-stream-evidence-gate-core/10+09_policy_hook/code_review_cloud_G08_1.log`
- `packages/go/streamgate/evidence_tail.go`
- `packages/go/streamgate/evidence_tail_test.go`
- `packages/go/streamgate/filter_registry.go`
- `packages/go/streamgate/filter_registry_test.go`
- `packages/go/streamgate/evaluation_contract_test.go`
- `packages/go/streamgate/stream_release_test.go`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`
- `agent-spec/index.md`
- `agent-contract/index.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`, 상태 `[승인됨]`, 잠금 `해제`, 사용자 리뷰 `없음`.
- 대상 Acceptance Scenario는 `S07`이며 Milestone Task `policy-hook`의 rolling/terminal/fragment mixed composition을 다룬다.
- Evidence Map `S07`은 mixed-mode/channel-bound tests와 strongest bounded requirement/single buffer assertion을 요구한다. 따라서 REVIEW_REVIEW_API-2는 대표 terminal mode에서도 compiled max rolling look-behind와 최소 hard bound가 실제 단일 tail buffer/token/commit 경로에 적용되는지를 검증한다.
- REVIEW_REVIEW_API-1은 S07을 09 arbiter expected set으로 안전하게 연결하는 plan/resolved/applicability exact-set 경계를 닫는다. 이 partial에는 Roadmap Targets를 두지 않으며 `policy-hook` 완료 판정은 후속 11에 남긴다.

### 테스트 환경 규칙

- `test_env=local`. `agent-test/local/rules.md`와 매칭 profile `agent-test/local/platform-common-smoke.md`를 읽고 적용했다.
- 현재 checkout은 `/config/workspace/iop-s0`이며 profile의 정적 repo-root 표기 `/config/workspace/iop`와 다르지만 모든 검증은 현재 checkout 상대 경로에서 실행되고 외부 checkout으로 나가지 않는다. source sync/setup은 필요 없다.
- Go 기준은 `PATH` 첫 바이너리다. `command -v go`, resolved path, `go version`, `go env GOROOT`를 먼저 기록하고 별도 `GOTOOLCHAIN` 우회를 사용하지 않는다.
- concrete 검증은 fresh focused/package/race, `make proto`, Edge/Node transport, `gofmt -d`, `git diff --check`다. Go test cache 출력은 허용하지 않아 모두 `-count=1`을 사용한다.
- protobuf/wire 생산 코드는 바꾸지 않지만 `packages/go/**` profile과 직전 검증 계약을 유지하기 위해 `make proto`와 transport tests를 재실행한다. 외부 서비스, port, credential, live provider, full-cycle runtime은 이 test-only 변경에 필요하지 않다.
- 구조적 blank/skeleton, 누락 규칙, `<확인 필요>` 값은 없다. 별도 fallback 검증이나 테스트 룰 유지보수도 필요하지 않다.

### 테스트 커버리지 공백

- `TestMultiRequirementPolicyHookExactSetBinding`은 정상 set과 resolved subset/extra/duplicate만 검증한다. resolved empty와 applicability missing/extra/duplicate가 없다.
- `TestMultiRequirementPolicyHookStorageEnvelope`은 plan accessors와 incompatible aggregate만 검증한다. 실제 channel state의 1500 hard bound/80 rolling window, terminal representative mode, prepared token 하나, confirm cursor 하나와 80-rune suffix를 검증하지 않는다.
- 생산 동작은 기존 fresh package/race 검증으로 통과했으므로 새로운 생산 수정은 계획하지 않는다.

### 심볼 참조

none. 이 후속은 심볼을 추가·삭제·변경하지 않는다.

### 분할 판단

split decision policy를 평가했다. 변경 파일은 `evidence_tail_test.go` 하나이고 두 항목은 같은 mixed-plan fixture와 동일 focused/package/race 검증을 공유한다. 독립 배포 단위, 별도 소유자, 계약 마이그레이션, 병렬로 닫을 수 있는 서로 다른 모듈이 없으며 분리하면 fixture와 최종 검증만 중복된다. 따라서 단일 plan이 더 안전하다. 디렉터리 `10+09_policy_hook`의 선행 index 09는 archived `agent-task/archive/2026/07/m-stream-evidence-gate-core/09_decision_arbiter/complete.log` 하나로 충족되고 추가 dependency는 없다.

### 범위 결정 근거

- 포함: `packages/go/streamgate/evidence_tail_test.go`의 두 기존 테스트 확장과 active plan/review evidence.
- 제외: `evidence_tail.go`, `filter_registry.go` 및 다른 production source, 다른 test 파일, proto/생성물, contract/spec/roadmap 문서. 직전 생산 변경은 검증 대상이지만 이번 후속의 수정 대상이 아니다.
- dispatcher-owned `agent-task/m-stream-evidence-gate-core/WORK_LOG.md`와 sibling task 상태는 수정하지 않는다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`; finalizer: `finalize-task-policy.sh`; finalizer_mode: `pair`.
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`. 근거는 한 테스트 파일의 두 명시된 gap, 기존 public test seam, 재현 가능한 local 명령, test-only ownership, 사용자/외부 결정 부재다.
- build grade scores: scope_coupling=1, state_concurrency=1, blast_irreversibility=0, evidence_diagnosis=1, verification_complexity=1, 합계 G04. route_basis=`local-fit`, capability_gap=`none`, none floor=`none`, lane=`local`, filename=`PLAN-local-G04.md`.
- build loop-risk: `temporal_state`(append/trigger/prepare/confirm 및 duplicate/adverse path), `boundary_contract`(plan/resolved/applicability exact validation), `variant_product`(set 변형과 mixed-mode bound/lifecycle 조합)이 matched되어 triggered=`true`; concurrent_consistency와 structured_interpretation은 false다.
- review closures: scope/context/verification/evidence/ownership/decision 모두 `true`. 실제 test assertions, archived findings, fresh 명령을 독립 대조할 수 있고 외부 판단이 없다.
- review grade scores: scope_coupling=1, state_concurrency=1, blast_irreversibility=0, evidence_diagnosis=1, verification_complexity=1, 합계 G04. route_basis=`official-review`, capability_gap=`none`, none floor=`none`, lane=`cloud`, target=`Codex gpt-5.6-sol xhigh`, filename=`CODE_REVIEW-cloud-G04.md`.

## 구현 체크리스트

- [x] [REVIEW_REVIEW_API-1] exact-set 테스트에 resolved empty와 applicability missing/extra/duplicate의 결정적 거절을 추가하고 정상 cross-channel explicit not-applicable을 유지한다.
- [x] [REVIEW_REVIEW_API-2] mixed terminal+rolling plan의 실제 channel storage envelope, prepare/confirm, 80-rune look-behind와 단일 commit cursor를 검증한다.
- [x] [VERIFY-3] focused/package/race/proto/transport를 fresh 실행하고 formatting/diff를 확인한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_API-1] Exact-set malformed applicability 행렬

문제: `packages/go/streamgate/evidence_tail_test.go:4738-4808`의 `TestMultiRequirementPolicyHookExactSetBinding`은 resolved subset/extra/duplicate만 확인한다. `plan_local_G08_2.log:193-205`가 필수로 정한 resolved empty와 applicability missing/extra/duplicate를 검증하지 않아 constructor/index/exact-set 경계가 후퇴해도 focused test가 통과한다.

해결 방법:

Before (`packages/go/streamgate/evidence_tail_test.go:4780`):

```go
t.Run("rejectsSubsetResolvedFilters", func(t *testing.T) {
	_, err := plan.BindEpochFilters(epoch, resolved[:1])
	if err == nil {
		t.Error("expected error when resolvedFilters is a subset of plan, got nil")
	}
})
```

After:

```go
tests := []struct {
	name    string
	epoch   EvidenceEpoch
	filters []ResolvedFilter
	wantErr string
}{
	{name: "resolvedEmpty", epoch: epoch, filters: nil, wantErr: "streamgate: resolved filter count mismatch: got 0, expected 2"},
	{name: "applicabilityMissing", epoch: epochWithOnlyF1, filters: resolved, wantErr: "streamgate: applicability count mismatch: got 1, expected 2"},
	{name: "applicabilityExtra", epoch: epochWithF1F2F3, filters: resolved, wantErr: "streamgate: applicability count mismatch: got 3, expected 2"},
}
```

- 기존 normal/subset/extra/duplicate resolved cases를 유지하고 resolved empty case를 추가한다.
- `NewEvidenceEpochWithApplicabilities`로 missing/extra applicability epoch를 만들고 `BindEpochFilters`의 정확한 오류를 단언한다.
- duplicate applicability는 epoch constructor가 먼저 거절하므로 중복 `FilterApplicability` slice로 constructor 오류를 정확히 단언한다. 내부 map을 직접 위조하지 않는다.
- 정상 case의 stable order와 `f2` cross-channel explicit not-applicable assertion을 유지한다.

수정 파일 및 체크리스트:

- [x] `packages/go/streamgate/evidence_tail_test.go`: existing exact-set test를 table-driven malformed set matrix로 확장한다.

테스트 작성:

- 경로/이름: `packages/go/streamgate/evidence_tail_test.go`, `TestMultiRequirementPolicyHookExactSetBinding`.
- fixture: plan filter `f1/f2`, exact resolved snapshot, original epoch apps, missing/extra/duplicate app variants.
- assertion: resolved empty/subset/extra/duplicate 및 applicability missing/extra/duplicate가 각각 결정적 오류를 내고 정상 exact set은 `f1,f2` stable order와 `f2` not-applicable을 유지한다.

중간 검증:

```bash
gofmt -d packages/go/streamgate/evidence_tail_test.go
go test -count=1 ./packages/go/streamgate -run '^TestMultiRequirementPolicyHookExactSetBinding$'
```

예상: formatting diff가 없고 모든 normal/malformed subcase가 fresh PASS한다.

### [REVIEW_REVIEW_API-2] Runtime storage envelope lifecycle

문제: `packages/go/streamgate/evidence_tail_test.go:4703-4736`은 `MaxBufferRunes`/`RollingEvidenceRunes` accessor와 incompatible aggregate만 검사한다. `evidence_tail.go:1434-1445,1476-1478,1898-1900`의 실제 channel allocation과 confirm trim이 대표 terminal requirement 값이 아니라 compiled 1500/80 envelope를 사용하는지는 검증하지 않는다.

해결 방법:

Before (`packages/go/streamgate/evidence_tail_test.go:4717`):

```go
if plan.MaxBufferRunes("ch") != 1500 {
	t.Errorf("MaxBufferRunes want 1500 (min hard bound), got %d", plan.MaxBufferRunes("ch"))
}
if plan.RollingEvidenceRunes("ch") != 80 {
	t.Errorf("RollingEvidenceRunes want 80 (max rolling evidence), got %d", plan.RollingEvidenceRunes("ch"))
}
```

After:

```go
tail, err := NewEvidenceTail(plan)
// Append one 100-rune text event, append the configured terminal trigger,
// prepare the terminal epoch, and confirm its single release event.
if got := tail.channelState["ch"].maxBufferRunes; got != 1500 { /* fail */ }
if got := tail.channelState["ch"].effectiveEvidenceRunes; got != 80 { /* fail */ }
if got := tail.CommittedCursor("ch"); got != 1 { /* fail */ }
// EffectiveLookBehind contains one sliced text event with the exact trailing 80 runes.
```

- 기존 accessor assertions와 incompatible aggregate case를 유지한다.
- rolling 80/max 2000과 terminal max 1500을 함께 둔 plan에서 strongest representative mode가 terminal임을 확인한다.
- 100-rune text event 뒤 configured terminal을 append하고 terminal epoch를 `PrepareRelease`/`ConfirmRelease(ReleasedEvents: 1)`로 처리한다.
- prepared snapshot event 수 1, opaque token non-empty, committed cursor 1, pending 0, runtime `maxBufferRunes=1500`, `effectiveEvidenceRunes=80`, exact trailing 80-rune text를 단언해 single buffer/token/commit을 증명한다.

수정 파일 및 체크리스트:

- [x] `packages/go/streamgate/evidence_tail_test.go`: existing storage-envelope test에 실제 tail lifecycle subcase를 추가한다.

테스트 작성:

- 경로/이름: `packages/go/streamgate/evidence_tail_test.go`, `TestMultiRequirementPolicyHookStorageEnvelope`.
- fixture: 100-rune distinct prefix/suffix text, rolling 80/max 2000 binding, terminal/max 1500 binding, configured terminal event.
- assertion: strongest terminal mode와 무관하게 runtime hard bound 1500/rolling window 80, one prepared event/token, one confirm/cursor, pending empty, exact 80-rune suffix를 유지한다.

중간 검증:

```bash
gofmt -d packages/go/streamgate/evidence_tail_test.go
go test -count=1 ./packages/go/streamgate -run '^TestMultiRequirementPolicyHookStorageEnvelope$'
```

예상: accessor, runtime lifecycle, incompatible aggregate subcases가 fresh PASS한다.

### [VERIFY-3] Fresh 전체 검증과 evidence

문제: 직전 명령은 모두 통과했지만 누락된 assertions가 실행 경로에 없었으므로 새 matrix를 포함한 fresh 증거가 필요하다.

해결 방법: production source를 수정하지 않고 아래 최종 검증을 현재 checkout에서 그대로 실행한다. 생성물 drift가 생기면 직접 편집하지 말고 범위를 기록한다.

수정 파일 및 체크리스트:

- [x] `packages/go/streamgate/evidence_tail_test.go`: focused/full/race 결과를 확인한다.
- [x] `CODE_REVIEW-cloud-G04.md`: 실제 stdout/stderr와 exit status를 기록한다.

테스트 작성: VERIFY 전용 새 파일은 만들지 않는다. REVIEW_REVIEW_API-1~2의 기존 test 확장을 실행한다.

중간 검증:

```bash
go test -count=1 ./packages/go/streamgate -run '^TestMultiRequirementPolicyHook(ExactSetBinding|StorageEnvelope)$'
go test -count=1 ./packages/go/streamgate
```

예상: 새 Required regression과 package suite가 fresh PASS한다.

## 의존 관계 및 구현 순서

1. archived `agent-task/archive/2026/07/m-stream-evidence-gate-core/09_decision_arbiter/complete.log`로 encoded predecessor 09 충족을 확인한다.
2. REVIEW_REVIEW_API-1과 REVIEW_REVIEW_API-2를 같은 test file에 구현하고 각각의 focused test를 실행한다.
3. VERIFY-3 전체 검증과 review evidence를 완료한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/go/streamgate/evidence_tail_test.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2, VERIFY-3 |
| `CODE_REVIEW-cloud-G04.md` | VERIFY-3 evidence |

## 최종 검증

```bash
command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT
bash -lc 'shopt -s nullglob; logs=(agent-task/m-stream-evidence-gate-core/09_*/complete.log agent-task/m-stream-evidence-gate-core/09+*/complete.log); if (( ${#logs[@]} == 0 )); then logs=(agent-task/archive/*/*/m-stream-evidence-gate-core/09_*/complete.log agent-task/archive/*/*/m-stream-evidence-gate-core/09+*/complete.log); fi; printf "%s\n" "${logs[@]}" | sort; test "${#logs[@]}" -eq 1'
gofmt -d packages/go/streamgate/evidence_tail_test.go
go test -count=1 ./packages/go/streamgate -run '^TestMultiRequirementPolicyHook(ExactSetBinding|StorageEnvelope)$'
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
```

예상: host Go identity와 유일한 09 dependency가 확인되고, formatting/focused/package/race/proto/transport/diff가 모두 fresh PASS한다. Go test cache 출력은 허용하지 않는다.

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