diff --git a/agent-task/responses_provider_bridge/CODE_REVIEW-cloud-G08.md b/agent-task/responses_provider_bridge/CODE_REVIEW-cloud-G08.md new file mode 100644 index 00000000..8559ad83 --- /dev/null +++ b/agent-task/responses_provider_bridge/CODE_REVIEW-cloud-G08.md @@ -0,0 +1,131 @@ + + +# 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 `Implementation Checklist`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> Execute the plan's selected root cause, scope, files, and dependency decisions as written. Do not choose another owner, narrow/expand the write boundary, or replace a fix with another verification attempt. +> 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 (`Code Review Result`, log rename, `complete.log`, archive moves, `Review-Only Checklist`) is review-agent-only, even after compaction/resume. + +## Overview + +date=2026-08-14 +task=responses_provider_bridge, plan=0, tag=API + +## For the Review Agent + +> **[REVIEW AGENT ONLY]** Compare every item with source and run fresh verification. Implementation output is handoff evidence only. Append verdict and routing signals, archive the active pair, and create `complete.log`/archive the task directory only on PASS. WARN/FAIL must follow the code-review skill and include reviewer-proven root cause plus one selected fix. + +## Implementation Item Completion + +| Item | Status | +|---|---| +| API-1 Profile-selected Responses request bridge | [ ] | +| API-2 Convert provider responses and streams to Responses | [ ] | +| API-3 Lock compatibility with tests and contracts | [ ] | + +## Implementation Checklist + +- [ ] Implement API-1 profile-selected Responses request bridging and fail-closed admission without caller-specific branches. +- [ ] Implement API-2 provider response/stream conversion back to canonical Responses while preserving tools, reasoning, usage, errors, and exactly-one terminal. +- [ ] Implement API-3 focused regression coverage and synchronize the OpenAI outer contract and living spec. +- [ ] Run the focused/package verification and, after exact-source dev deployment, the two failed Codex direct-route cycles. +- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output. + +## Review-Only Checklist + +> **[REVIEW AGENT ONLY]** Implementing agents must not modify or check this section. + +- [ ] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`. +- [ ] Verify verdict, dimensions, and Required/Suggested/Nit classifications match. +- [ ] Run applicable required verification and record fresh output. +- [ ] For each Required/Suggested finding, record evidence, root cause, one selected fix, files/symbols/tests, and acceptance commands. +- [ ] Archive active review to `code_review_cloud_G08_0.log` and plan to `plan_local_G08_0.log`. +- [ ] Verify the Agent-Ops managed `.gitignore` block. +- [ ] On PASS, write `complete.log`, remove active markdown files, and move the task directory to `agent-task/archive/YYYY/MM/responses_provider_bridge/`. +- [ ] On WARN/FAIL, write the next filesystem state required by the code-review skill and do not write `complete.log`. + +## Deviations from Plan + +_Record actual deviations and reasons. If none, replace with `None`._ + +## Key Design Decisions + +_Record actual implementation decisions._ + +## Reviewer Checkpoints + +- Native Responses profile and nil-profile fallback remain byte-compatible. +- Selection is based only on request requirements and concrete protocol profile, never caller/agent identity. +- Unsupported Responses controls fail before provider dispatch rather than being dropped. +- Claude uses Messages wire and Gemini uses Chat wire; both return canonical Responses JSON/SSE. +- Gemini thought signature survives function call → `call_id` → function result replay. +- StreamGate remains the single response/terminal owner, including recovery attempts. +- Contract/spec match code and keep normalized non-provider limitations unchanged. + +## Verification Results + +### API-1 Focused Request Bridge + +```bash +go test -count=1 ./apps/edge/internal/openai -run 'TestResponsesProtocolProfile(BridgesToAnthropicMessages|BridgesToGeminiChat|BridgeRejectsUnrepresentableControls|OperationPassthrough|OperationAdmissionTable)$' +``` + +_Paste actual stdout/stderr._ + +### API-2 Focused Response Bridge + +```bash +go test -count=1 ./apps/edge/internal/openai -run 'TestResponses(ProtocolProfileAnthropicBridgeResponse|ProtocolProfileGeminiBridgeResponseAndSignature|ProviderBridgeStreaming|ProviderBridgeStreamingTerminalExactlyOnce|ProviderBridgeProviderErrorSanitized)$' +``` + +_Paste actual stdout/stderr._ + +### Package and Race Verification + +```bash +gofmt -w apps/edge/internal/openai/provider_normalization.go apps/edge/internal/openai/responses_handler.go apps/edge/internal/openai/anthropic_bridge.go apps/edge/internal/openai/stream_gate_tunnel_codec.go apps/edge/internal/openai/responses_stream_gate.go apps/edge/internal/openai/responses_protocol_profile_test.go apps/edge/internal/openai/stream_gate_pipeline_test.go +go test -count=1 ./apps/edge/internal/openai -run 'TestResponses(ProtocolProfile|ProviderBridge)' +go test -count=1 ./apps/edge/internal/openai +go test -race -count=1 ./apps/edge/internal/openai -run 'TestResponsesProviderBridgeStreaming' +git diff --check +``` + +_Paste actual stdout/stderr._ + +### Contract/Spec Verification + +```bash +rg --sort path -n 'Responses.*(Messages|Chat)|Gemini.*(signature|thought)' agent-contract/outer/openai-compatible-api.md agent-spec/input/openai-compatible-surface.md +``` + +_Paste actual stdout/stderr._ + +### External Dev Preflight and Codex Cycles + +```bash +ssh toki@toki-labs.com '/bin/zsh -lc '\''cd /Users/toki/agent-work/iop-dev && git status --short --branch && git rev-parse HEAD && git rev-parse origin/dev && command -v go && go version && codex --version && claude --version && opencode --version && lsof -nP -iTCP:18083 -sTCP:LISTEN && lsof -nP -iTCP:18084 -sTCP:LISTEN'\''' +``` + +_Paste sanitized preflight, exact-source rebuild/deploy identity, and the two Codex → Claude/Gemini cycle outputs. Do not paste secrets or raw provider payloads._ + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING]** Fill every implementation-owned section, leave review-only sections unchanged, and report ready for review. + +## Section Ownership + +| Section | Owner | Note | +|---|---|---| +| Header, Overview, Review instructions | Fixed | Implementer must not modify | +| Implementation Item Completion | Implementer | Check status only | +| Implementation Checklist | Implementer | Check boxes only; text/order fixed | +| Review-Only Checklist | Review agent | Implementer must not modify | +| Deviations, Key Design Decisions | Implementer | Replace placeholders with actual evidence | +| Reviewer Checkpoints | Fixed | Review oracle | +| Verification Results | Implementer, then reviewer | Implementer records initial output; reviewer reruns fresh | +| Code Review Result | Review agent appends | Not present in stub | diff --git a/agent-task/responses_provider_bridge/PLAN-local-G08.md b/agent-task/responses_provider_bridge/PLAN-local-G08.md new file mode 100644 index 00000000..2a659fb2 --- /dev/null +++ b/agent-task/responses_provider_bridge/PLAN-local-G08.md @@ -0,0 +1,309 @@ + + +# Responses Provider Bridge Plan + +## For the Implementing Agent + +`CODE_REVIEW-cloud-G08.md`의 구현 담당 섹션 작성은 필수다. 아래 범위만 구현하고 검증 명령을 실제로 실행한 뒤, 결과 원문과 구현 결정을 active review 파일에 기록하고 두 active 파일은 그대로 둔 채 review 준비 완료를 보고한다. 최종 판정, 사용자 리뷰 분류, archive 이동, `complete.log` 작성은 code-review skill 전용이다. 막히면 blocker, 시도한 명령/출력, 재개 조건만 구현 증거란에 남기고 사용자 입력 도구 호출, control-plane stop 파일 생성, 다음 상태 분류를 하지 않는다. + +## Background + +Codex는 표준 설정에서 `/v1/responses` wire만 사용한다. 현재 IOP provider-pool은 Responses ingress를 native Responses operation으로만 입장시키므로 Claude Messages 및 Gemini OpenAI-compatible Chat profile을 dispatch 전에 거부한다. 이 plan은 caller/agent별 분기 없이 선택된 protocol profile에 따라 Responses 요청과 응답을 기존 provider wire로 변환하는 한 경계만 추가한다. + +## Analysis + +### Files Read + +- `apps/edge/internal/openai/responses_handler.go` +- `apps/edge/internal/openai/provider_normalization.go` +- `apps/edge/internal/openai/anthropic_handler.go` +- `apps/edge/internal/openai/anthropic_bridge.go` +- `apps/edge/internal/openai/anthropic_stream.go` +- `apps/edge/internal/openai/responses_stream_gate.go` +- `apps/edge/internal/openai/stream_gate_runtime.go` +- `apps/edge/internal/openai/stream_gate_tunnel_codec.go` +- `apps/edge/internal/openai/responses_protocol_profile_test.go` +- `apps/edge/internal/openai/anthropic_bridge_test.go` +- `apps/edge/internal/openai/stream_gate_pipeline_test.go` +- `apps/edge/internal/service/provider_pool.go` +- `apps/edge/internal/service/provider_tunnel.go` +- `packages/go/config/protocol_profile.go` +- `agent-contract/index.md` +- `agent-contract/outer/openai-compatible-api.md` +- `agent-contract/outer/anthropic-compatible-api.md` +- `agent-spec/index.md` +- `agent-spec/input/openai-compatible-surface.md` +- `agent-test/dev/rules.md` +- `agent-ops/rules/project/rules.md` +- `agent-ops/skills/private/dev-runtime-deploy/SKILL.md` + +### SDD Criteria + +not applicable. 이 작업은 현재 Milestone에 연결하지 않은 외부 API 호환성 결함 수정이며, 새 제품 의미나 저장 상태를 정의하지 않는다. + +### Verification Context + +- handoff 문서는 없고, 실제 dev cross-test 결과를 재현 근거로 사용했다. +- Codex → GPT는 통과했지만 Codex → Claude/Gemini는 파일 생성 전에 `no provider profile supports the requested Responses operation`으로 실패했다. Claude Code caller의 Claude/Gemini/GPT 및 OpenCode의 표준 provider 설정은 통과했다. +- `responses_handler.go:323-424`는 Responses 요구사항으로 후보를 고른 뒤 `protocolTunnelPreparer(... OperationResponses)`와 Responses body rewrite만 사용한다. +- `provider_normalization.go:429-564`는 Responses 요구사항에서 stream/structured-output을 수집하지 않고, `selectProviderOperation`에 Responses ingress driver 분기가 없어 Claude/Gemini profile을 거부한다. +- `anthropic_handler.go:658-710`은 반대 방향인 Messages ingress를 profile별 Messages/Chat/Responses로 변환하고, `anthropic_handler.go:318-340`은 선택 operation별 응답 writer를 사용한다. 이번 구현도 동일한 profile-operation 원칙을 따른다. +- 기존 StreamGate는 Responses caller에 native Responses tunnel bytes를 그대로 노출한다. bridge operation에서는 provider Chat/Messages wire를 그대로 내보내면 안 되므로, provider operation으로 decode한 semantic event를 caller-facing Responses JSON/SSE로 다시 render해야 한다. +- 캐시된 Go test 결과는 허용하지 않는다. 모든 Go 명령은 `-count=1`을 사용한다. + +#### External Verification Preflight + +- runner: `ssh toki@toki-labs.com` +- repo/workdir: `/Users/toki/agent-work/iop-dev` +- 기준 branch: `dev`; live 검증 직전 local/remote/runner의 exact SHA가 같고 runner가 clean이어야 한다. +- 기존 확인 CLI: Claude Code `2.1.177`, Codex `0.146.0`, OpenCode `1.18.3`; live 검증 전에 다시 `--version`으로 확인한다. +- endpoint: `https://toki-labs.com:18083/v1`; Edge listener `18083`, Node TCP `18084`. +- CA: `/Users/toki/agent-work/iop-dev/build/dev-runtime/.secrets/credential-plane/ca.pem`; Codex/agent 설정에는 복사된 사용자 소유 CA 경로를 사용하며 tracked 파일에 인증서나 token을 넣지 않는다. +- secret source: runner의 SOPS/age-managed dev secret. raw token, key, header 값은 task evidence에 기록하지 않는다. +- runtime identity: 기존 실행 중 binary를 최신으로 간주하지 않는다. `agent-test/dev/rules.md`와 private `dev-runtime-deploy` skill에 따라 exact verified SHA에서 Edge 및 참여 Node를 rebuild/redeploy/restart하고 binary source identity, process, port를 확인한다. +- OS/arch: runner macOS arm64, dev-runtime 참여 Node에는 macOS, Linux arm64, Windows amd64 산출물이 있으므로 같은 source ref에서 전체 rebuild한다. +- source가 origin에 없거나 runner SHA가 다르면 live test를 시작하지 않는다. 먼저 승인된 commit/push 흐름으로 exact implementation SHA를 `dev`에 반영한 뒤 clean sync한다. +- 성공했던 cross path는 반복하지 않는다. 배포 후 Codex 표준 custom model provider 설정으로 Claude와 Gemini direct route만 각각 한 cycle 실행하고, 각 cycle은 workspace 내부 파일 생성 및 내용 확인까지 포함한다. + +### Test Coverage Gaps + +| Behavior | Existing coverage | Gap to close | +|---|---|---| +| native Responses profile raw passthrough | `responses_protocol_profile_test.go` | 그대로 유지하는 회귀 assertion 필요 | +| Responses → Anthropic Messages request/response | 현재 거부 테스트만 존재 | non-stream text/tool/usage 및 unsupported field 테스트 필요 | +| Responses → Gemini Chat request/response | effort rewrite만 존재 | reasoning, tool call/result, thought signature 왕복 테스트 필요 | +| provider Chat/Messages stream → Responses SSE | 없음 | fragmented delta, tool args, reasoning, terminal exactly-once 테스트 필요 | +| bridge 중 오류/지원 불가 control | Responses operation 부재만 검증 | dispatch 전 fail-closed와 sanitized error 테스트 필요 | + +### Symbol References + +기존 public symbol rename/remove는 없다. `selectProviderOperation` 호출 지점은 `anthropic_bridge.go`, `anthropic_handler.go`, `provider_normalization.go`, `responses_handler.go`, `single_request_provider_stage.go`, `single_request_work_stage.go`, 관련 테스트에 있으며 기존 ingress 동작을 유지해야 한다. `newOpenAITunnelEndpointCodec` 호출 지점은 Responses/Chat StreamGate runtime과 pipeline tests에 있으므로 새 provider-wire 선택 인자는 모든 호출부에서 명시한다. + +### Split Judgment + +단일 plan이다. 분리할 수 없는 invariant는 “provider profile이 선택한 wire로 요청을 보냈다면 같은 attempt의 응답을 원래 Responses caller 계약으로 정확히 복원한다”이다. admission/request 변환과 response/SSE 복원을 따로 배포하면 잘못된 wire가 caller에게 노출되므로 분리하지 않는다. + +### Scope Rationale + +- `packages/go/config`의 built-in profile/schema는 이미 Messages, Chat, Responses operation과 Gemini effort/tool-call normalization을 표현하므로 변경하지 않는다. +- agent extension, proxy extension, caller product-name 분기, Codex 전용 설정 키를 추가하지 않는다. 일반 custom model provider의 Responses wire만 전제로 한다. +- Gemini reasoning 자체의 새 `thinkingLevel` 호환이나 profile mapping 확대는 별도 항목이다. 이번 plan은 현재 profile이 이미 선언한 reasoning mapping만 보존한다. +- `apps/agent/**`, bench agent 설치/config, AGY 경로, current Milestone/roadmap은 수정하지 않는다. +- native OpenAI Responses raw passthrough와 legacy nil-profile fallback은 유지한다. + +### Final Routing + +- evaluation_mode: `first-pass` +- finalizer: `finalize-task-policy.sh`, mode `pair` +- closures: build/review 모두 scope, context, verification, evidence, ownership, decision closed +- build scores: scope=2, state=1, blast=2, evidence=1, verification=2; grade `G08`; route `local-fit`; catalog `worker/local/G08` +- review scores: scope=2, state=1, blast=2, evidence=1, verification=2; grade `G08`; route `official-review`; catalog `review/cloud/G08` +- large_indivisible_context: `false` +- positive loop risks: `boundary_contract`, `structured_interpretation`, `variant_product` (3); risk boundary not matched +- recovery signals: review_rework_count=0, evidence_integrity_failure=false +- capability-gap evidence: 없음; 구현 판단과 검증 oracle이 닫혀 있다. +- canonical files: `PLAN-local-G08.md`, `CODE_REVIEW-cloud-G08.md` + +## Implementation Checklist + +- [ ] Implement API-1 profile-selected Responses request bridging and fail-closed admission without caller-specific branches. +- [ ] Implement API-2 provider response/stream conversion back to canonical Responses while preserving tools, reasoning, usage, errors, and exactly-one terminal. +- [ ] Implement API-3 focused regression coverage and synchronize the OpenAI outer contract and living spec. +- [ ] Run the focused/package verification and, after exact-source dev deployment, the two failed Codex direct-route cycles. +- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output. + +### [API-1] Profile-selected Responses request bridge + +#### Problem + +`provider_normalization.go:429-443` extracts only tools and effort, and `provider_normalization.go:505-528` has no `OperationResponses` driver selection. `responses_handler.go:409-424` also binds the preparer to native Responses before the selected plan is applied. + +```go +// apps/edge/internal/openai/provider_normalization.go:505 +operations := []config.ProtocolOperation{ingress} +switch ingress { +case config.OperationMessages: + // ... +case config.OperationChatCompletions: + // ... +} +``` + +#### Solution + +Add `OperationResponses` selection: `openai_responses → responses`, `anthropic_messages → messages`, `openai_chat → chat_completions`, subject to capability and normalized requirements. Decode `stream`, tools, reasoning effort, token/output limits, structured output, tool choice, parallel calls, and input item variants needed to decide lossless eligibility. For the selected plan, build the provider-native body and operation/path/header contract instead of invoking the native Responses-only preparer. + +```go +// after: selected profile controls the provider wire +case config.OperationResponses: + switch profile.Driver { + case config.ProtocolDriverOpenAIResponses: + operations = []config.ProtocolOperation{config.OperationResponses} + case config.ProtocolDriverAnthropicMessages: + operations = []config.ProtocolOperation{config.OperationMessages} + case config.ProtocolDriverOpenAIChat: + operations = []config.ProtocolOperation{config.OperationChatCompletions} + } +``` + +Map Responses `instructions`, message/input items, function calls/results, tools/tool choice, `parallel_tool_calls`, max output tokens, stream, structured output, and the profile-declared reasoning effort. Reuse the existing opaque Gemini thought-signature tool-id envelope: encode returned signature into Responses `call_id`, then decode it when the caller sends `function_call_output`. Reject controls/items that the selected wire cannot preserve before provider dispatch; do not silently drop unknown semantic controls. + +#### Modified Files and Checklist + +- [ ] `apps/edge/internal/openai/provider_normalization.go`: requirements, operation selection, Responses→Messages/Chat request converters, validation. +- [ ] `apps/edge/internal/openai/responses_handler.go`: apply selected operation, path, headers, and body for initial/recovery attempts. +- [ ] `apps/edge/internal/openai/anthropic_bridge.go`: reuse the existing Chat/Anthropic types and opaque Gemini signature helpers without renaming public behavior. + +#### Test Strategy + +Write tests in `responses_protocol_profile_test.go`: + +- `TestResponsesProtocolProfileBridgesToAnthropicMessages`: body/operation/path, text/tool/result/max-token mapping. +- `TestResponsesProtocolProfileBridgesToGeminiChat`: effort mapping, Chat body, tool choice, signature replay. +- `TestResponsesProtocolProfileBridgeRejectsUnrepresentableControls`: HTTP 400 before tunnel dispatch. +- Update the old Anthropic rejection cases to assert admission only when the requested features are representable. + +#### Verification + +```bash +go test -count=1 ./apps/edge/internal/openai -run 'TestResponsesProtocolProfile(BridgesToAnthropicMessages|BridgesToGeminiChat|BridgeRejectsUnrepresentableControls|OperationPassthrough|OperationAdmissionTable)$' +``` + +Expected: all named tests pass; native Responses and legacy behavior remain green. + +### [API-2] Convert provider responses and streams to Responses + +#### Problem + +`responses_stream_gate.go:1166-1183` always decodes a tunnel as Responses, while `responses_stream_gate.go:441-443` exposes raw tunnel wire for streaming attempts. After request bridging this would parse or return Anthropic/Chat wire as if it were Responses. + +```go +// apps/edge/internal/openai/responses_stream_gate.go:1177 +tunnelSource := newOpenAITunnelEndpointEventSource( + transport.tunnel.Stream(), transport.tunnel.WaitTimeout(), + rewriter, assembler, openAIRebuildEndpointResponses, codecState, +) +``` + +#### Solution + +Derive the provider decoder from `RunDispatch.ProfileOperation`: Responses remains lossless raw passthrough; Chat and Messages are decoded as provider wire and rendered through the existing Responses sink. Extend the endpoint codec with a Messages decoder or a narrow provider-wire discriminator, and ensure non-stream JSON and streaming SSE both produce canonical Responses message/reasoning/function-call items, usage, incomplete status, safe errors, and exactly one terminal. Keep response IDs/model echo stable at the public route and preserve Gemini signatures in the opaque call ID. + +```go +// after: provider wire and public wire are independent +providerWire := responsesProviderWire(dispatch.ProfileOperation) +publicWire := responsesPublicWire(providerWire) // raw only for native Responses +tunnelSource := newOpenAITunnelEndpointEventSource( + transport.tunnel.Stream(), transport.tunnel.WaitTimeout(), + rewriter, assembler, providerWire, codecState, +) +``` + +The bridge must handle fragmented Chat/Messages tool arguments, reasoning deltas, usage-only/final frames, provider error frames, and terminal ordering. Do not create a second response owner outside StreamGate. + +#### Modified Files and Checklist + +- [ ] `apps/edge/internal/openai/stream_gate_tunnel_codec.go`: decode Chat/Messages/Responses provider frames into normalized events and usage-safe state. +- [ ] `apps/edge/internal/openai/responses_stream_gate.go`: select raw vs bridge rendering per attempt operation for initial and recovery paths. +- [ ] `apps/edge/internal/openai/provider_normalization.go`: non-stream provider response→Responses conversion helpers. + +#### Test Strategy + +Write tests in `responses_protocol_profile_test.go` and `stream_gate_pipeline_test.go`: + +- `TestResponsesProtocolProfileAnthropicBridgeResponse` +- `TestResponsesProtocolProfileGeminiBridgeResponseAndSignature` +- `TestResponsesProviderBridgeStreaming`: table over Chat and Messages with fragmented text/reasoning/tool deltas. +- `TestResponsesProviderBridgeStreamingTerminalExactlyOnce` +- `TestResponsesProviderBridgeProviderErrorSanitized` + +Assertions include valid Responses JSON/SSE, function call ID/name/arguments, signature replay, usage mapping, event order, one completed/error terminal, one `[DONE]`, and no provider-native framing leak. + +#### Verification + +```bash +go test -count=1 ./apps/edge/internal/openai -run 'TestResponses(ProtocolProfileAnthropicBridgeResponse|ProtocolProfileGeminiBridgeResponseAndSignature|ProviderBridgeStreaming|ProviderBridgeStreamingTerminalExactlyOnce|ProviderBridgeProviderErrorSanitized)$' +``` + +Expected: all named bridge response tests pass for non-stream and stream fixtures. + +### [API-3] Lock compatibility with tests and contracts + +#### Problem + +`agent-contract/outer/openai-compatible-api.md` and `agent-spec/input/openai-compatible-surface.md` currently state provider-pool Responses requires a native Responses operation and uses raw passthrough. That statement becomes incomplete once profile-selected Messages/Chat bridges are supported. + +```text +# agent-spec/input/openai-compatible-surface.md:235 +A provider model-group route relays /v1/responses to the selected provider when that candidate declares the Responses operation/capability. +``` + +#### Solution + +Document that native Responses candidates preserve raw bytes, while Anthropic Messages and OpenAI Chat candidates may be selected only when every requested Responses feature can be represented and their responses are converted back to Responses. State fail-closed behavior, profile-driven reasoning mapping, opaque Gemini tool signature replay, and no caller identity branch. Keep normalized non-provider limitations unchanged. + +#### Modified Files and Checklist + +- [ ] `apps/edge/internal/openai/responses_protocol_profile_test.go`: handler/admission/non-stream regression matrix. +- [ ] `apps/edge/internal/openai/stream_gate_pipeline_test.go`: streaming codec/terminal regression matrix. +- [ ] `agent-contract/outer/openai-compatible-api.md`: external request/response compatibility contract. +- [ ] `agent-spec/input/openai-compatible-surface.md`: living implementation spec and change history. + +#### Test Strategy + +Tests are mandatory because this is a public API compatibility bug fix. No new test file is needed; extend the two existing focused suites. + +#### Verification + +```bash +go test -count=1 ./apps/edge/internal/openai +rg --sort path -n 'Responses.*(Messages|Chat)|Gemini.*(signature|thought)' agent-contract/outer/openai-compatible-api.md agent-spec/input/openai-compatible-surface.md +``` + +Expected: package tests pass fresh and both documents contain the bridge boundary without contradicting native passthrough. + +## Modified Files Summary + +| File | Items | +|---|---| +| `apps/edge/internal/openai/provider_normalization.go` | API-1, API-2 | +| `apps/edge/internal/openai/responses_handler.go` | API-1 | +| `apps/edge/internal/openai/anthropic_bridge.go` | API-1 | +| `apps/edge/internal/openai/stream_gate_tunnel_codec.go` | API-2 | +| `apps/edge/internal/openai/responses_stream_gate.go` | API-2 | +| `apps/edge/internal/openai/responses_protocol_profile_test.go` | API-1, API-2, API-3 | +| `apps/edge/internal/openai/stream_gate_pipeline_test.go` | API-2, API-3 | +| `agent-contract/outer/openai-compatible-api.md` | API-3 | +| `agent-spec/input/openai-compatible-surface.md` | API-3 | +| `agent-task/responses_provider_bridge/CODE_REVIEW-cloud-G08.md` | API-1, API-2, API-3 evidence | + +## Final Verification + +Run locally with fresh results: + +```bash +gofmt -w apps/edge/internal/openai/provider_normalization.go apps/edge/internal/openai/responses_handler.go apps/edge/internal/openai/anthropic_bridge.go apps/edge/internal/openai/stream_gate_tunnel_codec.go apps/edge/internal/openai/responses_stream_gate.go apps/edge/internal/openai/responses_protocol_profile_test.go apps/edge/internal/openai/stream_gate_pipeline_test.go +go test -count=1 ./apps/edge/internal/openai -run 'TestResponses(ProtocolProfile|ProviderBridge)' +go test -count=1 ./apps/edge/internal/openai +go test -race -count=1 ./apps/edge/internal/openai -run 'TestResponsesProviderBridgeStreaming' +git diff --check +``` + +Expected: formatting produces no unplanned paths, all tests pass, race detector is clean, and `git diff --check` is empty. + +Before live validation, require an approved exact implementation commit on `origin/dev`, then run the private `dev-runtime-deploy` skill from its clean-sync step through whole-runtime rebuild/redeploy/restart and identity/port checks. On the runner record only sanitized output for: + +```bash +ssh toki@toki-labs.com '/bin/zsh -lc '\''cd /Users/toki/agent-work/iop-dev && git status --short --branch && git rev-parse HEAD && git rev-parse origin/dev && command -v go && go version && codex --version && claude --version && opencode --version && lsof -nP -iTCP:18083 -sTCP:LISTEN && lsof -nP -iTCP:18084 -sTCP:LISTEN'\''' +``` + +Expected: clean `dev`, `HEAD == origin/dev == approved implementation SHA`, required tools exist, rebuilt Edge owns `18083`, and Node ingress listens on `18084`. + +Using standard Codex `model_providers..wire_api="responses"` custom providers and the existing copied CA/token setup, run only these two one-cycle cases against `https://toki-labs.com:18083/v1`: + +1. Codex → IOP Claude route: create a uniquely named file in a temporary test workspace and verify exact requested content. +2. Codex → IOP Gemini route: create a different uniquely named file, verify exact content, and include one normal tool-call continuation so call ID/signature replay is exercised. + +Expected: both commands exit 0, both files exist with exact content, no extension/workaround is enabled, and sanitized Edge evidence shows the selected provider operation is Messages for Claude and Chat for Gemini. Raw credentials, prompts, tool payloads, and generated file contents stay out of tracked evidence. + +After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.