<!-- task=m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness plan=3 tag=REVIEW_TEST milestone-task=claude-smoke -->

# Repair Claude smoke timing, route preflight, and interruption cleanup

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G09.md` is the mandatory final implementation step. Execute this packet exactly, run every verification command, paste actual stdout/stderr, 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 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, change the owner/write boundary, archive logs, or write `complete.log`.

## Background

The hardened credential-free suite passes, but three fake-only assumptions still prevent trustworthy S12 evidence. The manifest reads total time from a production request-start record that always carries zero, the Messages preflight accepts 404, and signal cleanup can block forever on a TERM-resistant direct child. This follow-up repairs only those repository-owned harness invariants; actual Claude/Mac qualification remains task 25.

## Archive Evidence Snapshot

- Reviewed plan: `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/plan_cloud_G10_2.log`; reviewed implementation/review: `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G10_2.log`; earlier loop evidence remains `plan_cloud_G07_0.log`, `code_review_cloud_G07_0.log`, `plan_cloud_G07_1.log`, and `code_review_cloud_G07_1.log` in the same task directory.
- Verdict: FAIL. Required R1 found that the harness discards the production terminal's actual request-total duration and serializes the request-start zero. Required R2 found that 404 proves no Messages route but still passes preflight. Required R3 found unbounded direct-PID TERM cleanup without process-group ownership or kill escalation.
- Fresh review verification passed dependency resolution, shell syntax, the current self-test, the Make self-test target, model/verifier inventory, target inventory, aggregate isolation, and `git diff --check`. Static producer/harness comparison proved the duration-source mismatch, and the current status regex accepted 404.
- Roadmap carryover: `milestone-task=claude-smoke` maps to approved SDD S12. This packet repairs the credential-free harness oracle and must not claim actual Claude/Mac qualification.

## Finding Resolution Map

| Finding | Mode | Exact fix | Changed precondition |
|---------|------|-----------|----------------------|
| R1 | `direct-fix` | Update `scripts/e2e-single-request-claude.sh` so a successful terminal record supplies request-total duration, closed terminal result facts are required, and the self-test emits production-faithful request/terminal timing. | A valid fake manifest will fail unless it preserves the production producer's zero request-start duration and actual terminal total. |
| R2 | `direct-fix` | Restrict the same script's unauthenticated `OPTIONS /v1/messages` probe to the current Edge route-proving 401 or 405 outcomes and add a 404 zero-child case. | A missing Messages route can no longer advance the Claude child marker. |
| R3 | `direct-fix` | Replace direct-PID unbounded cleanup in the same script with a dedicated child session/process-group supervisor, bounded TERM-to-KILL escalation, complete reap, and ignored-TERM/descendant fixtures. | Interruption becomes bounded and removes every raw/partial artifact even when the child does not cooperate. |

`ownership_closed=true`: R1-R3 are direct fixes inside the existing harness and embedded credential-free self-test; no external runner, user decision, or unordered dependency is required.

## Analysis

### Files Read

- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/plan_cloud_G10_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G10_2.log`
- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G07_1.log`
- `scripts/e2e-single-request-claude.sh`
- `scripts/fixtures/single-request-claude-smoke-manifest.schema.json`
- `Makefile`
- `apps/edge/internal/service/single_request_observation.go`
- `apps/edge/internal/service/single_request_metrics.go`
- `apps/edge/internal/openai/single_request_metrics.go`
- `apps/edge/internal/openai/routes.go`
- `apps/edge/internal/openai/anthropic_handler.go`
- `apps/edge/internal/openai/single_request_handler_test.go`
- `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/runtime/edge-node-execution.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-contract/inner/edge-node-runtime-wire.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`

### SDD Criteria

- Approved and unlocked SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`.
- First-line scope remains `milestone-task=claude-smoke`, mapped to Acceptance Scenario S12.
- S12 requires one real Claude request, `Gemini -> ornith-fast -> Gemini`, stage/total pure time, final file/verification, ingress POST delta one, and terminal one. Its Evidence Map requires actual Claude, ingress counter, Edge/Node/provider timing, and workspace before/after.
- R1 directly repairs the stage/total timing oracle; R2 proves the target Messages route before invocation; R3 preserves bounded, raw-free collection under interruption. External qualification remains outside this local packet.

### Verification Context

- No verification handoff was supplied. Repository-native evidence came from the current script/schema, production observation producer, Edge route registration/method guard, Make targets, testing domain rules, and local testing-smoke profile.
- Current host: Linux `aarch64`, Go `1.26.2`; no remote runner, installed Claude, provider credential, or live IOP endpoint is configured or required for this follow-up.
- Fresh reviewer commands passed: exact task-22 dependency resolution, `bash -n`, the current `--self-test`, the Make self-test target, model/verifier inventory, target inventory, aggregate isolation, and `git diff --check`.
- Fresh contradictory evidence: production `onRequest` emits no duration while `onTerminal` emits `totalMs`; the harness reads the former and discards the latter. The Messages regex independently accepts 404. Cleanup has no bounded wait or process-group kill.
- External verification preflight: not applicable to this credential-free repair. Task 25 remains responsible for the configured Mac runner, source sync, binaries, Edge config/process/listeners, append-only log, metrics, secret, disposable workspace, and actual S12 run.

### Test Coverage Gaps

- R1: current fake places 11 ms on request and 0 ms on terminal, the inverse of production, and asserts no total value. Make request duration zero, terminal duration non-zero, `has_result=true`, and assert the manifest uses the terminal value.
- R2: current listener covers 405 and 503 but not a missing 404 route. Add a 404 mode and assert exit non-zero, child count zero, no output, no partial publication, and cleanup.
- R3: current signal fake exits on TERM and has no descendant. Add a TERM-resistant child plus descendant and assert bounded supervisor exit, complete reap, empty raw root, and no final/partial output.

### Symbol References

- None. No production symbol, public target, schema key, or Make variable is renamed or removed.

### Split Judgment

- Keep one plan. Observation parsing, route preflight, and process cleanup are compact branches of one credential-free harness oracle and share one embedded self-test; splitting would duplicate its fixture/server/process lifecycle without independent completion value.
- Runtime predecessor 22 remains satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/22+21_executor_activation/complete.log`.

### Scope Rationale

- Include only `scripts/e2e-single-request-claude.sh` and the active review evidence file.
- Exclude the manifest schema because its total-duration and verification fields already express the required closed values; no key/type change is needed.
- Exclude Makefile, production Edge/Node runtime, config schema, contracts, specs, roadmap state, credentials, deployment, and actual Claude/Mac execution. Existing Make targets and production observation/API contracts are read-only oracles.

### Final Routing

- `status=routed`, `evaluation_mode=isolated-reassessment`, `missing_evidence=[]`, `blocked_reason=none`; all build/review closure fields (`scope_closed`, `context_closed`, `verification_closed`, `evidence_trusted`, `ownership_closed`, `decision_closed`) are true and there is no capability gap.
- Build scores `1/2/1/2/2` => G08, base `local-fit`, final `recovery-boundary`, `worker/cloud/G08`, `PLAN-cloud-G08.md`.
- Review scores `2/2/1/2/2` => G09, `official-review`, `review/cloud/G09`, `CODE_REVIEW-cloud-G09.md`.
- `large_indivisible_context=false`; positive loop risks are `temporal_state`, `concurrent_consistency`, `boundary_contract`, `structured_interpretation`, and `variant_product` (5). `review_rework_count=2`; `evidence_integrity_failure=false`; both risk and recovery boundaries match, with recovery precedence. Finalizer: `finalize-task-policy.sh`, mode `pair`.

## Dependencies and Execution Order

1. Preserve the production observation and Anthropic route contracts as read-only oracles.
2. Correct total-duration selection and route-status admission before changing process supervision.
3. Add the process-group supervisor and all three regression families, then run the full credential-free suite.

## Implementation Checklist

- [ ] Read total duration from the successful production terminal observation, require its closed result facts, and make the self-test assert production-faithful timing projection.
- [ ] Reject 404 and every non-401/non-405 Messages probe result before child invocation, with a zero-child missing-route fixture.
- [ ] Supervise Claude in a dedicated process group/session with bounded TERM-to-KILL escalation and prove ignored-TERM plus descendant cleanup.
- [ ] Run dependency, syntax, credential-free behavior, Make entry point, model/verifier inventory, target inventory, aggregate isolation, and diff verification freshly without claiming S12 qualification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [TEST-1] Use the production terminal as the total-time source

**Problem**

`scripts/e2e-single-request-claude.sh:812` reads `duration_ms` from the initial `request/total` record and line 843 publishes it. Production `onRequest` at `apps/edge/internal/service/single_request_observation.go:644` leaves duration at zero, while `onTerminal` at lines 629-638 carries the actual request total. The current fake reverses those values, so its passing result masks a real zero-duration manifest.

**Solution**

Use the successful terminal record's `duration_ms` as the manifest total, require its closed successful-result flag, and keep the request record only as the one-request lifecycle marker. Make the fixture match the production logger and assert the output total.

Before (`scripts/e2e-single-request-claude.sh:811`):

```python
total_duration = request.get("duration_ms")
...
terminal_duration = terminal.get("duration_ms")
```

After:

```python
assert request.get("duration_ms") == 0
total_duration = terminal.get("duration_ms")
assert terminal.get("has_result") is True
```

**Modified Files and Checklist**

- [ ] Update observation validation and manifest total projection in `scripts/e2e-single-request-claude.sh`.
- [ ] Change the embedded production-shaped records and assert the exact published total in `scripts/e2e-single-request-claude.sh`.

**Test Strategy**

Extend the embedded self-test. A valid record uses request duration 0 and terminal duration 11 with `has_result=true`; swapped/absent/false terminal facts fail manifest construction or validation.

**Verification**

Run `./scripts/e2e-single-request-claude.sh --self-test`; the valid manifest reports the terminal total and timing contradictions fail.

### [TEST-2] Prove the Messages route before invocation

**Problem**

`scripts/e2e-single-request-claude.sh:625` admits every 2xx/3xx/4xx response. A 404 from a server with no Messages route therefore passes and allows the Claude marker to advance.

**Solution**

Accept only the current unauthenticated Edge outcomes: 401 from the auth gate or 405 from the registered route's method guard. Treat 404 and all other outcomes as unavailable without printing the endpoint or body.

Before (`scripts/e2e-single-request-claude.sh:625`):

```bash
[[ "$code" =~ ^[234][0-9][0-9]$ ]] || fail 'Messages listener unavailable'
```

After:

```bash
case "$code" in
  401|405) ;;
  *) fail 'Messages listener unavailable' ;;
esac
```

**Modified Files and Checklist**

- [ ] Restrict the probe status contract in `scripts/e2e-single-request-claude.sh`.
- [ ] Add a `messages-missing` 404 fixture with the standard zero-child/redaction/cleanup assertions.

**Test Strategy**

Extend the embedded HTTP listener. Existing 405 passes, 401 is added as an allowed authenticated-boundary variant, and 404/503 both fail before invocation.

**Verification**

Run `bash -n scripts/e2e-single-request-claude.sh && ./scripts/e2e-single-request-claude.sh --self-test`; all route status cases pass their expected child-count assertions.

### [TEST-3] Bound and reap interruption cleanup

**Problem**

`scripts/e2e-single-request-claude.sh:445-448` sends TERM to one PID and calls unbounded `wait`. A TERM-resistant Claude or surviving descendant can hang signal handling and prevent deletion of raw stdout/stderr and publication temporaries.

**Solution**

Run Claude behind a small Python supervisor that creates a new session, owns the complete process group, forwards interruption, waits a fixed grace, escalates to group KILL, and reaps before returning. Keep shell cleanup bounded when terminating the supervisor and delete raw/publish artifacts only after ownership is settled.

Before (`scripts/e2e-single-request-claude.sh:445`):

```bash
kill -TERM "$CHILD_PID" >/dev/null 2>&1 || true
wait "$CHILD_PID" >/dev/null 2>&1 || true
```

After:

```text
parent signal -> bounded supervisor TERM -> Claude process-group TERM
grace expiry -> Claude process-group KILL -> reap -> raw/partial cleanup
```

**Modified Files and Checklist**

- [ ] Add bounded session/process-group supervision and reap logic in `scripts/e2e-single-request-claude.sh`.
- [ ] Add TERM-resistant and descendant fixtures that cannot leave a process, raw capture, or partial/final output.

**Test Strategy**

Extend the embedded self-test with a child that ignores TERM and spawns a descendant. Use the existing outer timeout only as a deadlock guard; the assertions must prove the harness exits within its own grace and the descendant PID no longer exists.

**Verification**

Run `make test-single-request-claude-smoke-self-test`; the suite completes without its outer timeout and reports bounded signal cleanup.

## Modified Files Summary

| File | Items |
|------|-------|
| `scripts/e2e-single-request-claude.sh` | TEST-1, TEST-2, TEST-3 |
| `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/CODE_REVIEW-cloud-G09.md` | TEST-1, TEST-2, TEST-3 implementation evidence |

## Final Verification

Fresh output is required; cached or reconstructed 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 the archived task-22 completion path and exits zero.
2. `bash -n scripts/e2e-single-request-claude.sh` — exits zero with no output.
3. `./scripts/e2e-single-request-claude.sh --self-test` — validates production-faithful timing, route-status fencing, model/runtime binding, workspace verification, redaction, cleanup, signals, and atomic publication without installed binaries, credentials, or network.
4. `make test-single-request-claude-smoke-self-test` — runs the same fresh credential-free suite and exits zero.
5. `bash -c "set -euo pipefail; rg --fixed-strings 'ANTHROPIC_MODEL=\"\$MODEL\"' scripts/e2e-single-request-claude.sh; ! rg --fixed-strings 'IOP_SINGLE_REQUEST_SMOKE_MODEL=\"\$MODEL\"' scripts/e2e-single-request-claude.sh; ! rg --fixed-strings \"'exit_code':0\" scripts/e2e-single-request-claude.sh"` — preserves actual model input and derived verifier evidence.
6. `rg --sort path -n 'test-single-request-claude-smoke|IOP_SINGLE_REQUEST_SMOKE_' Makefile` — lists the four isolated targets and only caller-supplied inputs.
7. `bash -c 'set -euo pipefail; if rg --sort path -n "test-single-request-claude-smoke([^:]*):.*test-single-request-claude-smoke$" Makefile; then exit 1; else test $? -eq 1; fi'` — exits zero only when no aggregate target includes the credentialed run.
8. `git diff --check` — exits zero with no whitespace errors.

Repository Edge-Node diagnostic, auxiliary E2E smoke, and full-cycle actual Claude/Mac execution are not run in this packet. Task 25 remains their owner.

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