<!-- task=m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual plan=6 tag=REVIEW_REVIEW_TEST milestone-task=hot-smoke -->

# Provider Credential Boundary and Pilot Evidence Integrity Closure

## For the Implementing Agent

Filling the implementation-owned sections in `CODE_REVIEW-cloud-G05.md` is mandatory. Execute this plan without changing its ownership or scope, run every verification command, paste actual stdout/stderr and exit status into the review artifact, keep both active files in place, and report ready for review. If blocked, record only the exact blocker, attempted command/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`, update roadmap state, or rerun the external Claude/Pi pilot; finalization belongs to the code-review skill.

## Background

The bounded 2x2 Claude/Pi pilot reached the local Edge/Node, but review found two trust-boundary defects in the retained evidence. Its cleanup command used unavailable `ss` in a pipeline that still returned success, and its Pi setup selected inbound `Authorization` as the legacy provider credential source even though the active OpenAI/Anthropic contracts require a distinct provider token header. This follow-up closes those repository-fixable defects. It does not rerun the external agents, does not rehabilitate the two Pi rows, and does not claim S16 or `hot-smoke` completion.

## Archive Evidence Snapshot

- `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/plan_cloud_G06_5.log` and `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/code_review_cloud_G06_5.log` are the immediately preceding pair. The review ended `FAIL` with `review_rework_count=5` and `evidence_integrity_failure=true`.
- Required R1: the archived cleanup command invokes unavailable `ss` without a fail-closed pipeline. Fresh review reproduction emitted `ss: command not found` while the surrounding test returned success, so the prose claiming zero listeners is invalid evidence.
- Required R2: the archived pilot set `provider_auth.from_header: "Authorization"`, contrary to the active contract that separates inbound IOP authentication from the request-time provider token. The retained `pi:direct` and `pi:repair` `401` rows are setup-invalid and must not be represented as provider or Hot Path diagnostics.
- The two Claude rows remain bounded client-preflight diagnostics (`GET /v1/models/<model_id>` returned 404). The complete S16 direct/pass/repair/failure/cancel matrix remains open; this task is only a `milestone-task=hot-smoke` contribution.

## Analysis

### Files Read

- `packages/go/config/validate.go` — complete configuration validation implementation, including `normalizeOpenAIProviderAuth`.
- `packages/go/config/edge_openai_config_test.go` — complete Edge OpenAI configuration regression suite and existing provider-auth default/override/blank-header tests.
- `packages/go/config/load.go:1-105` — `LoadEdge` import and normalization order for `normalizeOpenAIProviderAuth`.
- `apps/edge/internal/openai/provider_tunnel.go:189-218` — runtime provider-token forwarding path and its explicit non-reuse invariant.
- `agent-contract/outer/openai-compatible-api.md:80-87`, `agent-contract/outer/anthropic-compatible-api.md:45-51,80-86`, and `agent-contract/inner/edge-config-runtime-refresh.md:42-49` — inbound caller-auth and legacy provider-auth separation contracts.
- `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/plan_cloud_G06_5.log` and `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/code_review_cloud_G06_5.log` — selected pilot setup, retained rows, cleanup transcript, verdict, and R1/R2.
- `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/20+17,19_smoke_harness/complete.log` — direct split-predecessor completion evidence.
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-hot-path-one-shot-execution.md` and `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md` — active Milestone, S16, and evidence map.
- `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-test/local/node-smoke.md`, and `agent-test/local/platform-common-smoke.md` — local validation, race, isolation, and secret-handling requirements.

### Root Cause Selection

- R1 is an evidence-oracle defect: a missing executable was hidden by pipeline exit semantics, and the implementation recorded reconstructed prose rather than exact output.
- R2 is a configuration-boundary defect: runtime code assumes `from_header` is distinct from caller authentication, but configuration validation currently accepts `Authorization` and `X-Api-Key` case-insensitively.
- The selected production fix is validation at configuration admission. Do not weaken caller authentication, infer credentials in runtime code, or special-case the archived pilot.

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`, status `[승인됨]`, implementation lock released.
- Milestone metadata: `milestone-task=hot-smoke`.
- S16 requires actual Claude/Pi direct/pass/repair/failure/cancel evidence. This plan only prevents an invalid credential setup and repairs evidence integrity; it cannot close S16.
- Evidence remains raw-content- and credential-free. The archived Pi rows are explicitly invalidated rather than reinterpreted.

### Verification Context

- Source checkout: `/config/workspace/iop-s0`, branch `feature/iop-hot-path-one-shot-execution`, reviewed HEAD `703f3b723202959185c04bb32c2c68383b8d04a0`; preserve unrelated dirty-worktree changes.
- Execution checkout: `/config/workspace/iop-s2`; the reviewed transient root was `/config/workspace/iop-s2/.hot-path-short.lLH4MI` and is currently absent.
- `ss` is not installed in this environment. `/proc/net/tcp` and `/proc/net/tcp6` are available and expose LISTEN state `0A`, so they are the deterministic selected-port oracle.
- The four reviewed ports are decimal `28081`, `29090`, `29091`, and `29092`, represented as hexadecimal `6DB1`, `71A2`, `71A3`, and `71A4` in `/proc/net/tcp{,6}`.
- The existing Pi profile is only a presence-only source for exact-secret/endpoint retention scans. Never print its `apiKey` or `baseUrl`, and do not modify `/config/.pi/agent/models.json`.
- No external verification context is required. The prior authorization remains recorded, but this follow-up intentionally does not start Edge/Node or invoke Claude/Pi.

### Test Coverage Gaps

- Existing tests cover provider-auth defaults, custom headers, and blank headers but do not reject inbound caller-auth header names.
- The archived cleanup transcript did not prove executable availability, process absence, selected-port absence, or exact secret/endpoint retention with authentic output.
- This plan adds config regression coverage and a deterministic cleanup/evidence transcript. It does not add actual-agent coverage or alter S16 status.

### Symbol References

- `normalizeOpenAIProviderAuth` is called only by `LoadEdge` in `packages/go/config/load.go`.
- No public symbol is renamed or removed. A private helper may be added next to `normalizeOpenAIProviderAuth` for the case-insensitive inbound-header classification.

### Split Judgment

Keep one compact follow-up. Configuration admission and evidence reclassification jointly close the same provider-credential boundary, while the deterministic cleanup probe closes the paired evidence-integrity failure. Splitting would duplicate the same archived pilot context without enabling independent completion.

### Scope Rationale

Modify only `packages/go/config/validate.go`, `packages/go/config/edge_openai_config_test.go`, and implementation-owned sections of `CODE_REVIEW-cloud-G05.md`. Do not edit runtime forwarding, contracts, specs, roadmap files, shell harnesses, installed agents, Pi global configuration, iop-s2 tracked files, or unrelated dirty-worktree files. Do not start a runtime, use a provider credential for a request, or rewrite archived logs.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`; `status=routed`.
- Build closures: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`; capability gap none. Scores `1/1/0/2/1 = G05`. Base `local-fit`; `large_indivisible_context=false`; matched loop risks `temporal_state`, `boundary_contract`, `structured_interpretation`, `variant_product` (4); `review_rework_count=5`; `evidence_integrity_failure=true`; recovery boundary matched. Final route `recovery-boundary`, cloud, `PLAN-cloud-G05.md`.
- Review closures: all six true; capability gap none. Scores `1/1/0/2/1 = G05`. Route `official-review`, cloud, Codex `gpt-5.6-sol` xhigh, `CODE_REVIEW-cloud-G05.md`.

## Findings Resolution Map

| Finding | Resolution | Owner Files | Changed Preconditions / Verification |
|---------|------------|-------------|--------------------------------------|
| R1 | Direct fix | `CODE_REVIEW-cloud-G05.md` | Replace the unavailable-`ss` false-pass with availability-checked root/worktree/process and `/proc/net/tcp{,6}` listener probes; record exact output and exit status plus exact secret/endpoint retention counts. |
| R2 | Direct fix | `packages/go/config/validate.go`, `packages/go/config/edge_openai_config_test.go`, `CODE_REVIEW-cloud-G05.md` | Reject `Authorization` and `X-Api-Key` case-insensitively as provider credential source headers, preserve default/custom dedicated headers, and state that the archived Pi `401` rows are setup-invalid and provide no S16 evidence. |

## Dependencies and Execution Order

1. The split predecessor `20+17,19_smoke_harness` is complete at `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/20+17,19_smoke_harness/complete.log`.
2. Implement REVIEW_REVIEW_TEST-1 before running config tests so invalid caller-auth aliases fail at load time.
3. Run REVIEW_REVIEW_TEST-2 after the code change; its probes are read-only and must not depend on `ss`, a live runtime, or reconstructed output.
4. Fill the review artifact last, explicitly invalidating the archived Pi rows and withholding S16 completion.

## Implementation Checklist

- [ ] [REVIEW_REVIEW_TEST-1] Add fail-closed provider-auth header separation in Edge config admission and focused regression coverage for case-insensitive caller-auth collisions while preserving dedicated default/custom headers.
- [ ] [REVIEW_REVIEW_TEST-2] Replace the false-pass cleanup claim with exact deterministic root/worktree/process/port and credential-retention evidence, and explicitly classify both archived Pi rows as setup-invalid with no S16 credit.
- [ ] Fill implementation-owned sections in `CODE_REVIEW-cloud-G05.md` with actual implementation notes and exact verification output.

### [REVIEW_REVIEW_TEST-1] Fail-Closed Provider Credential Header Separation

#### Problem

`normalizeOpenAIProviderAuth` trims and defaults `from_header` but accepts the same headers used for inbound caller authentication. That makes the runtime comment and active OpenAI/Anthropic contracts unenforceable at configuration admission and allowed the pilot to reuse an IOP bearer token as a provider credential.

#### Solution

Add a private, case-insensitive classifier for inbound caller-auth headers. After resolving and trimming `from_header`, reject `Authorization` and `X-Api-Key` with a sanitized configuration error before target-header/scheme normalization. Keep `X-IOP-Provider-Authorization` as the default and keep arbitrary dedicated custom provider headers valid.

Before:

```go
if v.InConfig("openai.provider_auth.from_header") {
	auth.FromHeader = strings.TrimSpace(auth.FromHeader)
	if auth.FromHeader == "" {
		return fmt.Errorf("openai.provider_auth.from_header must not be empty when provider_auth is enabled")
	}
} else {
	auth.FromHeader = "X-IOP-Provider-Authorization"
}
```

After target shape:

```go
func isInboundCallerAuthHeader(header string) bool {
	switch strings.ToLower(strings.TrimSpace(header)) {
	case "authorization", "x-api-key":
		return true
	default:
		return false
	}
}

// Resolve auth.FromHeader exactly as today, then fail closed before use.
if isInboundCallerAuthHeader(auth.FromHeader) {
	return fmt.Errorf("openai.provider_auth.from_header must not reuse inbound caller authentication header %q", auth.FromHeader)
}
```

#### Modified Files and Checklist

- [ ] `packages/go/config/validate.go`: add the private classifier and reject both inbound caller-auth forms after default/trim resolution.
- [ ] `packages/go/config/edge_openai_config_test.go`: add table-driven rejection cases for case/whitespace variants of `Authorization` and `X-Api-Key`; retain the existing default and dedicated custom-header success controls.
- [ ] `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/CODE_REVIEW-cloud-G05.md`: record the chosen boundary, exact focused/full test output, and no-contract-change decision.

#### Test Strategy

Use `LoadEdge` fixtures because admission is the ownership boundary. Each forbidden alias must fail with an error naming `openai.provider_auth.from_header` and caller authentication without echoing any credential. Existing default and override tests remain positive controls. Run the full config package and the repository-required four-package race suite.

#### Verification

```bash
TMPDIR=/config/workspace/iop-s0 go test -count=1 ./packages/go/config -run 'TestLoadEdge_OpenAIProviderAuth(EnabledDefaults|Override|RejectsBlankHeaders|RejectsInboundCallerAuthHeaders)$'
TMPDIR=/config/workspace/iop-s0 go test -count=1 ./packages/go/config
TMPDIR=/config/workspace/iop-s0 go test -race -count=1 ./packages/go/config ./apps/edge/internal/openai ./apps/node/internal/adapter ./apps/node/internal/server
```

Expected: every command exits 0. The focused suite proves both forbidden caller-auth forms fail case-insensitively while default and dedicated custom provider headers still load.

### [REVIEW_REVIEW_TEST-2] Deterministic Cleanup Evidence and Pi Row Invalidation

#### Problem

The archived cleanup transcript is reconstructed prose backed by a command that succeeds even though `ss` is missing. Separately, the two Pi rows were produced under a contract-invalid provider-auth setup and cannot be classified as upstream-provider or Hot Path failures.

#### Solution

Record a fresh exact transcript using only availability-checked tools. Prove the reviewed transient root is absent, iop-s2 is clean, no process command references that root family, and no selected port is LISTENing in `/proc/net/tcp{,6}`. Load the existing Pi key and endpoint only into process-local variables, count exact retained matches in the active task directory without printing values or filenames, then unset both. In the new review, state that the old cleanup output is invalid and both archived Pi rows have disposition `invalid_auth_setup`; do not alter the archived artifacts or infer any result beyond that.

#### Modified Files and Checklist

- [ ] `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/CODE_REVIEW-cloud-G05.md`: paste the exact command block, stdout/stderr, and exit status; record zero root/process/listener/secret/endpoint retention facts and the two-row invalidation.

#### Test Strategy

Use `set -euo pipefail`, explicit tool availability, an exact transient-root path, a self-excluding process regex, and TCP state `0A` with the four selected hexadecimal ports. Treat any unavailable tool, nonzero retained count, dirty iop-s2 state, present root, process, or listener as a hard failure. No external request or credential-bearing argv is allowed.

#### Verification

```bash
set -euo pipefail
command -v awk
command -v git
command -v jq
command -v pgrep
command -v rg
pilot_root=/config/workspace/iop-s2/.hot-path-short.lLH4MI
task_dir=/config/workspace/iop-s0/agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual
test ! -e "$pilot_root"
test -z "$(git -C /config/workspace/iop-s2 status --porcelain)"
mapfile -t pilot_pids < <(pgrep -f '/config/workspace/iop-s2/[.]hot-path-short\.' || true)
pilot_process_count=${#pilot_pids[@]}
pilot_listener_count="$(awk 'NR > 1 && $4 == "0A" { split($2, address, ":"); if (address[2] ~ /^(6DB1|71A2|71A3|71A4)$/) count++ } END { print count+0 }' /proc/net/tcp /proc/net/tcp6)"
pilot_key="$(jq -er '.providers.iop.apiKey | strings | select(length > 0)' /config/.pi/agent/models.json)"
pilot_endpoint="$(jq -er '.providers.iop.baseUrl | strings | select(length > 0)' /config/.pi/agent/models.json)"
mapfile -t retained_secret_files < <(rg -lF -- "$pilot_key" "$task_dir" || true)
mapfile -t retained_endpoint_files < <(rg -lF -- "$pilot_endpoint" "$task_dir" || true)
retained_secret_count=${#retained_secret_files[@]}
retained_endpoint_count=${#retained_endpoint_files[@]}
unset pilot_key pilot_endpoint
printf 'pilot_root_absent=true\niop_s2_clean=true\npilot_process_count=%s\npilot_listener_count=%s\nretained_secret_count=%s\nretained_endpoint_count=%s\n' "$pilot_process_count" "$pilot_listener_count" "$retained_secret_count" "$retained_endpoint_count"
test "$pilot_process_count" -eq 0
test "$pilot_listener_count" -eq 0
test "$retained_secret_count" -eq 0
test "$retained_endpoint_count" -eq 0
git diff --check
```

Expected: tool paths are printed, the six named facts report `true`, `true`, `0`, `0`, `0`, `0`, `git diff --check` emits no output, and the block exits 0. Do not print credential/endpoint values or retained filenames.

## Reviewer Checkpoints

- Verify config admission rejects `Authorization` and `X-Api-Key` case-insensitively as `provider_auth.from_header` while the dedicated default and custom-header success controls still pass.
- Verify no runtime forwarding, caller-auth behavior, contract, spec, roadmap, shell harness, global agent config, or unrelated dirty file changed.
- Verify the cleanup transcript is actual stdout/stderr from the fixed command block, not prose reconstructed from expected state.
- Verify process and listener probes fail closed without `ss`, cover the exact reviewed root/ports, and report zero after cleanup.
- Verify the exact key/endpoint retention scan prints counts only, unsets process-local values, and reports zero retained matches.
- Verify `pi:direct` and `pi:repair` are explicitly reclassified as `invalid_auth_setup`, with no claim about upstream provider health, Hot Path correctness, or S16 progress.
- Verify S16 and `hot-smoke` remain open and no `complete.log` or roadmap update is produced by the implementing agent.

## Modified Files Summary

| File | Change |
|------|--------|
| `packages/go/config/validate.go` | Add fail-closed inbound caller-auth header rejection for legacy provider credential forwarding. |
| `packages/go/config/edge_openai_config_test.go` | Add positive and negative configuration admission regressions. |
| `agent-task/m-iop-hot-path-one-shot-execution/21+20_hot_smoke_actual/CODE_REVIEW-cloud-G05.md` | Record implementation evidence and explicitly invalidate untrusted pilot claims. |

## Risks and Assumptions

- Header names are case-insensitive by HTTP contract; validation must trim and compare case-insensitively.
- `Authorization` and `X-Api-Key` are the current inbound IOP caller-auth surfaces. New caller-auth forms must be added to the classifier if the public contracts expand.
- Rejecting ambiguous legacy configs is intentionally fail-closed. Dedicated custom headers remain supported, so no provider-token capability is removed.
- `/proc/net/tcp{,6}` is Linux-specific and intentionally selected for this reviewed environment; tool/file absence is a failure, not permission to summarize expected state.
- The retained Pi `401` rows cannot be repaired retroactively. A later authorized execution may produce new evidence under a distinct provider header, but that is outside this plan.

## Definition of Done

- Edge config load rejects case/whitespace variants of `Authorization` and `X-Api-Key` as `provider_auth.from_header`.
- Existing default `X-IOP-Provider-Authorization` and dedicated custom header behavior remains valid.
- Focused config tests, the full config package, the four-package race suite, cleanup/evidence probes, and `git diff --check` pass with exact retained output.
- The new review records the old cleanup transcript as invalid and both Pi rows as `invalid_auth_setup`, without modifying archived evidence.
- No credential, endpoint, raw agent output, transient runtime, process, selected listener, or iop-s2 worktree change remains.
- S16 and `hot-smoke` remain explicitly open; implementation leaves the active pair for review and performs no finalization.
