<!-- task=m-iop-hot-path-one-shot-execution/01_preset_schema plan=4 tag=REVIEW_API milestone-task=preset-schema,hot-preset -->

# Normalize Execution Preset Route Keys

## For the Implementing Agent

Implement this follow-up, run every verification command, and fill every implementation-owned section of `CODE_REVIEW-cloud-G05.md` with actual notes and stdout/stderr. Keep the active files 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, or write `complete.log`; finalization belongs to the code-review skill.

## Background

The canonical-reference and workspace-descriptor gaps are closed, but the preset validator still accepts two raw route keys that normalize to the same mode. This follow-up makes the normalized route map unambiguous before immutable generation publication consumes it, without expanding into runtime generation, model mapping, authorization, or workspace binding compilation.

## Archive Evidence Snapshot

- The current pair will be archived as `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/plan_cloud_G05_3.log` and `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/code_review_cloud_G06_3.log`.
- Verdict: FAIL. Required 1, Suggested 0, Nit 0.
- Required: normalize route mode keys into the retained map, reject normalized duplicates, and enforce exact normalized equality with `allowed_modes`.
- Reviewer evidence: focused config tests, the full config package, config race, config vet, package-wide vet, formatting, and `git diff --check` passed. A temporary focused reviewer test failed because a preset containing both `direct` and `" direct "` route keys loaded successfully. The temporary test file was removed. Package-wide Go tests remain non-closure evidence because unrelated fake-CLI suites fail on this host's PATH and executable-temp restrictions.
- Roadmap carryover: `milestone-task=preset-schema,hot-preset`; approved SDD S02/S04 remain the acceptance boundary. Immutable generation publication, model-to-preset one-of, authorization, request coordination, and workspace binding compilation remain in later children.

## Analysis

### Files Read

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-hot-path-one-shot-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-spec/index.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-contract/index.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/PLAN-cloud-G05.md`
- `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/CODE_REVIEW-cloud-G06.md`
- `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/plan_cloud_G06_2.log`
- `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/code_review_cloud_G06_2.log`
- `go.mod`
- `packages/go/config/config.go`
- `packages/go/config/edge_types.go`
- `packages/go/config/load.go`
- `packages/go/config/provider_types.go`
- `packages/go/config/execution_preset_types.go`
- `packages/go/config/execution_preset_config_test.go`

### SDD Criteria

The selected SDD at `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md` is approved and unlocked. The first-line scope remains `milestone-task=preset-schema,hot-preset`. S02 requires a normalized preset snapshot suitable for later generation isolation, and S04 requires registered `direct`/`light` startup shapes to fail closed. Evidence Map rows S02/S04 require the config fixture and mode-handler validation evidence, so the checklist adds both a normalization success case and a normalized-duplicate rejection case before repeating the full config regression boundary.

### Verification Context

No external verification handoff was supplied. Repository-native evidence came from the platform-common/testing rules, local platform-common profile, approved SDD, current config source/tests, and fresh reviewer commands. Go resolves to `/config/.local/bin/go` (`go1.26.2 linux/arm64`, GOROOT `/config/opt/go`). Focused config tests, the full config package, config race, config vet, package-wide vet, formatting, and `git diff --check` exit 0. `go test -count=1 ./packages/go/...` reaches unrelated `agentprovider/catalog`, `agentprovider/cli`, and CLI status fake-executable failures on this host and is not the closure oracle for this two-file validator fix. Full-cycle execution is not required because the descriptor remains data-only until the later generation/runtime children and no active config example enables it. No external provider, credential, port, or runner is required. Confidence: high.

### Test Coverage Gaps

- A route key with surrounding whitespace that should normalize to a registered allowed mode: missing.
- Two raw route keys that normalize to the same mode: missing and currently fail-open.
- Canonical selector/stage resolution, direct/light route shape, workspace alternative completeness, descriptor presence, operation-key normalization, unsupported handlers, strict decode, and provider-only compatibility: covered and must remain passing.

### Symbol References

None. This follow-up changes validator behavior and tests without renaming or removing a symbol.

### Split Judgment

Keep one compact follow-up. Route-key normalization and duplicate rejection are one atomic exact-correspondence invariant with one deterministic config test boundary.

### Scope Rationale

Modify only `execution_preset_types.go` and its config regression test. Do not change preset types, strict subtree decoding, model catalog behavior, runtime cloning/refresh, `models[].execution_preset`, authorization, selector execution, request state, workspace binding compilation, protocol streaming, or `configs/edge.yaml`; those are already stable here or assigned to later children.

### Final Routing

`evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh` (`pair`). Build and review closures are all true, with no capability gap. Build scores `(scope=1,state=0,blast=2,evidence=0,verification=1)` produce G04; review scores `(1,0,2,1,1)` produce G05. `large_indivisible_context=false`; positive risks are `boundary_contract`, `structured_interpretation`, and `variant_product` (3). Recovery signals are `review_rework_count=3` and `evidence_integrity_failure=true`, so build route basis is `recovery-boundary`, lane cloud, filename `PLAN-cloud-G04.md`. Official review is cloud G05 in `CODE_REVIEW-cloud-G05.md`.

## Implementation Checklist

- [ ] Rebuild route maps under normalized mode keys, reject empty or duplicate normalized keys deterministically, and enforce exact normalized equality with `allowed_modes` without changing stage validation.
- [ ] Add regression coverage for a valid whitespace-normalized route key and duplicate normalized route keys while preserving all existing preset and provider-only cases.
- [ ] Run focused, fresh, race, vet, formatting, and diff verification exactly as written.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Normalize route keys before exact correspondence checks

#### Problem

`packages/go/config/execution_preset_types.go:151-170` sorts raw route keys and trims them only for membership. It neither rebuilds `p.Routes` with normalized keys nor rejects two raw keys that converge, so `direct` plus `" direct "` is accepted and the extra route survives validation.

#### Solution

Normalize the route map before missing/extra correspondence checks. Iterate sorted raw keys for stable diagnostics, reject empty keys and duplicate normalized keys, retain each route under the normalized key, then compare and validate only the rebuilt map.

```go
// Before: execution_preset_types.go:151
routeKeys := make([]string, 0, len(p.Routes))
for rKey := range p.Routes {
    routeKeys = append(routeKeys, rKey)
}
sort.Strings(routeKeys)
for _, rKey := range routeKeys {
    trimmedKey := strings.TrimSpace(rKey)
    if _, ok := seenModes[trimmedKey]; !ok {
        return fmt.Errorf("... route key %q is not in allowed_modes", rKey)
    }
}
```

```go
// After
normalizedRoutes := make(map[string]ExecutionRoute, len(p.Routes))
for _, rawKey := range sortedRouteKeys(p.Routes) {
    mode := strings.TrimSpace(rawKey)
    if mode == "" {
        return fmt.Errorf("... route key must not be empty")
    }
    if _, duplicate := normalizedRoutes[mode]; duplicate {
        return fmt.Errorf("... duplicate route key %q after normalization", mode)
    }
    normalizedRoutes[mode] = p.Routes[rawKey]
}
p.Routes = normalizedRoutes
// Compare normalized route keys with seenModes, then validate in allowed-mode order.
```

#### Modified Files and Checklist

- [ ] `packages/go/config/execution_preset_types.go` — normalize the retained route map and reject empty or duplicate normalized keys before exact correspondence validation.
- [ ] `packages/go/config/execution_preset_config_test.go` — add normalization success and normalized-duplicate rejection subtests.

#### Test Strategy

Extend `TestLoadEdgeExecutionPresetCatalog` with a direct route key containing surrounding whitespace and assert the loaded map contains only `direct`. Extend `TestLoadEdgeExecutionPresetRejectsInvalidShape` with raw `direct` and `" direct "` keys and assert a deterministic duplicate-normalization error. Preserve the existing valid direct/light, canonical-reference, workspace-descriptor, strict-decode, handler, and provider-only cases.

#### Verification

Run `go test -count=1 ./packages/go/config -run 'TestLoadEdgeExecutionPreset(Catalog|RejectsInvalidShape)$'`; expect the normalized route to load under its canonical key and the duplicate normalized keys to fail.

## Modified Files Summary

| File | Items |
|------|-------|
| `packages/go/config/execution_preset_types.go` | REVIEW_API-1 |
| `packages/go/config/execution_preset_config_test.go` | REVIEW_API-1 |
| `agent-task/m-iop-hot-path-one-shot-execution/01_preset_schema/CODE_REVIEW-cloud-G05.md` | REVIEW_API-1 |

## Final Verification

Cached test output is not acceptable.

```bash
go test -count=1 ./packages/go/config -run 'TestLoadEdgeExecutionPreset(Catalog|RejectsInvalidShape)$'
go test -count=1 ./packages/go/config
go test -race -count=1 ./packages/go/config
go vet ./packages/go/config
go vet ./packages/go/...
gofmt -d packages/go/config/execution_preset_types.go packages/go/config/execution_preset_config_test.go
git diff --check
```

Expected: every command exits 0; route mode keys are retained only in normalized form, normalized duplicates fail deterministically, and all existing preset/provider compatibility cases remain passing.

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