<!-- task=m-stream-evidence-gate-core/20+17,19_edge_vertical_slice plan=2 tag=REVIEW_EDGE_VERTICAL_SLICE_CLOSURE -->

# Code Review Reference - REVIEW_EDGE_VERTICAL_SLICE_CLOSURE

> **[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/20+17,19_edge_vertical_slice, plan=2, tag=REVIEW_EDGE_VERTICAL_SLICE_CLOSURE

## 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:
  - `vertical-slice`: request snapshot부터 single re-admission과 release까지의 production host 한 사이클
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- 직전 loop: `agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/plan_cloud_G10_1.log`, `agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/code_review_cloud_G10_1.log`.
- 판정/요약: FAIL, Required 3 / Suggested 0 / Nit 0. provider-pool actual path switch, enabled tool-validation Core single owner, S16/full-cycle evidence가 미충족이다.
- 완료 유지: current-binding/resource exactly-once cleanup, terminal outcome metric, strategy별 recovery cap, `enabled` load/refresh 계약은 package/race tests에서 통과했다.
- 영향 파일: `apps/edge/internal/openai/{normalized_sse.go,buffered_sse.go,chat_completion.go,chat_handler.go,responses_handler.go,provider_tunnel.go,stream_gate_runtime.go,stream_gate_release_sink.go,tool_validation.go,stream_gate_vertical_slice_test.go,provider_tool_validation_test.go,provider_test_support_test.go}`, `scripts/e2e-openai-vllm.sh`.
- 검증 evidence: 리뷰 시 focused/package/race tests와 auxiliary mock smoke는 PASS했다. 후속 최종화 preflight에서는 active sibling `21+14,19_filter_observation_contract`의 untracked `filter_observation.go`/tests가 epoch/commit-state fixture 4건을 실패시켰지만 이번 target focused race tests는 PASS했다. `IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh`는 fake provider가 non-stream passthrough를 잘못 거부해 HTTP 400(exit 22)이었고, 필수 `scripts/dev/edge.sh` + `scripts/dev/node.sh` console full-cycle evidence는 없었다.
- Roadmap carryover: `vertical-slice`는 미완료이며 roadmap은 변경하지 않는다. 상세 finding 문맥이 필요할 때만 위 review log를 좁게 읽는다.

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

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

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

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

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-1 provider-pool Core cycle과 attempt별 framing | [x] |
| REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-2 enabled tool-validation Core single owner | [x] |
| REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-3 S16 및 실행 evidence closure | [x] |

## 구현 체크리스트

- [x] REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-1 provider-pool initial/recovery를 Core 한 사이클에 연결하고 actual normalized↔tunnel binding에 맞춰 commit 전 framing을 고정한다.
- [x] REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-2 enabled buffered/non-stream tool-validation의 hold/validate/rebuild/re-admission을 Core single owner로 이관하면서 structured `tool_calls` 출력을 보존한다.
- [x] REVIEW_EDGE_VERTICAL_SLICE_CLOSURE-3 S16 simultaneous violation/path-switch/preparer/backpressure fixture와 OpenAI auxiliary smoke 및 필수 dev console full-cycle evidence를 완성한다.
- [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_G10_2.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G10_2.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [x] PASS이면 active task 디렉터리 `agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/`를 `agent-task/archive/YYYY/MM/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-stream-evidence-gate-core/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이면 code-review skill의 판정에 맞는 다음 filesystem state를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- **runner 진입 signature**: 계획의 `s.runOpenAIChatStreamGate(w, flusher, dc, initialBinding)` 스케치 대신 direct route는 기존 `runOpenAIChatStreamGate(w, flusher, dc, handle)`를 유지하고, provider-pool route는 전용 `runOpenAIChatPoolStreamGate(w, dc, result)`가 담당한다. pool은 admission 이후에야 actual path를 알 수 있어 initial transport/sink/selector를 한 곳에서 결정해야 하고, 기존 direct 호출부와 `stream_gate_vertical_slice_test.go`의 기존 fixture를 그대로 유지할 수 있기 때문이다. 공통 조립은 `buildOpenAIChatStreamGateRuntimeFor(dc, cfg)` 하나로 수렴시켰다.
- **non-stream provider-pool도 Core로 이관**: 계획 항목 1은 streaming path switch를 중심으로 기술했지만, 최종 검증 10이 `retrySubmit(`을 runtime-disabled branch 안으로만 제한하므로 `completeChatCompletion`(non-stream JSON)도 enabled에서는 Core가 소유해야 한다. 그 결과 non-stream pool recovery가 tunnel candidate를 고를 수 있게 되어 `openAITunnelReleaseSink`에 buffered 모드(터미널에서 `RewriteComplete` 1회)를 추가했다. streaming tunnel은 기존대로 event source가 provider byte 순서대로 rewrite한다.
- **`openAIChatStreamGateConfig`에 preparer seam 추가**: production OpenAI 표면에는 host preparer가 없어 `preparer`/`prepFactory`는 nil로 남지만, 계획 항목 3이 요구한 "preparer success/failure/no-dispatch" evidence를 test-only Core 재구현 없이 실제 host wiring으로 만들려면 config에 seam이 필요했다. Core는 두 값을 함께 nil이거나 함께 non-nil이도록 강제하므로 production 동작은 변하지 않는다.
- **`TestStreamGateResponsesPoolRecoveryTerminal`은 최종 검증 6 regex에 걸리지 않는다**: 계획이 고정한 `-run 'TestStreamGate.*(ProviderPool|ToolValidation|Simultaneous|Preparer|Backpressure)'` 패턴에 `ResponsesPool`이 매칭되지 않는다. 명령은 계획대로 유지했고, 이 fixture는 최종 검증 4(package)와 5(race)에서 실행된다.
- **enabled tool-validation 재시도 한도 주체 변경**: legacy `maxToolValidationAttempts = 2`(재시도 1회)는 runtime-disabled 경로에만 남고, enabled 경로의 한도는 Core `max_request_fault_recovery`(기본 3)가 소유한다. 계획의 "request mutation/rebuild/dispatch/counter는 Core Coordinator가 소유한다"를 따른 결과이며, 기본 설정에서 tool-validation 재시도 허용량이 1 → 3으로 늘어난다. provider에 노출되는 `openai_tool_validation_attempt` / `retry_of` / `failure` metadata는 그대로 보존한다.

## 주요 설계 결정

- **attempt codec selector + composite sink**: `openAIStreamGateCodecSelector`는 event-source factory가 actual admission path를 확인하는 지점에서만 갱신되고, `openAICompositeReleaseSink`는 첫 commit 호출에서 delegate를 확정한 뒤 고정한다. 따라서 commit 이전 recovery만 framing을 바꿀 수 있고, commit 이후 path 변경은 이미 열린 응답을 다시 프레이밍하지 못한다. direct(model_routes) route는 tunnel attempt가 생길 수 없으므로 composite로 감싸지 않고 기존 단일 sink를 그대로 쓴다(바이트 동일성 유지).
- **buffered attempt는 content event를 발행하지 않는다**: `openAIBufferedChatEventSource`는 `response_start` → (`collectChatCompletionOutput` 1회) → `terminal`만 낸다. 조립된 응답은 request-local `openAIBufferedResultHolder`에 담기고, sink의 `Release`는 evidence-only no-op이며 실제 렌더링은 `CommitTerminal`에서 한 번만 일어난다. 임의 크기의 buffered 응답을 Core evidence buffer(`max_buffer_runes` 기본 4096)에 넣지 않으면서도 commit 전에 단일 판정 지점을 확보하기 위한 선택이다.
- **tool-validation filter의 hold mode는 terminal_gate**: hold mode `none`은 채널에 blocking requirement가 없을 때 Core가 `provider_error`에서만 trigger-ready로 만들기 때문에(`evidence_tail.go` `deriveTriggerReady`) terminal 판정이 아예 실행되지 않는다. 그래서 `terminal_gate` + subscribed `[text_delta, terminal]`로 등록해 content event가 하나도 없어도 terminal에서 applicable/trigger-ready가 되도록 했다.
- **filter applicability는 attempt마다 재해석**: `openAIToolValidationFilter.Applies()`는 `FilterContext.ExecutionPath()`가 `provider_tunnel`이면 false를 반환한다. pool recovery가 raw passthrough candidate를 고르면 normalized tool-validation 계약은 그 attempt에 적용되지 않고, sink도 tunnel framing으로 delegate한다(`TestStreamGatePoolToolValidationDropsOnTunnelCandidate`).
- **오류 envelope 보존**: `agent-contract/outer/openai-compatible-api.md`가 고정한 `tool_validation_error` 계약을 유지하기 위해, buffered sink는 Core terminal이 error이고 holder에 validation 실패가 남아 있으면 Core descriptor code 대신 `tool_validation_error` + 마지막 validation 사유를 그대로 쓴다. buffered SSE는 legacy와 동일하게 200 + SSE error event, non-stream JSON은 502 + error object이다.
- **Responses passthrough의 path 전환은 안전한 terminal로 수렴**: pure passthrough 표면은 normalized attempt를 렌더링할 수 없으므로, tunnel runtime의 event-source factory가 normalized transport를 거부한다. dispatcher가 그 attempt를 abort하고 Core가 error terminal로 수렴하므로 이미 진행 중인 응답이 재프레이밍되지 않는다(`TestStreamGateResponsesPoolRecoveryTerminal/normalized_candidate_terminates_safely`).
- **fake vLLM provider fixture 수정**: `scripts/e2e-openai-vllm.sh`의 fake provider는 `openai_compat` route가 non-stream 요청을 그대로 relay한다는 사실을 반영해 `stream=false`에 표준 `chat.completion` JSON을, `stream=true`에 기존 SSE를 응답한다. script의 두 assertion(비스트리밍/스트리밍)과 fixture의 모순이 해소된다.

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

- initial과 recovery가 모두 `SubmitProviderPool` admission을 사용하고 같은 `RequestRuntime`에서 actual provider/model/execution path가 바뀌는가.
- composite sink가 첫 commit 전에 actual codec을 고정하고 normalized SSE/JSON 또는 raw tunnel framing만 한 번 노출하는가.
- runtime-enabled buffered/non-stream tool-validation에서 Core 외 retry owner가 없고 disabled compatibility만 `retrySubmit`을 호출하는가.
- valid native/text tool call의 구조화, content cleanup, finish reason, usage와 오류 envelope가 기존 OpenAI 계약을 유지하는가.
- simultaneous violations, preparer success/failure, evaluation backpressure, exhaustion이 single plan/dispatch/terminal로 결정적으로 검증되는가.
- OpenAI auxiliary smoke와 dev Edge-Node console full-cycle을 구분하고 필수 full-cycle의 payload 순서까지 기록했는가.

## 검증 결과

각 명령의 실제 stdout/stderr를 그대로 기록한다. 명령을 바꾸면 `계획 대비 변경 사항`에 대체 명령과 이유를 적는다. 긴 출력은 repo 밖 저장 경로와 생성 명령을 기록한다. Go test cache는 허용하지 않으며, 0 tests 실행은 PASS evidence가 아니다.

### 사전 검증

- 명령: `go test -count=1 -run 'TestFilterObservation_KindSpecificRequiredFields|TestFilterObservation_EpochIDZeroMeansUnbound' ./packages/go/streamgate`
- 기대: active sibling baseline PASS. 계속 실패하면 이 task 범위를 확장하지 않고 exact output과 `21+14,19_filter_observation_contract` 완료/수정이라는 재개 조건을 기록한다.
- 실제 출력:

```
$ go test -count=1 -run 'TestFilterObservation_KindSpecificRequiredFields|TestFilterObservation_EpochIDZeroMeansUnbound' ./packages/go/streamgate
ok  	iop/packages/go/streamgate	0.007s
```

active sibling `21+14,19_filter_observation_contract`의 baseline은 복구되어 PASS다. 이 task는 해당 sibling source/test를 수정하지 않았다.

### 중간 검증 1

- 명령: `go test -count=1 -run 'TestStreamGateProviderPool|TestOpenAIAttemptDispatcherPool' ./apps/edge/internal/openai`
- 기대: 같은 Core cycle의 provider/path switch와 dispatcher regression PASS.
- 실제 출력:

```
$ go test -count=1 -run 'TestStreamGateProviderPool|TestOpenAIAttemptDispatcherPool' ./apps/edge/internal/openai
ok  	iop/apps/edge/internal/openai	0.012s
```

실행된 test 목록(같은 명령에 `-v`만 추가해 확인, 0 tests 아님):

```
=== RUN   TestOpenAIAttemptDispatcherPoolPathSwitchAndFreshAuth
--- PASS: TestOpenAIAttemptDispatcherPoolPathSwitchAndFreshAuth (0.00s)
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/normalized_to_tunnel
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/tunnel_to_normalized
--- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce (0.00s)
=== RUN   TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak
--- PASS: TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak (0.00s)
=== RUN   TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce
--- PASS: TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce (0.00s)
PASS
ok  	iop/apps/edge/internal/openai	0.011s
```

### 중간 검증 2

- 명령: `go test -count=1 -run 'TestStreamGate.*ToolValidation|TestChatCompletions.*ToolValidation' ./apps/edge/internal/openai`
- 기대: enabled Core single owner, structured output, disabled legacy compatibility PASS.
- 실제 출력:

```
$ go test -count=1 -run 'TestStreamGate.*ToolValidation|TestChatCompletions.*ToolValidation' ./apps/edge/internal/openai
ok  	iop/apps/edge/internal/openai	0.013s
```

실행된 test 목록(같은 명령에 `-v`만 추가해 확인, 0 tests 아님):

```
=== RUN   TestChatCompletionsToolChoiceNoneSkipsRuntimeToolValidation
--- PASS: TestChatCompletionsToolChoiceNoneSkipsRuntimeToolValidation (0.00s)
=== RUN   TestChatCompletionsProviderPoolNormalizedToolValidationRetryPreservesPoolRequest
--- PASS: TestChatCompletionsProviderPoolNormalizedToolValidationRetryPreservesPoolRequest (0.00s)
=== RUN   TestChatCompletionsProviderPoolToolValidationRetryRejectsTunnelResult
--- PASS: TestChatCompletionsProviderPoolToolValidationRetryRejectsTunnelResult (0.00s)
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_zero_blocks_every_retry
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_one_re-admits_exactly_once
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/runtime_disabled_keeps_the_legacy_retry_loop
--- PASS: TestStreamGateEnabledToolValidationHasSingleRecoveryOwner (0.00s)
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/non-stream_json
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream_exhaustion_keeps_the_tool_validation_error_envelope
--- PASS: TestStreamGateEnabledToolValidationPreservesStructuredOutput (0.00s)
=== RUN   TestStreamGatePoolToolValidationReAdmitsThroughPool
--- PASS: TestStreamGatePoolToolValidationReAdmitsThroughPool (0.00s)
=== RUN   TestStreamGatePoolToolValidationDropsOnTunnelCandidate
--- PASS: TestStreamGatePoolToolValidationDropsOnTunnelCandidate (0.00s)
PASS
ok  	iop/apps/edge/internal/openai	0.013s
```

### 중간 검증 3

- 명령: `go test -race -count=1 -run 'TestStreamGate.*(ProviderPool|Simultaneous|Preparer|Backpressure)' -v ./apps/edge/internal/openai`
- 기대: 모든 S16 host fixture PASS.
- 실제 출력:

```
$ go test -race -count=1 -run 'TestStreamGate.*(ProviderPool|Simultaneous|Preparer|Backpressure)' -v ./apps/edge/internal/openai
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/normalized_to_tunnel
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/tunnel_to_normalized
--- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce (0.00s)
    --- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/normalized_to_tunnel (0.00s)
    --- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/tunnel_to_normalized (0.00s)
=== RUN   TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak
--- PASS: TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak (0.00s)
=== RUN   TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce
--- PASS: TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce (0.00s)
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle/preparer_success_dispatches_once
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle/preparer_failure_dispatches_nothing
--- PASS: TestStreamGateChatRecoveryPreparerLifecycle (0.00s)
    --- PASS: TestStreamGateChatRecoveryPreparerLifecycle/preparer_success_dispatches_once (0.00s)
    --- PASS: TestStreamGateChatRecoveryPreparerLifecycle/preparer_failure_dispatches_nothing (0.00s)
=== RUN   TestStreamGateChatProducerBackpressureDuringEvaluation
--- PASS: TestStreamGateChatProducerBackpressureDuringEvaluation (0.15s)
PASS
ok  	iop/apps/edge/internal/openai	1.184s
```

### 중간 검증 4

- 명령: `bash -n scripts/e2e-openai-vllm.sh && IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh`
- 기대: non-stream JSON과 streaming SSE auxiliary OpenAI smoke PASS.
- 실제 출력:

```
$ bash -n scripts/e2e-openai-vllm.sh && IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh
[openai-vllm] OpenAI-compatible vLLM serving test PASSED (mode=fake).
$ echo $?
0
```

수정 전 baseline은 non-stream 요청에서 `curl: (22) The requested URL returned error: 400`이었다. fake provider가 `stream=false`에 표준 `chat.completion` JSON을 응답하도록 고친 뒤 non-stream(`IOP_OPENAI_VLLM_OK` + `finish_reason`)과 stream(`"content":"IOP_OPENAI_"` + `finish_reason` + `data: [DONE]`) assertion이 모두 통과한다.

### 최종 검증 1

- 명령: `command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT && command -v protoc && command -v claude && command -v agy && command -v codex && command -v opencode`
- 기대: host tool identity와 full-cycle command preflight 기록.
- 실제 출력:

```
$ command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT && command -v protoc && command -v claude && command -v agy && command -v codex && command -v opencode
/config/.local/bin/go
/config/opt/go/bin/go
go version go1.26.2 linux/arm64
/config/opt/go
/config/.local/bin/protoc
/config/.npm-global/bin/claude
/config/.local/bin/agy
/config/.npm-global/bin/codex
/config/.npm-global/bin/opencode
```

### 최종 검증 2

- 명령: `git rev-parse --show-toplevel && git branch --show-current && git rev-parse --short HEAD && git status --short`
- 기대: 현재 checkout/source state 기록.
- 실제 출력:

```
$ git rev-parse --show-toplevel && git branch --show-current && git rev-parse --short HEAD && git status --short
/config/workspace/iop-s0
stream-evidence-gate-core
071cd242
 M agent-contract/inner/edge-config-runtime-refresh.md
 D agent-task/m-stream-evidence-gate-core/17+13,16_openai_request_rebuilder/CODE_REVIEW-cloud-G10.md
 D agent-task/m-stream-evidence-gate-core/17+13,16_openai_request_rebuilder/PLAN-cloud-G10.md
 D agent-task/m-stream-evidence-gate-core/17+13,16_openai_request_rebuilder/code_review_cloud_G09_0.log
 D agent-task/m-stream-evidence-gate-core/17+13,16_openai_request_rebuilder/plan_cloud_G09_0.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/CODE_REVIEW-cloud-G07.md
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/PLAN-local-G07.md
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G07_3.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G07_4.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G07_5.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G08_0.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G08_1.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G08_2.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G07_3.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G07_4.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G07_5.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G08_0.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G08_1.log
 D agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G08_2.log
 D agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/CODE_REVIEW-cloud-G09.md
 D agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/PLAN-cloud-G09.md
 D agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/CODE_REVIEW-cloud-G08.md
 D agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/PLAN-local-G08.md
 M agent-task/m-stream-evidence-gate-core/WORK_LOG.md
 M apps/edge/internal/configrefresh/path_refresh_test.go
 M apps/edge/internal/openai/buffered_sse.go
 M apps/edge/internal/openai/chat_completion.go
 M apps/edge/internal/openai/chat_handler.go
 M apps/edge/internal/openai/dispatch_context.go
 M apps/edge/internal/openai/normalized_sse.go
 M apps/edge/internal/openai/openai_request_rebuilder.go
 M apps/edge/internal/openai/openai_request_rebuilder_test.go
 M apps/edge/internal/openai/provider_test_support_test.go
 M apps/edge/internal/openai/provider_tool_validation_test.go
 M apps/edge/internal/openai/provider_tunnel.go
 M apps/edge/internal/openai/responses_handler.go
 M apps/edge/internal/openai/stream_gate_dispatcher.go
 M apps/edge/internal/openai/tool_validation.go
 M configs/edge.yaml
 M packages/go/config/edge_types.go
 M packages/go/config/stream_evidence_gate_config_test.go
 M packages/go/streamgate/commit_boundary.go
 M packages/go/streamgate/commit_boundary_test.go
 M packages/go/streamgate/evidence_tail.go
 M packages/go/streamgate/evidence_tail_test.go
 M packages/go/streamgate/recovery_coordinator.go
 M packages/go/streamgate/recovery_coordinator_test.go
 M packages/go/streamgate/runtime.go
 M packages/go/streamgate/runtime_contract_test.go
 M packages/go/streamgate/runtime_test.go
 M packages/go/streamgate/stream_release.go
 M packages/go/streamgate/stream_release_test.go
 M scripts/e2e-openai-vllm.sh
?? agent-task/archive/2026/07/m-stream-evidence-gate-core/17+13,16_openai_request_rebuilder/
?? agent-task/archive/2026/07/m-stream-evidence-gate-core/19+18_core_runtime_loop/
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/CODE_REVIEW-cloud-G10.md
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/PLAN-cloud-G10.md
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/code_review_cloud_G09_0.log
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/code_review_cloud_G10_1.log
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/plan_cloud_G09_0.log
?? agent-task/m-stream-evidence-gate-core/20+17,19_edge_vertical_slice/plan_cloud_G10_1.log
?? agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/CODE_REVIEW-cloud-G06.md
?? agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/PLAN-local-G06.md
?? agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/code_review_cloud_G08_1.log
?? agent-task/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/plan_local_G08_1.log
?? apps/edge/internal/openai/stream_gate_release_sink.go
?? apps/edge/internal/openai/stream_gate_runtime.go
?? apps/edge/internal/openai/stream_gate_vertical_slice_test.go
?? packages/go/streamgate/filter_observation.go
?? packages/go/streamgate/filter_observation_test.go
```

### 최종 검증 3

- 명령: `make proto`
- 기대: generated proto consistency PASS, 예상 밖 generated diff 없음.
- 실제 출력:

```
$ 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
$ echo $?
0
$ git status --short proto/
```

`proto/` 아래 예상 밖 generated diff는 없다(출력 없음).

### 최종 검증 4

- 명령: `go test -count=1 ./packages/go/config ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/internal/configrefresh`
- 기대: fresh package PASS.
- 실제 출력:

```
$ go test -count=1 ./packages/go/config ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/internal/configrefresh
ok  	iop/packages/go/config	0.075s
ok  	iop/packages/go/streamgate	0.885s
ok  	iop/apps/edge/internal/openai	6.926s
ok  	iop/apps/edge/internal/service	5.865s
ok  	iop/apps/edge/internal/configrefresh	0.021s
```

### 최종 검증 5

- 명령: `go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service`
- 기대: race PASS.
- 실제 출력:

```
$ go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service
ok  	iop/packages/go/streamgate	1.956s
ok  	iop/apps/edge/internal/openai	8.322s
ok  	iop/apps/edge/internal/service	6.918s
```

### 최종 검증 6

- 명령: `go test -race -count=1 -run 'TestStreamGate.*(ProviderPool|ToolValidation|Simultaneous|Preparer|Backpressure)' -v ./apps/edge/internal/openai`
- 기대: 새 S16/tool-validation named fixtures 전부 PASS; 0 tests는 실패.
- 실제 출력:

```
$ go test -race -count=1 -run 'TestStreamGate.*(ProviderPool|ToolValidation|Simultaneous|Preparer|Backpressure)' -v ./apps/edge/internal/openai
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_zero_blocks_every_retry
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_one_re-admits_exactly_once
=== RUN   TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/runtime_disabled_keeps_the_legacy_retry_loop
--- PASS: TestStreamGateEnabledToolValidationHasSingleRecoveryOwner (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_zero_blocks_every_retry (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_one_re-admits_exactly_once (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/runtime_disabled_keeps_the_legacy_retry_loop (0.00s)
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/non-stream_json
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream
=== RUN   TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream_exhaustion_keeps_the_tool_validation_error_envelope
--- PASS: TestStreamGateEnabledToolValidationPreservesStructuredOutput (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationPreservesStructuredOutput/non-stream_json (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream (0.00s)
    --- PASS: TestStreamGateEnabledToolValidationPreservesStructuredOutput/buffered_stream_exhaustion_keeps_the_tool_validation_error_envelope (0.00s)
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/normalized_to_tunnel
=== RUN   TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/tunnel_to_normalized
--- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce (0.00s)
    --- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/normalized_to_tunnel (0.00s)
    --- PASS: TestStreamGateProviderPoolRecoverySwitchesActualPathOnce/tunnel_to_normalized (0.00s)
=== RUN   TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak
--- PASS: TestStreamGateProviderPoolExhaustionTerminatesWithoutPathLeak (0.00s)
=== RUN   TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce
--- PASS: TestStreamGateProviderPoolSimultaneousViolationsDispatchOnce (0.00s)
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle/preparer_success_dispatches_once
=== RUN   TestStreamGateChatRecoveryPreparerLifecycle/preparer_failure_dispatches_nothing
--- PASS: TestStreamGateChatRecoveryPreparerLifecycle (0.00s)
    --- PASS: TestStreamGateChatRecoveryPreparerLifecycle/preparer_success_dispatches_once (0.00s)
    --- PASS: TestStreamGateChatRecoveryPreparerLifecycle/preparer_failure_dispatches_nothing (0.00s)
=== RUN   TestStreamGateChatProducerBackpressureDuringEvaluation
--- PASS: TestStreamGateChatProducerBackpressureDuringEvaluation (0.15s)
=== RUN   TestStreamGatePoolToolValidationReAdmitsThroughPool
--- PASS: TestStreamGatePoolToolValidationReAdmitsThroughPool (0.00s)
=== RUN   TestStreamGatePoolToolValidationDropsOnTunnelCandidate
--- PASS: TestStreamGatePoolToolValidationDropsOnTunnelCandidate (0.00s)
PASS
ok  	iop/apps/edge/internal/openai	1.192s
```

13개 named fixture(subtest 포함)가 실제로 실행됐다(0 tests 아님). `TestStreamGateResponsesPoolRecoveryTerminal`은 이 regex에 매칭되지 않아 최종 검증 4/5에서 실행된다(`계획 대비 변경 사항` 참고).

### 최종 검증 7

- 명령: `bash -n scripts/e2e-openai-vllm.sh && IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh`
- 기대: auxiliary OpenAI non-stream+stream provider smoke PASS.
- 실제 출력:

```
$ bash -n scripts/e2e-openai-vllm.sh && IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh
[openai-vllm] OpenAI-compatible vLLM serving test PASSED (mode=fake).
$ echo $?
0
```

### 최종 검증 8

- 명령: `IOP_E2E_BIND_TIMEOUT=60 IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh`
- 기대: auxiliary mock smoke PASS로 별도 기록.
- 실제 출력:

```
$ IOP_E2E_BIND_TIMEOUT=60 IOP_E2E_PROFILE=mock ./scripts/e2e-smoke.sh
... (중략)
[node-message] IOP_E2E_HELLO_BASIC
IOP_E2E_HELLO_BASIC_TAIL
[node-event] complete run_id=manual-1785153587886129585 detail="idle-timeout"
[edge-message] 안녕하세요에 대한 응답 확인입니다. 다른 말 없이 IOP_E2E_HELLO_FORMAL 만 답하세요.
[node-event] start run_id=manual-1785153589114197544
[node-message] IOP_E2E_HELLO_FORMAL
IOP_E2E_HELLO_FORMAL_TAIL
[node-event] complete run_id=manual-1785153589114197544 detail="idle-timeout"
[edge-message] 고맙다는 말에 대한 짧은 확인입니다. 출력은 IOP_E2E_THANKS_SHORT 만 사용하세요.
[node-event] start run_id=manual-1785153590753488253
[node-message] IOP_E2E_THANKS_SHORT
IOP_E2E_THANKS_SHORT_TAIL
[node-event] complete run_id=manual-1785153590753488253 detail="idle-timeout"
===================
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
$ echo $?
0
```

보조 mock smoke이며 필수 full-cycle(최종 검증 9)을 대체하지 않는다.

### 최종 검증 9

- 명령: terminal A `./scripts/dev/edge.sh`, terminal B `./scripts/dev/node.sh`; Edge console 입력 순서 `/nodes`, `Convert token iop_manual_one and reply only with converted token`, `Convert token iop_manual_two and reply only with converted token`, `/capabilities`, `/transport`, `/sessions`, `/terminate-session`.
- 기대: registered, 각 run의 sent/start/non-empty message/complete, node local payload와 edge rendered payload의 내용·순서 일치, complete-after-last-message, command 결과 PASS. auxiliary smoke로 대체하지 않는다.
- 실제 출력:

`config_strategy`: tracked `configs/*.yaml`을 오염시키지 않도록 scratchpad에 임시 edge/node config를 만들고 임시 포트(edge transport 39090, edge metrics 39092, node metrics 39091)를 사용했다. console target은 tracked config와 동일한 실제 외부 CLI profile `codex`(adapter `cli`, mode `codex-app-server`)이며, 두 dev script가 이 checkout에서 직접 build/run한 binary를 사용한다.

terminal A `IOP_EDGE_CONFIG=<tmp>/edge.yaml ./scripts/dev/edge.sh`, terminal B `IOP_NODE_CONFIG=<tmp>/node.yaml ./scripts/dev/node.sh`.

Edge console 전체 출력:

```
[edge] config=/tmp/claude-1000/-config-workspace-iop-s0/560144f4-699e-46ab-8dd5-f446651fb4b3/scratchpad/fullcycle/edge.yaml
IOP Edge console listening on 127.0.0.1:39090
Console target node= adapter=cli target=codex session=default background=false
Start node.sh on another host, then type a message here.
Commands: /nodes, /node <id|alias>, /session <id>, /background on|off, /terminate-session, /status, /capabilities, /sessions, /transport, /exit
edge> [node0-evt] connected reason="registered"
  node0 = node-fullcycle-01 (fullcycle-node)
edge> [edge] sent run_id=manual-1785153669839428554 node=node0 adapter=cli target=codex session=default background=false
[node0-evt] start run_id=manual-1785153669839428554
[node0-msg] IOP_MANUAL_ONE
[node0-evt] complete run_id=manual-1785153669839428554 detail=""
edge> [edge] sent run_id=manual-1785153729008068887 node=node0 adapter=cli target=codex session=default background=false
[node0-evt] start run_id=manual-1785153729008068887
[node0-msg] IOP_MANUAL_TWO
[node0-evt] complete run_id=manual-1785153729008068887 detail=""
edge> [node0-capabilities] adapter=cli target=codex session=default
  adapter = cli
  capacity = 0
  in_flight = 0
  instance_key = 
  max_concurrency = 0
  provider_status = unknown
  queued = 0
  targets = codex
edge> [node0-transport] adapter=cli target=codex session=default
  adapter = cli
  connected = true
  node_id = node-fullcycle-01
  session_id = default
  state = connected
  target = codex
edge> [node0-sessions] adapter=cli target=codex session=default
sessions: 1
  [0] mode=codex-app-server target=codex session=default
edge> terminated session default node=node0
edge> bye
```

Node 로컬 출력의 같은 run payload 라인(`grep -nE '\[node-message\]' node.out`):

```
62:[node-message] IOP_MANUAL_ONE
75:[node-message] IOP_MANUAL_TWO
```

판정:

- register: `[node0-evt] connected reason="registered"` 표시됨.
- `/nodes`: `node0 = node-fullcycle-01 (fullcycle-node)` 표시됨.
- 메시지 1회차(run `manual-1785153669839428554`): `[edge] sent` → `[node0-evt] start` → 비어 있지 않은 `[node0-msg] IOP_MANUAL_ONE` → `[node0-evt] complete` 순서로 도착. node 로컬 `[node-message] IOP_MANUAL_ONE`과 내용/순서 일치, complete가 마지막 message 이후이고 complete 뒤 같은 run의 추가 message 없음.
- 메시지 2회차(같은 session, run `manual-1785153729008068887`): `[edge] sent` → `start` → `[node0-msg] IOP_MANUAL_TWO` → `complete`. node 로컬 `[node-message] IOP_MANUAL_TWO`와 내용/순서 일치, 동일하게 complete-after-last-message 만족.
- `/capabilities`: `[node0-capabilities] adapter=cli target=codex session=default` 결과 도착.
- `/transport`: `connected = true`, `node_id = node-fullcycle-01`, `state = connected` 도착.
- `/sessions`: `sessions: 1`, `[0] mode=codex-app-server target=codex session=default` 도착.
- `/terminate-session`: `terminated session default node=node0` 도착.

필수 full-cycle PASS. 보조 smoke(최종 검증 7/8)로 대체하지 않았다.

### 최종 검증 10

- 명령: `rg -n --sort path 'retrySubmit\(' apps/edge/internal/openai/buffered_sse.go apps/edge/internal/openai/chat_completion.go`
- 기대: 남은 두 legacy 호출이 명시적 runtime-disabled compatibility branch 안에만 존재한다.
- 실제 출력:

```
$ rg -n --sort path 'retrySubmit\(' apps/edge/internal/openai/buffered_sse.go apps/edge/internal/openai/chat_completion.go
apps/edge/internal/openai/buffered_sse.go:61:				next, submitErr := dc.retrySubmit(r.Context(), retryReq)
apps/edge/internal/openai/chat_completion.go:78:				next, submitErr := dc.retrySubmit(r.Context(), retryReq)
```

두 호출 모두 `streamBufferedChatCompletionLegacy` / `completeChatCompletionLegacy` 안에만 있고, 두 함수는 각각 `streamBufferedChatCompletion` / `completeChatCompletion`의 `if s.streamGateEnabled() { ...gate...; return }` 이후 branch에서만 호출된다. runtime-enabled 경로에서는 도달할 수 없다(`TestStreamGateEnabledToolValidationHasSingleRecoveryOwner/core_budget_zero_blocks_every_retry`가 Core budget 0에서 dispatch 1회만 발생함을 확인한다).

### 최종 검증 11

- 명령: `git diff --check`
- 기대: no output, exit 0.
- 실제 출력:

```
$ git diff --check
$ echo $?
0
```

출력 없음, exit 0.

---

> **[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 |

## 코드리뷰 결과

- 종합 판정: PASS

### 차원별 평가

| 차원 | 평가 | 근거 |
|---|---|---|
| correctness | Pass | provider-pool initial/recovery가 같은 Core runtime에서 재입장하고 normalized↔tunnel 실제 binding/framing 전환과 discarded-attempt 무누출이 구현·fixture에서 확인됐다. |
| completeness | Pass | 기존 FAIL의 provider-pool, enabled tool-validation single owner, S16 및 필수 실행 evidence 항목이 모두 구현 체크리스트와 현재 소스에서 충족됐다. |
| test coverage | Pass | path switch, simultaneous violation, preparer success/failure, backpressure, tool-validation budget/structured output, Responses 안전 terminal fixture가 package/race에서 실행됐다. |
| API contract | Pass | enabled 경로는 Core가 recovery를 단독 소유하고 disabled legacy 호환, OpenAI structured tool_calls/error envelope, raw tunnel framing을 보존한다. |
| code quality | Pass | 관련 파일 gofmt clean, stale retry 호출은 명시적 disabled legacy 함수 안에만 존재하며 debug/TODO와 diff whitespace 오류가 없다. |
| implementation deviation | Pass | pool 전용 runner, buffered renderer, preparer seam과 retry cap 주체 변경이 계획 목적·SDD 경계를 유지하며 review evidence에 구체적으로 설명됐다. |
| verification trust | Pass | reviewer fresh focused/package/race tests와 OpenAI fake/mock smoke가 모두 PASS했고 검증 중 관련 OpenAI 소스 해시가 유지되어 기록과 현재 코드가 일치한다. |
| spec conformance | Pass | SDD S06/S12/S14/S15/S16/S23의 single owner, single dispatch, actual binding, one-cycle, preparer/backpressure evidence를 충족한다. |

### 발견된 문제

- 없음
- Required: 0
- Suggested: 0
- Nit: 0

### 라우팅 신호

- `review_rework_count=2`
- `evidence_integrity_failure=false`

### 다음 단계
