<!-- task=m-iop-owned-single-request-agent-execution/23+22_error_cancel plan=0 tag=API milestone-task=error-cancel -->

# Closed single-request error, cancel, and length terminals

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G10.md` is the mandatory last implementation step. Run every verification command, 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 the review evidence fields; do not ask the user, call a user-input tool, create a control-plane stop file, or change the owner or scope.

## Background

The activated single-request executor will preserve one controller and one outer Anthropic request, but the current service and projector expose only a generic failure/cancel distinction and always finalize success as `end_turn`. SDD S11 requires every provider/tool timeout, budget exhaustion, repetition/no-progress, malformed call, context/output limit, and disconnect to converge through one closed terminal policy without fallback, partial success, or a second Claude request.

## Analysis

### Files Read

- `AGENTS.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/node/rules.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/finalize-task-routing/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/node-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/testing-smoke.md`
- `agent-roadmap/current.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-spec/index.md`
- `agent-spec/runtime/edge-node-execution.md`
- `agent-spec/runtime/stream-evidence-gate.md`
- `agent-contract/index.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-contract/inner/execution-runtime.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `apps/edge/internal/service/single_request.go`
- `apps/edge/internal/service/single_request_types.go`
- `apps/edge/internal/service/single_request_tool_loop.go`
- `apps/edge/internal/service/single_request_tool_types.go`
- `apps/edge/internal/service/single_request_observation.go`
- `apps/edge/internal/service/single_request_metrics.go`
- `apps/edge/internal/service/single_request_test.go`
- `apps/edge/internal/service/single_request_tool_loop_test.go`
- `apps/edge/internal/openai/anthropic_handler.go`
- `apps/edge/internal/openai/single_request_anthropic_stream.go`
- `apps/edge/internal/openai/single_request_handler_test.go`
- `apps/edge/internal/openai/single_request_anthropic_stream_test.go`
- `agent-task/m-iop-owned-single-request-agent-execution/18+17_plan_stage/PLAN-local-G06.md`
- `agent-task/m-iop-owned-single-request-agent-execution/19+18_work_stage/PLAN-cloud-G08.md`
- `agent-task/m-iop-owned-single-request-agent-execution/20+19_review_repair/PLAN-cloud-G07.md`
- `agent-task/m-iop-owned-single-request-agent-execution/21+20_single_request_executor/PLAN-local-G08.md`
- `agent-task/m-iop-owned-single-request-agent-execution/22+21_executor_activation/PLAN-local-G07.md`

### SDD Criteria

- Approved, unlocked SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`; `milestone-task=error-cancel` maps to S11.
- S11 requires a matrix covering stage/request exhaustion, repetition/no-progress, malformed calls, provider/tool timeout, output/context limits, and disconnect. Every row must prove no retry/fallback/partial-success/second ingress and exactly one standard error, cancel, or length outcome.
- Evidence Map row S11 requires the budget/error/cancel/length/repetition terminal matrix. That row drives API-1 through API-4 and the focused race matrix in Final Verification.

### Verification Context

- No handoff was supplied. Repository-native evidence is the local test rules, the existing service coordinator/tool-loop tests, the Anthropic buffered/SSE tests, the approved SDD, and the outer/runtime contracts.
- Starting checkout is branch `feature/iop-owned-single-request-agent-execution`, HEAD `06e43f2aba7f42acb407cecb9761f30cbcc1df3c`, initially clean. Go tests must use `-count=1`; race tests and `git diff --check` are mandatory. No external provider is needed for S11 because deterministic stage/provider/tool fixtures exercise the real coordinator and HTTP projectors.
- Task 22 is active and has no `complete.log`. Implementation must wait for it, resolve exactly one completion path, read that exact log, then read every completed task-18 through task-22 source file before editing. Any mismatch between those completed symbols and this plan is recorded as a blocker/deviation instead of guessed around.
- The common SDD suite includes config, streamgate, Edge OpenAI/service, Node runtime/transport/workspace, `make proto`, and diff checks. Fresh execution is required.

### Test Coverage Gaps

- Existing service tests cover tool iteration/output/deadline, request cancellation, cleanup races, and generic error classes, but no caller-safe terminal disposition survives from the executor through both Anthropic projectors.
- Existing provider-stage plans cover malformed response rejection and per-stage cancellation separately, but do not prove repetition/no-progress or one closed cross-stage failure policy.
- Existing buffered/SSE tests cover one success or generic error terminal and disconnect silence, but not a table of timeout, budget, malformed, context, output-length, and no-second-ingress outcomes.

### Symbol References

- `SingleRequestResult` is constructed and cloned in `apps/edge/internal/service/single_request.go` and consumed by the Anthropic buffered/stream projectors plus current tests. Extend it compatibly; update every repository composite literal found by the final symbol search.
- `singleRequestAnthropicTerminalKind`, `singleRequestAnthropicError`, `writeAnthropicSingleRequestTerminal`, and `pumpSingleRequestAnthropicStream` are the caller projection sites. No public HTTP request field or Edge-Node wire symbol is renamed.
- The predecessor-created `singleRequestProviderStage`, Plan/Work/Review runners, and `single_request_executor.go` are explicit future call sites. Their exact completed definitions must be reread after task 22 completes.

### Split Judgment

- This packet is the indivisible S11 correctness boundary: stage classification, service terminal ownership, and buffered/SSE projection must agree atomically or exactly-once behavior can regress. Splitting those layers would permit a typed internal outcome with a generic or duplicate public terminal.
- `23+22_error_cancel` depends on sibling 22. `agent-task/m-iop-owned-single-request-agent-execution/22+21_executor_activation/` is active and its `complete.log` is currently missing; no archive candidate was read.
- S12 harness construction is independent after task 22 and lives in task 24. Actual qualification waits for both this packet and task 24 in task 25.

### Scope Rationale

Include only the closed terminal vocabulary, stage failure/no-progress classification, one controller terminal winner, Anthropic buffered/SSE mapping, S11 matrix, and current contract/spec synchronization. Exclude retry/reselection, dynamic modes, Edge-Node protobuf changes, new metrics labels, actual Claude execution, runtime deployment, and roadmap state mutation.

### Final Routing

- `evaluation_mode=first-pass`; build/review closures are all true, with no capability gap.
- Build scores `2/2/2/2/2` => G10, base/final `grade-boundary`, `worker/cloud/G10`, `PLAN-cloud-G10.md`.
- Review scores `2/2/2/2/2` => G10, `official-review`, `review/cloud/G10`, `CODE_REVIEW-cloud-G10.md`.
- `large_indivisible_context=false`; positive loop risks are `temporal_state`, `concurrent_consistency`, `boundary_contract`, and `structured_interpretation` (4). `review_rework_count=0`; `evidence_integrity_failure=false`; finalizer `finalize-task-policy.sh`, mode `pair`.

## Dependencies and Execution Order

1. Resolve exactly one sibling-22 `complete.log` with Final Verification command 1, read only that log, then read the completed provider-stage, Plan/Work/Review, composite executor, activation, service, and projector files before implementation.
2. Define and test the closed service terminal contract before threading stage outcomes through the composite.
3. Project the same disposition through buffered and streaming Anthropic responses, then synchronize contracts/spec and run the full SDD suite.

## Implementation Checklist

- [ ] Add a closed, copy-safe single-request terminal disposition that distinguishes `end_turn`, `length`, sanitized error classes, and caller cancellation while preserving exactly-once cleanup/acknowledgement.
- [ ] Classify provider/tool timeout, stage/request budgets, repetition/no-progress, malformed calls, context/output limits, and disconnect in the completed stage/composite path without retry, fallback, partial-success, or retained waiters.
- [ ] Project the closed disposition consistently through buffered and SSE Anthropic responses and add the complete S11 one-ingress/one-terminal race matrix.
- [ ] Synchronize the Anthropic outer contract, execution runtime contract, and current implementation spec with the implemented error/cancel/length policy.
- [ ] Run dependency, focused race, compatibility, full SDD, proto, deterministic symbol/document, and diff verification freshly.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [API-1] Carry one closed terminal disposition

**Problem**

`apps/edge/internal/service/single_request.go:46-48` carries only output, while failed/cancelled progress at lines 304-317 loses the safe terminal reason before the endpoint sees it. The endpoint therefore cannot distinguish length, timeout, malformed/context failure, and caller cancel without inspecting raw errors.

**Solution**

Add a closed exported terminal DTO with validated kind and safe error class, carry it on finalizing results or terminal progress, deep-copy it, and make the first terminal winner authoritative across cleanup and endpoint acknowledgement.

Before (`apps/edge/internal/service/single_request.go:46-48`):

```go
type SingleRequestResult struct {
	Output string
}
```

After:

```go
type SingleRequestTerminalKind string
type SingleRequestTerminalErrorClass string

const (
	SingleRequestTerminalEndTurn   SingleRequestTerminalKind = "end_turn"
	SingleRequestTerminalLength    SingleRequestTerminalKind = "length"
	SingleRequestTerminalError     SingleRequestTerminalKind = "error"
	SingleRequestTerminalCancelled SingleRequestTerminalKind = "cancelled"
)

type SingleRequestTerminalDisposition struct {
	Kind       SingleRequestTerminalKind
	ErrorClass SingleRequestTerminalErrorClass
}

type SingleRequestResult struct {
	Output   string
	Terminal SingleRequestTerminalDisposition
}
```

Keep the exact error-class vocabulary closed and raw-free; zero-value legacy result literals normalize to `end_turn` only at validation.

**Modified Files and Checklist**

- [ ] Extend validation, cloning, terminal progress, cleanup join, and acknowledgement in `apps/edge/internal/service/single_request.go`.
- [ ] Add normal, invalid, length, error, cancel, cleanup-race, and terminal-winner coverage in `apps/edge/internal/service/single_request_test.go`.

**Test Strategy**

Write `TestSingleRequestTerminalDisposition*` table/race fixtures in `apps/edge/internal/service/single_request_test.go`; assert copy safety, zero-value compatibility, first-winner stability, cleanup-before-terminal, and no raw error text.

**Verification**

Run `go test -race ./apps/edge/internal/service -run 'TestSingleRequestTerminalDisposition' -count=1`; every disposition and terminal race passes once.

### [API-2] Classify every S11 stage outcome without fallback

**Problem**

The completed provider codec and stage loops will return heterogeneous provider finishes, decoder failures, repeated calls, context/output exhaustion, and context errors. Passing generic errors from the composite would collapse length into failure and allow repetition/no-progress to consume budgets without a deterministic stop.

**Solution**

Add a request-local quality guard that fingerprints bounded stage actions/results, rejects the first proven no-progress cycle, normalizes provider finish/HTTP/context outcomes, and returns only closed service dispositions. Thread it through the completed shared provider codec, Work/Review loops, and composite; do not redispatch another stage/model after a terminal classification.

Before (`agent-task/m-iop-owned-single-request-agent-execution/21+20_single_request_executor/PLAN-local-G08.md:84`):

```go
// The predecessor composite preserves cancellation and generic errors.
```

After:

```go
// The composite converts each stage outcome exactly once through the
// request-local quality guard, submits no later provider call, and returns the
// closed service terminal disposition to the existing controller.
```

**Modified Files and Checklist**

- [ ] Add closed outcome normalization and bounded fingerprints in `apps/edge/internal/openai/single_request_quality_gate.go`.
- [ ] Thread the guard through `apps/edge/internal/openai/single_request_provider_stage.go`, `apps/edge/internal/openai/single_request_work_stage.go`, `apps/edge/internal/openai/single_request_review_stage.go`, and `apps/edge/internal/openai/single_request_executor.go` after resolving task 22.
- [ ] Add deterministic provider/tool timeout, budgets, repeat/no-progress, malformed, context/output, cancel, waiter-cleanup, and no-later-dispatch fixtures in `apps/edge/internal/openai/single_request_quality_gate_test.go`.

**Test Strategy**

Write `TestSingleRequestQualityGate*` under `-race`. Record provider dispatch count, tool call count, controller envelopes, terminal disposition, pending waiter count, and safe output. Each failing row must stop at the first terminal condition with zero fallback dispatches.

**Verification**

Run `go test -race ./apps/edge/internal/openai -run 'TestSingleRequestQualityGate' -count=1`; the complete stage matrix passes without races or extra dispatches.

### [API-3] Emit one standard Anthropic terminal

**Problem**

`apps/edge/internal/openai/anthropic_handler.go:280-303` maps every failure to generic `api_error`, while lines 316-330 always emit `stop_reason=end_turn`. `apps/edge/internal/openai/single_request_anthropic_stream.go:368-397` likewise has only success, failure, and cancelled branches.

**Solution**

Map the closed service disposition through one Anthropic policy shared by buffered and SSE projectors: success=`end_turn`, output limit=`max_tokens`, validation/context=`invalid_request_error`, provider/timeout/budget/repetition/malformed=`api_error`, caller disconnect=silent internal cancel. A length outcome never exposes private partial stage content, and no error path writes a later success terminal.

Before (`apps/edge/internal/openai/anthropic_handler.go:316-325`):

```go
stopReason := "end_turn"
response := anthropicMessageResponse{
	StopReason: &stopReason,
}
```

After:

```go
policy := anthropicSingleRequestPolicy(result.Terminal)
response := anthropicMessageResponse{
	StopReason: &policy.stopReason,
}
```

**Modified Files and Checklist**

- [ ] Centralize safe status/error/stop-reason projection in `apps/edge/internal/openai/anthropic_handler.go`.
- [ ] Apply the same policy and serialized terminal ownership in `apps/edge/internal/openai/single_request_anthropic_stream.go`.
- [ ] Add buffered real-POST matrix coverage in `apps/edge/internal/openai/single_request_handler_test.go`.
- [ ] Add SSE ordering/race/length/error/disconnect coverage in `apps/edge/internal/openai/single_request_anthropic_stream_test.go`.

**Test Strategy**

Write `TestAnthropicSingleRequestErrorCancelMatrix` and `TestSingleRequestAnthropicStreamTerminalDisposition*`. Assert HTTP/SSE shape, stop reason/error type, terminal count one, ingress delta one, no `tool_use`, no private values, no success after error, and no wire bytes after disconnect.

**Verification**

Run `go test -race ./apps/edge/internal/openai -run 'Test(AnthropicSingleRequestErrorCancelMatrix|SingleRequestAnthropicStreamTerminalDisposition)' -count=1`; all matrix rows pass freshly.

### [API-4] Synchronize the terminal contract

**Problem**

The current outer contract describes generic failure/cancel and `end_turn`-only marked success, while the implementation spec defers S11. After API-1 through API-3, those statements would be stale.

**Solution**

Document the closed mapping table, exactly-once/no-fallback/no-second-ingress invariant, cancellation silence, length privacy, deterministic S11 evidence, and the unchanged Edge-Node wire. Update only current contract/spec documents.

Before (`agent-contract/outer/anthropic-compatible-api.md:131-134`):

```text
A coordinator failure or non-disconnect cancellation writes one sanitized
error event. Caller disconnect cancels execution and suppresses further output.
```

After:

```text
The marked projector applies one closed error/cancel/length policy to buffered
and SSE responses; every non-disconnect terminal is exclusive, and disconnect
is a silent internal cancel with no later ingress or wire output.
```

**Modified Files and Checklist**

- [ ] Update `agent-contract/outer/anthropic-compatible-api.md` with the exact endpoint mapping.
- [ ] Update `agent-contract/inner/execution-runtime.md` with the internal disposition/ownership contract.
- [ ] Update `agent-spec/runtime/edge-node-execution.md` with S11 implementation and evidence, leaving S12 pending.

**Test Strategy**

No document-only test is added; executable API-1 through API-3 fixtures back every statement, and deterministic search verifies the current text.

**Verification**

Run the document search in Final Verification; every terminal kind and S11/no-second-request statement must be present.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/service/single_request.go` | API-1 |
| `apps/edge/internal/service/single_request_test.go` | API-1 |
| `apps/edge/internal/openai/single_request_quality_gate.go` | API-2 |
| `apps/edge/internal/openai/single_request_quality_gate_test.go` | API-2 |
| `apps/edge/internal/openai/single_request_provider_stage.go` | API-2 |
| `apps/edge/internal/openai/single_request_work_stage.go` | API-2 |
| `apps/edge/internal/openai/single_request_review_stage.go` | API-2 |
| `apps/edge/internal/openai/single_request_executor.go` | API-2 |
| `apps/edge/internal/openai/anthropic_handler.go` | API-3 |
| `apps/edge/internal/openai/single_request_anthropic_stream.go` | API-3 |
| `apps/edge/internal/openai/single_request_handler_test.go` | API-3 |
| `apps/edge/internal/openai/single_request_anthropic_stream_test.go` | API-3 |
| `agent-contract/outer/anthropic-compatible-api.md` | API-4 |
| `agent-contract/inner/execution-runtime.md` | API-4 |
| `agent-spec/runtime/edge-node-execution.md` | API-4 |
| `agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/CODE_REVIEW-cloud-G10.md` | API-1, API-2, API-3, API-4 |

## 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/22_*/complete.log agent-task/m-iop-owned-single-request-agent-execution/22+*/complete.log agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/22_*/complete.log agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/22+*/complete.log); printf "%s\n" "${candidates[@]}"; ((${#candidates[@]} == 1))'` — prints exactly one task-22 completion path and exits zero before implementation or review.
2. `go test -race ./apps/edge/internal/service ./apps/edge/internal/openai -run 'Test(SingleRequestTerminalDisposition|SingleRequestQualityGate|AnthropicSingleRequestErrorCancelMatrix|SingleRequestAnthropicStreamTerminalDisposition)' -count=1` — the complete S11 disposition and HTTP/SSE matrix passes without races.
3. `go test -race ./apps/edge/internal/service -run 'TestSingleRequest(InternalToolLoop|Cleanup|EnvelopeOrdering|StageBudget)' -count=1` — unchanged coordinator budgets, cleanup, and terminal ordering pass freshly.
4. `go vet ./apps/edge/internal/service ./apps/edge/internal/openai && go test ./apps/edge/... -count=1` — changed Edge packages vet and regress cleanly.
5. `go test -race -count=1 ./packages/go/config ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/node/internal/node ./apps/node/internal/transport ./apps/node/internal/workspace` — the approved SDD common suite passes freshly.
6. `make proto && git diff --exit-code -- proto/gen/iop` — protobuf generation is reproducible and this packet introduces no wire delta.
7. `rg --sort path -n 'end_turn|max_tokens|invalid_request_error|api_error|disconnect|no second|second request|S11|error-cancel' agent-contract/outer/anthropic-compatible-api.md agent-contract/inner/execution-runtime.md agent-spec/runtime/edge-node-execution.md` — current documents contain the closed policy and S11 evidence.
8. `rg --sort path -n 'SingleRequestTerminal|SingleRequestResult|singleRequestAnthropic.*Policy' apps/edge/internal/service apps/edge/internal/openai --glob '*.go'` — every result/terminal construction and projection call site is visible for review.
9. `git diff --check` — no whitespace errors.

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