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

# Protocol Profile Core와 operation 기반 runtime 경계

## For the Implementing Agent

`CODE_REVIEW-cloud-G08.md`의 구현 담당 섹션을 채우는 것이 구현의 필수 마지막 단계다. 아래 검증을 실제로 실행하고 실제 메모와 stdout/stderr를 기록한 뒤 active 파일을 그대로 둔 채 review 준비 완료를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review skill만 수행한다. 막히면 구현 담당 evidence 필드에 정확한 blocker, 시도한 명령/출력, 재개 조건만 기록하며 사용자 질문, user-input tool, control-plane stop file, 다음 상태 분류, archive/complete 처리는 하지 않는다.

## Background

현재 provider-first config는 vendor type과 endpoint만 Node로 전달하고, Node adapter는 `/v1` suffix heuristic과 하드코딩된 Chat/Models path를 사용한다. 이 계획은 reusable profile overlay를 config load에서 concrete snapshot으로 해소하고, 선택된 profile과 operation id가 Edge-Service-Wire-Node-Adapter 경계를 끝까지 통과하도록 만든다. 이 경계가 완료되어야 후속 Messages ingress가 vendor 이름 분기 없이 native/bridge를 선택할 수 있다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operational-observability-provider-management/milestones/multi-provider-protocol-profile-native-messages.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/operational-observability-provider-management/milestones/multi-provider-protocol-profile-native-messages.md)
- Task ids:
  - `profile-contract`: 공통 provider profile과 immutable overlay/runtime 계약
  - `path-resolution`: profile operation 기반 URL resolver
  - `profile-catalog`: built-in/custom profile catalog와 admission
- 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/index.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-spec/index.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/config.go`
- `packages/go/config/normalize.go`
- `packages/go/config/validate.go`
- `packages/go/config/load.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`
- `packages/go/agentruntime/types.go`
- `apps/edge/internal/node/mapper.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/run_wire.go`
- `apps/node/internal/node/tunnel_handler.go`
- `apps/node/internal/adapters/config_set.go`
- `apps/node/internal/adapters/config_set_test.go`
- `apps/node/internal/adapters/factory.go`
- `apps/node/internal/adapters/openai_compat/adapter.go`
- `apps/node/internal/adapters/openai_compat/provider.go`
- `apps/node/internal/adapters/openai_compat/provider_tunnel.go`
- `apps/node/internal/adapters/openai_compat/request.go`
- `apps/node/internal/adapters/openai_compat/stream.go`
- `apps/node/internal/adapters/openai_compat/capabilities_test.go`
- `apps/node/internal/adapters/openai_compat/execute_test.go`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/multi-provider-protocol-profile-native-messages/SDD.md`, 상태 `[승인됨]`, 잠금 해제.
- S01 / `profile-contract`: valid overlay는 concrete profile이 되고 cycle, unknown base, conflicting driver, invalid operation/auth/capability 조합은 config load에서 실패해야 한다.
- S02 / `path-resolution`: `/v1`, `/v1beta/openai`, `/api/paas/v4`, `/anthropic/v1` fixture와 절대 URL이 prefix 손실/중복 없이 resolve되어야 한다.
- S03 / `profile-catalog`: OpenAI, Gemini, Anthropic, GLM, Kimi, MiniMax, MiMo, Grok, Seulgi Chat/Messages profile이 선언한 operation만 admission되고 header/model/path fixture가 일치해야 한다.
- Evidence Map S01-S03의 `config validation`, `URL resolver`, `provider request golden tests`를 각 구현 item과 final verification에 직접 배치했다. provider별 table 결과는 focused Go test stdout으로 남긴다.

### Verification Context

- 별도 handoff는 없었다.
- 읽은 검증 source: `agent-test/local/rules.md`, `agent-test/local/profiles/edge-smoke.md`, `agent-test/local/profiles/node-smoke.md`, `agent-test/local/profiles/platform-common-smoke.md`, `agent-ops/rules/project/domain/edge.md`, `agent-ops/rules/project/domain/node.md`, `agent-ops/rules/project/domain/platform-common.md`, `agent-ops/rules/project/domain/testing.md`.
- preflight 결과: `/config/.local/bin/go`, `go1.26.2 linux/arm64`, `GOROOT=/config/opt/go`; repository module 기준은 Go 1.24이며 현재 runner가 상위 버전이다.
- workspace `/config/workspace/iop-s0`, branch `feature/multi-provider-protocol-profile-native-messages`, HEAD `174a2a6b1375bd71c3a977aff91e6afb9c26bd58`.
- 분석 시 dirty state는 roadmap 문서 4건의 사용자 변경뿐이었다. 구현자는 이를 보존하고 plan 소유 파일 밖을 되돌리지 않는다.
- 기준 명령은 `make proto`, focused package test, `go vet ./packages/go/...`, `go vet ./apps/edge/...`, `go vet ./apps/node/...`, 각 subtree fresh test, `git diff --check`다. fresh evidence가 필요하므로 Go test cache는 허용하지 않고 `-count=1`을 사용한다.
- 외부 provider/credential은 S01-S03 evidence에 필요하지 않다. live smoke는 qualification Epic S10 범위이므로 이 plan의 blocker나 PASS 조건이 아니다.
- confidence: 높음. config→mapper→proto→Node config→adapter와 service candidate/tunnel call path를 모두 읽었고, repository-native httptest/table fixture로 network 없이 판정 가능하다.

### Test Coverage Gaps

- 현재 overlay/base cycle/concrete snapshot schema test가 없다: `packages/go/config/protocol_profile_test.go`를 신설한다.
- 현재 `joinOpenAIPath` test는 `/v1` 중복만 다루고 vendor prefix/absolute operation을 다루지 않는다: 기존 `execute_test.go`의 helper 직접 test를 concrete profile resolver 회귀로 교체하고 provider별 table test를 신설한다.
- 현재 mapper/proto/config-set은 profile 필드를 검증하지 않는다: Edge mapper, Node config fingerprint, Node tunnel operation focused test를 각각 신설한다.
- 현재 service candidate callback은 lifecycle capability만 노출한다: selected concrete profile 복제와 operation propagation test를 신설한다.

### Symbol References

- 제거 대상 `joinOpenAIPath`: 호출 위치는 `apps/node/internal/adapters/openai_compat/provider.go:88`, `request.go:28`, `provider_tunnel.go:25`; 직접 test 위치는 `execute_test.go:533`이다. 모두 `ConcreteProtocolProfile.ResolveOperationURL` 또는 adapter operation resolver로 교체한다.
- `ProviderTunnelRequest.Path`는 제거하지 않는다. 구버전 Edge/Node payload 호환 fallback으로 유지하고 새 `Operation`이 있으면 profile path가 우선한다.
- 기존 `prepareTunnelFunc` 시그니처는 바꾸지 않는다. selected profile이 필요한 Messages용으로 별도 `PrepareProtocolTunnel` hook을 추가해 기존 Chat/Responses와 stream-gate call site를 깨지 않는다.

### Split Judgment

- 전체 Milestone은 세 plan으로 split한다.
- `01_protocol_core`의 stable contract는 config load에서 확정된 concrete profile, operation-aware service/wire DTO, Node adapter URL resolver다. PASS evidence는 S01-S03 table/focused tests와 전체 Go regression이다. 선행 dependency는 없다.
- `02+01_messages_ingress`는 이 plan의 concrete profile/selected-candidate hook을 소비하며 S05-S07 direct Edge evidence로 닫는다.
- `03+02_contract_responses_sync`는 구현 source of truth가 완성된 뒤 S04/S08 contract drift와 Responses/Pi regression을 닫는다.
- 이 plan 자체는 config, wire, Edge service, Node adapter가 하나의 concrete profile invariant를 공유해야 하므로 더 쪼개면 중간 PASS에서 raw path/vendor heuristic이 다시 source of truth가 된다. 각 item은 같은 plan 안에서 순서대로 검증한다.

### Scope Rationale

- `apps/edge/internal/openai`의 public Messages handler와 bridge는 후속 `02+01_messages_ingress` 소유로 제외한다.
- contract/spec 원문 갱신과 Responses migration은 구현 경로가 모두 존재한 뒤 수행해야 하므로 `03+02_contract_responses_sync`로 제외한다.
- `agent-client/claude/iop-claude-gateway.py`는 migration reference일 뿐 수정하거나 production hop으로 확장하지 않는다.
- credential 저장/rotation, qualification live smoke, fallback/marketplace는 Milestone의 후속 Epic 또는 비목표다.

### Final Routing

- status: `routed`; evaluation_mode: `first-pass`; finalizer: `finalize-task-policy.sh` (`pair`).
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`. capability gap 없음.
- build grade scores: scope_coupling=2, state_concurrency=1, blast_irreversibility=2, evidence_diagnosis=1, verification_complexity=2 → G08.
- build route: base=`local-fit`, large_indivisible_context=`false`, matched loop risks=`boundary_contract`, `structured_interpretation`, `variant_product` (3), review_rework_count=0, evidence_integrity_failure=false, recovery signals 없음, route=`local-fit`.
- review closures: 모두 `true`; grade scores 2/1/2/1/2 → G08; route=`official-review`, cloud, Codex `gpt-5.6-sol`, xhigh.
- canonical files: `PLAN-local-G08.md`, `CODE_REVIEW-cloud-G08.md`.

## Implementation Checklist

- [ ] Define and validate reusable protocol profile overlays, built-in/custom catalog entries, legacy alias normalization, and immutable concrete provider snapshots with S01/S03 table tests.
- [ ] Carry the concrete profile and operation id through protobuf generation, Edge mapper/service candidate selection, Node config fingerprints, and tunnel DTOs while retaining legacy path fallback.
- [ ] Replace hardcoded Models/Chat/tunnel URL assembly with profile operation admission and exact relative/absolute URL resolution, then prove all S02 provider fixtures.
- [ ] Run focused and full local verification with fresh Go test results and preserve unrelated roadmap changes.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [API-1] Protocol profile schema, overlay resolution, catalog

#### Problem

`EdgeConfig` has only provider pool/nodes/models (`packages/go/config/edge_types.go:30-53`), while `NodeProviderConf` stores type/endpoint/header fields directly (`packages/go/config/provider_types.go:27-95`). There is no reusable profile id, base overlay, driver/operation/auth/capability validation, concrete snapshot, or model-level deterministic token counter.

Before (`packages/go/config/edge_types.go:30-53`, `packages/go/config/provider_types.go:82-94`):

```go
type EdgeConfig struct {
    ProviderPool EdgeProviderPoolConf
    Nodes        []NodeDefinition
    Models       []ModelCatalogEntry
}

type NodeProviderConf struct {
    Provider string
    Endpoint string
    BaseURL  string
    Headers  map[string]string
}
```

#### Solution

Add a focused config domain file and keep root/provider integration in existing structs. The catalog combines immutable built-ins with top-level custom overlays, resolves at most one `base`, deep-copies maps/slices/extensions, rejects cycles/unknown bases/driver conflicts, and writes only a `ConcreteProtocolProfile` snapshot onto each provider. `profile` is the new explicit selector; legacy provider types normalize to the matching built-in profile only when no explicit selector exists. Add a model catalog token-counter declaration for Chat-only count-tokens without upstream calls.

After (representative API):

```go
// packages/go/config/protocol_profile.go
package config

import (
    "fmt"
    "net/url"
    "sort"
    "strings"
)

type ProtocolDriver string

const (
    ProtocolDriverOpenAIChat        ProtocolDriver = "openai_chat"
    ProtocolDriverAnthropicMessages ProtocolDriver = "anthropic_messages"
    ProtocolDriverOpenAIResponses   ProtocolDriver = "openai_responses"
)

type ProtocolProfileConf struct {
    Base         string                 `mapstructure:"base" yaml:"base,omitempty"`
    Driver       ProtocolDriver         `mapstructure:"driver" yaml:"driver,omitempty"`
    BaseURL      string                 `mapstructure:"base_url" yaml:"base_url,omitempty"`
    Operations   map[string]string      `mapstructure:"operations" yaml:"operations,omitempty"`
    Auth         ProtocolAuthConf       `mapstructure:"auth" yaml:"auth,omitempty"`
    Capabilities []string               `mapstructure:"capabilities" yaml:"capabilities,omitempty"`
    ModelMapping map[string]string      `mapstructure:"model_mapping" yaml:"model_mapping,omitempty"`
    Extensions   map[string]any         `mapstructure:"extensions" yaml:"extensions,omitempty"`
}

type ConcreteProtocolProfile struct {
    ID string
    ProtocolProfileConf
}
```

`EdgeConfig.ProtocolProfiles`, `NodeProviderConf.Profile`, excluded-from-YAML `NodeProviderConf.RuntimeProfile`, and `ModelCatalogEntry.TokenCounter` are added. `normalize.go` resolves aliases/overlays before `validate.go` checks provider references; `configs/edge.yaml` documents built-in ids and one custom overlay without secrets.

#### Modified Files and Checklist

- [ ] `packages/go/config/edge_types.go`: add top-level `protocol_profiles` catalog.
- [ ] `packages/go/config/provider_types.go`: add provider `profile` selector and concrete runtime snapshot field.
- [ ] `packages/go/config/protocol_profile.go`: add drivers, operations, auth/capability schema, built-ins, overlay resolver, deep clone, URL validation, legacy alias mapping.
- [ ] `packages/go/config/normalize.go`: resolve catalog and provider snapshots once during load normalization.
- [ ] `packages/go/config/validate.go`: reject invalid profile references/combinations before runtime construction.
- [ ] `packages/go/config/protocol_profile_test.go`: add S01/S03 normal/boundary/table tests.
- [ ] `configs/edge.yaml`: document built-in/custom profile use and token counter without real endpoint secrets.

#### Test Strategy

Write `TestProtocolProfileOverlayResolution`, `TestProtocolProfileOverlayRejected`, `TestLegacyProviderAliasProfileNormalization`, and `TestBuiltInProtocolProfileCatalog` in `packages/go/config/protocol_profile_test.go`. Fixtures cover one-level/multi-level overlays, deep-copy immutability, cycle, unknown base, driver conflict, malformed absolute/relative path, invalid auth/capability, explicit profile precedence, every named built-in, unsupported operation, and custom overlay. Existing provider catalog load tests remain regression coverage.

#### Verification

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

Expected: all S01/S03 cases pass; invalid fixtures fail with field/profile id in the error.

### [API-2] Concrete profile wire and selected-candidate contract

#### Problem

The wire sends only provider label/endpoint/headers (`proto/iop/runtime.proto:323-331`) and raw tunnel `path` (`proto/iop/runtime.proto:65-78`). Mapper compilation (`apps/edge/internal/node/mapper.go:194-210`) and Node config decode (`apps/node/internal/adapters/factory.go:44-55`) cannot preserve a validated driver/operation/capability snapshot. Service admission exposes only served model, provider id, execution path, and lifecycle capabilities (`apps/edge/internal/service/provider_pool.go:40-54`).

Before:

```proto
message ProviderTunnelRequest {
  string path = 6;
}

message OpenAICompatAdapterConfig {
  string provider = 1;
  string endpoint = 2;
  map<string, string> headers = 3;
}
```

#### Solution

Define a concrete-only protobuf profile with no `base` field, append it to `OpenAICompatAdapterConfig`, and append `operation` to `ProviderTunnelRequest` without renumbering existing fields. Generate `runtime.pb.go` via `make proto`. Mapper and Node config conversion deep-copy the snapshot; ConfigSet fingerprint naturally includes it. Extend internal `candidateNode`, `ProviderPoolCandidate`, and `RunDispatch` with copied profile id/driver/capability data, and add a new `PrepareProtocolTunnel(req, candidate)` hook while preserving the old hook signature. Service re-resolution must refresh profile fields from `NodeProviderConf` exactly as it refreshes capacity/type.

After (representative wire):

```proto
message ConcreteProtocolProfile {
  string id = 1;
  string driver = 2;
  string base_url = 3;
  map<string, string> operations = 4;
  ProtocolAuth auth = 5;
  repeated string capabilities = 6;
  map<string, string> model_mapping = 7;
  google.protobuf.Struct extensions = 8;
}

message ProviderTunnelRequest {
  // fields 1..12 unchanged
  string operation = 13;
}

message OpenAICompatAdapterConfig {
  // fields 1..7 unchanged
  ConcreteProtocolProfile protocol_profile = 8;
}
```

#### Modified Files and Checklist

- [ ] `proto/iop/runtime.proto`: add concrete profile/auth messages and append-only profile/operation fields.
- [ ] `proto/gen/iop/runtime.pb.go`: regenerate, never hand-edit.
- [ ] `apps/edge/internal/node/mapper.go`: map resolved concrete profile into provider adapter payload.
- [ ] `apps/edge/internal/node/protocol_profile_mapper_test.go`: prove no `base` state crosses wire and maps are copied.
- [ ] `apps/edge/internal/service/model_queue_types.go`: carry concrete profile in candidate state.
- [ ] `apps/edge/internal/service/provider_resolution.go`: populate/refresh candidate profile and classify tunnel path from driver instead of vendor name when present.
- [ ] `apps/edge/internal/service/provider_pool.go`: expose immutable profile facts, new protocol-aware prepare hook, and dispatch metadata while retaining existing hook behavior.
- [ ] `apps/edge/internal/service/provider_tunnel.go`: append operation to built wire request.
- [ ] `apps/edge/internal/service/protocol_profile_test.go`: verify candidate copy, re-resolution, callback, unsupported profile admission, and operation wire propagation.
- [ ] `packages/go/agentruntime/types.go`: append `Operation` to runtime tunnel request.
- [ ] `apps/node/internal/node/tunnel_handler.go`: copy protobuf operation into runtime request.
- [ ] `apps/node/internal/node/protocol_profile_tunnel_test.go`: prove operation survives parser/handler boundary and legacy path still works.
- [ ] `packages/go/config/adapter_types.go`: carry concrete profile in `OpenAICompatConf`.
- [ ] `apps/node/internal/adapters/factory.go`: decode protobuf concrete profile with deep copies.
- [ ] `apps/node/internal/adapters/config_set.go`: include the concrete snapshot in adapter construction/fingerprint.
- [ ] `apps/node/internal/adapters/protocol_profile_config_test.go`: verify profile changes produce config update and inherited state is absent.

#### Test Strategy

Add focused normal/boundary tests for protobuf round-trip, mapper deep copy, config refresh fingerprint change, selected candidate callback, queue re-resolution after profile refresh, and legacy payloads with no profile. Existing field numbers and Path semantics are asserted to preserve mixed-version fallback.

#### Verification

```bash
make proto
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/node -run 'Test.*ProtocolProfile|Test.*TunnelOperation'
```

Expected: generated code is current; concrete profile and operation survive every boundary; legacy no-profile payload remains accepted.

### [API-3] Operation admission and exact URL resolution

#### Problem

Models and Chat paths are hardcoded in `provider.go:88` and `request.go:28`; raw tunnel calls `joinOpenAIPath` (`provider_tunnel.go:25`). That helper only strips duplicate `/v1` suffixes (`stream.go:217-230`) and cannot express Gemini `/v1beta/openai`, GLM `/api/paas/v4`, absolute operations, or unsupported-operation rejection.

Before (`apps/node/internal/adapters/openai_compat/stream.go:217-230`):

```go
func joinOpenAIPath(baseURL, path string) string {
    u, err := url.Parse(baseURL)
    if err != nil {
        return strings.TrimRight(baseURL, "/") + path
    }
    basePath := strings.TrimRight(u.Path, "/")
    if strings.HasSuffix(basePath, "/v1") && strings.HasPrefix(path, "/v1/") {
        path = strings.TrimPrefix(path, "/v1")
    }
    u.Path = basePath + path
    return u.String()
}
```

#### Solution

Use one resolver owned by the validated concrete profile: an absolute operation URL is returned unchanged; a relative path is joined once to normalized `base_url`; missing operations return a typed unsupported-operation error before HTTP dispatch. Adapter helpers ask for `models`, `chat_completions`, `messages`, `count_tokens`, or `responses`; only a no-profile legacy payload falls back to endpoint plus existing paths. Remove vendor/suffix heuristics and retain query components according to the declared operation URL.

After (representative adapter call):

```go
urlStr, err := a.operationURL(req.Operation, req.Path)
if err != nil {
    _ = emitTunnelError(ctx, sink, req, 0, err)
    return err
}
httpReq, err := http.NewRequestWithContext(ctx, req.Method, urlStr, bytes.NewReader(req.Body))
```

#### Modified Files and Checklist

- [ ] `apps/node/internal/adapters/openai_compat/adapter.go`: retain a cloned concrete profile and central operation resolver.
- [ ] `apps/node/internal/adapters/openai_compat/provider.go`: resolve `models` operation.
- [ ] `apps/node/internal/adapters/openai_compat/request.go`: resolve `chat_completions` operation.
- [ ] `apps/node/internal/adapters/openai_compat/provider_tunnel.go`: prefer operation id, reject unsupported operations, retain legacy Path fallback only when profile absent.
- [ ] `apps/node/internal/adapters/openai_compat/stream.go`: remove `joinOpenAIPath` and unused URL heuristic imports.
- [ ] `apps/node/internal/adapters/openai_compat/execute_test.go`: replace helper-specific `/v1` test with public operation resolver compatibility assertions.
- [ ] `apps/node/internal/adapters/openai_compat/protocol_profile_test.go`: add OpenAI/Gemini/Anthropic/GLM/Kimi/MiniMax/MiMo/Grok/Seulgi and absolute URL request fixtures.

#### Test Strategy

Write `TestProtocolProfileOperationURLFixtures`, `TestProtocolProfileUnsupportedOperationRejectedBeforeDispatch`, `TestProtocolProfileRequestHeadersAndModelMapping`, and `TestLegacyEndpointPathFallback`. Each httptest server asserts final `RequestURI`, header declaration, served model rewrite, and zero requests for unsupported operations. This is the S02 provider table evidence.

#### Verification

```bash
go test -count=1 ./apps/node/internal/adapters/openai_compat -run 'Test(ProtocolProfile|LegacyEndpointPathFallback)'
```

Expected: every named provider fixture reaches the exact declared URL once; unsupported operation makes zero upstream requests.

### [API-4] Whole-path compatibility and evidence

#### Problem

The feature spans config load, generated wire, service admission and Node HTTP dispatch. Focused unit tests alone would miss a stale generated file, an adapter fingerprint omission, or a legacy provider alias regression.

#### Solution

Run fresh focused tests after each item, then all package/Edge/Node vet and tests. Inspect the diff for generated-code-only changes and ensure no production `joinOpenAIPath` or vendor-type protocol decision remains. Do not alter the user's unrelated roadmap edits.

#### Modified Files and Checklist

- [ ] `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/CODE_REVIEW-cloud-G08.md`: record actual implementation decisions, deviations, and full command output.

#### Test Strategy

No additional test file: this item executes the repository-level regression contract after API-1 through API-3 tests are present. Fresh test output is required; cached results are not accepted.

#### Verification

```bash
command -v go && go version && go env GOROOT
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/...
rg -n --sort path 'joinOpenAIPath|HasSuffix\(.*"/v1"' apps/node/internal/adapters/openai_compat packages/go/config
git diff --check
```

Expected: all vet/tests pass; `rg` has no production heuristic matches (test migration notes may be intentional and must be recorded); diff check is clean.

## Modified Files Summary

| File | Items |
|------|-------|
| `packages/go/config/edge_types.go` | API-1 |
| `packages/go/config/provider_types.go` | API-1 |
| `packages/go/config/protocol_profile.go` | API-1, API-3 |
| `packages/go/config/normalize.go` | API-1 |
| `packages/go/config/validate.go` | API-1 |
| `packages/go/config/protocol_profile_test.go` | API-1 |
| `configs/edge.yaml` | API-1 |
| `proto/iop/runtime.proto` | API-2 |
| `proto/gen/iop/runtime.pb.go` | API-2 |
| `apps/edge/internal/node/mapper.go` | API-2 |
| `apps/edge/internal/node/protocol_profile_mapper_test.go` | API-2 |
| `apps/edge/internal/service/model_queue_types.go` | API-2 |
| `apps/edge/internal/service/provider_resolution.go` | API-2 |
| `apps/edge/internal/service/provider_pool.go` | API-2 |
| `apps/edge/internal/service/provider_tunnel.go` | API-2 |
| `apps/edge/internal/service/protocol_profile_test.go` | API-2 |
| `packages/go/agentruntime/types.go` | API-2 |
| `apps/node/internal/node/tunnel_handler.go` | API-2 |
| `apps/node/internal/node/protocol_profile_tunnel_test.go` | API-2 |
| `packages/go/config/adapter_types.go` | API-2 |
| `apps/node/internal/adapters/factory.go` | API-2 |
| `apps/node/internal/adapters/config_set.go` | API-2 |
| `apps/node/internal/adapters/protocol_profile_config_test.go` | API-2 |
| `apps/node/internal/adapters/openai_compat/adapter.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/provider.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/request.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/provider_tunnel.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/stream.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/execute_test.go` | API-3 |
| `apps/node/internal/adapters/openai_compat/protocol_profile_test.go` | API-3 |
| `agent-task/m-multi-provider-protocol-profile-native-messages/01_protocol_core/CODE_REVIEW-cloud-G08.md` | API-4 |

## Final Verification

Run in this order; Go test cache is not acceptable:

```bash
command -v go && go version && go env GOROOT
make proto
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/node -run 'Test.*ProtocolProfile|Test.*TunnelOperation'
go test -count=1 ./apps/node/internal/adapters/openai_compat -run 'Test(ProtocolProfile|LegacyEndpointPathFallback)'
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/...
rg -n --sort path 'joinOpenAIPath|HasSuffix\(.*"/v1"' apps/node/internal/adapters/openai_compat packages/go/config
git diff --check
```

Expected: proto generation is reproducible, focused S01-S03 evidence and all regressions pass, heuristic search has no unclassified production result, and diff check is clean.

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