<!-- task=m-iop-hot-path-one-shot-execution/10+07,09_light_flow plan=2 tag=REVIEW_API milestone-task=light-flow -->

# Harden Cross-Stage Correlation Tokens and Protocol Evidence

## For the Implementing Agent

Implement this follow-up, run every verification command, and fill the implementation-owned sections of `CODE_REVIEW-cloud-G05.md` with actual notes and stdout/stderr. Keep both active files in place and report ready for official 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 code review.

## Background

The second light-flow review confirmed that ordinary selector/local correlations now reach the shared stage prompt. It also found that provider-owned opaque strings can create new prompt lines because the serializer emits them without a safe-token fence, while the claimed Messages tunnel regression passes an empty candidate and therefore exercises only the OpenAI fallback builder. This follow-up closes the input-isolation and exact dual-protocol evidence gaps without changing the light-flow state machine.

## Archive Evidence Snapshot

- Current pair after review finalization: `plan_local_G05_1.log` and `code_review_cloud_G05_1.log`; verdict `FAIL`, `review_rework_count=2`, `evidence_integrity_failure=true`.
- Required finding 1: `writeStageCorrelation` interpolates provider-owned `ResponseID` and `Terminal` values as raw prompt lines after only non-empty validation, allowing delimiter/control-text injection into the next stage.
- Required finding 2: captured outbound assertions accept a missing normalized prompt, check headings instead of exact correlations, and call `PrepareProtocolTunnel` with an empty candidate, so the Anthropic case never executes the Messages builder.
- Affected files: `apps/edge/internal/openai/hot_path_stage_input.go` and `apps/edge/internal/openai/hot_path_light_test.go`.
- Fresh reviewer evidence: focused light/review race tests, common race tests, focused vet, formatting, and `git diff --check` all pass, but source inspection contradicts the claimed Messages production-path coverage.
- The preceding loop remains available as `plan_cloud_G10_0.log` and `code_review_cloud_G10_0.log`; predecessors 07 and 09 remain satisfied by their exact archived `complete.log` files.

## Dependencies and Execution Order

- Index 07 is satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/07+02,04,06_route_selector_direct/complete.log`.
- Index 09 is satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/09+06,08_artifact_pair/complete.log`.
- Preserve the existing `10+07,09_light_flow` task path. Complete safe-token validation and the exact outbound matrix together because the test oracle depends on the final serialized representation.

## Analysis

### Files Read

- `apps/edge/internal/openai/hot_path_stage_input.go`
- `apps/edge/internal/openai/hot_path_dispatch.go`
- `apps/edge/internal/openai/hot_path_light_test.go`
- `agent-task/m-iop-hot-path-one-shot-execution/10+07,09_light_flow/PLAN-local-G05.md`
- `agent-task/m-iop-hot-path-one-shot-execution/10+07,09_light_flow/CODE_REVIEW-cloud-G05.md`
- `agent-task/m-iop-hot-path-one-shot-execution/10+07,09_light_flow/plan_cloud_G10_0.log`
- `agent-task/m-iop-hot-path-one-shot-execution/10+07,09_light_flow/code_review_cloud_G10_0.log`
- `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`

### SDD Criteria

The selected SDD is `[승인됨]` and unlocked. The preserved `milestone-task=light-flow` maps to Acceptance Scenario S08 and Evidence Map row S08. S08 requires immutable stage-input isolation plus deterministic pass/defect review state-machine evidence; therefore this checklist fails closed on unsafe opaque correlation tokens and verifies the exact selector/local stage and response values in normalized, OpenAI Chat tunnel, and Anthropic Messages tunnel payloads.

### Verification Context

No separate verification handoff was supplied. Repository-native context comes from `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, the active plan/review pair, source, SDD, and fresh reviewer commands. The current host is `/config/workspace/iop-s0` with `/config/.local/bin/go`, Go `1.26.2 linux/arm64`, and a shared dirty worktree. Deterministic package verification requires no credential, provider, device, external runner, or interactive session. Fresh `-count=1` focused/common race commands, focused vet, formatting, and diff checks are the required oracle; live Hot Path smoke remains S16 scope. Confidence is high because the service captures `ProviderPoolDispatchRequest`, its actual selected candidate fixes the protocol driver, and `BuildBody` exposes the exact provider payload.

### Test Coverage Gaps

- No test supplies newline/control/delimiter text through provider-owned correlation fields and proves the stage input fails closed before prompt construction.
- Captured `Run.Input["prompt"]` assertions are conditional and do not require the normalized prompt to exist.
- Captured local/review assertions check section headings rather than exact predecessor stage/response values.
- `buildTunnelBodyFromRequest` passes a zero-value candidate, so both endpoint variants inspect the OpenAI fallback body and the Anthropic Messages branch is uncovered.

### Symbol References

None. No symbol rename or removal is planned; the existing `validLogicalRequestID` safe-token predicate is reused.

### Split Judgment

Keep one compact plan. Correlation validation and the normalized/Chat/Messages regression matrix jointly define one cross-stage input invariant and cannot independently PASS. Archived predecessor 07 and 09 completion logs satisfy the directory-declared dependencies.

### Scope Rationale

Limit production changes to stage-correlation validation/serialization and tests to the existing light-flow fixture. Do not change phase transitions, route/credential revalidation, artifact mapping, public response identity, workspace tool semantics, cleanup, heavy mode, contracts, specs, or live smoke.

### Final Routing

`evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh` pair. Build and review closures are true. Build scores `(1,0,1,2,1)` produce G05 with base `local-fit`; review scores `(1,0,1,2,1)` produce G05. `large_indivisible_context=false`; positive risks are `boundary_contract`, `structured_interpretation`, and `variant_product` (`loop_risk_count=3`). `review_rework_count=2` and `evidence_integrity_failure=true` trigger `recovery-boundary`, so the build route is cloud G05 with `PLAN-cloud-G05.md`. Official review is cloud G05 with `CODE_REVIEW-cloud-G05.md`, Codex `gpt-5.6-sol` xhigh. No capability gap or user decision exists.

## Implementation Checklist

- [ ] Reject unsafe or incomplete selector/local correlation tokens before provider-visible prompt construction.
- [ ] Require exact normalized, OpenAI Chat tunnel, and Anthropic Messages tunnel correlation/isolation evidence.
- [ ] Run focused, common race, vet, format, and diff verification with fresh test execution.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Fail closed on unsafe correlation tokens

#### Problem

`apps/edge/internal/openai/hot_path_stage_input.go:65` validates only non-empty stage/response values, while `writeStageCorrelation` at line 117 writes every field with raw `%s`. Provider response IDs and terminal values are opaque JSON strings; a value containing a newline can create a new instruction-shaped prompt line and violate the S08 isolation boundary.

#### Solution

Validate every emitted selector/local correlation field as the existing bounded `validLogicalRequestID` token class before prompt construction. Require stage, response, run, provider, and terminal tokens for a committed success; reject empty, over-256-byte, whitespace, control, delimiter, or other non-token characters. Keep the current readable serializer only after validation succeeds, so accepted values are exact and cannot alter line structure.

Before (`apps/edge/internal/openai/hot_path_stage_input.go:65`):

```go
if strings.TrimSpace(in.SelectorCommit.StageID) == "" || strings.TrimSpace(in.SelectorCommit.ResponseID) == "" {
	return fmt.Errorf("selector commit correlation is incomplete")
}
```

After:

```go
if err := validateStageCorrelation("selector", in.SelectorCommit); err != nil {
	return err
}
if in.Role == "review" {
	if err := validateStageCorrelation("local", in.LocalCommit); err != nil {
		return err
	}
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/hot_path_stage_input.go` — validate every emitted committed-correlation field with the bounded safe-token predicate before serialization.
- [ ] `apps/edge/internal/openai/hot_path_light_test.go` — add table cases for empty, newline, control, delimiter, and overlength provider correlation values and require fail-closed prompt construction.

#### Test Strategy

Extend `TestHotPathStageInputIsolation` with local/review cases that mutate `ResponseID`, `ProviderID`, and `Terminal` using newline/control/delimiter and overlength inputs. Assert `prompt` returns a field-specific error and no provider-visible string. Retain exact accepted selector/local values and local-role omission assertions.

#### Verification

```bash
go test -race -count=1 ./apps/edge/internal/openai -run '^TestHotPathStageInputIsolation$'
```

Expected: PASS with fresh execution; every unsafe token fails closed and ordinary exact correlations remain visible only in the allowed roles.

### [REVIEW_API-2] Exercise exact normalized and dual-protocol tunnel payloads

#### Problem

`apps/edge/internal/openai/hot_path_light_test.go:340` treats `Run.Input["prompt"]` as optional and lines 337-400 check only headings. `buildTunnelBodyFromRequest` at line 411 passes an empty candidate, selecting the fallback at `hot_path_dispatch.go:968`; the Anthropic fixture therefore never reaches the Messages builder at lines 993-1008.

#### Solution

Pass the fixture's actual `ProviderPoolCandidate` through the captured-request assertion helpers. Return or inspect the prepared path/operation with the body to prove the OpenAI case uses `/v1/chat/completions` and the Anthropic case uses `/v1/messages`. Require `Run.Input["prompt"]` to exist and assert exact predecessor stage/response tokens in `Run.Prompt`, normalized input, and decoded tunnel messages. Keep local-correlation omission and forbidden-data absence checks on every representation.

Before (`apps/edge/internal/openai/hot_path_light_test.go:411`):

```go
prepared, err := req.PrepareProtocolTunnel(req.Tunnel, edgeservice.ProviderPoolCandidate{})
```

After:

```go
prepared, err := req.PrepareProtocolTunnel(req.Tunnel, selected)
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/hot_path_light_test.go` — carry the actual candidate, require normalized prompt presence, assert exact selector/local stage and response tokens, verify protocol path/operation, and check forbidden data in both tunnel bodies.

#### Test Strategy

Extend `TestHotPathLightLocalTransition` through `assertCleanupPending`. Derive the exact selector stage from the pair request, the exact local stage from the completion request, and endpoint-specific response IDs (`chatcmpl-scripted-pair`/`msg-scripted-pair`, `chatcmpl-light-complete`/`msg-light-complete`). Assert local requests include only selector correlation; review requests include selector and local correlation. Decode the produced JSON and prove the prompt sits in Chat or Messages content according to the selected candidate rather than relying on substring-only fallback behavior.

#### Verification

```bash
go test -race -count=1 ./apps/edge/internal/openai -run 'TestHotPath(LightLocal|StageInput)'
```

Expected: PASS for OpenAI and Anthropic variants with exact normalized and selected-protocol tunnel assertions.

## Modified Files Summary

| File | Items |
|---|---|
| `apps/edge/internal/openai/hot_path_stage_input.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/hot_path_light_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `agent-task/m-iop-hot-path-one-shot-execution/10+07,09_light_flow/CODE_REVIEW-cloud-G05.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

```bash
go test -race -count=1 ./apps/edge/internal/openai -run 'TestHotPath(Light|Review|StageInput)'
go test -race -count=1 ./packages/go/streamgate ./packages/go/config ./apps/edge/internal/openai ./apps/edge/internal/service
go vet ./apps/edge/internal/openai
gofmt -d apps/edge/internal/openai/hot_path_stage_input.go apps/edge/internal/openai/hot_path_light_test.go
git diff --check
```

Expected: every command exits 0; both Go test commands use fresh `-count=1`; OpenAI and Anthropic selected-protocol payload assertions pass; formatting and diff checks produce no output.

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