<!-- task=m-node-provider-execution-liveness-recovery/10+09_stall_recovery plan=9 tag=REVIEW_API milestone-task=bounded-retry -->

# Preserve Responses Cross-Path Recovery Execution Context

## For the Implementing Agent

Implement only the two direct fixes below. Run every verification command exactly as written, fill the implementation-owned sections of `CODE_REVIEW-cloud-G06.md` with actual notes and complete raw output, keep the active pair in place, and report ready for review. Finalization belongs to the code-review skill. 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

Responses exact replay now defers normalized validation until provider-path selection, but its recovery `PrepareRun` copies only part of the normalized dispatch context. When a stalled tunnel is replaced by a normalized run, the configured ingress timeout is lost and the service may substitute a different default. The current production matrix keeps the failed and replacement execution paths identical, so every declared verification remains green while this path-switch regression survives.

## Archive Evidence Snapshot

- The reviewed plan=8 pair is archived in this task directory as `plan_cloud_G09_8.log` and `code_review_cloud_G10_8.log` with verdict `FAIL`.
- Required R1: the recovery `PrepareRun` overlay omits `TimeoutSec`, `MaxQueue`, and `QueueTimeoutMS`; an initial tunnel followed by a normalized replacement records `TimeoutSec=0` instead of the ingress value 5.
- All twelve declared verification commands passed on fresh reviewer reruns. A focused production-handler probe failed with `replacement TimeoutSec=0, want ingress timeout 5`; its temporary test file was removed.
- Routing signals are `review_rework_count=6` and `evidence_integrity_failure=true`.

## Finding Resolution Map

| Finding | Mode | Exact Fix / Evidence | Changed or Satisfied Precondition |
|---------|------|----------------------|-----------------------------------|
| Required R1 | `direct-fix` | In `apps/edge/internal/openai/responses_stream_gate.go`, make recovery `PrepareRun` copy the complete normalized request-owned execution context. In `apps/edge/internal/openai/stream_gate_stall_recovery_test.go`, add production-handler rows whose failed and replacement attempts use different provider paths and assert the selected request context. | A normalized recovery no longer inherits the incomplete tunnel-capable base Run, and the matrix exercises candidate-path transitions instead of validating only same-path retries. |

## Analysis

### Files Read

- `apps/edge/internal/openai/responses_stream_gate.go`
- `apps/edge/internal/openai/responses_handler.go`
- `apps/edge/internal/openai/dispatch_context.go`
- `apps/edge/internal/openai/stream_gate_stall_recovery_test.go`
- `apps/edge/internal/openai/provider_test_support_test.go`
- `apps/edge/internal/service/provider_pool.go`
- `agent-contract/inner/execution-runtime.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-spec/runtime/stream-evidence-gate.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-roadmap/phase/operational-observability-provider-management/milestones/node-provider-execution-liveness-recovery.md`
- `agent-roadmap/sdd/operational-observability-provider-management/node-provider-execution-liveness-recovery/SDD.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/plan_cloud_G10_7.log`
- `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/code_review_cloud_G10_7.log`
- `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/plan_cloud_G09_8.log`
- `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/code_review_cloud_G10_8.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/node-provider-execution-liveness-recovery/SDD.md`; approved, lock released, and no user-review gate is active.
- Contribution: `milestone-task=bounded-retry`; targeted Acceptance Scenario S05.
- S05 permits replay only for a confirmed, transport-uncommitted, uncancelled, side-effect-safe request with remaining shared fault budget and a recovery owner. Its Evidence Map requires commit-boundary/shared-budget, provider-pool failover, no-owner terminal, new run identity, and bounded-dispatch evidence.
- These rows make the request-local execution boundary and the actual provider-path switch part of the implementation checklist. Final verification retains production handler, shared package, race, vet, and local smoke evidence.

### Verification Context

- No separate verification handoff was supplied. Repository-native evidence comes from the active implementation, the two cited archived pairs, the declared local test profiles, and the exact commands below.
- Verification runs in `/config/workspace/iop-s1` against the current checkout. The fake-vLLM and provider-capacity profiles require no external account, credential, remote runner, device, or live provider; no external verification preflight is required.
- Fresh reviewer reruns passed all twelve plan=8 commands. The focused temporary production-handler probe `go test -count=1 ./apps/edge/internal/openai -run '^TestReviewProbeResponsesTunnelToNormalizedRecoveryContext$'` failed with `replacement TimeoutSec=0, want ingress timeout 5`; the temporary file was removed after diagnosis.
- Constraint: public `stream=true` remains valid only for a selected tunnel candidate. Cross-path normalized recovery must use a non-stream public replay and preserve the original route timeout; tunnel recovery must retain its candidate-specific raw request context.
- Gap: the current scripted matrix records both pool requests but does not vary the two scripted paths in any successful row.
- Confidence: high. The failing recorded run request directly traverses `handleResponses`, candidate selection, recovery admission, `PrepareRun`, and the production runtime.

### Test Coverage Gaps

- Exact Responses replay to the same tunnel path is covered for `stream=false` and `stream=true`, in both semantic modes.
- Exact Responses replay to the same normalized path is covered for `stream=false`, in both semantic modes.
- Tunnel-to-normalized and normalized-to-tunnel recovery are not covered. The first transition exposes the missing timeout overlay; the reverse transition is the complementary candidate-specific request-context branch.
- Lower-level filter/controller/dispatcher, compatibility, package, race, and smoke tests remain regression evidence but do not exercise the missing path product.

### Symbol References

- None. No symbol is renamed or removed.

### Split Judgment

- Keep one plan. The complete request-context overlay and the production path-switch matrix form one compact recovery-attempt invariant; separating them would leave either an unproved fix or a knowingly failing test packet.
- The dependent subtask `10+09_stall_recovery` requires predecessor index 09. It is satisfied by `agent-task/archive/2026/08/m-node-provider-execution-liveness-recovery/09+08_retry_candidate_policy/complete.log`.

### Scope Rationale

- Exclude `responses_handler.go` and `service/provider_pool.go` edits: their initial `PrepareRun` and candidate-selection order are the correct reference behavior, and the defect is the recovery adapter's partial overlay.
- Exclude Chat admission, Node watchdog, typed failure/wire mapping, Edge health overlay, provider selection policy, shared budget logic, and public schema changes; fresh review found no defect in those owners.
- Exclude contract, spec, config, and protobuf edits because the active documents already require request-local bounded recovery and no contract meaning changes.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`; no capability gap.
- Build closures: scope/context/verification/evidence/ownership/decision are all closed. Grade scores are scope=1, state=1, blast=1, evidence=2, verification=1, producing G06. Base route basis is `local-fit`; final route basis is `recovery-boundary`; lane `cloud`; filename `PLAN-cloud-G06.md`.
- Build signals: `large_indivisible_context=false`; matched loop risks are `temporal_state`, `boundary_contract`, and `variant_product`; count=3; `review_rework_count=6`; `evidence_integrity_failure=true`; risk boundary not matched; recovery boundary matched.
- Review closures are all closed. Grade scores are scope=1, state=1, blast=1, evidence=2, verification=1; route basis `official-review`; lane `cloud`; grade `G06`; filename `CODE_REVIEW-cloud-G06.md`; adapter `codex`; model `gpt-5.6-sol`; reasoning effort `xhigh`.

## Implementation Checklist

- [ ] REVIEW_API-1 makes recovery `PrepareRun` overlay `TimeoutSec`, `MaxQueue`, and `QueueTimeoutMS` from the selected normalized Responses dispatch context without changing tunnel or continuation semantics.
- [ ] REVIEW_API-2 adds deterministic semantic-false and semantic-true Responses path-switch rows that prove the selected run/tunnel request context, provider avoidance, new identity, bounded dispatch, exactly-once close, sanitized output, and one public terminal.
- [ ] Run and record every exact final verification command separately after both implementation items are complete.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Complete the normalized recovery request overlay

**Problem:** `apps/edge/internal/openai/responses_stream_gate.go:1079-1083` copies prompt, input, metadata, token estimate, and context class into a selected normalized replacement but omits `TimeoutSec`, `MaxQueue`, and `QueueTimeoutMS`. The tunnel-capable base Run does not own those normalized route values, so tunnel-to-normalized recovery can dispatch with timeout zero; the initial Responses provider-pool path already copies the complete set at `apps/edge/internal/openai/responses_handler.go:434-441`.

**Solution:** Keep candidate-dependent decoding and attempt-state binding unchanged. Extend the recovery `PrepareRun` overlay to copy the three missing execution fields from `attemptDC.submitReq`, matching the initial Responses normalized preparation boundary.

Before:

```go
// responses_stream_gate.go:1079
runReq.Prompt = attemptDC.submitReq.Prompt
runReq.Input = attemptDC.submitReq.Input
runReq.Metadata = attemptDC.submitReq.Metadata
runReq.EstimatedInputTokens = attemptDC.submitReq.EstimatedInputTokens
runReq.ContextClass = attemptDC.submitReq.ContextClass
```

After:

```go
runReq.Prompt = attemptDC.submitReq.Prompt
runReq.Input = attemptDC.submitReq.Input
runReq.Metadata = attemptDC.submitReq.Metadata
runReq.EstimatedInputTokens = attemptDC.submitReq.EstimatedInputTokens
runReq.ContextClass = attemptDC.submitReq.ContextClass
runReq.TimeoutSec = attemptDC.submitReq.TimeoutSec
runReq.MaxQueue = attemptDC.submitReq.MaxQueue
runReq.QueueTimeoutMS = attemptDC.submitReq.QueueTimeoutMS
```

**Modified Files and Checklist:**

- [ ] `apps/edge/internal/openai/responses_stream_gate.go`: copy the complete normalized execution context in recovery `PrepareRun`.

**Test Strategy:** A bug-fix regression is mandatory and belongs to REVIEW_API-2 in `stream_gate_stall_recovery_test.go`. Existing exact replay, private continuation, and same-path rows remain unchanged regression coverage.

**Verification:** The focused cross-path matrix command must pass ten fresh iterations and record the normalized replacement with `TimeoutSec=5` rather than zero.

### [REVIEW_API-2] Prove candidate-path transitions through the production handler

**Problem:** `apps/edge/internal/openai/stream_gate_stall_recovery_test.go:272-275` constructs both scripted attempts with `tc.path`, and lines 313-314 assert both closes against that same path. The matrix therefore cannot expose state or request-field loss when recovery selects a different execution path.

**Solution:** Give recovery cases separate initial and replacement paths. Add non-stream Responses `provider_tunnel_to_normalized` and `normalized_to_provider_tunnel` rows for semantic false and true. Keep existing same-path products. Use `scriptedPoolRunService.snapshot` to assert the actual selected request collections and verify the normalized replacement retains ingress `TimeoutSec=5`; assert each attempt closes once through its own path, provider-a is avoided without unsafe fallback, attempt ids differ, exactly two pool admissions occur, no duplicate cancel or raw stall data escapes, and one endpoint-native terminal is emitted.

Before:

```go
// stream_gate_stall_recovery_test.go:272
service := newScriptedPoolRunService(
    stallMatrixFailureAttempt(tc.path, "attempt-a", "provider-a", "unavailable"),
    stallMatrixSuccessAttempt(tc.endpoint, tc.path, tc.stream, "attempt-b", "provider-b", marker),
)
```

After:

```go
service := newScriptedPoolRunService(
    stallMatrixFailureAttempt(tc.initialPath, "attempt-a", "provider-a", "unavailable"),
    stallMatrixSuccessAttempt(tc.endpoint, tc.replacementPath, tc.stream, "attempt-b", "provider-b", marker),
)
```

**Modified Files and Checklist:**

- [ ] `apps/edge/internal/openai/stream_gate_stall_recovery_test.go`: model initial/replacement paths independently, add both Responses cross-path products for both semantic modes, and assert selected request context plus lifecycle invariants.
- [ ] `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/CODE_REVIEW-cloud-G06.md`: record implementation decisions and complete raw output for every command.

**Test Strategy:** Extend `TestOpenAIStallRecoveryMatrix` rather than adding a helper-only unit test. Reuse the scripted pool and typed confirmed-stall frames so the regression traverses the production Responses handler, recovery admission, provider-pool selection, selected transport, and public sink.

**Verification:** Run the focused tunnel-to-normalized rows ten times, then the full matrix ten times. Both must pass with the asserted request context and terminal lifecycle.

## Dependencies and Execution Order

1. Predecessor `09+08_retry_candidate_policy` is satisfied by `agent-task/archive/2026/08/m-node-provider-execution-liveness-recovery/09+08_retry_candidate_policy/complete.log`.
2. Complete REVIEW_API-1 before relying on the new tunnel-to-normalized regression.
3. Complete REVIEW_API-2, run every final verification command, and fill the active review evidence.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/edge/internal/openai/responses_stream_gate.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/stream_gate_stall_recovery_test.go` | REVIEW_API-2 |
| `agent-task/m-node-provider-execution-liveness-recovery/10+09_stall_recovery/CODE_REVIEW-cloud-G06.md` | REVIEW_API-2 |

## Final Verification

1. `go test -count=10 ./apps/edge/internal/openai -run 'TestOpenAIStallRecoveryMatrix/recover/responses/provider_tunnel_to_normalized'` — PASS both semantic modes and preserve `TimeoutSec=5` on the normalized replacement.
2. `go test -count=10 ./apps/edge/internal/openai -run '^TestOpenAIStallRecoveryMatrix$'` — PASS every production same-path, cross-path, safety-guard, budget, and terminal row.
3. `go test -count=10 ./apps/edge/internal/openai -run '^TestOpenAISemanticGateDisabledCompatibility$'` — PASS every runtime-owned endpoint compatibility row.
4. `go test -count=20 ./apps/edge/internal/openai -run '^(TestOpenAIStallRecoveryFilter|TestOpenAIAttemptControllerConfirmedStall|TestOpenAIAttemptDispatcherStalledProvider)$'` — PASS every iteration.
5. `go test -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/internal/controlplane` — PASS.
6. `go test -race -count=3 ./apps/edge/internal/service` — PASS with no race report.
7. `go test -race -count=3 ./apps/edge/internal/openai` — PASS with no race report.
8. `go vet ./packages/go/streamgate ./apps/edge/internal/service ./apps/edge/internal/openai ./apps/edge/internal/controlplane` — no diagnostics.
9. `./scripts/e2e-smoke.sh` — PASS.
10. `IOP_VLLM_MODE=fake ./scripts/e2e-openai-vllm.sh` — PASS.
11. `./scripts/e2e-provider-capacity-smoke.sh` — PASS.
12. `git diff --check` — no whitespace errors.

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