<!-- task=m-principal-provider-credential-slot-routing/09+08_migration_attribution plan=2 tag=REVIEW_API milestone-task=compat-migration,slot-attribution -->

# Plan - Close managed credential-source validation gaps

## For the Implementing Agent

Filling implementation-owned sections in `CODE_REVIEW-*-G??.md` is mandatory. Implement the scoped change, run every verification command, record actual decisions and stdout/stderr in the active review file, keep the active PLAN/review pair in place, and report ready for review. If blocked, record only the exact blocker, attempted commands/output, and resume condition in implementation-owned evidence fields. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, write `complete.log`, or perform review finalization.

## Background

The prior review found that managed credential-plane mode still accepts static provider credentials when a custom protocol profile names a non-heuristic auth header or embeds URL user-info. This violates the approved single-source migration contract even though the submitted package, surface, race, vet, diff, and label checks pass. The follow-up is limited to closing that validation gap and proving it with fresh regression tests.

## Archive Evidence Snapshot

- The closing prior-loop artifacts will be `agent-task/m-principal-provider-credential-slot-routing/09+08_migration_attribution/plan_cloud_G09_1.log` and `agent-task/m-principal-provider-credential-slot-routing/09+08_migration_attribution/code_review_cloud_G10_1.log`; the verdict is `FAIL` with Required=1, Suggested=0, Nit=0, `review_rework_count=1`, and `evidence_integrity_failure=false`.
- Required finding: `packages/go/config/validate.go` accepts a provider header matching `RuntimeProfile.Auth.Header` when the header name is not heuristic, and it does not inspect resolved profile base/absolute-operation URLs for user-info. A temporary reviewer reproducer returned `nil` for `X-Custom-Key` plus a credential-bearing profile URL.
- Fresh reviewer verification passed config/bootstrap/input tests, OpenAI/service focused and race tests, `go vet`, `git diff --check`, and deterministic attribution-label scans. The temporary reproducer and executable temporary directory were removed.
- The predecessor remains satisfied by `agent-task/archive/2026/08/m-principal-provider-credential-slot-routing/08+03,04,07_secure_delivery/complete.log` with `verdict=PASS`. No sibling archive search is required.
- Follow-up files are limited to `packages/go/config/validate.go`, `packages/go/config/credential_plane_config_test.go`, and the active review evidence file.

## Analysis

### Files Read

- `packages/go/config/validate.go`
- `packages/go/config/credential_plane_config_test.go`
- `packages/go/config/protocol_profile.go` (protocol auth/profile declarations used by the validator)

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md`; status `[Approved]`, lock released, no user review.
- Generation header scope: `milestone-task=compat-migration,slot-attribution`.
- Targeted Acceptance Scenarios: S14 requires legacy-only behavior to remain available while enabled mode uses only the Control Plane source and rejects dual-source conflicts; S15 requires slot attribution to remain separate from inbound `token_ref` and secret-bearing labels.
- Evidence Map: S14 requires legacy/CP mode configuration and migration-table tests with dual-source rejection and Responses regression evidence. S15 requires two-slot dispatch/usage attribution plus a metric-label allowlist.
- The Required fix directly restores S14. The implementation checklist adds profile-derived conflict fixtures, while final verification reruns the existing OpenAI/service and race suites so the carried S15 evidence remains intact.

### Verification Context

- Handoff: none supplied.
- Sources read: the three files under `Files Read`; `agent-test/local/rules.md`; `agent-test/local/edge-smoke.md`; `agent-test/local/platform-common-smoke.md`; the active Milestone and approved SDD; `agent-contract/inner/edge-config-runtime-refresh.md`; `agent-contract/outer/openai-compatible-api.md`; `agent-spec/runtime/provider-pool-config-refresh.md`; `agent-spec/input/openai-compatible-surface.md`; and the exact predecessor `complete.log` cited above.
- Commands already applied by the reviewer: fresh config/bootstrap/input tests, OpenAI/service focused and race tests, `go vet ./apps/edge/... ./packages/go/...`, `git diff --check`, deterministic attribution scans, and a temporary focused reproducer.
- Preconditions: Go resolves to `/config/.local/bin/go`, reviewer verification observed Go 1.26.2 and `GOROOT=/config/opt/go`, and the checkout is intentionally dirty with prior task work that must be preserved.
- Constraint: `/tmp` is mounted `noexec`; Go commands that execute nested test binaries must use the explicit executable task temporary directory in Final Verification and remove it afterward.
- External Verification Preflight: not applicable. The fix is load-time config validation and all required evidence is repository-native; no remote host, credential, port, or live provider is needed.
- Gap and confidence: the missing profile-derived cases are isolated and reproduced. Confidence is high because the validator returned `nil` for a concrete managed-mode conflict while the broader submitted suites independently passed.

### Test Coverage Gaps

- Custom effective auth header: missing. Add a case where `RuntimeProfile.Auth.Header` is `X-Custom-Key` and `provider.Headers` contains that exact case-insensitive name.
- Resolved profile base URL user-info: missing. Add a case that rejects a credential-bearing `RuntimeProfile.BaseURL` with a safe path-only error.
- Resolved absolute operation URL user-info: missing. Add a case that rejects user-info in an absolute `RuntimeProfile.Operations` URL.
- Non-secret custom provider header: not explicitly protected from false positives. Add an accepted case whose header differs from the effective auth header.
- Legacy disabled mode and S15 attribution behavior already have tests; rerun them without changing those code paths.

### Symbol References

None. No symbol is renamed or removed.

### Split Judgment

Keep one plan. The indivisible invariant is: when `credential_plane.enabled=true`, every static provider credential source discoverable through the effective resolved profile must be rejected before listener startup, without changing disabled legacy behavior. The encoded predecessor `08` is satisfied by the exact archived PASS `complete.log` in the snapshot.

### Scope Rationale

- Exclude `apps/edge/internal/openai`, `apps/edge/internal/service`, and metric code because review found no defect in caller rejection, dispatch attribution, or label separation.
- Exclude protocol-profile schema/resolution changes; the immutable `RuntimeProfile` already exposes the effective auth header, base URL, and operations needed by validation.
- Exclude contract/spec/roadmap edits because their approved CP-only rule is correct; implementation must conform to it.
- Exclude external provider smoke and credential use because this bug is deterministic startup validation and local fixtures provide complete evidence.

### Final Routing

- `evaluation_mode=pair`; finalizer=`finalize-task-policy.sh`, mode=`pair`.
- Build closure: executable=true, data/control visibility=true, concrete fixtures=true, observable outcomes=true, verification commands=true. Scores: scope=1, state=0, blast=2, evidence=1, verification=1; grade G05; base/final route=`local-fit`; canonical file=`PLAN-local-G05.md`.
- Review closure: the same five closures are true. Scores: scope=1, state=0, blast=2, evidence=1, verification=1; route=`official-review`; grade G05; canonical file=`CODE_REVIEW-cloud-G05.md`.
- `large_indivisible_context=false`. Positive loop risks: `boundary_contract`, `structured_interpretation`, `variant_product`; count=3, so no risk boundary.
- Recovery signals: `review_rework_count=1`, `evidence_integrity_failure=false`; no recovery boundary. Capability gap: none.

## Implementation Checklist

- [ ] [REVIEW_API-1] Reject profile-declared static credential sources in managed mode, preserve disabled legacy behavior, and add focused regression coverage.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Reject profile-declared managed credential sources

**Problem:** `packages/go/config/validate.go:88-108` classifies provider headers only through generic name heuristics and checks only legacy provider URL fields. It misses a configured header that matches the resolved profile's custom auth header and misses user-info in `RuntimeProfile.BaseURL` or absolute operation URLs. `packages/go/config/credential_plane_config_test.go:67-108` has no fixtures for those sources or for an allowed non-secret custom header.

**Solution:** Derive the effective auth header from `provider.RuntimeProfile` before scanning `provider.Headers`, reject a case-insensitive header match in addition to existing heuristics, and inspect the resolved base URL and each resolved absolute operation URL with `urlContainsUserInfo`. Return only a safe provider/profile path; never include a header value, URL, username, or password. Preserve the existing disabled-mode early return and all legacy heuristics.

Before (`packages/go/config/validate.go:86-108`):

```go
for providerIndex := range node.Providers {
	provider := &node.Providers[providerIndex]
	for header := range provider.Headers {
		if isCredentialHeaderName(header) {
			return fmt.Sprintf("nodes[%d].providers[%d].headers[%q]", nodeIndex, providerIndex, header)
		}
	}
	// Existing env/arg and legacy Endpoint/BaseURL checks follow.
}
```

After:

```go
for providerIndex := range node.Providers {
	provider := &node.Providers[providerIndex]
	profile := provider.RuntimeProfile
	effectiveAuthHeader := ""
	if profile != nil {
		effectiveAuthHeader = strings.TrimSpace(profile.Auth.Header)
	}
	for header := range provider.Headers {
		if isCredentialHeaderName(header) || (effectiveAuthHeader != "" && strings.EqualFold(strings.TrimSpace(header), effectiveAuthHeader)) {
			return fmt.Sprintf("nodes[%d].providers[%d].headers[%q]", nodeIndex, providerIndex, header)
		}
	}
	// Preserve env/arg and legacy Endpoint/BaseURL checks.
	if profile != nil {
		if urlContainsUserInfo(profile.BaseURL) {
			return fmt.Sprintf("nodes[%d].providers[%d].profile[%q].base_url", nodeIndex, providerIndex, profile.ID)
		}
		for operation, rawURL := range profile.Operations {
			if urlContainsUserInfo(rawURL) {
				return fmt.Sprintf("nodes[%d].providers[%d].profile[%q].operations[%q]", nodeIndex, providerIndex, profile.ID, operation)
			}
		}
	}
}
```

**Modified Files and Checklist:**

- [ ] `packages/go/config/validate.go`: correlate provider headers with the resolved auth header and reject resolved profile URL user-info using safe paths only.
- [ ] `packages/go/config/credential_plane_config_test.go`: extend `TestCredentialPlaneEnabledRejectsEveryCompetingSource` for custom auth header, profile base URL, and absolute operation URL; add an accepted non-secret-header case and assert errors never echo fixture values.
- [ ] `agent-task/m-principal-provider-credential-slot-routing/09+08_migration_attribution/CODE_REVIEW-cloud-G05.md`: record actual decisions, deviations, commands, stdout/stderr, and exit statuses.

**Test Strategy:** Write regression tests in `packages/go/config/credential_plane_config_test.go`. Reuse `validManagedEdgeConfig` and explicit `ConcreteProtocolProfile` fixtures. The rejection table must assert the safe path and absence of the redacted fixture value. A separate acceptance test must prove that an unrelated custom header remains valid when it is neither heuristic nor the effective profile auth header. Existing disabled-mode coverage remains unchanged.

**Verification:** Run the exact Final Verification commands. The focused config tests must reject all profile-declared credential variants, accept the unrelated header, and fresh full suites must pass.

## Modified Files Summary

| File | Item |
|------|------|
| `packages/go/config/validate.go` | REVIEW_API-1 |
| `packages/go/config/credential_plane_config_test.go` | REVIEW_API-1 |
| `agent-task/m-principal-provider-credential-slot-routing/09+08_migration_attribution/CODE_REVIEW-cloud-G05.md` | REVIEW_API-1 evidence |

## Final Verification

Run from `/config/workspace/iop-s0`. Cached test output is not acceptable; every Go test uses `-count=1`.

```bash
command -v go
go version
go env GOROOT
git status --short --branch
export IOP_G05_TMPDIR=/config/workspace/iop-s0/.agent-g05-test-tmp
test ! -e "$IOP_G05_TMPDIR"
mkdir "$IOP_G05_TMPDIR"
TMPDIR="$IOP_G05_TMPDIR" go test -count=1 ./packages/go/config -run 'TestCredentialPlane'
TMPDIR="$IOP_G05_TMPDIR" go test -count=1 ./packages/go/config ./apps/edge/internal/bootstrap ./apps/edge/internal/input
TMPDIR="$IOP_G05_TMPDIR" go test -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service
TMPDIR="$IOP_G05_TMPDIR" go test -count=1 -race ./apps/edge/internal/openai ./apps/edge/internal/service
TMPDIR="$IOP_G05_TMPDIR" go vet ./apps/edge/... ./packages/go/...
git diff --check
rg --sort path -n 'X-Custom-Key|profile.*base_url|profile.*operations' packages/go/config/validate.go packages/go/config/credential_plane_config_test.go
rmdir "$IOP_G05_TMPDIR"
test ! -e "$IOP_G05_TMPDIR"
```

Expected: tool discovery identifies the configured Go toolchain; the working tree report preserves unrelated edits; all fresh tests, race tests, and vet exit `0`; `git diff --check` is silent; the deterministic scan shows only the intended safe-path implementation and fixtures; the temporary directory is absent afterward.

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