<!-- task=m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition plan=5 tag=REVIEW_REVIEW_REVIEW_API milestone-task=error-cancel -->

# Reject mismatched Hot Path dispatch handles before stage execution

## For the Implementing Agent

Implement every checklist item, run the exact verification commands, and fill the implementation-owned sections of `CODE_REVIEW-cloud-G07.md` with actual notes and stdout/stderr. Keep both active files in place and report ready for review; finalization belongs to the code-review skill. 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`.

## Background

The result-scoped rejection owner now covers validation, unsupported paths, malformed dual handles, and repeated observation, but downstream stage dispatch still delegates a nil expected handle when the result owns only the opposite handle. That early error sends no `CANCEL_RUN` and leaves the owned opposite handle open. The previous review evidence also claimed a full-Edge pass on a host whose `/tmp` is `noexec`; the supported executable temporary path must be explicit in the verification contract.

## Archive Evidence Snapshot

- The current loop will archive to `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/plan_cloud_G07_4.log` and `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/code_review_cloud_G07_4.log` with verdict `FAIL`, 2 Required findings, 0 Suggested findings, and 0 Nits.
- Required correctness finding: downstream progressive and buffered switches accept a path whose expected handle is nil and delegate before aborting the result-scoped owner; a normalized-path/tunnel-only real-entry-point reproducer observed zero cancel calls and left the tunnel open. The symmetric tunnel-path/run-only variants share the defect.
- Required verification finding: the active review claimed `go test -count=1 ./apps/edge/...` exited 0, while a fresh exact run failed when the bootstrap integration test tried to execute its fixture from `/tmp`, which is mounted `noexec` on this host.
- Fresh review evidence passed the focused Hot Path race suite, the common streamgate/config/openai/service race suite, Edge vet, formatting, and `git diff --check`. `TMPDIR=/config/workspace/iop-s0 go test -count=1 ./apps/edge/...` also passed and is the repository-local supported full-Edge command for this host.
- Roadmap carryover remains `milestone-task=error-cancel`; SDD S13 requires standard terminal failure with no partial success or hidden provider work. This follow-up does not assert milestone completion.

## Analysis

### Files Read

- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/PLAN-cloud-G07.md`
- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/CODE_REVIEW-cloud-G07.md`
- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/code_review_cloud_G10_0.log`
- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/code_review_cloud_G10_1.log`
- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/code_review_cloud_G10_2.log`
- `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/code_review_cloud_G06_3.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`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-spec/runtime/stream-evidence-gate.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `apps/edge/internal/openai/hot_path_dispatch.go`
- `apps/edge/internal/openai/hot_path_stage_stream.go`
- `apps/edge/internal/openai/hot_path_terminal_control.go`
- `apps/edge/internal/openai/hot_path_terminal_control_test.go`
- `apps/edge/internal/bootstrap/reconnect_readiness_integration_test.go`
- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.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=error-cancel`.
- Target scenario: S13. Its Evidence Map requires endpoint error/cancel/length regressions proving no custom partial-success state; rejected post-dispatch results must also leave no hidden provider work.
- S13 therefore drives one pre-execution path/handle-shape gate, exact immutable `CANCEL_RUN` ownership for every invalid result shape, per-handle exact close counts, and fresh common/full-Edge verification.

### Verification Context

- No verification handoff was supplied. Repository-native sources are `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, the active plan, the Go module, and the focused package tests.
- Local preflight: `/config/workspace/iop-s0`, branch `feature/iop-hot-path-one-shot-execution`, HEAD `f79fe3c7`, shared dirty worktree, `/config/.local/bin/go`, `go1.26.2 linux/arm64`.
- `/tmp` is mounted `rw,nosuid,nodev,noexec`; the exact unqualified full-Edge command fails only when the bootstrap integration fixture is executed there. The repository root is writable and executable, and `TMPDIR=/config/workspace/iop-s0 go test -count=1 ./apps/edge/...` passed with automatic temporary-directory cleanup.
- Fresh reviewer results: focused race passed (`ok iop/apps/edge/internal/openai 4.007s`); common race passed for streamgate/config/openai/service; unqualified full Edge failed at `TestActualNodeReconnectReadyPumpsQueuedWaiterExactlyOnce` with `permission denied`; the repository-root `TMPDIR` full Edge passed; vet, gofmt, and diff checks passed.
- A temporary package-local real-entry-point reproducer failed with `cancel calls=0, want 1` for normalized path plus tunnel-only ownership and was removed after the run.
- No external runner, credential, provider, device, port, or long-running runtime is required. Confidence is high because the leak is deterministic and the executable temporary-path constraint was directly preflighted.

### Test Coverage Gaps

- Downstream normalized path with `Run=nil`: not covered for either no-handle or tunnel-only ownership in buffered or progressive mode.
- Downstream tunnel path with `Tunnel=nil`: not covered for either no-handle or run-only ownership in buffered or progressive mode.
- Existing validation, unsupported-path, dual-handle, and repeated-owner rows pass but do not exercise these missing/wrong-handle shapes.
- Full-Edge verification is executable on this host only when `TMPDIR` points to the executable repository filesystem; the active evidence omitted that precondition.

### Symbol References

- No public or internal symbol is renamed or removed. The new result-shape helper remains package-local and is called only from `submitHotPathStage`.

### Split Judgment

- Keep one plan. Path/handle validation, exact disposal, and its variant matrix are one compact ownership invariant and cannot independently PASS if separated.
- Runtime dependencies encoded by `16+14,15_terminal_disposition` remain satisfied by the already recorded predecessor evidence; this follow-up does not alter the subtask dependency graph.

### Scope Rationale

- Included: downstream provider-pool result-shape validation in `hot_path_dispatch.go`, deterministic regressions in `hot_path_terminal_control_test.go`, and truthful task-local verification evidence.
- Excluded: selector behavior already covered by its matrix, successful stage streaming, endpoint status/body mapping, cleanup/orphan redesign, service/provider-pool schema, bootstrap test implementation, roadmap mutation, and credentialed/live-provider smoke.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer `finalize-task-policy.sh pair`.
- Build closures are true for scope, context, verification, evidence, ownership, and decision. Scores `1/2/1/2/1` produce `G07`; base basis is `local-fit`, final basis is `recovery-boundary`, lane `cloud`, filename `PLAN-cloud-G07.md`.
- Review closures are true. Scores `1/2/1/2/1` produce `G07`; basis `official-review`, lane `cloud`, filename `CODE_REVIEW-cloud-G07.md`, adapter `codex`, model `gpt-5.6-sol`, reasoning `xhigh`.
- `large_indivisible_context=false`; positive loop risks are `concurrent_consistency`, `boundary_contract`, and `variant_product` (3). Recovery signals are `review_rework_count=3` and `evidence_integrity_failure=true`; no capability gap applies.

## Implementation Checklist

- [ ] [REVIEW_REVIEW_REVIEW_API-1] Reject every downstream provider-pool missing/wrong-handle shape before progressive or buffered execution and dispose it through the existing result-scoped owner with typed validation disposition.
- [ ] [REVIEW_REVIEW_REVIEW_API-2] Extend the downstream matrix across normalized/tunnel, buffered/progressive, no-handle/opposite-handle variants and record fresh focused/common/full-Edge verification using the supported executable `TMPDIR`.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_REVIEW_REVIEW_API-1] Pre-execution path/handle ownership gate

**Problem:** `apps/edge/internal/openai/hot_path_dispatch.go:1260` and `:1274` dispatch solely on `result.Path`. A normalized result with `Run=nil` and a non-nil owned `Tunnel`, or the symmetric tunnel result with `Tunnel=nil` and an owned `Run`, reaches the nil-handle error in the live/buffered helper without invoking `rejection`. No cancel is sent and the opposite handle is not closed.

**Solution:** Add one package-local validator for the provider-pool result shape and call it immediately after creating the result-scoped owner, before dispatch metadata validation or either progressive/buffered switch. Accept only normalized plus exactly one run handle or tunnel plus exactly one tunnel handle. Reject no-handle, opposite-handle, dual-handle, and unsupported-path results through the same owner, then return the existing `validation_error` disposition with source `stage_dispatch_path`. Keep valid execution ownership transfer unchanged.

Before (`apps/edge/internal/openai/hot_path_dispatch.go:1245`):

```go
rejection := s.newHotPathRejectedDispatchOwner(result)
if result.Run != nil && result.Tunnel != nil {
    s.abortHotPathRejectedDispatch(rejection)
    return normalizedStageOutput{}, hotPathStageCorrelation{}, newHotPathDispositionError(...)
}
if err := validateHotPathStageDispatch(snapshot, route, result.DispatchInfo); err != nil {
```

After:

```go
rejection := s.newHotPathRejectedDispatchOwner(result)
if err := validateHotPathStageResultShape(result); err != nil {
    s.abortHotPathRejectedDispatch(rejection)
    return normalizedStageOutput{}, hotPathStageCorrelation{}, newHotPathDispositionError(
        hotPathDispositionValidationError, "stage_dispatch_path", snapshot.StageID, err,
    )
}
if err := validateHotPathStageDispatch(snapshot, route, result.DispatchInfo); err != nil {
```

**Modified Files and Checklist:**

- [ ] Modify `apps/edge/internal/openai/hot_path_dispatch.go` with the exact result-shape gate and reuse the existing owner for every invalid shape.

**Test Strategy:** Covered by REVIEW_REVIEW_REVIEW_API-2. Do not add a package or public API.

**Verification:** The focused wrong/missing-handle rows return typed `validation_error`, issue one exact `CANCEL_RUN`, and close each non-nil owned handle once.

### [REVIEW_REVIEW_REVIEW_API-2] Missing/wrong-handle matrix and trusted verification

**Problem:** `apps/edge/internal/openai/hot_path_terminal_control_test.go:1009` covers validation mismatch, unsupported paths, and buffered dual handles but omits the invalid shapes that bypass disposal. The prior full-Edge evidence also omitted the host's executable-temp precondition and contradicted a fresh run.

**Solution:** Extend `TestHotPathRejectedDispatchStageMatrix` with buffered and progressive rows for both execution paths, covering no handles and only the opposite handle. Each row must assert typed `validation_error`, the immutable cancel tuple, total cancel count one, and close count one for every non-nil handle. Preserve existing valid/unsupported/dual-handle rows. Record exact fresh output for the repository-root `TMPDIR` full-Edge command rather than copying or reconstructing a pass.

Before (`apps/edge/internal/openai/hot_path_terminal_control_test.go:1020`):

```go
{name: "buffered_normalized_validation", path: "normalized", withRun: true, invalid: true},
{name: "progressive_tunnel_validation", stream: true, path: "provider_tunnel", withTunnel: true, invalid: true},
{name: "buffered_unsupported", path: "unknown", withRun: true},
```

After:

```go
{name: "buffered_normalized_no_handle", path: "normalized"},
{name: "progressive_normalized_opposite_handle", stream: true, path: "normalized", withTunnel: true},
{name: "buffered_tunnel_opposite_handle", path: "provider_tunnel", withRun: true},
{name: "progressive_tunnel_no_handle", stream: true, path: "provider_tunnel"},
```

Add the complementary buffered/progressive rows so both path and handle-shape axes are complete.

**Modified Files and Checklist:**

- [ ] Modify `apps/edge/internal/openai/hot_path_terminal_control_test.go` with the complete downstream missing/wrong-handle matrix.
- [ ] Record actual implementation and verification evidence in `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/CODE_REVIEW-cloud-G07.md`.

**Test Strategy:** Update the existing table-driven package-local test using `rejectFixturedRun`, `rejectFixturedTunnel`, and `rejectPoolService`. No network or provider process is used. Fresh race execution is mandatory.

**Verification:** Run every Final Verification command. All commands exit 0, the focused matrix proves exact disposal, and the full-Edge command uses the executable repository filesystem for temporary binaries.

## Modified Files Summary

| File | Item |
|---|---|
| `apps/edge/internal/openai/hot_path_dispatch.go` | REVIEW_REVIEW_REVIEW_API-1 |
| `apps/edge/internal/openai/hot_path_terminal_control_test.go` | REVIEW_REVIEW_REVIEW_API-2 |
| `agent-task/m-iop-hot-path-one-shot-execution/16+14,15_terminal_disposition/CODE_REVIEW-cloud-G07.md` | REVIEW_REVIEW_REVIEW_API-2 |

## Final Verification

```bash
findmnt -no TARGET,OPTIONS /tmp
go test -race -count=1 ./apps/edge/internal/openai -run 'TestHotPath(RejectedDispatch|TerminalDisposition|ActiveStageCancel|CancelCompleteRace|Cleanup)'
go test -race -count=1 ./packages/go/streamgate ./packages/go/config ./apps/edge/internal/openai ./apps/edge/internal/service
TMPDIR=/config/workspace/iop-s0 go test -count=1 ./apps/edge/...
go vet ./apps/edge/...
gofmt -d apps/edge/internal/openai/hot_path_dispatch.go apps/edge/internal/openai/hot_path_terminal_control_test.go
git diff --check
```

Expected: `findmnt` confirms `/tmp` is `noexec` on this runner; every subsequent command exits 0; all downstream invalid result shapes send one exact `CANCEL_RUN`, close every non-nil returned handle once, retain typed validation disposition, and leave no generated temporary artifact in the repository. Go test caching is not accepted where `-count=1` is specified.

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