<!-- task=m-iop-hot-path-one-shot-execution/03+01_preset_model_config plan=2 tag=REVIEW_API milestone-task=preset-model -->

# Canonical Preset Mapping and Refresh Contract Follow-up

## For the Implementing Agent

Implement this follow-up, run every verification command, and fill all implementation-owned sections in `CODE_REVIEW-cloud-G07.md` with actual notes and stdout/stderr. Keep the active files in place and report ready for review; finalization is review-agent-only. If blocked, record only the exact blocker, attempted commands/output, and resume condition in the 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`.

## Background

The one-of model-to-preset admission is present, but the loaded model retains surrounding whitespace on a valid preset id and config refresh does not report mapping changes. The active config contract and tracked example also remain provider-only, so they disagree with the new YAML surface. This follow-up closes those normalization, live-refresh, regression-test, and source-of-truth gaps without entering virtual model authorization or dispatch.

## Archive Evidence Snapshot

- Current pair after archive: `agent-task/m-iop-hot-path-one-shot-execution/03+01_preset_model_config/plan_local_G03_1.log` and `agent-task/m-iop-hot-path-one-shot-execution/03+01_preset_model_config/code_review_cloud_G03_1.log`.
- Verdict: FAIL; Required 3, Suggested 0, Nit 0.
- Affected behavior: canonical non-empty `models[].execution_preset` storage, applied refresh classification, and the config contract/example.
- Reviewer evidence: focused config, race, vet, formatting, and diff checks passed; a padded valid preset id remained padded, and changing one model from `preset-a` to `preset-b` produced an empty `configrefresh.Classify` change list. `go test -count=1 ./packages/go/...` additionally encountered unrelated current-host `/tmp` executable permission failures outside this packet.
- Roadmap carryover: keep `milestone-task=preset-model`; predecessor evidence is `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/01_preset_schema/complete.log`.

## Dependencies and Execution Order

- Runtime predecessor `01_preset_schema` is satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/01_preset_schema/complete.log`.

## Analysis

### Files Read

- `packages/go/config/load.go`
- `packages/go/config/provider_types.go`
- `packages/go/config/model_execution_preset_config_test.go`
- `apps/edge/internal/configrefresh/classify.go`
- `apps/edge/internal/configrefresh/execution_preset_classify_test.go`
- `apps/edge/internal/bootstrap/runtime.go`
- `configs/edge.yaml`
- `agent-contract/inner/edge-config-runtime-refresh.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/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/edge-smoke.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`; status `[승인됨]`, lock released.
- Milestone contribution: `milestone-task=preset-model`.
- Targeted scenario: S01 and its `preset-model` Evidence Map row for model/preset catalog admission; S14 supplies the fail-closed invalid-reference boundary.
- Interface Contract lines for `models[].execution_preset` require provider-map mutual exclusion, and the preset catalog plus mapping must live-apply only to new logical requests. Those requirements drive canonical storage, `StatusApplied` refresh evidence, and config-contract synchronization.

### Verification Context

- No external verification handoff was supplied. Repository-native sources were the active PLAN/review evidence, local/domain test rules, config tests, config-refresh tests, SDD, active contract, and tracked example.
- Local preflight: `/config/.local/bin/go`, Go `1.26.2` on `linux/arm64`, `GOROOT=/config/opt/go`; package-level verification needs no credential or external service.
- Fresh reviewer commands passed for `./packages/go/config`, its race run, config vet, configrefresh package tests, configrefresh vet, gofmt diff, and `git diff --check`.
- Focused temporary reviewer regressions failed deterministically: padded `execution_preset` remained padded; model mapping refresh returned zero changes. The temporary probes were removed after capture.
- `go test -count=1 ./packages/go/...` is not a packet pass criterion because unrelated CLI/catalog tests failed to execute fake binaries from `/tmp` with permission denied. Focused affected packages provide the deterministic oracle here.
- Repository-internal Edge/Node diagnostics, auxiliary E2E smoke, live-provider calls, and full-cycle execution are not required because this packet repairs config normalization, dry-run/apply reporting, and documentation without activating model authorization or execution.
- Confidence: high.

### Test Coverage Gaps

- Existing config tests cover whitespace-only unset values but do not cover a valid non-empty preset id with surrounding whitespace or assert its canonical stored value.
- Existing preset classifier tests cover `execution_presets[]` catalog changes but not `models[].execution_preset` mapping changes or `ChangedModels` attribution.
- Contract/example coverage is text-based; deterministic `rg --sort path` plus direct review is sufficient after the source and example are synchronized.

### Symbol References

- None. No symbol is renamed or removed.

### Split Judgment

- Keep one compact follow-up: normalization, refresh reporting, regression tests, and the config source-of-truth describe one model-to-preset contract and must pass together.
- The `03+01` predecessor index `01` is satisfied by the archived `complete.log` named above; there is no unresolved split dependency.

### Scope Rationale

- Include only canonical preset-id storage, model mapping refresh classification/reporting, regression tests, the inner config contract, and the tracked YAML example.
- Exclude principal projection, virtual model list/admission, response echo, route authorization, stage dispatch, request coordinator state, runtime snapshot generation internals, and external smoke; those remain in later milestone children.
- Do not update `agent-spec` in this subtask; living-spec reconciliation remains a milestone completion gate after the full model surface exists.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`, mode `pair`.
- Build closures are all true: scope, context, verification, evidence, ownership, and decisions are closed; no capability gap.
- Build scores `(2,1,2,1,1)` produce G07 with base `local-fit`. `large_indivisible_context=false`; matched loop risk is `boundary_contract` (1). `review_rework_count=1` and `evidence_integrity_failure=true` select `recovery-boundary`, so the canonical build file is `PLAN-cloud-G07.md`.
- Review closures are all true; scores `(2,1,2,1,1)` produce official cloud G07 with Codex `gpt-5.6-sol` xhigh and canonical file `CODE_REVIEW-cloud-G07.md`.

## Implementation Checklist

- [ ] Canonicalize and persist non-empty model execution-preset ids after successful reference resolution, with a focused regression.
- [ ] Classify model execution-preset mapping changes as live-applied changes and verify stable changed-model reporting.
- [ ] Synchronize the active config contract and tracked Edge YAML example with the provider-versus-preset one-of, normalization, reference, and refresh semantics.
- [ ] Run predecessor, focused, affected-package, race, vet, formatting, contract-search, 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] Persist canonical model preset ids

#### Problem

`packages/go/config/load.go:216-233` trims `ExecutionPreset` for comparison but only writes back the empty case. A valid value such as `"  fast-path  "` resolves and survives in non-canonical form, so exact downstream lookup can diverge from admission.

#### Solution

Write the trimmed id back only after reference resolution succeeds.

```go
// Before: packages/go/config/load.go:218-232
presetID := strings.TrimSpace(m.ExecutionPreset)
if presetID == "" {
    m.ExecutionPreset = ""
    continue
}
// lookup ...
if !found {
    return nil, fmt.Errorf(...)
}

// After
presetID := strings.TrimSpace(m.ExecutionPreset)
if presetID == "" {
    m.ExecutionPreset = ""
    continue
}
// lookup ...
if !found {
    return nil, fmt.Errorf(...)
}
m.ExecutionPreset = presetID
```

#### Modified Files and Checklist

- [ ] `packages/go/config/load.go` — persist the normalized non-empty preset id after successful lookup.
- [ ] `packages/go/config/model_execution_preset_config_test.go` — add a provider-backed preset fixture with surrounding whitespace and assert canonical storage.

#### Test Strategy

Extend `TestLoadEdgeModelExecutionPresetOneOf` with `non-empty execution_preset is normalized`. Use a real provider-backed selector model plus a virtual public model, load `"  fast-path  "`, and require `cfg.Models[virtual].ExecutionPreset == "fast-path"` while existing dangling and provider-only cases remain unchanged.

#### Verification

Run `go test -count=1 ./packages/go/config -run 'TestLoadEdgeModelExecutionPresetOneOf|TestModelCatalogEntry_ValidateVirtualEntryUnit'`; expect PASS.

### [REVIEW_API-2] Report model preset mapping refreshes

#### Problem

`apps/edge/internal/configrefresh/classify.go:349-370` enumerates model fields but omits `ExecutionPreset`. Changing a model from `preset-a` to `preset-b` therefore returns no change, skips the expected changed-model report, and conflicts with the SDD's live-apply mapping contract.

#### Solution

Add the scalar applied diff beside the other model fields.

```go
// Before: apps/edge/internal/configrefresh/classify.go:363-369
appendIfChanged(changes, fmt.Sprintf("models[%q].default_thinking_token_budget", modelID), StatusApplied, cur.DefaultThinkingTokenBudget, next.DefaultThinkingTokenBudget)
appendDeepIfChanged(changes, fmt.Sprintf("models[%q].providers", modelID), StatusApplied, cur.Providers, next.Providers)

// After
appendIfChanged(changes, fmt.Sprintf("models[%q].default_thinking_token_budget", modelID), StatusApplied, cur.DefaultThinkingTokenBudget, next.DefaultThinkingTokenBudget)
appendIfChanged(changes, fmt.Sprintf("models[%q].execution_preset", modelID), StatusApplied, cur.ExecutionPreset, next.ExecutionPreset)
appendDeepIfChanged(changes, fmt.Sprintf("models[%q].providers", modelID), StatusApplied, cur.Providers, next.Providers)
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/configrefresh/classify.go` — classify model preset mapping changes as `StatusApplied`.
- [ ] `apps/edge/internal/configrefresh/execution_preset_classify_test.go` — add `TestClassifyModelExecutionPresetLiveApply` and assert path, class, summary, and `ChangedModels`.

#### Test Strategy

Add the named table-free regression with one stable model id whose preset changes. Require exactly `models["virtual-model"].execution_preset`, `StatusApplied`, the all-applied summary, and `ChangedModels == ["virtual-model"]`.

#### Verification

Run `go test -count=1 ./apps/edge/internal/configrefresh -run 'TestClassifyExecutionPresetLiveApply|TestClassifyModelExecutionPresetLiveApply'`; expect PASS.

### [REVIEW_API-3] Synchronize the config source of truth

#### Problem

`agent-contract/inner/edge-config-runtime-refresh.md:57` and `configs/edge.yaml:340-342` still define top-level models solely as provider-pool mappings. They omit the new mutually exclusive virtual preset form, canonicalization/reference behavior, and live-apply change path.

#### Solution

Document one-of semantics, trim-and-resolve behavior, provider-only validation scope, `models["<id>"].execution_preset` live-apply classification, and new-request visibility. Add a comment-only direct preset plus virtual model example that references an existing provider-backed selector model and contains no credential or private endpoint.

#### Modified Files and Checklist

- [ ] `agent-contract/inner/edge-config-runtime-refresh.md` — update config and refresh contract rules.
- [ ] `configs/edge.yaml` — update top-level model comments and add a safe comment-only preset-backed model example.

#### Test Strategy

No parser test is added for comments/contract prose. Existing config tests prove the executable schema; deterministic search and reviewer inspection prove both source-of-truth files expose the expected keys and semantics.

#### Verification

Run `rg --sort path -n 'execution_preset|execution_presets' agent-contract/inner/edge-config-runtime-refresh.md configs/edge.yaml`; expect matches in both files for the one-of form and refresh/example text.

## Modified Files Summary

| File | Items |
|------|-------|
| `packages/go/config/load.go` | REVIEW_API-1 |
| `packages/go/config/model_execution_preset_config_test.go` | REVIEW_API-1 |
| `apps/edge/internal/configrefresh/classify.go` | REVIEW_API-2 |
| `apps/edge/internal/configrefresh/execution_preset_classify_test.go` | REVIEW_API-2 |
| `agent-contract/inner/edge-config-runtime-refresh.md` | REVIEW_API-3 |
| `configs/edge.yaml` | REVIEW_API-3 |
| `agent-task/m-iop-hot-path-one-shot-execution/03+01_preset_model_config/CODE_REVIEW-cloud-G07.md` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |

## Final Verification

```bash
test -f agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/01_preset_schema/complete.log
go test -count=1 ./packages/go/config -run 'TestLoadEdgeModelExecutionPresetOneOf|TestModelCatalogEntry_ValidateVirtualEntryUnit'
go test -count=1 ./apps/edge/internal/configrefresh -run 'TestClassifyExecutionPresetLiveApply|TestClassifyModelExecutionPresetLiveApply'
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go test -race -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go vet ./packages/go/config ./apps/edge/internal/configrefresh
gofmt -d packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go apps/edge/internal/configrefresh/classify.go apps/edge/internal/configrefresh/execution_preset_classify_test.go
rg --sort path -n 'execution_preset|execution_presets' agent-contract/inner/edge-config-runtime-refresh.md configs/edge.yaml
git diff --check
```

Expected: every command exits 0; non-empty preset ids are stored canonically, mapping refresh emits one applied model change with stable reporting, provider-only behavior remains unchanged, and the contract/example describe the implemented schema. Test cache output is not acceptable for Go tests. Repository-internal Edge/Node diagnostics, auxiliary E2E smoke, live-provider calls, and full-cycle execution are omitted because this packet does not activate an execution route.

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