<!-- task=m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress plan=1 tag=REVIEW_API milestone-task=request-identity -->

# Complete Preset Ingress Identity and Messages Operation Isolation

## For the Implementing Agent

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

## Background

The first ingress review found that preset Chat and Messages turns carry request and stage ids but omit the required per-HTTP-turn call id. It also found that the shared Anthropic pool builder joins count-tokens requests to the logical execution coordinator and that endpoint evidence does not cover owner-affinity or tool-schema mutation rejection. This follow-up closes those three gaps without changing coordinator internals or output protocol behavior.

## Archive Evidence Snapshot

- Current pair: `agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/PLAN-local-G07.md` and `agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/CODE_REVIEW-cloud-G07.md`.
- Predicted archives: `plan_local_G07_0.log` and `code_review_cloud_G07_0.log`; verdict `FAIL`, Required=3, Suggested=0, Nit=0.
- Required findings: add a trusted per-turn call id; prevent preset count-tokens from creating execution state; add cross-owner and tool-schema mutation zero-dispatch endpoint evidence.
- Fresh evidence: focused preset identity race, common race, vet, and diff checks passed; full `./apps/edge/...` passed with an executable `/config` TMPDIR after the host `/tmp` noexec failure was isolated.
- Roadmap carryover: preserve `milestone-task=request-identity`; approved SDD S05 and its request-identity Evidence Map remain the acceptance source.

## Dependencies and Execution Order

- `04+02,03_preset_model_authorization` is satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/04+02,03_preset_model_authorization/complete.log`.
- `05+02,04_request_coordinator` is satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/05+02,04_request_coordinator/complete.log`.

## Analysis

### Files Read

- `agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/PLAN-local-G07.md`
- `agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/CODE_REVIEW-cloud-G07.md`
- `apps/edge/internal/openai/request_identity_ingress.go`
- `apps/edge/internal/openai/request_identity_handler_test.go`
- `apps/edge/internal/openai/request_coordinator.go`
- `apps/edge/internal/openai/request_lineage.go`
- `apps/edge/internal/openai/server.go`
- `apps/edge/internal/openai/chat_handler.go`
- `apps/edge/internal/openai/anthropic_handler.go`
- `apps/edge/internal/openai/dispatch_context.go`
- `apps/edge/internal/openai/principal.go`
- `apps/edge/internal/openai/route_resolution.go`
- `apps/edge/internal/openai/principal_routes.go`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/outer/anthropic-compatible-api.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

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`; status `[승인됨]`, lock released.
- Header scope: `milestone-task=request-identity`.
- Acceptance target: S05 requires only the same-principal active frontier to resume exactly once and rejects owner/affinity, lineage, tool-schema, and missing-state mismatches before dispatch.
- Evidence Map: S05 requires full-history/frontier, lineage/tool-schema mutation, public/provider tool-id, cross-principal/missing-state, and concurrency race evidence. The checklist adds trusted request/call/stage metadata and the missing endpoint rejection variants; final verification retains fresh race evidence.

### Verification Context

- Handoff: resolved read-only from `agent-test/local/rules.md` and `agent-test/local/edge-smoke.md`; repository-native fallback came from `go.mod`, the current plan, SDD, handlers, and tests.
- Environment: local checkout `/config/workspace/iop-s0`; Go from `/config/.local/bin/go`, `go1.26.2 linux/arm64`, GOROOT `/config/opt/go`.
- Commands: fresh focused and race tests, full affected Edge tests, Edge vet, and `git diff --check`; cached output is not accepted because all Go commands use `-count=1` where applicable.
- Preconditions: both predecessor `complete.log` files must exist; full Edge tests require an executable TMPDIR because host `/tmp` is mounted noexec.
- External verification: none. No provider endpoint or credential is required for deterministic fake-dispatch coverage.
- Constraints: do not expose credentials, run live providers, or leave verification tools in the repository.
- Gaps: full-cycle/live preset smoke remains assigned to S16 `hot-smoke`, not this request-identity correction.
- Confidence: high; rules and profile are usable and every required local command was freshly preflighted.

### Test Coverage Gaps

- Request and stage metadata exist, but no call id is generated or asserted for either endpoint.
- Chat covers cross-principal, missing-state, and history mutation, but not cross-owner state or tool-schema mutation at the handler boundary.
- Anthropic count-tokens has no assertion that it bypasses logical execution state and identity metadata.
- Existing same-principal Chat and Anthropic resume tests remain useful and should be extended rather than replaced.

### Symbol References

- No symbol is renamed or removed.
- `joinPresetChatIngress` is called by `handleChatCompletions`.
- `joinPresetAnthropicIngress` is called only through `anthropicPoolRequest`, which serves both Messages and count-tokens and therefore needs an operation gate.
- `logicalRequestCoordinator.newCallID` exists but has no production caller.

### Split Judgment

Keep one plan. Per-turn identity, operation isolation, and rejection evidence share one compact invariant: only preset Chat/Messages execution turns may mutate coordinator state, and every accepted turn must carry trusted request/call/stage correlation before provider dispatch. Splitting would duplicate the same handler fixture and final race oracle.

### Scope Rationale

Exclude coordinator state-machine redesign, public/provider tool-id response rewriting, direct/light mode transitions, workspace artifacts, cleanup, terminal streaming, durable resume, config schema, and live provider smoke. This follow-up changes only ingress metadata, Anthropic operation gating, and deterministic handler regression evidence.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer=`finalize-task-policy.sh`, mode=`pair`.
- Build closures: scope/context/verification/evidence/ownership/decision all true. Scores `(2,2,1,2,1)` produce G08 with base `local-fit`; `large_indivisible_context=false`.
- Positive loop risks: `temporal_state`, `concurrent_consistency`, `boundary_contract`, `variant_product` (4); risk boundary matched.
- Recovery signals: `review_rework_count=1`, `evidence_integrity_failure=true`; recovery boundary matched and selects cloud build `PLAN-cloud-G08.md`.
- Review closures are true; scores `(2,2,1,2,1)` produce official cloud G08 review `CODE_REVIEW-cloud-G08.md` using Codex `gpt-5.6-sol` xhigh.
- Capability gap: none.

## Implementation Checklist

- [ ] Attach trusted request/call/stage identity to preset Chat and Messages turns and prove cross-owner/tool-schema rejection dispatches nothing.
- [ ] Keep preset Anthropic count-tokens outside the logical execution coordinator and prove operation isolation.
- [ ] Run dependency, focused, race, full Edge, vet, 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] Complete per-turn identity and rejection evidence

#### Problem

`apps/edge/internal/openai/request_identity_ingress.go:38-46` and `104-112` allocate and attach a stage id after begin/resume but never call the existing `newCallID`, so an inbound HTTP turn has no internal call identity. `apps/edge/internal/openai/request_identity_handler_test.go:295-423` also omits cross-owner and tool-schema mutation cases required by the plan and SDD S05.

#### Solution

Allocate a fresh call id once for every accepted preset ingress turn and attach it with trusted request and stage metadata. Caller-supplied internal identity fields must be overwritten. Extend the existing Chat/Anthropic turn tests to assert one stable logical request id, non-empty stage ids, and distinct non-empty call ids per HTTP turn; add cross-owner and tool-schema mutation zero-dispatch rejections.

Before (`request_identity_ingress.go:38-46`):

```go
stageID, err := s.requestCoordinator.newStageID()
if err != nil {
    return err
}
if _, err := s.requestCoordinator.activateStage(snap.ID, ownerEdgeID, stageID); err != nil {
    return err
}
runMeta["iop_logical_request_id"] = snap.ID
runMeta["iop_stage_id"] = stageID
```

After:

```go
stageID, err := s.requestCoordinator.newStageID()
if err != nil {
    return err
}
callID, err := s.requestCoordinator.newCallID()
if err != nil {
    return err
}
if _, err := s.requestCoordinator.activateStage(snap.ID, ownerEdgeID, stageID); err != nil {
    return err
}
runMeta["iop_logical_request_id"] = snap.ID
runMeta["iop_call_id"] = callID
runMeta["iop_stage_id"] = stageID
```

Apply the same ordering to Chat/Anthropic begin and continuation branches. Do not change coordinator transition semantics in this follow-up.

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/request_identity_ingress.go` — allocate and attach trusted call identity on all four accepted branches.
- [ ] `apps/edge/internal/openai/request_identity_handler_test.go` — assert identity metadata and add cross-owner/tool-schema zero-dispatch cases.

#### Test Strategy

Extend `TestPresetRequestIdentityAcrossChatTurns` and `TestPresetRequestIdentityAcrossAnthropicTurns` to inspect fake dispatch metadata. Add focused subtests under `TestPresetRequestIdentityRejectionCases` for a waiting record owned by another Edge and for a changed `tools` schema; each must return the endpoint-standard error without increasing provider submissions.

#### Verification

Run `go test -count=1 ./apps/edge/internal/openai -run 'TestPresetRequestIdentity'`; expect PASS with request-id stability, per-turn call-id uniqueness, and zero-dispatch owner/toolset rejection assertions.

### [REVIEW_API-2] Isolate Anthropic count-tokens from execution state

#### Problem

`apps/edge/internal/openai/anthropic_handler.go:127` uses `anthropicPoolRequest` for native count-tokens fallback, while the unconditional preset branch at lines 161-165 joins the logical execution coordinator. A count-only request can therefore allocate a logical request and active stage even though it is not a Messages execution turn.

#### Solution

Gate preset coordinator joining by the concrete Messages operation. Preserve existing candidate selection and body/header behavior for count-tokens.

Before (`anthropic_handler.go:161-165`):

```go
if dispatch.IsPreset {
    if err := s.joinPresetAnthropicIngress(r, dispatch, body, metadata); err != nil {
        return edgeservice.ProviderPoolDispatchRequest{}, err
    }
}
```

After:

```go
if dispatch.IsPreset && operation == config.OperationMessages {
    if err := s.joinPresetAnthropicIngress(r, dispatch, body, metadata); err != nil {
        return edgeservice.ProviderPoolDispatchRequest{}, err
    }
}
```

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/anthropic_handler.go` — restrict logical ingress joining to Messages execution.
- [ ] `apps/edge/internal/openai/request_identity_handler_test.go` — add preset count-tokens coordinator/metadata isolation regression coverage.

#### Test Strategy

Add `TestPresetRequestIdentityAnthropicCountTokensBypassesCoordinator` using the existing native tunnel fake without a local TokenCounter. Assert HTTP success, one count-tokens provider submission, zero coordinator records, and absence of logical request/call/stage metadata on the pool request.

#### Verification

Run `go test -count=1 ./apps/edge/internal/openai -run 'TestPresetRequestIdentityAnthropicCountTokensBypassesCoordinator'`; expect PASS.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/request_identity_ingress.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/anthropic_handler.go` | REVIEW_API-2 |
| `apps/edge/internal/openai/request_identity_handler_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/CODE_REVIEW-cloud-G08.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

```bash
test -f agent-task/m-iop-hot-path-one-shot-execution/04+02,03_preset_model_authorization/complete.log || test -f agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/04+02,03_preset_model_authorization/complete.log
test -f agent-task/m-iop-hot-path-one-shot-execution/05+02,04_request_coordinator/complete.log || test -f agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/05+02,04_request_coordinator/complete.log
go test -count=1 ./apps/edge/internal/openai -run 'TestPresetRequestIdentity'
go test -race -count=1 ./apps/edge/internal/openai -run 'TestPresetRequestIdentity'
go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service
mkdir -p /config/.tmp-iop-request-identity
TMPDIR=/config/.tmp-iop-request-identity go test -count=1 ./apps/edge/...
rmdir /config/.tmp-iop-request-identity
go vet ./apps/edge/...
git diff --check
```

Expected: every command exits 0; accepted preset Chat/Messages dispatch metadata contains trusted request/call/stage ids, request ids remain stable across continuation, call ids differ per HTTP turn, owner/tool-schema mismatches dispatch nothing, preset count-tokens creates no logical execution state, and legacy/provider-only bypass remains unchanged.

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