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

# Code Review Reference - REVIEW_API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `Implementation Checklist`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> Execute the plan's selected root cause, scope, files, and dependency decisions as written. Do not choose another owner, narrow/expand the write boundary, or replace a fix with another verification attempt.
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
> Finalization (`Code Review Result`, log rename, `complete.log`, archive moves, `Review-Only Checklist`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## Overview

date=2026-08-07
task=m-iop-owned-single-request-agent-execution/23+22_error_cancel, plan=3, tag=REVIEW_API

## Archive Evidence Snapshot

- The failed implementation pair is archived as `agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/plan_cloud_G10_2.log` and `agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/code_review_cloud_G10_2.log`; the review verdict is `FAIL` with Required R1, R2, and R3, `review_rework_count=1`, and `evidence_integrity_failure=true`.
- R1 affects `apps/edge/internal/openai/single_request_quality_gate.go` and `apps/edge/internal/openai/single_request_executor.go`: live `context.Background()` plus raw `context.Canceled` produced `cancelled` instead of a provider/internal error, so buffered/SSE could suppress a real failure.
- R2 affects `apps/edge/internal/openai/single_request_provider_stage.go`: a generic upstream `RESPONSE_START` with status 400 produced `error/context` instead of `error/provider`, conflicting with the outer contract's `api_error` rule for upstream 400.
- R3 affects `apps/edge/internal/service/single_request.go` and the executor parent-context handoff: a real `WallClockMS=10` expiry produced `error/timeout` instead of `error/budget`, and a provider-stage return can race the service terminal owner after the parent request context expires.
- Fresh reviewer verification passed focused/full race suites, `go vet`, Edge package tests, the approved SDD common suite, deterministic searches, and `git diff --check`. Focused review reproducers contradicted the submitted complete-matrix claim. Temporary reproducer files were removed after recording the outcomes in the archived review.
- Task 22 remains satisfied by exactly `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/22+21_executor_activation/complete.log`. The S11 `error-cancel` milestone/SDD mapping and no-proto/no-second-ingress boundary remain unchanged.

## For the Review Agent

> **[REVIEW AGENT ONLY]** The finalization steps below are review-agent only. Implementing agents must not execute this section.

Compare implementation of each item against source files and verify that output in `Verification Results` matches code.
Review completion means the following steps are finished:

1. Append verdict and `review_rework_count` / `evidence_integrity_failure` routing signals.
2. Archive `CODE_REVIEW-cloud-G10.md` → `code_review_cloud_G10_3.log` and `PLAN-cloud-G10.md` → `plan_cloud_G10_3.log`.
3. If PASS, write `complete.log` and move active task directory to `agent-task/archive/YYYY/MM/m-iop-owned-single-request-agent-execution/23+22_error_cancel/`. If WARN/FAIL, fully write the next filesystem state required by the code-review skill.
4. If PASS and task group is `m-<milestone-slug>`, preserve the first-line `milestone-task` metadata in `complete.log` and report it for the runtime aggregation event. Roadmap state evaluation belongs to `sync-milestone-workstate`.
5. Check applicable `Review-Only Checklist` items at the final `.log` location before reporting.

---

## Implementation Item Completion

| Item | Status |
|------|---------|
| REVIEW_API-1 | [x] |
| REVIEW_API-2 | [x] |
| REVIEW_API-3 | [x] |

## Implementation Checklist

- [x] Make cancellation classification context-authoritative in the quality gate and composite fallback, then prove live-context provider/service cancellation is an error while real caller disconnect remains silent.
- [x] Separate generic upstream HTTP 400/5xx provider failures from deterministic HTTP 413 context-limit evidence and add the response-start status matrix.
- [x] Make the service the sole parent request-context terminal owner, classify request wall-clock expiry as budget across monitor/executor races, and preserve independent stage/provider timeout classification.
- [x] Run focused race, compatibility, full Edge/SDD, protobuf reproducibility, deterministic contract/symbol, and diff-hygiene verification freshly.
- [x] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Review-Only Checklist

> **[REVIEW AGENT ONLY]** This checklist is used only by the review agent.
> Implementing agents must not modify or check this section.

- [x] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`.
- [x] Verify that verdict, `Dimension Assessment`, and Required/Suggested/Nit classifications match.
- [x] Archive active `CODE_REVIEW-*-G??.md` to `code_review_cloud_G10_3.log`.
- [x] Archive active `PLAN-*-G??.md` to `plan_cloud_G10_3.log`.
- [x] Verify that the Agent-Ops managed block in `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores `agent-roadmap/current.md`.
- [ ] If PASS, write `complete.log` based on `agent-ops/skills/common/code-review/templates/complete-log-template.md` and leave no active `.md` files.
- [ ] If PASS, move active task directory `agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/` to `agent-task/archive/YYYY/MM/m-iop-owned-single-request-agent-execution/23+22_error_cancel/` and update this checklist at the final archive path.
- [ ] If PASS and task group is `m-<milestone-slug>`, preserve and report `milestone-task` metadata for runtime aggregation, without modifying roadmap or directly calling `update-roadmap`.
- [ ] If PASS for split work, remove empty active parent `agent-task/m-iop-owned-single-request-agent-execution/` or verify it was kept due to remaining siblings/files.
- [x] If WARN/FAIL, write the next filesystem state matching code-review verdict and do not write `complete.log`.

## Deviations from Plan

- Added one test-only update in `apps/edge/internal/service/single_request_observation_test.go`, which was not listed in the modified-files table. The required full Edge regression exposed its stale expectation that request wall-clock expiry is observed as `timeout`; the assertion now requires `internal_tool_budget`, matching REVIEW_API-3. Production scope did not expand.
- External Claude/Mac full-cycle execution was not run. It remains explicitly assigned to SDD S12/`claude-smoke` and is excluded from this deterministic S11 follow-up.

## Key Design Decisions

- Raw `context.Canceled` is not cancellation authority. Provider paths require a cancelled authoritative context, while service paths additionally accept only the owned `ErrSingleRequestCancelled` sentinel; otherwise the failure remains provider/internal-tool classified.
- Only HTTP 413 is deterministic response-start context-limit evidence. Generic HTTP 400 and 5xx statuses remain provider failures.
- `submitSingleRequestClosedTerminal` returns an expired/cancelled parent context without submitting a competing envelope. The service maps its immutable request deadline to budget in both monitor and executor-return paths and disables a stage timer whose deadline is not earlier than the request deadline. A genuinely earlier stage deadline remains timeout.
- Existing buffered/SSE projectors, contract/spec vocabulary, metrics, protobuf, retry/fallback behavior, and ingress shape were left unchanged. Spec update not needed: the living specs and Anthropic contract already describe the corrected request-budget, provider-error, and caller-disconnect policy.

## Reviewer Checkpoints

- Confirm R1 checks both quality-gate branches and `submitSingleRequestClosedTerminal`: live-context raw cancellation is provider/internal failure, real parent cancellation is caller-owned, and an expired parent context produces no competing stage envelope.
- Confirm buffered and SSE end-to-end regressions use the production composite/provider stage and emit exactly one sanitized `api_error` for live-context provider cancellation instead of a silent terminal.
- Confirm R2 classifies generic response-start 400/5xx as provider while 413 and explicit provider context finish reasons remain context limit.
- Confirm R3 fixes both the request monitor and executor-return race, leaves caller cancellation silent, preserves independent stage/provider timeout, and drains bridge/tool waiters without later dispatch.
- Confirm no production projector, contract/spec, metric vocabulary, protobuf, Edge-Node wire, retry, fallback, or second-ingress behavior was added.
- Confirm the S11 Evidence Map matrix is now exercised by real request-context expiry and end-to-end public shapes, not only synthetic dispositions.

## Verification Results

Paste actual stdout/stderr for every command. If output is too long, record the exact command and saved output path; summaries are insufficient.

### 1. Failed review and predecessor evidence

Command:

```sh
test -f agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/code_review_cloud_G10_2.log && test -f agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/22+21_executor_activation/complete.log && rg -n 'Overall Verdict: FAIL|Required R1|Required R2|Required R3|review_rework_count=1|evidence_integrity_failure=true' agent-task/m-iop-owned-single-request-agent-execution/23+22_error_cancel/code_review_cloud_G10_2.log
```

Output:

```text
284:- Overall Verdict: FAIL
295:  - Required R1 — `apps/edge/internal/openai/single_request_quality_gate.go:72`, `apps/edge/internal/openai/single_request_quality_gate.go:116`, and the fallback at `apps/edge/internal/openai/single_request_executor.go:122` classify any raw `context.Canceled` error as caller cancellation even when the supplied request context is still live. A focused call to `providerFailure(context.Background(), context.Canceled, ...)` produced `{Kind:cancelled ErrorClass:}` instead of the required provider failure, which can silently suppress a real provider/internal error at the Anthropic surface. Classify cancellation only from an authoritatively cancelled request/stage context or an owned service cancellation sentinel; keep a raw `context.Canceled` from a live context in the provider/internal error class, and add buffered/SSE regression coverage proving it is not silently dropped.
296:  - Required R2 — `apps/edge/internal/openai/single_request_provider_stage.go:138` maps every upstream HTTP 400 response to `errProviderStageContextLimit`. A focused `RESPONSE_START` status-400 frame was classified as `{Kind:error ErrorClass:context}`, so an ordinary provider rejection is projected as caller-facing `invalid_request_error` rather than the contract's sanitized `api_error`. Reserve context-limit classification for deterministic context-limit evidence such as HTTP 413 or an explicit closed provider signal; keep generic 400 responses as provider failures and add a status matrix regression.
297:  - Required R3 — `apps/edge/internal/service/single_request.go:293` and `apps/edge/internal/service/single_request.go:772` handle expiry of the immutable request wall-clock context with `singleRequestErrorClassTimeout`; `apps/edge/internal/openai/single_request_executor.go:76` can also race the service owner by submitting a stage timeout after its parent request context expires. A focused request with `WallClockMS=10` and a blocked executor produced `{Kind:error ErrorClass:timeout}` instead of `{Kind:error ErrorClass:budget}`. Make the service the sole owner of parent request-context termination, emit the closed budget class for request wall-clock exhaustion, and add a real request-budget regression that proves exactly one terminal outcome, no later provider/tool work, and no retained waiter while preserving an independently expired stage/provider timeout as `timeout`.
299:  - `review_rework_count=1`
300:  - `evidence_integrity_failure=true`
301:- Next Step: Prepare and implement a `REVIEW_API` follow-up plan for Required R1, R2, and R3; do not write `complete.log`.
```

### 2. New ownership and classification race regressions

Command:

```sh
go test -race ./apps/edge/internal/service ./apps/edge/internal/openai -run 'Test(SingleRequestRequestWallClockBudgetDisposition|SingleRequestQualityGateCancellationOwnership|SingleRequestQualityGateProviderHTTPStatusClassification|SingleRequestExecutorParentContextOwnership|SingleRequestExecutorRequestBudgetOwnership|AnthropicSingleRequestLiveContextProviderCancellationBuffered|SingleRequestAnthropicStreamLiveContextProviderCancellation)$' -count=1
```

Output:

```text
ok  	iop/apps/edge/internal/service	1.507s
ok  	iop/apps/edge/internal/openai	1.316s
```

### 3. Complete S11 and compatibility matrices

Command:

```sh
go test -race ./apps/edge/internal/service ./apps/edge/internal/openai -run 'Test(SingleRequestTerminalDisposition|SingleRequestQualityGate|SingleRequestExecutor(Cancellation|StageFailures|TerminalWaiterCleanup)|AnthropicSingleRequestErrorCancelMatrix|SingleRequestAnthropicStreamTerminalDisposition)' -count=1
```

Output:

```text
ok  	iop/apps/edge/internal/service	1.059s
ok  	iop/apps/edge/internal/openai	1.118s
```

### 4. Edge vet and package regressions

Command:

```sh
go vet ./apps/edge/internal/service ./apps/edge/internal/openai && go test ./apps/edge/... -count=1
```

Output:

```text
ok  	iop/apps/edge/cmd/edge	0.181s
ok  	iop/apps/edge/internal/authprojection	0.034s
ok  	iop/apps/edge/internal/bootstrap	0.468s
ok  	iop/apps/edge/internal/configrefresh	0.110s
ok  	iop/apps/edge/internal/controlplane	6.626s
ok  	iop/apps/edge/internal/edgecmd	0.120s
ok  	iop/apps/edge/internal/edgevalidate	0.051s
ok  	iop/apps/edge/internal/events	0.029s
ok  	iop/apps/edge/internal/input	0.064s
ok  	iop/apps/edge/internal/input/a2a	0.046s
ok  	iop/apps/edge/internal/node	0.043s
ok  	iop/apps/edge/internal/openai	8.393s
ok  	iop/apps/edge/internal/opsconsole	0.050s
ok  	iop/apps/edge/internal/service	6.915s
ok  	iop/apps/edge/internal/transport	4.734s
```

### 5. Approved SDD common race suite

Command:

```sh
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
```

Output:

```text
ok  	iop/packages/go/config	1.998s
ok  	iop/packages/go/streamgate	2.039s
ok  	iop/apps/edge/internal/openai	12.807s
ok  	iop/apps/edge/internal/service	8.048s
ok  	iop/apps/node/internal/node	3.707s
ok  	iop/apps/node/internal/transport	6.617s
ok  	iop/apps/node/internal/workspace	5.923s
```

### 6. Protobuf reproducibility

Command:

```sh
b_before=$(sha256sum proto/gen/iop/runtime.pb.go) && make proto && b_after=$(sha256sum proto/gen/iop/runtime.pb.go) && test "$b_before" = "$b_after" && printf '%s\n' "$b_after"
```

Output:

```text
protoc \
	--go_out=. \
	--go_opt=module=iop \
	--proto_path=. \
	proto/iop/runtime.proto \
	proto/iop/node.proto \
	proto/iop/control.proto \
	proto/iop/job.proto
5c9d6c580ecf9c9fba857787b5d2757757d7289b267b44074bf6c2108b4cc1a9  proto/gen/iop/runtime.pb.go
```

### 7. Contract/spec and production-symbol searches

Command:

```sh
rg --sort path -n 'upstream error \(400/502\)|every other `error/\*`|caller disconnect|request wall-clock|error-cancel|S11' agent-contract/outer/anthropic-compatible-api.md agent-spec/runtime/edge-node-execution.md agent-spec/input/openai-compatible-surface.md && rg --sort path -n 'providerFailure|serviceFailure|submitSingleRequestClosedTerminal|StatusRequestEntityTooLarge|singleRequestErrorClassInternalToolBudget' apps/edge/internal/openai apps/edge/internal/service --glob '*.go'
```

Output:

```text
agent-contract/outer/anthropic-compatible-api.md:128:| every other `error/*` | `502 api_error` with a fixed safe message | one `error` event of type `api_error` |
agent-contract/outer/anthropic-compatible-api.md:129:| `cancelled` | no response body after caller disconnect | no later event after caller disconnect |
agent-contract/outer/anthropic-compatible-api.md:138:completion only and cannot write a second terminal. This is the implemented S11
agent-contract/outer/anthropic-compatible-api.md:139:`error-cancel` boundary; external Claude qualification remains deferred to S12.
agent-contract/outer/anthropic-compatible-api.md:189:iteration/output/deadline or request wall-clock budgets fail closed without
agent-contract/outer/anthropic-compatible-api.md:394:6. on caller disconnect, silent cancellation with no later event.
agent-contract/outer/anthropic-compatible-api.md:426:- `api_error`: provider dispatch 실패, tunnel unavailable, timeout, upstream error (400/502)
agent-spec/runtime/edge-node-execution.md:104:    notes: Request-local S11 stage classification and fixed-hash repetition/no-progress detection
agent-spec/runtime/edge-node-execution.md:107:    notes: S11 provider, timeout, budget, malformed, context, length, cancel, tool, and no-progress terminal evidence
agent-spec/runtime/edge-node-execution.md:206:| single-request S11 terminal policy | One validated, copy-safe terminal disposition is frozen across envelope/result/progress with kinds `end_turn`, `length`, `error`, and `cancelled`. Error classes are `provider`, `validation`, `timeout`, `budget`, `repetition`, `malformed`, `context`, `internal_tool`, and `workspace_cleanup`. Cleanup can replace a pending success/length before publication; no acknowledgement race can publish a second terminal. |
agent-spec/runtime/edge-node-execution.md:211:| internal workspace tool loop | The service decodes only `workspace_read`, `workspace_list`, `workspace_write`, `workspace_delete`, and `workspace_command`, opens the admitted workspace once, dispatches one call at a time on the frozen generation, and delivers one deep-copied typed result to the emitting executor continuation. Unique request/stage/tool correlation, per-stage iteration/output/deadline limits, request wall-clock budget, and typed cancel fail closed without external continuation or reselection. |
agent-spec/runtime/edge-node-execution.md:236:- The service freezes the first public terminal candidate. Legacy successful results normalize to `end_turn`; output limits produce `length`; caller disconnect produces silent `cancelled`; validation/context become `invalid_request_error`; other errors become `api_error`. Buffered and SSE projectors share that policy, emit at most one terminal, and never expose private partial stage content for `length`. This completes deterministic S11 `error-cancel` evidence without changing the Edge-Node protobuf wire. S12 external Claude/Mac qualification remains pending.
agent-spec/runtime/edge-node-execution.md:329:- `go test -race ./apps/edge/internal/service ./apps/edge/internal/openai -run 'Test(SingleRequestTerminalDisposition|SingleRequestQualityGate|AnthropicSingleRequestErrorCancelMatrix|SingleRequestAnthropicStreamTerminalDisposition)' -count=1` — deterministic S11 error-cancel/length matrix, first-terminal ownership, one ingress, no second request, disconnect silence, and raw-free output evidence.
agent-spec/runtime/edge-node-execution.md:341:- The composite single-request executor is installed at Edge input startup (`apps/edge/internal/input/manager.go`), wiring the active Plan -> Work -> Review stage pipeline for single-request execution. Private stage outcomes use the implemented closed S11 terminal policy and stop without retry/fallback or a second request. Deterministic local activation and terminal evidence are proven, while actual external Claude qualification remains explicitly deferred to S12 (`claude-smoke`).
agent-spec/runtime/edge-node-execution.md:346:- 2026-08-07: Implemented the S11 `error-cancel` boundary: one frozen service terminal disposition, request-local typed stage classification, fixed-hash repetition/no-progress detection, shared buffered/SSE Anthropic mapping, silent disconnect cancellation, private-partial suppression for `max_tokens`, and deterministic one-ingress/one-terminal/no-second-request evidence. The Edge-Node protobuf wire is unchanged and S12 remains pending.
agent-spec/input/openai-compatible-surface.md:143:    notes: Request-local S11 stage classification and fixed-hash repetition/no-progress detection
agent-spec/input/openai-compatible-surface.md:146:    notes: S11 timeout, budget, repetition, malformed, context, length, cancel, and tool terminal evidence
agent-spec/input/openai-compatible-surface.md:167:| marked single-request S11 terminal policy | The service freezes one closed `end_turn`, `length`, `error`, or `cancelled` disposition. `error` classes are provider, validation, timeout, budget, repetition, malformed, context, internal-tool, and workspace-cleanup. Buffered and SSE share one projection: `end_turn`; `max_tokens` with no private partial output; `400 invalid_request_error` for validation/context; `502 api_error` for other failures; and silent cancellation after caller disconnect. No terminal classification retries, falls back, opens a second request, or later writes success. |
agent-spec/input/openai-compatible-surface.md:169:| marked internal workspace tool loop | The service accepts only closed read/list/write/delete/command calls from the saved internal stage, opens the admitted Node workspace once, executes calls sequentially on the frozen connection generation, correlates one result to one unique request/stage/tool identity, and resumes only through the emitting executor's optional continuation. Strict decoding, capability checks, cumulative per-stage iteration/output/deadline limits, request wall-clock budget, and typed cancellation fail closed without fallback or another Messages request. |
agent-spec/input/openai-compatible-surface.md:257:- A marked single-request Messages dispatch requires the narrow service coordinator capability and never falls back to the generic provider pool. The handler copies the immutable binding and request input and counts the accepted HTTP admission once with no labels. The service projects exactly one frozen terminal candidate through both response modes: buffered/SSE `end_turn`; buffered/SSE `max_tokens` without private partial content; `invalid_request_error` for validation/context; `api_error` for provider, timeout, budget, repetition, malformed, internal-tool, and workspace-cleanup failures; or silent cancellation after caller disconnect. The streaming path maps only fixed plan/work/review/repair summaries, serializes pings and monotonic text-block indices with one terminal owner, stops and joins liveness before terminal/return, and acknowledges completion only after `message_stop`. Arbitrary progress, reasoning, tool/provider/credential/workspace data, raw failures, and internal stage terminals stay private. No classified terminal triggers retry, fallback, partial success, a second request, or a later success terminal. Count-tokens does not enter or increment this path.
agent-spec/input/openai-compatible-surface.md:315:- The marked single-request SSE projector and private workspace continuation do not add event kinds, filters, release rules, or recovery behavior to the generic Stream Evidence Gate. The active Plan -> Work -> Review composite and request-artifact cleanup use the closed S11 `error-cancel`/length policy with deterministic local evidence. S11 is implemented; only actual external Claude qualification remains explicitly deferred to S12 (`claude-smoke`).
agent-spec/input/openai-compatible-surface.md:320:- 2026-08-07: Implemented and documented S11 `error-cancel`: one closed service terminal disposition, request-local typed failure/no-progress classification, shared buffered/SSE `end_turn`/`max_tokens`/`invalid_request_error`/`api_error` mapping, silent disconnect, private-partial suppression, and deterministic one-ingress/one-terminal/no-second-request evidence. S12 external qualification remains pending.
apps/edge/internal/openai/anthropic_handler.go:668:        writeAnthropicError(w, http.StatusRequestEntityTooLarge, "invalid_request_error", "request body is too large")
apps/edge/internal/openai/single_request_executor.go:76:        return submitSingleRequestClosedTerminal(ctx, req.RequestID, seqCtrl, err)
apps/edge/internal/openai/single_request_executor.go:93:        return submitSingleRequestClosedTerminal(ctx, req.RequestID, seqCtrl, err)
apps/edge/internal/openai/single_request_executor.go:111:       return submitSingleRequestClosedTerminal(ctx, req.RequestID, seqCtrl, err)
apps/edge/internal/openai/single_request_executor.go:117:func submitSingleRequestClosedTerminal(ctx context.Context, requestID string, ctrl edgeservice.SingleRequestController, stageErr error) error {
apps/edge/internal/openai/single_request_executor_test.go:480:           err := submitSingleRequestClosedTerminal(
apps/edge/internal/openai/single_request_executor_test.go:484:                   newSingleRequestQualityGate().providerFailure(ctx, context.Canceled, errProviderStageGeneric),
apps/edge/internal/openai/single_request_executor_test.go:495:           err := submitSingleRequestClosedTerminal(
apps/edge/internal/openai/single_request_executor_test.go:499:                   newSingleRequestQualityGate().providerFailure(ctx, context.DeadlineExceeded, errProviderStageGeneric),
apps/edge/internal/openai/single_request_executor_test.go:508:           if err := submitSingleRequestClosedTerminal(context.Background(), "quality-request", controller, context.Canceled); err != nil {
apps/edge/internal/openai/single_request_plan_stage.go:41:              return nil, quality.serviceFailure(ctx, err, errSingleRequestPlanStage)
apps/edge/internal/openai/single_request_plan_stage.go:55:              return nil, quality.serviceFailure(ctx, err, errSingleRequestPlanStage)
apps/edge/internal/openai/single_request_provider_stage.go:79:          return nil, quality.providerFailure(stageCtx, err, errProviderStageGeneric)
apps/edge/internal/openai/single_request_provider_stage.go:82:          return nil, quality.providerFailure(stageCtx, errProviderStageGeneric, errProviderStageGeneric)
apps/edge/internal/openai/single_request_provider_stage.go:91:          return nil, quality.providerFailure(stageCtx, err, errProviderStageGeneric)
apps/edge/internal/openai/single_request_provider_stage.go:95:          return nil, quality.providerFailure(stageCtx, err, errProviderStageGeneric)
apps/edge/internal/openai/single_request_provider_stage.go:138:                         if frame.GetStatusCode() == http.StatusRequestEntityTooLarge {
apps/edge/internal/openai/single_request_quality_gate.go:64:        return g.providerFailure(context.Background(), err, cause)
apps/edge/internal/openai/single_request_quality_gate.go:67:func (g *singleRequestQualityGate) providerFailure(ctx context.Context, err, cause error) error {
apps/edge/internal/openai/single_request_quality_gate.go:111:func (g *singleRequestQualityGate) serviceFailure(ctx context.Context, err, cause error) error {
apps/edge/internal/openai/single_request_quality_gate_test.go:55:                     return g.providerFailure(context.Background(), errors.New("private provider detail"), errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:58:                     return g.providerFailure(timedOutCtx, context.DeadlineExceeded, errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:65:                     return g.providerFailure(cancelledCtx, context.Canceled, errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:80:                     if err := submitSingleRequestClosedTerminal(context.Background(), "quality-request", sequence, stageErr); err != nil {
apps/edge/internal/openai/single_request_quality_gate_test.go:117:                            return g.providerFailure(context.Background(), context.Canceled, errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:124:                            return g.providerFailure(cancelledCtx, context.Canceled, errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:131:                            return g.serviceFailure(context.Background(), context.Canceled, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_quality_gate_test.go:138:                            return g.serviceFailure(cancelledCtx, context.Canceled, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_quality_gate_test.go:145:                            return g.serviceFailure(context.Background(), edgeservice.ErrSingleRequestCancelled, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_quality_gate_test.go:168:           {name: "413 is context", status: http.StatusRequestEntityTooLarge, want: edgeservice.SingleRequestTerminalDisposition{Kind: edgeservice.SingleRequestTerminalError, ErrorClass: edgeservice.SingleRequestTerminalErrorContext}},
apps/edge/internal/openai/single_request_quality_gate_test.go:189:                   stageErr := newSingleRequestQualityGate().providerFailure(context.Background(), providerErr, errProviderStageGeneric)
apps/edge/internal/openai/single_request_quality_gate_test.go:196:                   if err := submitSingleRequestClosedTerminal(context.Background(), "quality-request", controller, stageErr); err != nil {
apps/edge/internal/openai/single_request_quality_gate_test.go:225:                   stageErr := newSingleRequestQualityGate().providerFailure(context.Background(), codecErr, errProviderStageGeneric)
apps/edge/internal/openai/single_request_review_stage.go:88:            return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:103:           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:120:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:124:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:157:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:169:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:173:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:184:                           return nil, quality.serviceFailure(ctx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:213:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:221:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_review_stage.go:228:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestReviewStage)
apps/edge/internal/openai/single_request_work_stage.go:178:             return nil, quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:192:             return nil, quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:223:                            return nil, quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:227:                            return nil, quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:238:                            return nil, quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:411:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:419:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/single_request_work_stage.go:426:           return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
apps/edge/internal/openai/stream_gate_ingress.go:279:          writeError(w, http.StatusRequestEntityTooLarge, "invalid_request_error", "request body exceeds configured limit")
apps/edge/internal/openai/stream_gate_ingress_test.go:106:      if recorder.Code != http.StatusRequestEntityTooLarge {
apps/edge/internal/openai/stream_gate_ingress_test.go:134:      if recorder.Code != http.StatusRequestEntityTooLarge {
apps/edge/internal/openai/stream_gate_ingress_test.go:175:                      if recorder.Code != http.StatusRequestEntityTooLarge || len(service.reqsSnapshot()) != 0 {
apps/edge/internal/service/single_request.go:293:                                        h.failLockedWithErrorClass(ErrSingleRequestInternalToolBudget, singleRequestErrorClassInternalToolBudget)
apps/edge/internal/service/single_request.go:780:                    h.failLockedWithErrorClass(ErrSingleRequestInternalToolBudget, singleRequestErrorClassInternalToolBudget)
apps/edge/internal/service/single_request.go:981:            return singleRequestErrorClassInternalToolBudget
apps/edge/internal/service/single_request.go:1007:   case observed == singleRequestErrorClassInternalToolBudget:
apps/edge/internal/service/single_request.go:1040:           return singleRequestErrorClassInternalToolBudget
apps/edge/internal/service/single_request_observation.go:67:       singleRequestErrorClassInternalToolBudget singleRequestErrorClass = "internal_tool_budget"
apps/edge/internal/service/single_request_observation.go:152:              singleRequestErrorClassInternalToolBudget, singleRequestErrorClassInternalToolFailed,
apps/edge/internal/service/single_request_observation.go:201:              singleRequestErrorClassInternalToolBudget, singleRequestErrorClassInternalToolFailed,
apps/edge/internal/service/single_request_observation_test.go:1137:          if terminal := terminalEvent(t, observer); terminal.ErrorClass != singleRequestErrorClassInternalToolBudget {
apps/edge/internal/service/single_request_observation_test.go:1254:          if terminal := terminalEvent(t, observer); terminal.ErrorClass != singleRequestErrorClassInternalToolBudget {
apps/edge/internal/service/single_request_observation_test.go:1305:          {name: "budget", err: fmt.Errorf("wrapped: %w", ErrSingleRequestInternalToolBudget), want: singleRequestErrorClassInternalToolBudget},
apps/edge/internal/service/single_request_tool_loop.go:217:     outcome, errorClass = singleRequestOutcomeError, singleRequestErrorClassInternalToolBudget
```

### 8. Diff hygiene

Command:

```sh
git diff --check
```

Output:

```text
[no stdout or stderr; exit status 0]
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## Section Ownership

| Section | Owner | Note |
|---------|-------|------|
| Header comment, Overview, Review Agent Instructions | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Implementation Item Completion (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| Implementation Checklist (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
| Review-Only Checklist | Review agent only | Implementing agent must not modify or check this section |
| Deviations from Plan, Key Design Decisions | Implementing agent | Replace placeholder text with actual content |
| Reviewer Checkpoints | Fixed at stub creation | Pre-filled from plan |
| Verification Results (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `Deviations from Plan` entry |
| Code Review Result | Review agent appends | Not included in stub |

## Code Review Result

- Overall Verdict: FAIL
- Dimension Assessment:
  - Correctness: Fail
  - Completeness: Fail
  - Test Coverage: Fail
  - API Contract: Fail
  - Code Quality: Pass
  - Implementation Deviation: Fail
  - Verification Trust: Fail
  - Spec Conformance: Fail
- Findings:
  - Required R1 — `apps/edge/internal/service/single_request_tool_loop.go:233-260` and `apps/edge/internal/service/single_request_artifact.go:196-208` classify a child operation context's `DeadlineExceeded` as `timeout` before checking the service-owned `execCtx` and immutable request deadline. A fresh race-enabled reproduction across 2,000 actual internal-tool request wall-clock expiries produced 67–99 `error/timeout` terminals per 200-request run, with only the remainder reaching `error/budget`. This violates SDD S11 and the submitted R3 ownership claim. Route tool and artifact failures through a service-owned classifier that prioritizes caller cancellation and request wall-clock exhaustion, preserves `timeout` only for a genuinely earlier child/stage deadline, and add real internal-tool and artifact request-wall-clock race regressions.
- Routing Signals:
  - `review_rework_count=2`
  - `evidence_integrity_failure=true`
- Next Step: Prepare and implement a `REVIEW_API` follow-up plan for Required R1; do not write `complete.log`.
