<!-- task=m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing plan=3 tag=REVIEW_API milestone-task=cleanup-observation -->

# Repair Failure-linked Single-request Observation Evidence

## For the Implementing Agent

Implement R4-R6 exactly within the listed write boundary, run every verification command, and fill the implementation-owned sections of `CODE_REVIEW-cloud-G07.md` with actual notes and stdout/stderr. Keep the active pair in place and report ready for review. If blocked, record only the exact blocker, attempted commands/output, and resume condition in implementation-owned evidence fields. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`; finalization belongs to the code-review skill.

## Background

The real service lifecycle now emits observation events, but an error or cancellation can close its semantic stage before the in-flight Node tool settles. The same path loses typed terminal error classes, and emitted request/stage/tool/cleanup/terminal records do not share one raw-free request correlation. This follow-up closes those linked S07 evidence invariants and adds regressions for the variants that the passing suite did not exercise.

## Archive Evidence Snapshot

- Closed pair: `agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/plan_cloud_G07_2.log` and `agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/code_review_cloud_G07_2.log`.
- Verdict: FAIL with Required R4-R6, zero Suggested findings, and zero Nit findings.
- Affected behavior: in-flight tool/stage settlement, terminal error classification, and request-local linkage of raw-free lifecycle timing/outcomes.
- Fresh reviewer verification: packet 05/12/13 checks passed; focused race passed in 1.067s; the service package passed in 6.244s; vet and `git diff --check` passed. A temporary real-service tool-failure reproducer was removed after reporting `ToolCount=0`, terminal `ErrorClass="provider"`, and correlations `stage="single_request.stage.plan" tool="" terminal=""`.
- Roadmap carryover: `milestone-task=cleanup-observation`; approved SDD scenario S07 requires linked raw-free stage/tool/cleanup/total timing and outcomes.

## Finding Resolution Map

| Finding | Mode | Exact fix | Changed precondition |
|---------|------|-----------|----------------------|
| R4 | `direct-fix` | Defer stage emission while an actual tool is in flight, emit the tool first, include it in the owning stage count, and resume timing only when that stage remains active in `apps/edge/internal/service/single_request_observation.go`; add real error/cancel regressions in `apps/edge/internal/service/single_request_observation_test.go`. | Tool failure and cancellation can no longer emit a zero-tool stage or leave a phantom resumed timer. |
| R5 | `direct-fix` | Replace raw error-string matching with typed `errors.Is` classification captured before cleanup joins secondary failures in `apps/edge/internal/service/single_request.go`; assert exact terminal classes in `apps/edge/internal/service/single_request_observation_test.go`. | Terminal events preserve the winning provider/validation/budget/tool/cleanup/timeout/cancel class. |
| R6 | `direct-fix` | Generate one bounded raw-input-independent correlation in `apps/edge/internal/service/single_request_observation.go`, attach it to every lifecycle DTO, and prove equality/separation/exclusion in `apps/edge/internal/service/single_request_observation_test.go`. | Concurrent requests produce independently linkable stage/tool/cleanup/total evidence without caller-derived identifiers. |

`ownership_closed=true`: R4-R6 are repository-local direct fixes with deterministic service tests and require no user decision, authorization, or unordered dependency.

## Analysis

### Files Read

- `apps/edge/internal/service/service.go`
- `apps/edge/internal/service/single_request.go`
- `apps/edge/internal/service/single_request_tool_loop.go`
- `apps/edge/internal/service/single_request_observation.go`
- `apps/edge/internal/service/single_request_observation_test.go`
- `apps/edge/internal/service/single_request_test.go`
- `apps/edge/internal/service/single_request_tool_loop_test.go`
- `apps/edge/internal/service/single_request_cleanup_test.go`
- `agent-spec/runtime/edge-node-execution.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-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/plan_cloud_G07_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/code_review_cloud_G07_2.log`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`, approved with its lock released and no `USER_REVIEW.md`.
- Header scope: `milestone-task=cleanup-observation`.
- Target scenario: S07 requires request-owned cleanup plus linked raw-free stage/tool/total timing and outcomes across success, error, and cancellation.
- Evidence Map S07 requires scoped lifecycle/timing evidence. The checklist keeps an in-flight tool inside its semantic stage until settlement, preserves exact closed error classes, and gives all request-local DTOs one non-caller-derived correlation.

### Verification Context

- No separate verification handoff was supplied. Repository-native sources were the active plan/review history, approved SDD S07, local test rules, Edge smoke profile, service lifecycle tests, and current source.
- Current host reported `go version go1.26.2 linux/arm64`; the package targets Go 1.24-compatible code and requires no external service, credential, port, or remote runner.
- Packet 05, 12, and 13 completion checks passed. Fresh reviewer commands passed the focused observation race, service package regression, vet, and whitespace checks.
- A focused real-service tool-failure reproducer contradicted the claimed coverage with `ToolCount=0`, terminal class `provider`, and unlinked correlations; the temporary probe was removed immediately after capture.
- Constraints: preserve unrelated dirty work; do not change production adapters, API/wire/config/schema, living specs, or use the Agent-Ops dispatcher as a product harness.
- External Verification Preflight: not applicable. Actual Claude/Mac S12 full-cycle evidence remains a separate Milestone task and is not claimed here.
- Confidence: high.

### Test Coverage Gaps

- Existing real lifecycle coverage proved successful tool pause/resume and cleanup timing but not tool failure or cancellation ownership.
- Provider failure and caller cancellation tests ran without an in-flight tool.
- Cleanup failure checked terminal outcome but not every exact terminal error class.
- Correlation tests validated a raw-id helper instead of lifecycle-wide equality and request separation.

### Symbol References

- `newSingleRequestCorrelationID` is package-private. References are confined to the observation accumulator and tests.
- No exported symbol, wire type, or external call site is renamed or removed.

### Split Judgment

- Keep one plan. Tool settlement ordering, terminal class preservation, and correlation linkage form one observation record invariant.

### Scope Rationale

- Include only the service-owned timing accumulator, terminal classification, and deterministic service tests.
- Exclude Prometheus/zap adapters, bootstrap wiring, Node logs, HTTP correlation, API/wire/config/schema changes, living-spec updates, dashboards, and actual Claude/Mac smoke.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer `finalize-task-policy.sh` in `pair` mode.
- Build closures were all true. Scores `1/2/1/2/1 = G07`; base `local-fit`, final basis `recovery-boundary`, lane `cloud`, filename `PLAN-cloud-G07.md`.
- Review closures were all true. Scores `1/2/1/2/1 = G07`; basis `official-review`, lane `cloud`, filename `CODE_REVIEW-cloud-G07.md`.
- `large_indivisible_context=false`; positive loop risks were `temporal_state`, `concurrent_consistency`, `boundary_contract`, and `variant_product` (`loop_risk_count=4`).
- Recovery signals: `review_rework_count=2`, `evidence_integrity_failure=true`; capability gap: none.

## Implementation Checklist

- [ ] Settle each in-flight Node tool before its semantic stage emits on error/cancel, count it once, and resume stage timing only while the same stage remains active.
- [ ] Preserve exact terminal error classes with typed sentinel classification before cleanup joins secondary failures.
- [ ] Attach one bounded raw-input-independent request correlation to every request/stage/tool/cleanup/terminal DTO and prove within-request equality plus cross-request separation.
- [ ] Run dependency, focused race, package, vet, and whitespace verification with cache bypass where supported.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Implementation Plan

### [REVIEW_API-1] Close tool/stage ordering and terminal error ownership

**Problem**

- `apps/edge/internal/service/single_request.go:466` and `:483` close the active stage before an in-flight tool reaches the deferred `onToolExit`.
- `apps/edge/internal/service/single_request_observation.go:560` resumes timing unconditionally after tool exit, even when the stage already emitted.
- `apps/edge/internal/service/single_request.go:762` searches typed error messages for enum spellings that those messages never contain.

**Solution**

Retain one pending stage-close record while a tool is active. Emit the tool, increment its owning stage count, and then emit the pending stage without restarting its timer. Capture terminal classes with typed `errors.Is` matching before cleanup joins secondary errors; cleanup changes the class only when it converts pending success.

**Modified Files and Checklist**

- [ ] `apps/edge/internal/service/single_request_observation.go` — defer stage emission across an in-flight tool and prevent post-terminal timer resume.
- [ ] `apps/edge/internal/service/single_request.go` — store and emit the primary typed terminal error class without raw string inspection.
- [ ] `apps/edge/internal/service/single_request_observation_test.go` — add real service tool-error/cancel ordering and exact terminal-class assertions.

**Test Strategy**

- Extend real lifecycle coverage with one Node tool error and one cancellation while a Node tool is blocked.
- Add closed error-class assertions for provider, validation, budget, tool failure, cleanup conversion, timeout, and cancel.

**Verification**

- `go test -race ./apps/edge/internal/service -run 'TestSingleRequestObservation' -count=1`

### [REVIEW_API-2] Generate one raw-free correlation per request accumulator

**Problem**

- Stage records used a stage-only constant while tool, cleanup, terminal, and request records had no request correlation.

**Solution**

Generate one bounded correlation in `newSingleRequestTimingAccumulator` without caller input, store it on the accumulator, and copy it unchanged into every request, stage, tool, cleanup, and terminal DTO.

**Modified Files and Checklist**

- [ ] `apps/edge/internal/service/single_request_observation.go` — generate/store one bounded raw-free correlation and attach it to every DTO.
- [ ] `apps/edge/internal/service/single_request_observation_test.go` — prove same-request equality, different-request inequality, length/allowlist, and sentinel exclusion.

**Test Strategy**

- Extend success/error/cancel/tool/cleanup cases to require one non-empty correlation and assert distinct correlations across concurrent accumulators.

**Verification**

- `go test -race ./apps/edge/internal/service -run 'TestSingleRequestObservation' -count=1`

## Modified Files Summary

| File | Item |
|------|------|
| `apps/edge/internal/service/single_request.go` | REVIEW_API-1 |
| `apps/edge/internal/service/single_request_observation.go` | REVIEW_API-1, REVIEW_API-2 |
| `apps/edge/internal/service/single_request_observation_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `agent-task/m-iop-owned-single-request-agent-execution/14+05,12,13_observation_timing/CODE_REVIEW-cloud-G07.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

1. `test -f agent-task/m-iop-owned-single-request-agent-execution/05+03_single_ingress/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/05+03_single_ingress/complete.log' | wc -l)" -eq 1`
2. `test -f agent-task/m-iop-owned-single-request-agent-execution/12+05,06,08,11_internal_tool_loop/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/12+05,06,08,11_internal_tool_loop/complete.log' | wc -l)" -eq 1`
3. `test -f agent-task/m-iop-owned-single-request-agent-execution/13+12_workspace_cleanup/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/13+12_workspace_cleanup/complete.log' | wc -l)" -eq 1`
4. `go test -race ./apps/edge/internal/service -run 'TestSingleRequestObservation' -count=1`
5. `go test ./apps/edge/internal/service -count=1`
6. `go vet ./apps/edge/internal/service`
7. `git diff --check`

Expected: predecessor evidence remains unique; in-flight error/cancel tools settle before their owning stage, terminal classes are exact, every request's raw-free lifecycle events share one unique bounded correlation, and the service package remains clean. Cached test output is not acceptable.

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