<!-- task=m-multi-provider-protocol-profile-native-messages/01_protocol_core plan=1 tag=REVIEW_API -->

# Repair Protocol Profile Runtime Contract and Evidence

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G09.md` is the mandatory final implementation step. Run every verification command, paste actual notes and stdout/stderr, keep both active files in place, and report ready for review; only the code-review skill may finalize, rename logs, or write `complete.log`. If blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive artifacts, or write `complete.log`.

## Background

The first implementation introduced the profile schema but broke profile-backed production requests and did not preserve the concrete snapshot across runtime boundaries. Its S02/S03 tests exercise helpers instead of actual requests, so the reported green suite contradicts the production path. This follow-up restores legacy endpoint compatibility, completes the selected-candidate contract, and replaces placeholder evidence with deterministic HTTP and full-cycle coverage.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/plan_local_G08_01.log`
- Prior review: `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/code_review_cloud_G08_01.log`
- Verdict: `FAIL`; Required=6, Suggested=0, Nit=0; `review_rework_count=1`; `evidence_integrity_failure=true`.
- Required carryover: profile-backed Models/Chat/Responses fail or ignore provider endpoints; S01/S03 validation/catalog data is incomplete; extensions and pointers violate immutable snapshot semantics; selected-candidate protocol preparation is absent; request tests do not execute the claimed path; Dart protobuf output is stale.
- Affected areas: `packages/go/config`, Edge mapper/provider-pool/OpenAI callers, Node profile adapter, runtime protobuf consumers, Dart generated output, and the credential-free OpenAI-compatible smoke.
- Verification evidence: fresh focused and full Go suites passed only after reviewer-local `GOCACHE`/`GOTMPDIR` overrides, but a reviewer model-discovery reproducer failed with `openai_compat adapter: endpoint is required`; current request tests use suffix checks, ignored errors, empty bodies, or non-value Struct assertions; no provider-first Edge→Node→upstream full-cycle ran.
- Roadmap carryover: the same `profile-contract`, `path-resolution`, and `profile-catalog` Task ids 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:
  - `profile-contract`: common provider profile and immutable overlay/runtime contract
  - `path-resolution`: profile-operation URL resolver
  - `profile-catalog`: built-in/custom profile catalog and admission
- Completion mode: check-on-pass

## Analysis

### Files Read

- `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/PLAN-local-G08.md`
- `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/CODE_REVIEW-cloud-G08.md`
- `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/inner/edge-node-runtime-wire.md`
- `agent-contract/inner/edge-config-runtime-refresh.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/adapter_types.go`
- `packages/go/config/load.go`
- `packages/go/config/normalize.go`
- `packages/go/config/validate.go`
- `packages/go/config/protocol_profile.go`
- `packages/go/config/protocol_profile_test.go`
- `packages/go/config/provider_catalog_config_test.go`
- `packages/go/config/provider_catalog_validation_config_test.go`
- `configs/edge.yaml`
- `proto/iop/runtime.proto`
- `proto/gen/iop/runtime.pb.go`
- `apps/client/lib/gen/proto/iop/runtime.pb.dart`
- `apps/client/lib/gen/proto/iop/runtime.pbjson.dart`
- `apps/client/test/iop_wire/generated_proto_import_test.dart`
- `apps/edge/internal/node/mapper.go`
- `apps/edge/internal/node/protocol_profile_mapper_test.go`
- `apps/edge/internal/service/model_queue_types.go`
- `apps/edge/internal/service/provider_resolution.go`
- `apps/edge/internal/service/provider_pool.go`
- `apps/edge/internal/service/provider_tunnel.go`
- `apps/edge/internal/service/run_types.go`
- `apps/edge/internal/service/protocol_profile_test.go`
- `apps/edge/internal/openai/chat_handler.go`
- `apps/edge/internal/openai/responses_handler.go`
- `apps/edge/internal/openai/provider_dispatch_test.go`
- `apps/node/internal/adapters/factory.go`
- `apps/node/internal/adapters/config_set.go`
- `apps/node/internal/adapters/protocol_profile_config_test.go`
- `apps/node/internal/adapters/openai_compat/adapter.go`
- `apps/node/internal/adapters/openai_compat/provider.go`
- `apps/node/internal/adapters/openai_compat/request.go`
- `apps/node/internal/adapters/openai_compat/provider_tunnel.go`
- `apps/node/internal/adapters/openai_compat/stream.go`
- `apps/node/internal/adapters/openai_compat/execute_test.go`
- `apps/node/internal/adapters/openai_compat/protocol_profile_test.go`
- `apps/node/internal/node/tunnel_handler.go`
- `apps/node/internal/node/protocol_profile_tunnel_test.go`
- `scripts/e2e-openai-lemonade.sh`
- `Makefile`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/multi-provider-protocol-profile-native-messages/SDD.md`; status `[approved]`; implementation lock released.
- S01 / `profile-contract`: valid overlays must become immutable concrete profiles; cycle, unknown base, driver conflict, and invalid operation/auth/capability combinations must fail at config load; legacy aliases must converge on the same concrete semantics without losing their endpoint.
- S02 / `path-resolution`: relative and absolute operation URLs for `/v1`, `/v1beta/openai`, `/api/paas/v4`, and `/anthropic/v1` fixtures must resolve without prefix duplication or loss.
- S03 / `profile-catalog`: Chat and Messages variants for the named providers must admit only declared operations and build the exact path, header, and mapped-model request.
- Evidence Map S01-S03 requires config validation, URL resolver, and provider request golden tests. Those rows require actual request construction and provider-by-provider table output, so helper-only suffix assertions and ignored dispatch errors cannot close the Tasks.

### Verification Context

- No handoff was supplied. Sources read: `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/node-smoke.md`, `agent-test/local/platform-common-smoke.md`, and the matching Edge, Node, platform-common, and testing domain rules.
- Runner: local checkout `/config/workspace/iop-s0`; branch `feature/multi-provider-protocol-profile-native-messages`; HEAD `174a2a6b1375bd71c3a977aff91e6afb9c26bd58`; dirty worktree contains this task implementation plus unrelated user-owned roadmap edits that must be preserved.
- Runtime: Linux/aarch64, Go `/config/.local/bin/go` version `go1.26.2`, `GOROOT=/config/opt/go`, protoc `/config/.local/bin/protoc` version `29.3`, Flutter `/sdk/flutter/bin/flutter`, Dart `/sdk/flutter/bin/dart`. `protoc-gen-dart` is currently absent; install it outside the repository with `flutter pub global activate protoc_plugin` before `make proto-dart` if the preflight remains empty.
- Fresh reviewer verification: focused config/Edge/Node/openai_compat tests, `go vet ./packages/go/...`, `go vet ./apps/node/...`, full packages/Edge/Node tests, and `git diff --check` passed with `GOCACHE=/config/workspace/iop-s0/.gocache` and a workspace `GOTMPDIR`; `go vet ./apps/edge/...` retains the unrelated existing `input_estimator.go` WriteByte warning. The implementation's recorded `packages/go/agentworkspace` failure did not reproduce under the reviewer runner.
- A focused reviewer reproducer exercised profile-backed model discovery and failed before HTTP dispatch with `openai_compat adapter: endpoint is required`. Official Anthropic documentation checked on 2026-08-01 uses base `https://api.anthropic.com` and operation `/v1/messages`; Xiaomi MiMo documentation says to use the plan-specific Base URL and supports separate OpenAI/Anthropic formats, so unverified hard-coded endpoints must not be invented.
- No external credential or host is required. The deterministic full-cycle runner is `scripts/e2e-openai-lemonade.sh` in fake mode, using temporary loopback ports, temporary config, Edge and Node dev entrypoints, and a fake upstream. It must be converted from legacy `model_routes`/manual adapter wiring to the provider-first profile path.
- Confidence: high. The broken callers and contradiction are direct source and execution evidence; the remaining provider catalog values must be sourced from current official docs and locked by fixtures during implementation.

### Test Coverage Gaps

- Provider endpoint overlay and legacy aliases: not covered; add config-load and full-cycle assertions that the local endpoint wins without mutating built-ins.
- Profile-backed Models/Chat/Responses operations: not covered by current production-caller tests; assert non-empty operation ids and actual loopback requests.
- Auth declaration and model mapping: current test explicitly expects no auth and never asserts a body; replace it with exact request header/body golden assertions.
- Extension and pointer immutability: current tests check only non-nil or values without mutation; add nested Struct round-trip and post-construction mutation assertions.
- Candidate selection/re-resolution: current tests expose only id/driver and never invoke a selected-candidate prepare hook; add callback, dispatch metadata, selected provider, and refresh-isolation assertions.
- MiniMax/MiMo Messages variants and capability/token-counter validation: absent; add normal and rejection tables.
- Dart consumer generation: absent; assert field 8 profile construction and tunnel operation field availability in the client generated-proto test.
- User execution pipeline: no provider-first profile smoke; update fake Lemonade smoke to exercise Edge config load, mapper, wire, Node adapter, model discovery, and Chat request.

### Symbol References

- Add `prepareProtocolTunnelFunc` and `ProviderPoolDispatchRequest.PrepareProtocolTunnel`; retain `prepareTunnelFunc` and `PrepareTunnel` for source compatibility. Existing `PrepareTunnel` call sites are `apps/edge/internal/service/provider_pool.go` and `apps/edge/internal/service/run_dispatch_internal_test.go`.
- Change `classifyProviderExecutionPath` to accept/use concrete profile driver information; call sites are in `apps/edge/internal/service/provider_resolution.go` and its tests.
- No public field is removed or renumbered. `ProviderTunnelRequest.Path` remains the no-profile mixed-version fallback; `Operation` is mandatory when a concrete profile is selected.

### Split Judgment

- This is one recovery plan. The indivisible invariant is: one provider-selected concrete profile snapshot must determine endpoint, operation admission, request preparation, execution path, and evidence from config load through the actual upstream request. Splitting config, selected-candidate, or Node request work would leave a passing intermediate state with competing vendor/path sources of truth.
- The subtask directory is unchanged and has no predecessor marker; no new dependency is introduced. Downstream `02+01_messages_ingress` still depends on this task's PASS contract.

### Scope Rationale

- Exclude the public Anthropic Messages ingress/bridge, contract/spec synchronization, and Responses redesign; they remain downstream subtasks. Existing Responses behavior is only repaired where the new profile attachment directly regressed it.
- Exclude live provider qualification and credentials. Current official documentation informs catalog fixtures, while deterministic loopback servers provide required S01-S03 evidence.
- Preserve the four unrelated roadmap edits and all user-owned changes. Do not clean `.gocache`, rewrite unrelated source, or modify central `agent-ops/rules/common/**` and `agent-ops/skills/common/**`.

### Final Routing

- `status=routed`; `evaluation_mode=isolated-reassessment`; finalizer=`finalize-task-policy.sh`; mode=`pair`.
- Build closures: scope/context/verification/evidence/ownership/decision are all `true`; closure basis is the complete plan, direct reviewer reproducer, SDD S01-S03, local fake-upstream runner, and exact file ownership; capability gap: none.
- Build scores: scope_coupling=2, state_concurrency=1, blast_irreversibility=2, evidence_diagnosis=2, verification_complexity=2 => G09. Base/final route basis=`grade-boundary`; lane=`cloud`; filename=`PLAN-cloud-G09.md`.
- Build signals: `large_indivisible_context=false`; matched loop risks=`boundary_contract`, `structured_interpretation`, `variant_product`; count=3; `review_rework_count=1`; `evidence_integrity_failure=true`; risk boundary=false; recovery boundary=true. Grade boundary remains the final basis.
- Review closures: all `true`; scores 2/1/2/2/2 => G09; route=`official-review`; lane=`cloud`; adapter=`codex`; model=`gpt-5.6-sol`; reasoning=`xhigh`; filename=`CODE_REVIEW-cloud-G09.md`.

## Implementation Checklist

- [ ] Repair S01/S03 catalog validation and provider endpoint normalization, including accurate Chat/Messages variants, immutable built-ins, and the Chat-only token-counter contract.
- [ ] Preserve the concrete profile across mapper, protobuf, Node, adapter, queue, and selected-candidate boundaries; add protocol-aware preparation and driver-based dispatch while keeping legacy hooks compatible.
- [ ] Make Models, Chat, Responses, and tunnel request construction consume operation/auth/model mapping from the selected profile and prove exact provider requests rather than helper output.
- [ ] Regenerate all Go/Dart protobuf consumers and run fresh focused, full regression, and provider-first Edge→Node→fake-upstream full-cycle verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Restore catalog, overlay, and endpoint semantics

#### Problem

`resolveProtocolProfiles` attaches the public `openai` built-in to every legacy vLLM/OpenAI-compatible provider but never overlays `NodeProviderConf.Endpoint` (`packages/go/config/normalize.go:14-32`). Custom profiles overwrite reserved built-ins (`normalize.go:40-47`), profile validation does not close capability/auth combinations (`protocol_profile.go:498-528`), MiniMax/MiMo Messages variants are absent, and `TokenCounter` is unused (`provider_types.go:195-197`).

Before (`packages/go/config/normalize.go:27-31`):

```go
resolved, err := ResolveProtocolProfile(p.Profile, p.Type, catalog)
if err != nil {
    return fmt.Errorf("nodes[%d].providers[%d] %q: %w", i, j, p.ID, err)
}
p.RuntimeProfile = &resolved
```

#### Solution

Resolve an immutable catalog that rejects custom ids colliding with built-ins. Build a provider-specific clone and apply the provider endpoint/base override to that clone only, with explicit precedence and URL validation, so local legacy providers do not contact public built-in hosts. Define a closed capability set and driver/operation/auth/capability matrix, validate model token-counter mode/range and reject counters for any model group that can select a Messages/Responses profile. Split vendor Chat and native Messages variants into stable ids; use current official operation/header/base data and record the documentation check date in tests/comments rather than guessing unavailable plan-specific endpoints.

After (representative):

```go
resolved, err := ResolveProtocolProfile(selector, p.Type, catalog)
if err != nil {
    return fmt.Errorf("nodes[%d].providers[%d] %q: %w", i, j, p.ID, err)
}
resolved = resolved.Clone()
if endpoint := effectiveProtocolEndpoint(*p); endpoint != "" {
    resolved.BaseURL = endpoint
}
p.RuntimeProfile = &resolved
```

#### Modified Files and Checklist

- [ ] `packages/go/config/protocol_profile.go`: accurate variant catalog, closed validation matrix, collision/clone helpers, and endpoint rules.
- [ ] `packages/go/config/normalize.go`: error-returning immutable catalog and provider-specific endpoint overlay.
- [ ] `packages/go/config/validate.go`: token-counter and model-group/provider-profile compatibility validation.
- [ ] `packages/go/config/provider_types.go`: clarify and enforce token-counter semantics without new ambiguous fields.
- [ ] `packages/go/config/protocol_profile_test.go`: S01/S03 table tests for collision, capability/auth, endpoint precedence, variants, token counter, and built-in immutability.
- [ ] `configs/edge.yaml`: correct profile ids/operation examples and remove incorrect hard-coded endpoint guidance.

#### Test Strategy

Write/replace table tests `TestProtocolProfileProviderEndpointOverlay`, `TestProtocolProfileCatalogCollisionRejected`, `TestProtocolProfileCapabilityMatrixRejected`, `TestProtocolProfileChatOnlyTokenCounter`, and `TestBuiltInProtocolProfileCatalog`. Fixtures must cover every Chat/Messages variant, explicit custom overlay, legacy vLLM endpoint, nested map mutation, unknown capability, wrong driver operation, auth header/scheme, and models selecting mixed drivers.

#### Verification

```bash
go test -count=1 ./packages/go/config/... -run 'Test(ProtocolProfile|LegacyProviderAlias|BuiltInProtocolProfile)'
```

Expected: all S01/S03 tables pass; invalid fixtures fail during load/validation with profile/model ids; legacy endpoint fixtures retain loopback URLs; built-ins remain unchanged after resolution.

### [REVIEW_API-2] Complete immutable wire and selected-candidate contract

#### Problem

Edge writes an empty Struct for every extension map (`apps/edge/internal/node/mapper.go:282-285`), Node drops extensions (`apps/node/internal/adapters/factory.go:61-89`), adapter and queue candidates retain mutable profile pointers (`adapter.go:50-62`, `provider_resolution.go:297-305`), and provider-pool dispatch offers only the old `PrepareTunnel(req)` hook (`provider_pool.go:68-73`). Execution classification still uses provider type (`provider_resolution.go:369-383`) and selected profile facts are absent from `RunDispatch` (`run_types.go:45-64`).

Before (`apps/edge/internal/service/provider_pool.go:68-73`):

```go
type ProviderPoolDispatchRequest struct {
    Run             SubmitRunRequest
    Tunnel          SubmitProviderTunnelRequest
    PrepareTunnel   prepareTunnelFunc
    PrepareRun      prepareRunFunc
    AcceptCandidate ProviderPoolCandidatePredicate
}
```

#### Solution

Round-trip the exact nested extension tree through `structpb.Struct`, decode it on Node, and clone profiles when entering mapper/candidate/adapter/config snapshots. Add a source-compatible protocol-aware hook that receives an immutable selected candidate snapshot; prefer it when supplied and retain `PrepareTunnel` for old callers. Carry selected profile id/driver/capabilities and provider id into dispatch metadata, refresh clones during queue re-resolution, classify from driver when present, and use the actually selected provider in tunnel preparation and dispatch.

After (representative):

```go
type ProviderPoolDispatchRequest struct {
    Run                   SubmitRunRequest
    Tunnel                SubmitProviderTunnelRequest
    PrepareProtocolTunnel prepareProtocolTunnelFunc
    PrepareTunnel         prepareTunnelFunc
    PrepareRun            prepareRunFunc
    AcceptCandidate       ProviderPoolCandidatePredicate
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/node/mapper.go`: serialize the cloned extension values instead of an empty Struct.
- [ ] `apps/edge/internal/node/protocol_profile_mapper_test.go`: assert nested value equality and source/proto mutation isolation.
- [ ] `apps/node/internal/adapters/factory.go`: decode extensions and return a fully cloned concrete profile.
- [ ] `apps/node/internal/adapters/protocol_profile_config_test.go`: assert nested round-trip and config fingerprint changes.
- [ ] `apps/edge/internal/service/model_queue_types.go`: own a cloned profile snapshot in each candidate.
- [ ] `apps/edge/internal/service/provider_resolution.go`: clone on resolution/refresh and classify from driver with legacy type fallback.
- [ ] `apps/edge/internal/service/provider_pool.go`: add selected-candidate protocol preparation, selected provider binding, and old-hook compatibility.
- [ ] `apps/edge/internal/service/provider_tunnel.go`: preserve selected operation/provider fields in the final wire request.
- [ ] `apps/edge/internal/service/run_types.go`: expose copied selected profile facts in dispatch metadata.
- [ ] `apps/edge/internal/service/protocol_profile_test.go`: replace the empty test with dispatch, re-resolution, mutation, hook, and driver-path regressions.
- [ ] `apps/node/internal/adapters/openai_compat/adapter.go`: clone the incoming runtime profile before storing it.

#### Test Strategy

Expand `TestConcreteProfileToProtoExtensions`, `TestProtocolProfileCandidateCopy`, `TestProtocolProfileReResolution`, and `TestProtocolProfileSubmitProviderPoolTunnelOperation`. Mutate every source map/slice/nested extension after each boundary and assert the snapshot is unchanged. Exercise both new and legacy prepare hooks, selected provider mismatch, queued refresh, driver-based path, and operation wire propagation.

#### Verification

```bash
go test -count=1 ./apps/edge/internal/node ./apps/edge/internal/service -run 'Test.*ProtocolProfile|Test.*TunnelOperation'
go test -count=1 ./apps/node/internal/adapters -run 'Test.*ProtocolProfile'
```

Expected: exact extensions and profile facts survive all boundaries, mutations do not cross snapshots, the selected candidate owns provider/driver/operation, and legacy hooks still pass.

### [REVIEW_API-3] Drive real requests from profile operations

#### Problem

Profile-backed model discovery returns `errEndpointRequired` unconditionally (`apps/node/internal/adapters/openai_compat/provider.go:85-88`). Production Chat and Responses callers set only legacy `Path` (`chat_handler.go:236-245`, `responses_handler.go:357-367`), which makes `ResolveOperationURL("")` fail. Auth and model mappings are carried but never applied, while the current tests call only the resolver, ignore tunnel errors, or assert the missing behavior.

Before (`apps/node/internal/adapters/openai_compat/protocol_profile_test.go:94-106`):

```go
url, err := fx.profile.ResolveOperationURL(fx.op)
if err != nil {
    t.Fatalf("ResolveOperationURL: %v", err)
}
if !strings.HasSuffix(url, fx.wantPath) {
    t.Errorf("expected URL to end with %q, got %q", fx.wantPath, url)
}
```

#### Solution

Use one request-preparation function for Models, normalized Chat, Responses, and raw tunnel calls. When a concrete profile exists, require an operation, resolve the exact URL, map the selected/canonical model to the upstream model before encoding/dispatch, and apply the declared auth target header/scheme to the request-time credential already supplied by Edge without persisting credentials. Keep endpoint+Path only when no profile exists. Set explicit Chat/Responses operations in production callers and pass selected profile facts to protocol preparation.

After (representative test oracle):

```go
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    assert.Equal(t, wantURI, r.RequestURI)
    assert.Equal(t, wantAuth, r.Header.Get(wantAuthHeader))
    assert.Equal(t, wantModel, decodeModel(t, r.Body))
    requests.Add(1)
}))
```

#### Modified Files and Checklist

- [ ] `apps/node/internal/adapters/openai_compat/provider.go`: profile-backed Models request and exact header handling.
- [ ] `apps/node/internal/adapters/openai_compat/request.go`: profile operation and upstream model mapping for normalized Chat.
- [ ] `apps/node/internal/adapters/openai_compat/provider_tunnel.go`: shared profile request preparation for tunnel operations.
- [ ] `apps/node/internal/adapters/openai_compat/protocol_profile_test.go`: actual HTTP golden table for URL/header/body/count and zero-dispatch rejection.
- [ ] `apps/node/internal/adapters/openai_compat/execute_test.go`: production Execute/model-discovery regressions and legacy fallback.
- [ ] `apps/edge/internal/openai/chat_handler.go`: set `chat_completions` for concrete profile dispatch.
- [ ] `apps/edge/internal/openai/responses_handler.go`: set `responses` for concrete profile dispatch without redesigning Responses.
- [ ] `apps/edge/internal/openai/provider_dispatch_test.go`: assert caller operation, selected-candidate preparation, and legacy route compatibility.

#### Test Strategy

Replace `TestProtocolProfileOperationURLFixtures` and `TestProtocolProfileRequestHeadersAndModelMapping` with table-driven actual request tests. Each fixture must point the concrete clone to its `httptest.Server`, issue the production adapter method, require nil error, assert exact `RequestURI` rather than suffix, declared auth header, mapped JSON model, response/request count, and zero upstream requests for unsupported/empty operations. Include Models, Chat, Responses, absolute URL, legacy no-profile, and every S03 catalog variant.

#### Verification

```bash
go test -count=1 ./apps/node/internal/adapters/openai_compat -run 'Test(ProtocolProfile|LegacyEndpointPathFallback|OpenAICompat.*Models)'
go test -count=1 ./apps/edge/internal/openai -run 'Test.*(ProtocolProfile|ProviderDispatch|Responses)'
```

Expected: each fixture reaches the exact loopback URL once with the expected header/model; production callers always provide operations for profiles; unsupported operations dispatch zero requests; legacy endpoints still work.

### [REVIEW_API-4] Regenerate consumers and prove the complete user path

#### Problem

The Go protobuf contains the appended profile/operation fields, but tracked Dart output still ends `OpenAICompatAdapterConfig` at field 7 (`apps/client/lib/gen/proto/iop/runtime.pb.dart:2365-2410`). The original verification did not run `make proto-dart` or a provider-first full-cycle; `scripts/e2e-openai-lemonade.sh` currently uses legacy `model_routes` and manually configured adapters, bypassing profile normalization and Edge-generated Node config.

Before (`apps/client/lib/gen/proto/iop/runtime.pb.dart:2395-2410`):

```dart
..aI(6, _omitFieldNames ? '' : 'queueTimeoutMs')
..aI(7, _omitFieldNames ? '' : 'requestTimeoutMs')
..hasRequiredFields = false;
```

#### Solution

Install `protoc-gen-dart` outside the repository if preflight reports it missing, then run repository generators and commit all changed Dart runtime outputs. Extend the generated import test to instantiate/read field 8 and tunnel operation field 13. Convert fake Lemonade smoke config to top-level model catalog plus `nodes[].providers[]` with an explicit/custom OpenAI profile pointing at the fake upstream; assert Models and Chat hit the exact fake paths/model and no public host. Run the focused, full regression, generation reproducibility, and full-cycle commands fresh.

After (representative generated API assertion):

```dart
final adapter = OpenAICompatAdapterConfig(
  protocolProfile: ConcreteProtocolProfile(id: 'openai'),
);
expect(adapter.protocolProfile.id, 'openai');
expect(ProviderTunnelRequest(operation: 'chat_completions').operation,
    'chat_completions');
```

#### Modified Files and Checklist

- [ ] `apps/client/lib/gen/proto/iop/runtime.pb.dart`: regenerate; never hand-edit.
- [ ] `apps/client/lib/gen/proto/iop/runtime.pbjson.dart`: regenerate descriptor JSON; never hand-edit.
- [ ] `apps/client/test/iop_wire/generated_proto_import_test.dart`: assert concrete profile and operation generated APIs.
- [ ] `scripts/e2e-openai-lemonade.sh`: fake provider-first profile full-cycle with exact request assertions.
- [ ] `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/CODE_REVIEW-cloud-G09.md`: record real decisions, deviations, preflight, and stdout/stderr.

#### Test Strategy

Update the existing Dart import test and Lemonade smoke rather than creating new files. The smoke must start the repository Edge/Node entrypoints, send `/v1/models` and non-stream/stream Chat requests, and have the fake upstream reject wrong paths/models/headers. No external endpoint or credential is allowed in default fake mode.

#### Verification

```bash
command -v protoc-gen-dart || flutter pub global activate protoc_plugin
make proto
make proto-dart
cd apps/client && flutter test test/iop_wire/generated_proto_import_test.dart
cd /config/workspace/iop-s0 && IOP_LEMONADE_MODE=fake ./scripts/e2e-openai-lemonade.sh
```

Expected: generators exit zero and are reproducible, the Dart field assertions pass, and the real Edge→Node→fake-upstream provider-first execution prints the smoke PASS marker without using an external host.

## Modified Files Summary

| File | Items |
|------|-------|
| `packages/go/config/protocol_profile.go` | REVIEW_API-1 |
| `packages/go/config/normalize.go` | REVIEW_API-1 |
| `packages/go/config/validate.go` | REVIEW_API-1 |
| `packages/go/config/provider_types.go` | REVIEW_API-1 |
| `packages/go/config/protocol_profile_test.go` | REVIEW_API-1 |
| `configs/edge.yaml` | REVIEW_API-1 |
| `apps/edge/internal/node/mapper.go` | REVIEW_API-2 |
| `apps/edge/internal/node/protocol_profile_mapper_test.go` | REVIEW_API-2 |
| `apps/node/internal/adapters/factory.go` | REVIEW_API-2 |
| `apps/node/internal/adapters/protocol_profile_config_test.go` | REVIEW_API-2 |
| `apps/edge/internal/service/model_queue_types.go` | REVIEW_API-2 |
| `apps/edge/internal/service/provider_resolution.go` | REVIEW_API-2 |
| `apps/edge/internal/service/provider_pool.go` | REVIEW_API-2 |
| `apps/edge/internal/service/provider_tunnel.go` | REVIEW_API-2 |
| `apps/edge/internal/service/run_types.go` | REVIEW_API-2 |
| `apps/edge/internal/service/protocol_profile_test.go` | REVIEW_API-2 |
| `apps/node/internal/adapters/openai_compat/adapter.go` | REVIEW_API-2 |
| `apps/node/internal/adapters/openai_compat/provider.go` | REVIEW_API-3 |
| `apps/node/internal/adapters/openai_compat/request.go` | REVIEW_API-3 |
| `apps/node/internal/adapters/openai_compat/provider_tunnel.go` | REVIEW_API-3 |
| `apps/node/internal/adapters/openai_compat/protocol_profile_test.go` | REVIEW_API-3 |
| `apps/node/internal/adapters/openai_compat/execute_test.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/chat_handler.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/responses_handler.go` | REVIEW_API-3 |
| `apps/edge/internal/openai/provider_dispatch_test.go` | REVIEW_API-3 |
| `apps/client/lib/gen/proto/iop/runtime.pb.dart` | REVIEW_API-4 |
| `apps/client/lib/gen/proto/iop/runtime.pbjson.dart` | REVIEW_API-4 |
| `apps/client/test/iop_wire/generated_proto_import_test.dart` | REVIEW_API-4 |
| `scripts/e2e-openai-lemonade.sh` | REVIEW_API-4 |
| `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/CODE_REVIEW-cloud-G09.md` | REVIEW_API-4 |

## Final Verification

Go test cache is not acceptable. Use a temporary workspace `GOTMPDIR` if the host `/tmp` mount is non-executable; do not create verification tools in the repository.

```bash
command -v go && go version && go env GOROOT
command -v protoc && protoc --version
command -v flutter && flutter --version
command -v protoc-gen-dart || flutter pub global activate protoc_plugin
make proto
make proto-dart
go test -count=1 ./packages/go/config/... -run 'Test(ProtocolProfile|LegacyProviderAlias|BuiltInProtocolProfile)'
go test -count=1 ./apps/edge/internal/node ./apps/edge/internal/service -run 'Test.*ProtocolProfile|Test.*TunnelOperation'
go test -count=1 ./apps/node/internal/adapters ./apps/node/internal/adapters/openai_compat -run 'Test.*ProtocolProfile|Test.*LegacyEndpointPathFallback|Test.*OpenAICompat.*Models'
go test -count=1 ./apps/edge/internal/openai -run 'Test.*(ProtocolProfile|ProviderDispatch|Responses)'
go vet ./packages/go/...
go vet ./apps/edge/...
go vet ./apps/node/...
go test -count=1 ./packages/go/...
go test -count=1 ./apps/edge/...
go test -count=1 ./apps/node/...
cd apps/client && flutter test test/iop_wire/generated_proto_import_test.dart
cd /config/workspace/iop-s0 && IOP_LEMONADE_MODE=fake ./scripts/e2e-openai-lemonade.sh
cd /config/workspace/iop-s0 && rg -n --sort path 'joinOpenAIPath|HasSuffix\(.*"/v1"|api\.minimo\.ai|api\.anthropic\.com/anthropic/v1' apps packages configs
git diff --check
git status --short
```

Expected: generators are current; S01-S03 actual-request tests and every full Go subtree pass; Dart import test passes; the provider-first full-cycle reaches only the fake upstream and prints its PASS marker; every search result is either absent or explicitly justified in review evidence; diff check is clean; unrelated roadmap edits remain present. The existing Edge vet warning may be recorded only if it remains byte-for-byte unrelated and all task-specific checks pass.

After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
