<!-- task=m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator plan=5 tag=REVIEW_REVIEW_API milestone-task=single-ingress -->

# Close Final-candidate Handoff and Acknowledgement Guards

## For the Implementing Agent

Implement this plan exactly within the listed write boundary, run every verification command, fill all implementation-owned sections of `CODE_REVIEW-cloud-G08.md` with actual notes and stdout/stderr, keep the active pair in place, and report ready for review. Finalization belongs only to the code-review skill. If blocked, record exact blocker evidence, attempted commands/output, and the 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 previous follow-up closed executor-return, binding-copy, envelope-order, and terminal-race defects, but it still does not hand the final user result to the surface before terminal acknowledgement. This packet closes the remaining candidate validation and immutable delivery gap without expanding into HTTP admission or Anthropic wire translation.

## Archive Evidence Snapshot

- Current archived pair: `plan_cloud_G08_4.log` and `code_review_cloud_G08_4.log`; the review verdict is FAIL with stable Required R1 and R5, zero Suggested findings, and zero Nits.
- Required R2-R4 are closed by fresh race-tested executor-return, immutable binding/result storage, envelope ordering, and saved-stage validation.
- Fresh dependency, formatting, focused race, service, Edge vet, full Edge, and diff checks pass, but `TestSingleRequestProgressRedactionAndFinalCandidateDelivery` explicitly requires every progress result to be nil; `evidence_integrity_failure=true`.
- Roadmap carryover remains `milestone-task=single-ingress`: this packet supplies only the S01 coordinator foundation. Packet 05 still owns HTTP admission, the Edge ingress counter, endpoint tests, and outer/input documentation.

## Finding Resolution Map

| Finding | Mode | Exact fix | Changed precondition |
|---------|------|-----------|----------------------|
| Required R1 | direct-fix | In `apps/edge/internal/service/single_request.go`, reject result payloads outside `finalizing`, require and clone a non-nil finalizing candidate, and fail closed if successful acknowledgement has no retained candidate. Add nil/stale candidate regressions in `apps/edge/internal/service/single_request_test.go`. | `completed` can no longer be selected without the exact candidate supplied by the finalizing envelope. |
| Required R5 | direct-fix | In `apps/edge/internal/service/single_request.go`, publish a second clone of the retained candidate only in the reserved finalizing progress event while keeping executor message/error and non-final result data redacted. Strengthen the saturated progress regression in `apps/edge/internal/service/single_request_test.go`. | The surface can read and commit an immutable final result before acknowledgement without exposing internal executor payloads. |

## Analysis

### Files Read

- `agent-task/m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator/PLAN-cloud-G08.md`
- `agent-task/m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator/CODE_REVIEW-cloud-G08.md`
- `agent-task/m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator/plan_local_G07_3.log`
- `agent-task/m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator/code_review_cloud_G08_3.log`
- `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log`
- `apps/edge/internal/service/service.go`
- `apps/edge/internal/service/single_request.go`
- `apps/edge/internal/service/single_request_test.go`
- `apps/edge/internal/service/single_request_types.go`
- `apps/edge/internal/service/single_request_types_test.go`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-contract/inner/execution-runtime.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.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-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-owned-single-request-agent-execution/SDD.md`; status approved, SDD lock released, no user review.
- Milestone contribution: `single-ingress`; targeted S01 and decisions D02/D10 require an Edge-owned state machine, one outer terminal after surface commit, and exposure of only redacted progress plus the final user result.
- The S01 Evidence Map ultimately requires the Edge ingress counter, Claude invocation integration evidence, and Anthropic contract sync. This packet supplies the coordinator candidate/acknowledgement boundary only; packet 05 remains responsible for HTTP evidence.
- The implementation checklist and verification below therefore require a surface-readable immutable final candidate before acknowledgement while preserving the existing private envelope and exactly-once terminal invariants.

### Verification Context

- No separate verification handoff was supplied. Repository-native fallback uses the archived findings, approved SDD, Edge domain/test rules, local Edge smoke profile, current coordinator source/tests, runtime spec, and execution contract.
- Local workdir is `/config/workspace/iop-s0`; reviewer preflight found Go `go1.26.2 linux/arm64` and a shared dirty worktree. No credential, external provider, remote runner, or network service is required.
- Split predecessor 02 is satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log`.
- Fresh reviewer commands passed the dependency, formatting, focused race, service, Edge vet, full Edge, and `git diff --check` checks. Confidence is high because the public API ordering is direct: `Progress()` is readable before acknowledgement, while `Wait()` returns only after acknowledgement.

### Test Coverage Gaps

- Existing tests now cover executor-supplied completion rejection, early executor returns, binding/result storage copies, sequence/saved-stage failures, redacted messages/errors, saturation, and exactly-one terminal races.
- No test requires a finalizing envelope to contain its own result or rejects a result submitted on an earlier stage.
- The saturated progress test proves only delivery of the `finalizing` enum and explicitly rejects the non-nil final result the surface needs.
- No test mutates the surface-visible final candidate and proves that the coordinator-owned result remains unchanged.

### Symbol References

- No symbol is renamed or removed.
- `SingleRequestEnvelope`, `SingleRequestProgress`, `AcknowledgeTerminal`, and `SingleRequestExecution` are referenced only in `apps/edge/internal/service` and its tests; no endpoint caller exists yet.

### Split Judgment

- Keep one atomic follow-up. Candidate validation, progress cloning, acknowledgement, and their regressions are one compact producer-to-surface ownership invariant.
- Subtask `03+02_single_request_coordinator` depends on predecessor index 02, satisfied by archived `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log`.

### Scope Rationale

- Include only `single_request.go`, its focused test file, and implementation evidence.
- Exclude `service.go`, binding types, preset files, HTTP/Anthropic admission, ingress metrics, concrete Node/workspace/provider execution, contract/spec edits, and real Claude smoke because the remaining defects are local to final-candidate ownership at the existing service API.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer `finalize-task-policy.sh` in `pair` mode.
- Build closures are all true; scores 1/2/1/2/2 = G08. Base route is `local-fit`; `review_rework_count=2` and `evidence_integrity_failure=true` select `recovery-boundary`, cloud lane, canonical `PLAN-cloud-G08.md`.
- Build signals: `large_indivisible_context=false`; positive loop risks `temporal_state`, `concurrent_consistency`, and `boundary_contract` (3); no capability gap.
- Review closures are all true; scores 1/2/1/2/2 = G08; route `official-review`, cloud lane, canonical `CODE_REVIEW-cloud-G08.md`.

## Dependencies and Execution Order

1. Preserve the completed packet 02 preset-binding boundary at `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log`; do not modify its files.
2. Close candidate validation and the pre-acknowledgement surface handoff together.
3. Add regressions and run fresh race/full Edge verification.

## Implementation Checklist

- [ ] Require finalizing envelopes to carry a defensively copied candidate, reject result payloads on other stages, and guard successful acknowledgement against a missing retained candidate.
- [ ] Publish a separately cloned candidate only in the reserved finalizing progress event while keeping executor messages/errors and non-final results redacted.
- [ ] Add nil/stale candidate and saturated immutable surface-delivery regressions, then run dependency, formatting, focused race, package, vet, full Edge, and `git diff --check` verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Implementation Plan

### [REVIEW_REVIEW_API-1] Close final-candidate validation and surface delivery

**Problem**

- `apps/edge/internal/service/single_request.go:255` accepts `finalizing` without a result and line 256 retains result payloads from any stage.
- `apps/edge/internal/service/single_request.go:268` acknowledges success without proving a final candidate exists.
- `apps/edge/internal/service/single_request.go:383` emits only stage/message progress, while `apps/edge/internal/service/single_request_test.go:287` asserts that even finalizing progress has no result.

**Solution**

Before (`apps/edge/internal/service/single_request.go:255`):

```go
h.state = env.Stage
if env.Result != nil {
	h.result = cloneSingleRequestResult(env.Result)
}
h.emitProgressLocked(h.state, h.state == SingleRequestStateFinalizing)
```

After, validate result placement before state mutation and retain only the cloned finalizing candidate:

```go
candidate, err := h.validateEnvelopeResultLocked(env)
if err != nil {
	h.failLocked(err)
	return ErrSingleRequestInvalidState
}

h.state = env.Stage
if candidate != nil {
	h.result = candidate
}
h.emitProgressLocked(h.state, h.state == SingleRequestStateFinalizing)
```

The validator must reject every non-finalizing result, require a non-nil result for `finalizing`, and return a clone. In `AcknowledgeTerminal(true)`, check `h.result != nil` before setting `acknowledged`; fail closed if the invariant is broken.

Before (`apps/edge/internal/service/single_request.go:383`):

```go
h.notifyProgressLocked(SingleRequestProgress{
	RequestID: h.req.RequestID,
	Stage:     stage,
	Message:   safeSingleRequestProgressMessage(stage),
}, critical)
```

After, publish a separate clone only for the finalizing boundary:

```go
progress := SingleRequestProgress{
	RequestID: h.req.RequestID,
	Stage:     stage,
	Message:   safeSingleRequestProgressMessage(stage),
}
if stage == SingleRequestStateFinalizing {
	progress.Result = cloneSingleRequestResult(h.result)
}
h.notifyProgressLocked(progress, critical)
```

This result is the final public candidate, not executor message/error or internal-stage payload. Mutating the surface copy must not affect `Wait()`.

**Modified Files and Checklist**

- [ ] `apps/edge/internal/service/single_request.go` — enforce final-result placement/presence, acknowledgement defense, and immutable finalizing progress delivery.
- [ ] `apps/edge/internal/service/single_request_test.go` — add missing/stale result failures and prove saturated pre-acknowledgement candidate delivery plus copy isolation.

**Test Strategy**

- Add `TestSingleRequestFinalCandidateRequired` with nil-finalizing and earlier-stage-result variants; both must fail closed and release `Wait()`.
- Strengthen `TestSingleRequestProgressRedactionAndFinalCandidateDelivery` to require the copied final output under saturation, reject raw message/error leakage, mutate the progress result, acknowledge success, and prove `Wait()` returns the unchanged retained result.
- Keep existing R2-R4 and acknowledgement-ready terminal race regressions unchanged and run every `TestSingleRequest` under `-race -count=1`.

**Verification**

- `test -z "$(gofmt -l apps/edge/internal/service/single_request.go apps/edge/internal/service/single_request_test.go)"`
- `go test -race ./apps/edge/internal/service -run 'TestSingleRequest' -count=1`
- Expected: result placement fails closed, the final candidate is observable and isolated before acknowledgement, and all coordinator races pass.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/edge/internal/service/single_request.go` | REVIEW_REVIEW_API-1 |
| `apps/edge/internal/service/single_request_test.go` | REVIEW_REVIEW_API-1 |
| `agent-task/m-iop-owned-single-request-agent-execution/03+02_single_request_coordinator/CODE_REVIEW-cloud-G08.md` | REVIEW_REVIEW_API-1 |

## Final Verification

1. `test -f agent-task/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/02+01_preset_binding/complete.log' | wc -l)" -eq 1`
2. `test -z "$(gofmt -l apps/edge/internal/service/single_request.go apps/edge/internal/service/single_request_test.go)"`
3. `go test -race ./apps/edge/internal/service -run 'TestSingleRequest' -count=1`
4. `go test ./apps/edge/internal/service -count=1`
5. `go vet ./apps/edge/...`
6. `go test ./apps/edge/... -count=1`
7. `git diff --check`

Expected: the dependency remains uniquely satisfied; finalizing requires its own immutable candidate; the surface receives a cloned result before acknowledgement under saturation; internal messages/errors and non-final result payloads remain closed; all coordinator and Edge checks pass. HTTP ingress, concrete workspace execution, and actual Claude smoke remain unclaimed.

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