<!-- task=m-iop-owned-single-request-agent-execution/18+17_plan_stage plan=3 tag=REVIEW_API milestone-task=plan-stage -->

# Close the Plan-stage result and evidence gaps

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G05.md` is the mandatory final implementation step. Execute this plan's exact write boundary and verification commands, paste actual output, keep both active files in place, and report ready for review. Only the official code-review skill may append a verdict, archive files, write `complete.log`, or classify the next state. 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, or change ownership.

## Background

The private Plan runner and ordered provider codec now exist, and all recorded commands are green. Review found that the codec still accepts non-assistant or non-success Chat choices and that the checked test matrix is much narrower than the explicit S08 plan contract. This follow-up closes the response-envelope defect and makes the existing spec claims rest on deterministic executable evidence without installing the composite executor.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/plan_cloud_G07_2.log`.
- Prior review: `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/code_review_cloud_G07_2.log`.
- Verdict: `FAIL`; Required R1-R2, Suggested 0, Nit 0; `review_rework_count=2`, `evidence_integrity_failure=true`.
- R1: `decodeSingleRequestChatResponse` accepts non-assistant choices and non-success `finish_reason` values when their content contains valid Plan JSON.
- R2: the provider and Plan tests omit most planned frame-order, exact dispatch, deadline/limit, response-envelope, cancellation, envelope, artifact, and rendered-size cases while the checklist and spec claim that evidence.
- Fresh reviewer evidence: dependency discovery, canonical test discovery, focused tests, vet, `go test ./apps/edge/... -count=1`, no-activation, spec search, formatting, and `git diff --check` all passed; direct source inspection proved the missing assertions.
- Roadmap carryover: this packet continues to contribute only `milestone-task=plan-stage` and SDD S08. Work, Review/repair, composite activation, generic error/cancel integration, and S12 Claude/Mac qualification remain deferred.

## Finding Resolution Map

| Finding | Mode | Exact fix and changed precondition |
|---------|------|------------------------------------|
| Required R1 | `direct-fix` | Update `apps/edge/internal/openai/single_request_provider_stage.go` and `apps/edge/internal/openai/single_request_provider_stage_test.go` to require one index-zero assistant choice with the successful `stop` terminal and reject every other role/reason/result shape generically. The precondition changes from accepting semantically incomplete Chat terminals to a directly tested successful-assistant boundary. |
| Required R2 | `direct-fix` | Expand `apps/edge/internal/openai/single_request_provider_stage_test.go` and `apps/edge/internal/openai/single_request_plan_stage_test.go` with the complete planned fail-closed matrix and exact close/no-write assertions. The precondition changes from green but incomplete discovery to executable evidence matching SDD S08 and the current spec. |

## Analysis

### Files Read

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.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/edge-smoke.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-spec/index.md`
- `agent-contract/index.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-spec/runtime/edge-node-execution.md`
- `apps/edge/internal/openai/chat_types.go`
- `apps/edge/internal/openai/single_request_provider_stage.go`
- `apps/edge/internal/openai/single_request_provider_stage_test.go`
- `apps/edge/internal/openai/single_request_plan_stage.go`
- `apps/edge/internal/openai/single_request_plan_stage_test.go`
- `apps/edge/internal/openai/single_request_preset_binding.go`
- `apps/edge/internal/service/single_request.go`
- `apps/edge/internal/service/single_request_artifact.go`
- `apps/edge/internal/service/single_request_types.go`
- `apps/edge/internal/service/provider_pool.go`
- `apps/edge/internal/service/provider_tunnel.go`
- `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/plan_cloud_G07_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/code_review_cloud_G07_2.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`; status `[승인됨]`, lock released, no user review.
- First-line contribution: `plan-stage`.
- Target: S08 requires the immutable user task and empty request job to produce a small plan plus verification criteria through Gemini 3.6 Flash high and then complete an internal `plan.md` write.
- Evidence Map row: S08 requires a Gemini plan request/options/artifact fixture and expects `plan-stage` high-option and small-plan evidence.
- Consequence: the checklist and final verification require a successful-assistant Chat envelope, exact frozen dispatch/body facts, the complete ordered-frame/deadline/output matrix, strict Plan JSON/rendering, planning-envelope behavior, and successful/failing closed artifact writes.

### Verification Context

- No external verification handoff was supplied. Repository-native sources are the local test rules, Edge smoke profile, approved SDD S08, current provider-pool/controller implementations, and adjacent tests.
- Current checkout: branch `feature/iop-owned-single-request-agent-execution`, HEAD `22a8b81201e89d75c1e6c92342a8081472e8e436`, dirty with this milestone's predecessor and sibling work; unrelated changes must be preserved.
- Toolchain: `go version go1.26.2 linux/arm64`.
- Fresh review passed the unique predecessor check, canonical discovery, focused packages, vet, all Edge packages, no-activation check, deterministic spec search, `gofmt -d`, and `git diff --check`.
- Gap: green commands selected only three provider tests and two Plan tests; source inspection showed no successful-assistant terminal validation and no complete planned failure matrix.
- External Verification Preflight: not applicable. This private component is not installed, and deterministic provider frames plus a fake controller are the approved S08 evidence. Actual Claude/Mac execution remains S12 after activation.
- Confidence: high; R1 is visible at the response acceptance condition, and R2 is visible in the complete test sources.

### Test Coverage Gaps

- Response envelope: no role, choice-index, finish-reason, content-shape, or extra-choice rejection coverage; current code accepts several invalid variants.
- Frozen authority: one success test inspects a subset of body options but does not prove every reserved field, complete Run/Tunnel request, credential binding, or returned dispatch field.
- Frame state: body-before-start, provider error, and missing terminal exist; duplicate start, body after terminal, duplicate terminal, non-2xx, nil/unknown/usage frames, and exact boundary bytes are absent.
- Deadline/failure: cancellation before submission exists; acquired-tunnel deadline, generic submit error, nil/normalized result, and close-on-every-acquired-path evidence are absent.
- Plan result/artifact: four JSON failures exist; provider failure, context cancellation, envelope rejection, artifact-write rejection, empty input, exact/over rendered bound, and no-write assertions are absent.

### Symbol References

- No symbol is renamed or removed. The private `decodeSingleRequestChatResponse`, provider test helpers, and Plan test helpers are referenced only in the same package tests and stage files.

### Split Judgment

- Keep one packet. Successful Chat-envelope acceptance and the test matrix are one compact S08 verification boundary; splitting source validation from its regression evidence would not produce an independently trustworthy PASS state.
- The encoded predecessor `17_internal_artifact_wire` is satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/17_internal_artifact_wire/complete.log`.

### Scope Rationale

- Modify only the private provider response codec, its tests, the Plan-stage tests, and the active review artifact.
- Do not change admission/config contracts, provider-pool/service semantics, controller artifact APIs, Work/Review drivers, composite construction/installation, public Anthropic output, roadmap state, or S12 external evidence.
- The current spec text may remain unchanged only after the expanded executable evidence makes its ordered-frame/strict-result/artifact statements true; any discovered mismatch must be recorded as a blocker rather than silently widening the write boundary.

### Final Routing

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`.
- Build closures: scope/context/verification/evidence/ownership/decision all true. Scores `1/1/0/2/1` => G05, base `local-fit`; `review_rework_count=2` and `evidence_integrity_failure=true` select `recovery-boundary`, yielding `worker/cloud/G05` and `PLAN-cloud-G05.md`.
- Review closures: all true. Scores `1/1/0/2/1` => G05, `official-review`, `review/cloud/G05`, `CODE_REVIEW-cloud-G05.md`.
- `large_indivisible_context=false`; positive loop risks are `temporal_state`, `boundary_contract`, and `structured_interpretation` (`loop_risk_count=3`); `risk_boundary_matched=false`, `recovery_boundary_matched=true`; no capability gap.

## Dependencies and Execution Order

1. The `17_internal_artifact_wire` predecessor remains satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/17_internal_artifact_wire/complete.log`.
2. Tighten the provider response acceptance condition first, then add the response/dispatch/frame/deadline tests that prove it.
3. Complete the Plan runner failure matrix and rerun the exact S08 verification set without installing the outer executor.

## Implementation Checklist

- [ ] Require one successful index-zero assistant Chat choice and add deterministic provider response, dispatch, frame-order, exact-limit, deadline, generic-error, and close regressions.
- [ ] Add the missing Plan provider/cancellation/envelope/artifact/render-bound failure matrix with exact no-write and generic-error assertions.
- [ ] Run dependency, discovery, focused, vet, broader Edge, no-activation, spec, formatting, and diff-hygiene checks with fresh output.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Enforce one successful assistant result

**Problem**

`apps/edge/internal/openai/single_request_provider_stage.go:171` accepts any single choice without tool calls. A `user` role or `finish_reason=length|tool_calls` with syntactically valid Plan JSON reaches `WriteInternalArtifact`, contradicting the fail-closed successful-assistant result boundary.

**Solution**

Validate the complete selected choice before returning output. Require index zero, role `assistant`, finish reason `stop`, and no tool calls; keep all rejection errors generic and retain the existing exact-one-choice/trailing-value checks.

Before (`apps/edge/internal/openai/single_request_provider_stage.go:171`):

```go
if err := decoder.Decode(&decoded); err != nil || decoder.More() || len(decoded.Choices) != 1 || len(decoded.Choices[0].Message.ToolCalls) != 0 {
```

After:

```go
choice := decoded.Choices[0]
if choice.Index != 0 || choice.Message.Role != "assistant" || choice.FinishReason != "stop" || len(choice.Message.ToolCalls) != 0 {
    return nil, errProviderStageGeneric
}
```

**Modified Files and Checklist**

- [ ] Update `apps/edge/internal/openai/single_request_provider_stage.go` with the successful-assistant result predicate.
- [ ] Update `apps/edge/internal/openai/single_request_provider_stage_test.go` with role/index/finish/content/choice failure cases and stable generic-error assertions.

**Test Strategy**

Add `TestSingleRequestProviderStageRejectsResponseEnvelope` with table cases for wrong/empty role, nonzero index, empty/`length`/`tool_calls` finish reason, unexpected tool calls, malformed/unknown/trailing JSON, invalid content shape, zero choices, and multiple choices. Retain one valid `assistant`/`stop` case and assert every acquired tunnel closes.

**Verification**

Run `go test ./apps/edge/internal/openai -run '^TestSingleRequestProviderStage(UsesFrozenOptionsAndDispatch|RejectsResponseEnvelope)$' -count=1`; all successful and rejected response-envelope cases must pass freshly.

### [REVIEW_API-2] Make the S08 evidence matrix complete

**Problem**

`apps/edge/internal/openai/single_request_provider_stage_test.go:93`, line 129, and `apps/edge/internal/openai/single_request_plan_stage_test.go:63` contain only a fraction of the failure cases required by the archived Plan and checked active review. The current spec therefore cites deterministic ordered-frame, strict-result, deadline, and artifact evidence that is not present in source.

**Solution**

Expand table-driven fixtures around the existing private seams. Capture the effective `ProviderPoolDispatchRequest`, vary every returned dispatch field and frame order, use exact byte limits and a blocked acquired tunnel for deadline behavior, inject provider/controller failures, and assert generic errors, tunnel close, envelope ordering, and artifact no-write behavior.

Before (`apps/edge/internal/openai/single_request_plan_stage_test.go:63`):

```go
for _, raw := range []string{"{}", "{\"plan\":\"x\"}", "{\"plan\":\"x\",\"verification\":\"y\",\"unknown\":1}", "{\"plan\":\"x\",\"verification\":\"y\"} {}"} {
```

After:

```go
tests := []struct {
    name string
    arrange func(*planStageFixture)
}{
    // strict result, exact bound, provider, cancellation, envelope, and artifact failures
}
```

**Modified Files and Checklist**

- [ ] Expand `apps/edge/internal/openai/single_request_provider_stage_test.go` for reserved body fields, exact Run/Tunnel authority, every selected-dispatch mismatch, normalized/nil results, ordered-frame variants, exact byte cap, acquired-tunnel deadline, provider errors, and close behavior.
- [ ] Expand `apps/edge/internal/openai/single_request_plan_stage_test.go` for malformed/empty/unknown/trailing/duplicate result shapes, exact/over rendered size, provider failure, context cancellation, envelope rejection, artifact failure, invalid input, and no-write/no-provider assertions.

**Test Strategy**

Use table-driven tests named under `TestSingleRequestProviderStage...` and `TestSingleRequestPlanStage...` so the canonical discovery regex selects every family. Tests must exercise production functions directly, use deterministic channels/fakes only, and assert stable sentinels instead of raw provider/controller text.

**Verification**

Run `go test ./apps/edge/internal/openai -run 'TestSingleRequest(ProviderStage|PlanStage)' -count=1`; the complete provider/result/artifact matrix must pass freshly.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/single_request_provider_stage.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/single_request_provider_stage_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `apps/edge/internal/openai/single_request_plan_stage_test.go` | REVIEW_API-2 |
| `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/CODE_REVIEW-cloud-G05.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

Fresh output is required; Go tests use `-count=1` and cached output is not acceptable.

1. `bash -c 'set -euo pipefail; shopt -s nullglob; candidates=(agent-task/m-iop-owned-single-request-agent-execution/17_*/complete.log agent-task/m-iop-owned-single-request-agent-execution/17+*/complete.log agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/17_*/complete.log agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/17+*/complete.log); printf "%s\n" "${candidates[@]}"; ((${#candidates[@]} == 1))'` — prints exactly one predecessor completion path and exits zero.
2. `bash -c 'set -euo pipefail; listed=$(go test ./apps/edge/internal/openai -list "TestSingleRequest(ProviderStage|PlanStage)"); printf "%s\n" "$listed"; rg -q "^TestSingleRequestProviderStage" <<<"$listed"; rg -q "^TestSingleRequestPlanStage" <<<"$listed"'` — both canonical test families remain discoverable.
3. `go test ./apps/edge/internal/openai -run '^TestSingleRequestProviderStage' -count=1` — full provider authority/response/dispatch/frame/deadline matrix passes freshly.
4. `go test ./apps/edge/internal/openai -run '^TestSingleRequestPlanStage' -count=1` — full Plan JSON/render/envelope/artifact matrix passes freshly.
5. `go test ./apps/edge/internal/service ./apps/edge/internal/openai -run 'TestSingleRequest(Binding|PresetBinding|ProviderStage|PlanStage)' -count=1` — admission and S08 integration fixtures pass freshly.
6. `go vet ./apps/edge/internal/service ./apps/edge/internal/openai` — changed packages vet cleanly.
7. `go test ./apps/edge/... -count=1` — broader Edge regression passes freshly.
8. `test ! -e apps/edge/internal/openai/single_request_executor.go && bash -c 'set -euo pipefail; if rg --sort path -n "NewSingleRequestExecutor|SetSingleRequestExecutor" apps/edge/internal/input/manager.go; then exit 1; else test $? -eq 1; fi'` — exits zero with no incomplete production activation.
9. `rg --sort path -n 'Plan stage|plan\.md|not installed|deferred' agent-spec/runtime/edge-node-execution.md` — current spec still limits the component to implemented-but-not-installed Plan behavior and deferred later stages/S12.
10. `gofmt -d apps/edge/internal/openai/single_request_provider_stage.go apps/edge/internal/openai/single_request_provider_stage_test.go apps/edge/internal/openai/single_request_plan_stage.go apps/edge/internal/openai/single_request_plan_stage_test.go` — exits zero with no output.
11. `git diff --check` — exits zero with no whitespace errors.

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