<!-- task=m-principal-provider-credential-slot-routing/04+03_principal_routes plan=4 tag=REVIEW_REVIEW_REVIEW_API -->

# Managed Surface Binding Assertion Recovery

## For the Implementing Agent

Filling implementation-owned sections of `CODE_REVIEW-cloud-G03.md` is mandatory. Run every verification command exactly, paste each command's actual stdout/stderr under its own section, keep the active files in place, and report ready for review. Only code-review may append a verdict, rename logs, write `complete.log`, archive task artifacts, or classify a user-review stop. If blocked, record exact blocker evidence, attempted commands/output, and resume conditions only in implementation-owned fields; do not ask the user, call user-input tools, create control-plane stop files, or classify the next state.

## Background

The fixture and recovery corrections now pass fresh focused, race, affected-package, and full Edge tests. The remaining gap is narrower: the successful Chat, Responses, and Messages cases do not prove the complete recorded Run/Tunnel binding, and Chat does not prove the caller-facing model echo. This follow-up adds only the missing deterministic assertions and evidence without changing production behavior or activating projection transport.

## Archive Evidence Snapshot

- Failed pair after review archive: `agent-task/m-principal-provider-credential-slot-routing/04+03_principal_routes/plan_cloud_G05_3.log` and `agent-task/m-principal-provider-credential-slot-routing/04+03_principal_routes/code_review_cloud_G05_3.log`.
- Verdict: FAIL; Required=1, Suggested=0, Nit=0; `review_rework_count=3`, `evidence_integrity_failure=false`.
- Required finding: prove the internal group, explicit provider, and exact trusted route/credential metadata on both recorded Run and Tunnel templates for Chat, Responses, and Messages, and prove Chat rewrites a served-model response echo to the public route.
- Affected implementation area: `apps/edge/internal/openai/principal_routes_test.go` and the implementation-owned evidence fields in the new review stub.
- Fresh reviewer verification: predecessor, formatting, vet, focused tests, race tests, affected packages, full `./apps/edge/...`, dormant-activation, and diff checks all pass; evidence fidelity is trusted.
- Roadmap carryover: this remains a dormant foundation packet with no Roadmap Targets; PASS must not check `model-discovery` or `explicit-selection` before authenticated projection transport activation.

## Analysis

### Files Read

- `apps/edge/internal/openai/principal_routes_test.go`
- `apps/edge/internal/openai/provider_test_support_test.go`
- `apps/edge/internal/openai/principal_routes.go`
- `apps/edge/internal/openai/provider_tunnel.go`
- `apps/edge/internal/openai/stream_gate_runtime.go`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/outer/anthropic-compatible-api.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md`; status `[승인됨]`, lock released, no `USER_REVIEW.md`.
- This dormant evidence supports S06 / `model-discovery` and S07 / `explicit-selection`; Evidence Map S04-S08 requires principal isolation and explicit no-fallback routing evidence.
- Because authenticated projection transport is not activated, this plan intentionally has no Roadmap Targets and does not claim either Milestone Task complete.

### Verification Context

- No external verification handoff was supplied. Repository-native sources were `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, the Edge/testing domain rules, the active pair and exact prior review evidence, the two test files, and the compatible API contracts.
- Local preflight: Go resolves to `/config/.local/bin/go`; `go version go1.26.2 linux/arm64`; `GOROOT=/config/opt/go`. Package verification requires no credential, network, remote runner, provider process, or live subscription.
- Every required Go test uses `-count=1`; cached results are not acceptable. The existing tests and reviewer executions prove production compilation and regression health but do not substitute for the missing field-level oracle.
- No live/full-cycle smoke is a valid oracle for this test-only dormant projection packet because the current plain connector cannot activate the cache. Confidence is high once all three surface fixtures inspect their recorded templates and the Chat echo.

### Test Coverage Gaps

- Chat asserts Run metadata and only the Tunnel model-group key; it does not assert Tunnel provider/metadata or the public response model echo.
- Responses and Messages assert only Run model-group/metadata and never inspect their recorded Tunnel requests.
- Fixture fidelity, local/native count-tokens, adverse-order recovery re-entry, generation swap, and no-fallback coverage are already present and passing.

### Symbol References

- None. This follow-up changes no production symbol or public API.

### Split Judgment

- Keep one compact test packet. One shared binding oracle must cover three protocol surfaces consistently; splitting would permit partial PASS evidence.
- Runtime predecessor `03+01,02_projection_auth` is satisfied by `agent-task/archive/2026/08/m-principal-provider-credential-slot-routing/03+01,02_projection_auth/complete.log`.

### Scope Rationale

- Modify no production Go file, shared fake, contract, spec, roadmap, Control Plane wire/store, connector/runtime activation, credential lease, provider queue accounting, or provider auth injection.
- Reuse `providerFakeRunService` snapshots and existing fixture helpers; keep the change in `principal_routes_test.go`.

### Final Routing

- `status=routed`, `evaluation_mode=isolated-reassessment`, finalizer=`finalize-task-policy.sh`, pair mode.
- Build closures are all true; scores `1+0+0+1+1=G03`; base=`local-fit`, route=`recovery-boundary`, lane=`cloud`, canonical file=`PLAN-cloud-G03.md`.
- Review closures are all true; scores `1+0+0+1+1=G03`; route=`official-review`, lane=`cloud`, adapter=`codex`, model=`gpt-5.6-sol`, reasoning=`xhigh`, canonical file=`CODE_REVIEW-cloud-G03.md`.
- `large_indivisible_context=false`; positive loop risks=`boundary_contract`, `variant_product` (2); `review_rework_count=3`; `evidence_integrity_failure=false`; recovery boundary matched; no capability gap.

## Implementation Checklist

- [ ] Assert complete Run/Tunnel binding and contract-consistent caller-facing model behavior for successful managed Chat, Responses, and Messages dispatch while retaining the fixture, count-tokens, recovery, generation-swap, and no-fallback cases.
- [ ] Run fresh formatting, vet, focused, race, affected-package, full Edge, dormant-activation, and diff verification, recording only each command's actual stdout/stderr in its own review section.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_REVIEW_REVIEW_API-1] Complete the managed surface binding oracle

**Problem:** `apps/edge/internal/openai/principal_routes_test.go:425-435` checks Chat Run metadata but only the Tunnel model-group key. The Responses and Messages cases at lines 449-487 inspect only Run values. No case asserts Tunnel provider/trusted metadata, and the Chat fixture body has no provider model field whose caller-facing echo can be verified.

**Solution:** Add a test-local assertion helper that snapshots the last Run and exactly one Tunnel request and compares `ModelGroupKey`, explicit `ProviderID`, `iop_route_id`, `iop_route_revision`, `iop_credential_slot_ref`, and `iop_credential_revision` on both templates. Call it from Chat, Responses, and Messages. Give Chat a provider response with `model: "served-model"` and assert the HTTP response contains `model: "route-public"`; keep Responses and Anthropic passthrough expectations unchanged.

Before (`apps/edge/internal/openai/principal_routes_test.go:425-435`):

```go
if fake.poolLastRun.ModelGroupKey != "catalog-internal" {
    t.Fatalf("model group=%q", fake.poolLastRun.ModelGroupKey)
}
meta := fake.poolLastRun.Metadata
if meta[routeMetaID] != "route-public" || meta[routeMetaRevision] != "4" || meta[credentialSlotMeta] != "credential-slot" || meta[credentialRevisionMeta] != "9" {
    t.Fatalf("trusted metadata=%+v", meta)
}
reqs := fake.tunnelReqsSnapshot()
if len(reqs) == 0 || reqs[len(reqs)-1].ModelGroupKey != "catalog-internal" {
    t.Fatalf("tunnel reqs=%+v", reqs)
}
```

After:

```go
assertManagedBinding := func(t *testing.T, fake *providerFakeRunService) {
    t.Helper()
    run := fake.poolLastRunSnapshot()
    tunnels := fake.tunnelReqsSnapshot()
    if len(tunnels) != 1 {
        t.Fatalf("tunnel requests=%d, want 1", len(tunnels))
    }
    assertManagedTemplate(t, run.ModelGroupKey, run.ProviderID, run.Metadata)
    assertManagedTemplate(t, tunnels[0].ModelGroupKey, tunnels[0].ProviderID, tunnels[0].Metadata)
}
```

**Modified Files and Checklist:**

- [ ] `apps/edge/internal/openai/principal_routes_test.go`: add the shared full-template assertion, apply it to Chat/Responses/Messages, and assert the Chat public model echo.

**Test Strategy:** Update `TestManagedSurfacesUseDistinctBinding`. Require exactly one provider-pool/Tunnel dispatch per subtest, exact Run/Tunnel group/provider/trusted metadata values, a Chat served-model provider response rewritten to the public route, and unchanged Responses/Anthropic passthrough success. Retain the existing recovery and count-tokens tests unchanged.

**Verification:** `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/edge/internal/openai -run 'ManagedSurfacesUseDistinctBinding'` passes.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/principal_routes_test.go` | REVIEW_REVIEW_REVIEW_API-1 |
| `agent-task/m-principal-provider-credential-slot-routing/04+03_principal_routes/CODE_REVIEW-cloud-G03.md` | REVIEW_REVIEW_REVIEW_API-1 |

## Final Verification

Run from `/config/workspace/iop-s0`. Every Go test is fresh (`-count=1`); paste actual stdout/stderr only under the matching review command.

1. `test "$(find agent-task/archive -type f -path '*/m-principal-provider-credential-slot-routing/03+01,02_projection_auth/complete.log' -print | wc -l)" -eq 1` — exactly one predecessor completion exists; exit 0.
2. `test -z "$(gofmt -d apps/edge/internal/openai/principal_routes_test.go)"` — the modified test file is formatted; exit 0 with no stdout.
3. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go vet ./apps/edge/internal/authprojection ./apps/edge/internal/openai ./apps/edge/internal/service` — no vet findings.
4. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/edge/internal/authprojection ./apps/edge/internal/openai -run 'ManagedSurfacesUseDistinctBinding|ManagedProjectionFixture|ManagedCountTokensLocalAndNative|ManagedRecoveryReentersProviderPool|GenerationSwap|NoFallback'` — managed fixture/surface/recovery cases pass.
5. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -race -count=1 ./apps/edge/internal/authprojection ./apps/edge/internal/openai -run 'ManagedSurfacesUseDistinctBinding|ManagedCountTokensLocalAndNative|ManagedRecoveryReentersProviderPool|GenerationSwap|NoFallback'` — managed surface/recovery cases pass under the race detector.
6. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/edge/internal/authprojection ./apps/edge/internal/openai ./apps/edge/internal/service` — affected package suites pass.
7. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/edge/...` — full Edge regression suite passes.
8. `if rg --sort path -n 'PrincipalProjection' apps/control-plane/internal/wire/edge_server.go apps/edge/internal/controlplane/connector.go apps/edge/internal/bootstrap/runtime.go; then exit 1; else exit 0; fi` — unauthenticated runtime remains dormant; exit 0 with no stdout.
9. `git diff --check` — no whitespace errors.

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