<!-- task=m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress plan=2 tag=REVIEW_API -->

# Edge-native Anthropic Messages ingress and Chat bridge follow-up

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G09.md` is the mandatory final implementation step. The direct predecessor is already complete at the exact archive evidence path listed below; do not wait for an active predecessor directory that PASS finalization has moved. Implement the plan, record actual verification stdout/stderr and design decisions, keep the active files in place, and report ready for review. Verdicts, log renames, `complete.log`, and archive moves remain review-agent-only actions.

## Background

The first attempt stopped before implementation because its dependency gate checked only the active predecessor path and prohibited the archive lookup required by split-task rules. The predecessor actually passed and was archived. The Edge still exposes only OpenAI Models, Chat Completions, and Responses, while the approved SDD requires direct Anthropic-compatible Messages routes that select a concrete protocol profile and then either preserve native Anthropic wire semantics or bridge OpenAI Chat semantics.

## Archive Evidence Snapshot

- Direct predecessor PASS evidence: `agent-task/archive/2026/08/m-multi-provider-protocol-profile-native-messages/01_protocol_core/complete.log`.
- Prior plan archive target: `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/plan_cloud_G09_01.log`.
- Prior review archive target: `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/code_review_cloud_G09_01.log`.
- Prior verdict: `FAIL`; findings: Required=2, Suggested=0, Nit=0.
- Raw finding 1: the prior plan required only an active predecessor `complete.log`, although split-task dependency evidence must be resolved active-first and then from the matching archive path.
- Raw finding 2: API-1 through API-4, all scoped production/test files, and all implementation verification were absent because implementation never started.
- Affected scope: Edge Anthropic routes/auth/models/counting, native Messages tunnel, Messages-to-Chat bridge, direct Edge fixtures, and implementation evidence.
- Roadmap carryover: `messages-surface`, `messages-native`, and `messages-chat-bridge` remain unchecked until this follow-up passes.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/multi-provider-protocol-profile-native-messages.md`
- Milestone link: [Milestone document](agent-roadmap/phase/operational-observability-provider-management/milestones/multi-provider-protocol-profile-native-messages.md)
- Task ids:
  - `messages-surface`: canonical/alias routes, principal auth, Anthropic model schema, and count-tokens
  - `messages-native`: native Messages raw tunnel preservation
  - `messages-chat-bridge`: Messages-to-Chat semantic and streaming bridge
- Completion mode: check-on-pass

## Analysis

### Files Read

- `agent-roadmap/phase/operational-observability-provider-management/milestones/multi-provider-protocol-profile-native-messages.md`
- `agent-roadmap/sdd/operational-observability-provider-management/multi-provider-protocol-profile-native-messages/SDD.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `packages/go/config/edge_types.go`
- `packages/go/config/provider_types.go`
- `packages/go/config/protocol_profile.go`
- `packages/go/agentruntime/types.go`
- `apps/edge/internal/openai/server.go`
- `apps/edge/internal/openai/routes.go`
- `apps/edge/internal/openai/principal.go`
- `apps/edge/internal/openai/common_types.go`
- `apps/edge/internal/openai/route_resolution.go`
- `apps/edge/internal/openai/sse_writer.go`
- `apps/edge/internal/openai/chat_handler.go`
- `apps/edge/internal/openai/provider_tunnel.go`
- `apps/edge/internal/openai/responses_handler.go`
- `apps/edge/internal/openai/openai_auth_routes_models_test.go`
- `apps/edge/internal/openai/provider_dispatch_test.go`
- `apps/edge/internal/openai/provider_tunnel_auth_test.go`
- `apps/edge/internal/openai/provider_test_support_test.go`
- `apps/edge/internal/service/provider_pool.go`
- `apps/edge/internal/service/provider_tunnel.go`
- `apps/edge/internal/service/provider_resolution.go`
- `apps/edge/internal/service/run_types.go`
- `agent-client/claude/iop-claude-gateway.py`
- `agent-task/archive/2026/08/m-multi-provider-protocol-profile-native-messages/01_protocol_core/complete.log`
- `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/plan_cloud_G09_01.log`
- `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/code_review_cloud_G09_01.log`

### SDD Criteria

- The SDD is approved and its implementation lock is released.
- S05 / `messages-surface`: canonical `/v1/messages`, `/v1/messages/count_tokens`, version-selected `/v1/models`, and `/anthropic/v1/*` aliases must use the same principal and internal model snapshot and reach Edge handlers without an `agent-client` hop.
- S06 / `messages-native`: Anthropic, MiniMax, MiMo, and Seulgi fixtures must preserve request/response/error bytes, content-block and SSE ordering, and the terminal event except for documented model/auth rewrites.
- S07 / `messages-chat-bridge`: system, text/image, tools, tool use/result/choice, thinking, usage, stop reasons, provider errors, and streaming events must transform in both directions; unsupported semantics must fail before a provider wire dispatch.
- The S05-S07 evidence-map rows are covered by API-1 through API-3 focused tests and API-4 full regression evidence.

### Verification Context

- No separate handoff exists.
- Validation rules read: `agent-test/local/rules.md`, Edge/Node/platform local profiles, and Edge/Node/platform/testing domain rules.
- Toolchain preflight: `/config/.local/bin/go`, `go1.26.2 linux/arm64`, `GOROOT=/config/opt/go`.
- Workspace basis: `/config/workspace/iop-s0`, branch `feature/multi-provider-protocol-profile-native-messages`, HEAD `174a2a6b1375bd71c3a977aff91e6afb9c26bd58`.
- The current working tree includes predecessor-owned protocol-core changes in shared Edge files. Treat them as the implementation basis and do not revert or overwrite unrelated user changes.
- Focused tests use `httptest`, the existing fake provider-pool/tunnel support, and checked-in fixtures. Fresh Go results require `-count=1`.
- No live Claude/provider credential or standalone gateway process is required. The repository fake Lemonade cycle is the required credential-free integration regression. External live qualification belongs to S10 and is not a PASS condition here.

### Test Coverage Gaps

- No canonical/alias Anthropic route, `x-api-key`, dual-header conflict, or version-selected model-shape tests exist.
- No native Messages request/response/error/SSE preservation or exactly-once terminal tests exist.
- No Messages-to-Chat mixed-content, tool, thinking, usage, stop, error, or stream golden tests exist.
- No native count-tokens or deterministic local-counter/unsupported branch tests exist.

### Symbol References

- No existing symbol is intentionally renamed or removed.
- Existing `bearerTokenFromHeader`, `resolvePrincipal`, and `principalFromRequest` call sites remain compatible; a request credential helper adds `X-Api-Key` handling before the existing constant-time resolver.
- Existing `handleModels` remains the route handler and selects the Anthropic encoder only when `anthropic-version` is present.
- Predecessor-provided profile facts (`ProtocolDriverAnthropicMessages`, operation declarations, `ProviderPoolCandidate`, `PrepareProtocolTunnel`, and dispatch profile metadata) are consumed rather than redesigned.

### Split Judgment

- The parent is split into protocol core, Messages ingress, and contract/Responses units.
- The direct `01_protocol_core` predecessor is satisfied by `agent-task/archive/2026/08/m-multi-provider-protocol-profile-native-messages/01_protocol_core/complete.log`. The active location was checked first and is absent because PASS finalization archived it; this exact archive path is the valid fallback evidence.
- The stable contract for this child is one Edge ingress that chooses a concrete profile and then emits either native Anthropic wire output or bridged Chat output with one request-local terminal state.
- Further separation of surface, native, and bridge would leave an intermediate public handler with only one selected-profile path and would not independently satisfy S05.

### Scope Rationale

- Profile schemas, operation wire contracts, and candidate callback infrastructure are predecessor-owned and must not be redesigned.
- Contract/spec synchronization and Responses/Pi regressions belong to `03+02_contract_responses_sync`.
- `agent-client/claude/iop-claude-gateway.py` is read-only migration evidence and must not remain a production hop.
- Credential storage/rotation, provider live qualification, and vendor fallback are out of scope.

### Final Routing

- status: `routed`; evaluation mode: follow-up; finalizer: `finalize-task-policy.sh` in `pair` mode.
- build closures: all true; capability gap: none.
- build grade scores: scope_coupling=2, state_concurrency=2, blast_irreversibility=2, evidence_diagnosis=1, verification_complexity=2; grade G09.
- build route: base `grade-boundary`; `large_indivisible_context=false`; matched loop risks: `temporal_state`, `boundary_contract`, `structured_interpretation`, `variant_product` (4); risk boundary matched; `review_rework_count=1`; `evidence_integrity_failure=false`; no recovery signal.
- review closures: all true; scores 2/2/2/1/2; route `official-review`, cloud, Codex `gpt-5.6-sol`, xhigh; grade G09.
- canonical active files: `PLAN-cloud-G09.md`, `CODE_REVIEW-cloud-G09.md`.

## Dependencies and Execution Order

1. Resolve the direct predecessor active-first, then use the exact archive fallback. The active path is absent and the archived PASS evidence exists at `agent-task/archive/2026/08/m-multi-provider-protocol-profile-native-messages/01_protocol_core/complete.log`; the dependency is satisfied.
2. Build the surface and strict semantic validation first, then native and bridge selected-profile paths, then direct Edge and full regression evidence.
3. Do not enumerate sibling archives or invent additional dependencies. Only the exact evidence path above is authorized for this split dependency.

## Implementation Checklist

- [ ] Add canonical/alias Anthropic routes, shared bearer/`x-api-key` principal resolution, version-selected model schema, and native/local count-tokens behavior with S05 tests.
- [ ] Implement profile-selected native Messages tunneling with a header allowlist, model rewrite, byte/SSE/error preservation, and exactly-once terminal evidence for S06.
- [ ] Implement strict Messages-to-Chat non-stream/stream transformation for supported content, tools, thinking, usage, stop, and error semantics, rejecting unsupported inputs before provider wire dispatch for S07.
- [ ] Run focused direct-Edge fixtures, full Edge/Node regression, and the fake Lemonade integration cycle with fresh results.
- [ ] Fill all implementation-owned sections in `CODE_REVIEW-cloud-G09.md` with actual implementation notes and verification output.

### [REVIEW_API-1] Anthropic public surface, auth, models, and count tokens

#### Problem

`routes()` registers only OpenAI endpoints, `principalFromRequest` reads only Authorization, and `handleModels` always emits the OpenAI list schema. Claude Code-shaped Messages and count-tokens requests cannot reach Edge, and dual credential behavior is undefined.

Before:

```go
func (s *Server) routes() *http.ServeMux {
	mux := http.NewServeMux()
	mux.HandleFunc("/v1/models", s.withAuth(s.handleModels))
	mux.HandleFunc("/v1/chat/completions", s.withAuth(s.handleChatCompletions))
	mux.HandleFunc("/v1/responses", s.withAuth(s.handleResponses))
	return mux
}
```

#### Solution

Register canonical and `/anthropic` aliases on the same handlers. Accept Bearer or `X-Api-Key`, require identical token values if both are non-empty, and pass the normalized token through the existing resolver without logging it. Use `anthropic-version`, never User-Agent inference, to select the Anthropic model schema. For count-tokens, use a deterministic catalog counter locally when available; otherwise admit only a selected native profile that explicitly supports `count_tokens`. Candidate rejection must happen before provider wire dispatch and map to a typed unsupported response.

After (representative):

```go
func (s *Server) registerAnthropicRoutes(mux *http.ServeMux) {
	mux.HandleFunc("/v1/messages", s.withAuth(s.handleAnthropicMessages))
	mux.HandleFunc("/v1/messages/count_tokens", s.withAuth(s.handleAnthropicCountTokens))
	mux.HandleFunc("/anthropic/v1/messages", s.withAuth(s.handleAnthropicMessages))
	mux.HandleFunc("/anthropic/v1/messages/count_tokens", s.withAuth(s.handleAnthropicCountTokens))
	mux.HandleFunc("/anthropic/v1/models", s.withAuth(s.handleModels))
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/routes.go`: register canonical/alias handlers and select model response shape by version header.
- [ ] `apps/edge/internal/openai/principal.go`: normalize Bearer/`X-Api-Key`, reject unequal dual credentials, and reuse the existing resolver.
- [ ] `apps/edge/internal/openai/common_types.go`: add Anthropic model/error/count DTOs or neutral snapshot helpers.
- [ ] `apps/edge/internal/openai/anthropic_types.go`: define strict request, content, tool, usage, event, and validation types.
- [ ] `apps/edge/internal/openai/anthropic_handler.go`: validate requests, choose count policy, and dispatch by selected profile.
- [ ] `apps/edge/internal/openai/anthropic_surface_test.go`: cover routes, auth, model schemas, and count branches.
- [ ] `apps/edge/internal/openai/testdata/anthropic/models_anthropic.json`: store expected Anthropic model output.

#### Test Strategy

Add `TestAnthropicCanonicalAndAliasRoutes`, `TestAnthropicPrincipalBearerAndAPIKey`, `TestAnthropicPrincipalDualCredentialConflict`, `TestAnthropicModelsVersionSelectsShape`, and `TestAnthropicCountTokensNativeLocalUnsupported`. Assert identical model ids across schemas, no credential disclosure, and zero provider wire calls for local/unsupported counter branches.

#### Verification

```bash
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropic(Canonical|Principal|Models|CountTokens)'
```

### [REVIEW_API-2] Native Messages tunnel preservation

#### Problem

Current public tunnel handlers are Chat/Responses-specific. There is no native Messages operation selection, header policy, top-level model rewrite, or raw Anthropic response/error/SSE relay.

#### Solution

Submit one provider-pool request with an unset initial operation and an `AcceptCandidate` predicate that accepts only concrete Anthropic Messages or OpenAI Chat profiles with the required capability. In `PrepareProtocolTunnel`, configure the request after selection. For native profiles, select `messages` or `count_tokens`, rewrite only the top-level model, apply profile auth plus the allowed version/known-beta/content headers, and preserve provider status, body bytes, SSE order, and allowed response headers. A request-local terminal gate must emit exactly one END or ERROR.

After (representative):

```go
poolReq.PrepareProtocolTunnel = func(
	req edgeservice.SubmitProviderTunnelRequest,
	candidate edgeservice.ProviderPoolCandidate,
) (edgeservice.SubmitProviderTunnelRequest, error) {
	if candidate.ProtocolDriver == string(config.ProtocolDriverAnthropicMessages) {
		return prepareNativeAnthropicTunnel(req, candidate, ingress)
	}
	return prepareAnthropicChatBridgeTunnel(req, candidate, ingress)
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/anthropic_native.go`: prepare selected-profile native requests and relay ordered raw results.
- [ ] `apps/edge/internal/openai/anthropic_handler.go`: filter candidates and route native result/error states.
- [ ] `apps/edge/internal/openai/anthropic_native_test.go`: cover provider fixtures, fragmented frames, headers, errors, and terminal exactly-once.
- [ ] `apps/edge/internal/openai/testdata/anthropic/native_message.json`: store canonical native request/response bytes.
- [ ] `apps/edge/internal/openai/testdata/anthropic/native_stream.sse`: store ordered native streaming output.
- [ ] `apps/edge/internal/openai/testdata/anthropic/native_error.json`: store native provider error output.

#### Test Strategy

Add native table tests for Anthropic, MiniMax, MiMo, and Seulgi profiles. Deliberately fragment fake tunnel frames and compare output with golden bytes after only the documented model/header rewrites. Assert one terminal for success and error paths.

#### Verification

```bash
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropicNative'
```

### [REVIEW_API-3] Strict Messages-to-Chat bridge and stream state machine

#### Problem

The only existing bridge is a standalone migration script outside Edge. Existing OpenAI SSE output does not implement Anthropic event sequencing, and unsupported structured semantics can neither be preserved nor safely rejected by a direct Edge handler.

#### Solution

Validate semantic support before provider-pool submission. After selection of an OpenAI Chat profile, transform top-level system, text/image blocks, tool definitions, tool use/result/choice, sampling/stop limits, and capability-gated thinking into Chat JSON. Convert non-stream and SSE results back to Anthropic message/content events while preserving tool ids/arguments, reasoning, usage, finish reasons, and provider errors. Keep all accumulators request-local, reject unknown blocks/options/beta before wire dispatch, and emit `message_stop` exactly once.

After (representative):

```go
type anthropicBridgeStream struct {
	started bool
	stopped bool
	blocks  map[int]*anthropicBlockState
	usage   anthropicUsage
}

func (s *anthropicBridgeStream) finish() ([]anthropicSSEEvent, error) {
	if s.stopped {
		return nil, fmt.Errorf("duplicate Chat terminal")
	}
	s.stopped = true
	return s.terminalEvents(), nil
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/anthropic_bridge.go`: implement strict request/response transforms and capability gates.
- [ ] `apps/edge/internal/openai/anthropic_stream.go`: implement Chat SSE parsing and ordered Anthropic event emission.
- [ ] `apps/edge/internal/openai/anthropic_handler.go`: invoke the bridge only for a selected OpenAI Chat profile.
- [ ] `apps/edge/internal/openai/anthropic_bridge_test.go`: cover mixed content, tools, thinking, usage, stop, errors, streams, and pre-dispatch rejection.
- [ ] `apps/edge/internal/openai/testdata/anthropic/chat_bridge_cases.json`: store non-stream golden cases.
- [ ] `apps/edge/internal/openai/testdata/anthropic/chat_bridge_stream.sse`: store Chat input and Anthropic output streaming fixtures.

#### Test Strategy

Add golden and state-machine tests for text, image, tool round trips, thinking capability, provider errors, fragmented SSE, duplicate terminals, and unsupported blocks. Assert exact semantic equality, ordered content blocks, reconstructed tool arguments, mapped usage/stop reasons, and zero wire calls on pre-dispatch rejection.

#### Verification

```bash
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropicChatBridge'
```

### [REVIEW_API-4] Direct Edge and full regression evidence

#### Problem

Pure transform unit tests do not prove that route/auth/profile selection and service result handling use the new implementation. The prior attempt has no implementation or verification evidence.

#### Solution

Exercise canonical and alias HTTP routes through an in-process Edge server and fake provider-pool/tunnel callbacks for non-stream, stream, models, and count tokens. Then run all Edge and Node tests, static checks, the repository fake Lemonade cycle, deterministic route inspection, and diff hygiene.

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/provider_test_support_test.go`: extend the existing fake service only as needed for selected profile and frame assertions.
- [ ] `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/CODE_REVIEW-cloud-G09.md`: record actual decisions, deviations, and every verification result.

#### Test Strategy

Use repository-native fixtures and fake services only. `TestAnthropicCanonicalAndAliasRoutes` is the direct Edge evidence; the fake Lemonade cycle supplies credential-free cross-component regression evidence.

#### Verification

```bash
command -v go && go version && go env GOROOT
go vet ./apps/edge/...
go test -count=1 ./apps/edge/...
go test -count=1 ./apps/node/...
IOP_LEMONADE_MODE=fake ./scripts/e2e-openai-lemonade.sh
rg -n --sort path '"/(anthropic/)?v1/(messages|models)' apps/edge/internal/openai
git diff --check
```

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/routes.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/principal.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/common_types.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/anthropic_types.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/anthropic_handler.go` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |
| `apps/edge/internal/openai/anthropic_native.go` | REVIEW_API-2 |
| `apps/edge/internal/openai/anthropic_bridge.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/anthropic_stream.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/provider_test_support_test.go` | REVIEW_API-4 |
| `apps/edge/internal/openai/anthropic_surface_test.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/anthropic_native_test.go` | REVIEW_API-2 |
| `apps/edge/internal/openai/anthropic_bridge_test.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/testdata/anthropic/models_anthropic.json` | REVIEW_API-1 |
| `apps/edge/internal/openai/testdata/anthropic/native_message.json` | REVIEW_API-2 |
| `apps/edge/internal/openai/testdata/anthropic/native_stream.sse` | REVIEW_API-2 |
| `apps/edge/internal/openai/testdata/anthropic/native_error.json` | REVIEW_API-2 |
| `apps/edge/internal/openai/testdata/anthropic/chat_bridge_cases.json` | REVIEW_API-3 |
| `apps/edge/internal/openai/testdata/anthropic/chat_bridge_stream.sse` | REVIEW_API-3 |
| `agent-task/m-multi-provider-protocol-profile-native-messages/02+01_messages_ingress/CODE_REVIEW-cloud-G09.md` | REVIEW_API-4 |

## Final Verification

Run in order with fresh Go results:

```bash
test -f agent-task/archive/2026/08/m-multi-provider-protocol-profile-native-messages/01_protocol_core/complete.log
command -v go && go version && go env GOROOT
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropic(Canonical|Principal|Models|CountTokens)'
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropicNative'
go test -count=1 ./apps/edge/internal/openai -run 'TestAnthropicChatBridge'
go vet ./apps/edge/...
go test -count=1 ./apps/edge/...
go test -count=1 ./apps/node/...
IOP_LEMONADE_MODE=fake ./scripts/e2e-openai-lemonade.sh
rg -n --sort path '"/(anthropic/)?v1/(messages|models)' apps/edge/internal/openai
git diff --check
```

Expected: archived predecessor evidence exists; S05-S07 focused and direct Edge fixtures pass; Edge/Node and fake integration regressions pass; route inspection has no unexplained duplicates; diff checks are clean.

After completing all code changes, fill every implementation-owned section in `CODE_REVIEW-cloud-G09.md`.
