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

# Harden Claude smoke evidence binding and cleanup

## For the Implementing Agent

Filling the implementation-owned sections of `CODE_REVIEW-cloud-G10.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 the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields; do not ask the user, call user-input tools, create control-plane stop files, or change the owner or write boundary.

## Background

The credential-free harness and Make entry point run successfully, but the passing fake path does not establish the requested Claude model, required external preflight facts, a real workspace verification result, or failure-safe raw capture cleanup. This follow-up keeps external S12 qualification in task 25 and repairs the repository-owned evidence contract so a later credentialed run can be trusted.

## Archive Evidence Snapshot

- Reviewed plan: `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/plan_cloud_G07_1.log`; reviewed implementation/review: `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G07_1.log`; prior pristine pair: `plan_cloud_G07_0.log` and `code_review_cloud_G07_0.log` in the same task directory.
- Verdict: FAIL. Required R1 found that the requested model was not passed through Claude's actual model input and the manifest copied engine facts from constants instead of the validated runtime binding. Required R2 found incomplete fail-before-invocation preflight. Required R3 found a hard-coded verification success for any pre-existing result file. Required R4 found raw temporary capture leaks and non-guaranteed atomic publication.
- Fresh review verification passed the dependency check, shell syntax, current self-test, Make self-test target, target inventory, aggregate isolation, and `git diff --check`. The reviewer also observed multiple fresh `/tmp/tmp.*/out`, `err`, `fresh`, and `manifest` files left by failure-path self-tests.
- Roadmap carryover: `milestone-task=claude-smoke` maps to SDD S12. This packet repairs only the credential-free harness contract; it must not claim the actual Claude/Mac qualification.

## Finding Resolution Map

| Finding | Mode | Exact fix | Changed precondition |
|---------|------|-----------|----------------------|
| R1 | `direct-fix` | Update `scripts/e2e-single-request-claude.sh` and `scripts/fixtures/single-request-claude-smoke-manifest.schema.json` so the child uses `ANTHROPIC_MODEL`, the base/model digests match runtime evidence, validated stage-engine facts feed the manifest, and the binding digest covers the checked config plus public model and closed sequence. | A successful fake run will exercise the same model-binding input and reject public-model, config, binding, or engine mismatches. |
| R2 | `direct-fix` | Extend the same script/schema with closed runner/workspace facts, executable/help/version/config/listener/log/metric checks, and zero-child negative fixtures. | Every required external fact will fail before the Claude child marker can advance. |
| R3 | `direct-fix` | Derive workspace change and verification evidence from a fixed absent-before result, exact expected content, and an actually executed verifier rather than a manifest constant. | Pre-existing, unchanged, wrong-content, and failed-verification cases will be rejected. |
| R4 | `direct-fix` | Replace return-only cleanup with all-exit/signal cleanup of one validated temp root and publish only a validated redacted same-directory temporary manifest by atomic rename. | Success, failure, and interruption fixtures will leave no raw captures or partial output. |

`ownership_closed=true`: all four findings are repository-fixable inside the existing harness/schema write boundary and have no unordered dependency or user decision.

## Analysis

### Files Read

- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/plan_cloud_G07_1.log`
- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G07_1.log`
- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/plan_cloud_G07_0.log`
- `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/code_review_cloud_G07_0.log`
- `scripts/e2e-single-request-claude.sh`
- `scripts/fixtures/single-request-claude-smoke-manifest.schema.json`
- `Makefile`
- `apps/edge/internal/edgecmd/config.go`
- `apps/edge/internal/edgecmd/root.go`
- `apps/edge/internal/service/single_request_observation.go`
- `apps/edge/internal/service/single_request_metrics.go`
- `apps/edge/internal/openai/single_request_metrics.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-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 and its Evidence Map row.
- S12 requires one actual Claude request against a writable Mac workspace, actual `Gemini → ornith-fast → Gemini` stage order, stage-pure and total time, a final file and verification, ingress POST delta one, and terminal one.
- The S12 Evidence Map requires actual Claude, ingress counter, Edge/Node/provider timing evidence, and workspace before/after. Therefore the harness must bind model/runtime facts before invocation and derive, not assert, workspace/terminal evidence. Actual credentialed qualification remains outside this packet.

### Verification Context

- No verification handoff was supplied. Repository-native evidence came from the current script/schema, Edge config command, single-request observation producers, Make targets, local testing rules, and fresh reviewer executions.
- Fresh PASS commands: the exact task-22 dependency resolver; `bash -n scripts/e2e-single-request-claude.sh`; `./scripts/e2e-single-request-claude.sh --self-test`; `make test-single-request-claude-smoke-self-test`; deterministic Make target/input inventory; aggregate isolation; `git diff --check`.
- Fresh failing evidence: after the self-tests, `/tmp` contained multiple new raw capture directories with `out`, `err`, `fresh`, or `manifest`, proving `RETURN` cleanup does not cover `fail`/exit paths.
- Current host: Linux `aarch64`, Go `1.26.2`; local deterministic work must not invoke installed Claude, Edge, a provider, or a network endpoint. Cached output is not acceptable.

#### External Verification Preflight Contract

- The later `--run` target is a caller-authorized runner controlling a synchronized checkout, the declared Edge binary/config, one live append-only Edge observation log, the Messages and metrics listeners, a disposable workspace owned by the declared Darwin Node, and one named non-empty secret environment variable.
- Preflight must compare branch/HEAD/worktree, runner OS/arch, workspace-owner OS/arch, CLI and Edge digests/version/help, config digest and `iop-edge config check`, base/model digests, stage sequence/binding digest, observation identity/readability, metrics availability, and endpoint liveness before the child marker advances.
- No authorized remote runner or credential is configured for this review. That is not a blocker because this packet's PASS oracle is the credential-free self-test; task 25 owns the actual external run.

### Test Coverage Gaps

- R1: the current fake does not assert `ANTHROPIC_MODEL` and runtime evidence has no base/model digest rejection. Add positive child-env capture and negative base/model/config/binding/engine cases.
- R2: current negative preflights do not assert that the invocation marker remains unchanged. Add table-driven fake preflight cases for every required executable, help/version, config, listener, log, metric, source, runtime, and secret fact.
- R3: current tests mutate only a completed manifest. Add run-path cases for pre-existing result, no change, wrong content, and nonzero verifier status.
- R4: current tests exercise stale/rotated failures but do not assert raw temporary cleanup or atomic output absence. Run each failure under a dedicated self-test temp root and verify no raw capture or partial target remains.

### Symbol References

- No production symbol is renamed or removed. The public Make target names and arguments remain unchanged.

### Split Judgment

- Keep one plan. Schema keys, runtime evidence parsing, child environment, fixed workspace verification, raw capture lifecycle, and atomic publication form one manifest-validity invariant; none independently produces trustworthy PASS evidence.
- Runtime predecessor 22 is 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`, its closed manifest schema, the active review evidence file, and credential-free fixtures embedded in `--self-test`.
- Exclude `Makefile` because its four isolated targets and caller-supplied arguments are already correct; retain their regression checks.
- Exclude production Edge/Node runtime, config schema, contracts, specs, roadmap state, credentials, deployment, tracked runtime evidence, and the actual Claude/Mac run. No S12 qualification claim is allowed.

### Final Routing

- `evaluation_mode=isolated-reassessment`; build and review `scope_closed`, `context_closed`, `verification_closed`, `evidence_trusted`, `ownership_closed`, and `decision_closed` are all true; no capability gap.
- Build scores `2/2/2/2/2` => G10, base and 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`, `structured_interpretation`, and `variant_product` (5). `review_rework_count=1`; `evidence_integrity_failure=false`; finalizer `finalize-task-policy.sh`, mode `pair`.

## Dependencies and Execution Order

1. Preserve the completed task-22 observation/runtime surfaces and current Make entry points.
2. Freeze the revised schema and runtime-evidence/binding checks before changing the run collector.
3. Implement real workspace verification and all-path cleanup/publication against that schema.
4. Expand the credential-free self-test last, then rerun every final command fresh.

## Implementation Checklist

- [ ] Bind the requested Claude public model, base URL digest, checked Edge config, closed stage-engine facts, and manifest stage records to one validated immutable runtime evidence snapshot.
- [ ] Complete fail-before-invocation preflight for runner/workspace identity, executables/help/version, Edge config, listeners, observation log, metrics, source, secret-name, and output safety, with zero-child negative fixtures.
- [ ] Derive fixed workspace change and verification evidence from an absent-before result, exact expected content, and an actually executed verifier.
- [ ] Guarantee raw capture cleanup on success/failure/interruption and publish only a validated redacted manifest through same-directory atomic rename.
- [ ] Run dependency, syntax, credential-free behavior, Make entry point, model-binding inventory, aggregate isolation, cleanup/publication, 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] Bind the actual Claude model and immutable stage evidence

**Problem**

`scripts/e2e-single-request-claude.sh:69` validates no digest for `BASE_URL` or `MODEL`, and line 89 exports a private test variable rather than Claude's `ANTHROPIC_MODEL`. Lines 82-85 populate engine families from fixed literals, so the later manifest is not tied to the exact public model input used by the child.

**Solution**

Extend the runtime evidence and closed manifest with digest-only base/public-model facts and consume the validated closed engine sequence rather than recreating it in the builder. Recompute the stage binding over the checked config digest, public-model digest, and exact `gemini/ornith-fast/gemini` sequence. Pass the model through Claude's actual environment contract and assert the fake received it.

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

```bash
env ANTHROPIC_BASE_URL="$BASE_URL" ANTHROPIC_API_KEY="$secret_value" IOP_SINGLE_REQUEST_SMOKE_MODEL="$MODEL" ...
```

After:

```bash
env ANTHROPIC_BASE_URL="$BASE_URL" \
    ANTHROPIC_MODEL="$MODEL" \
    ANTHROPIC_API_KEY="$secret_value" \
    "$CLAUDE_BIN" "${CLAUDE_FLAGS[@]}" "$PROMPT"
```

The manifest builder receives only the already validated engine-family tuple and binding digest; it does not invent either value.

**Modified Files and Checklist**

- [ ] Update runtime evidence parsing, binding digest derivation, child environment, and stage projection in `scripts/e2e-single-request-claude.sh`.
- [ ] Add closed digest/binding fields and exact key validation in `scripts/fixtures/single-request-claude-smoke-manifest.schema.json`.
- [ ] Add positive and negative model/base/config/binding/engine fixtures inside `--self-test`.

**Test Strategy**

The self-test fake records only closed comparisons: expected model/base digests and exact engine tuple. It must pass the correct binding and reject a changed model, base, config digest, stage order, engine family, or binding digest before/after collection as appropriate.

**Verification**

Run `./scripts/e2e-single-request-claude.sh --self-test`; the fake confirms `ANTHROPIC_MODEL` and all binding mismatch fixtures fail.

### [TEST-2] Close the fail-before-invocation preflight

**Problem**

`scripts/e2e-single-request-claude.sh:69` accepts regular files and caller JSON without requiring executable binaries, required Claude flags/version, Edge config validation, listener liveness, observation readiness, or a validated runner/workspace relationship. An invalid external target can reach the Claude child before these facts are known.

**Solution**

Split preflight into closed checks with redacted failures. Require executable Claude/Edge binaries, capture and validate Claude version/help for the pinned flags, run `iop-edge version` and `iop-edge config check --config`, compare actual runner identity and the declared Darwin workspace-owner identity to runtime evidence, validate source/base/model/binary/config digests, probe the Edge health/Messages and metrics listeners without printing URLs, and snapshot one readable regular observation file plus a safe non-existing output target. Never echo secrets, endpoints, model aliases, paths, or raw command output.

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

```bash
for p in "$CLAUDE_BIN" "$EDGE_BIN" "$EDGE_CONFIG" "$OBSERVATION_FILE" "$SCHEMA"; do
    [ -f "$p" ] || fail 'runtime input unavailable'
done
```

After:

```bash
validate_executables_and_help
validate_source_and_runtime_identity
validate_edge_config_and_binding
validate_listener_metric_and_log_preflight
validate_workspace_and_output_preflight
```

**Modified Files and Checklist**

- [ ] Implement the bounded preflight and redacted command capture in `scripts/e2e-single-request-claude.sh`.
- [ ] Represent only closed/digest runner and runtime facts in `scripts/fixtures/single-request-claude-smoke-manifest.schema.json`.
- [ ] Add a table-driven fake case for every preflight mismatch and assert the child marker remains zero.

**Test Strategy**

All preflight tests use executable fakes, file-backed local fixtures, and a local fake listener only. They do not invoke installed Claude/Edge or any provider. Every failure asserts no child invocation and no raw value in captured stdout/stderr.

**Verification**

Run `bash -n scripts/e2e-single-request-claude.sh && ./scripts/e2e-single-request-claude.sh --self-test`; syntax, positive preflight, and all zero-child negative cases pass.

### [TEST-3] Derive the workspace verification result

**Problem**

`scripts/e2e-single-request-claude.sh:85` writes `verification.exit_code=0` as a constant, and line 89 accepts any existing `smoke-result.txt`. The child can perform no task and still produce a valid manifest.

**Solution**

Make the disposable task deterministic: preflight requires `smoke-result.txt` to be absent, the fixed prompt requests exact non-sensitive content, the post-run workspace digest must differ, and a fixed local verifier compares the result to that content. Feed the verifier's actual zero status and result digest into the manifest; never serialize the content or path. Keep the schema closed with `workspace.changed=true`, a verifier-command digest, result digest, and `exit_code=0`.

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

```python
'verification': {'result_file_digest': result, 'exit_code': 0}
```

After:

```python
'workspace': {'before_digest': before, 'after_digest': after, 'changed': True},
'verification': {
    'command_digest': verifier_digest,
    'result_file_digest': result,
    'exit_code': verifier_status,
}
```

**Modified Files and Checklist**

- [ ] Implement absent-before, changed-workspace, exact-content, and actual verifier-status checks in `scripts/e2e-single-request-claude.sh`.
- [ ] Add the closed changed/verifier fields to `scripts/fixtures/single-request-claude-smoke-manifest.schema.json`.
- [ ] Add pre-existing, unchanged, wrong-content, and failed-verifier run fixtures.

**Test Strategy**

The valid fake creates the exact fixed result once. Four negative fakes cover pre-existing output, no workspace change, wrong content, and verifier failure; none may publish a manifest.

**Verification**

Run `./scripts/e2e-single-request-claude.sh --self-test`; only the exact fixed workspace transition passes.

### [TEST-4] Clean raw captures and publish atomically

**Problem**

`scripts/e2e-single-request-claude.sh:89` installs a `RETURN` trap, but `fail` exits the shell. Failure-path self-tests therefore leave raw `out`, `err`, and evidence fragments under `/tmp`. Moving a manifest from the default temporary filesystem to an arbitrary output directory is not an atomic publication guarantee.

**Solution**

Own one exact run temporary directory with an `EXIT` plus signal cleanup trap, validate the cleanup target before removal, and clear traps only after cleanup. Keep raw stdout/stderr and observation fragments inside it with restrictive permissions. Create the redacted publication temp file in the already validated output directory, validate it, then rename it to a non-existing final target on the same filesystem. On every failure or signal, remove raw captures and the publication temp without touching any broader path.

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

```bash
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
...
mv "$tmp/manifest" "$OUTPUT"
```

After:

```bash
install_run_cleanup_trap "$run_tmp" "$publish_tmp"
...
validate_manifest "$publish_tmp" "$SCHEMA"
mv -- "$publish_tmp" "$OUTPUT"
cleanup_run_artifacts
clear_run_cleanup_trap
```

**Modified Files and Checklist**

- [ ] Implement validated all-exit/signal cleanup and restrictive raw capture creation in `scripts/e2e-single-request-claude.sh`.
- [ ] Implement same-directory non-overwriting atomic publication after schema/redaction validation.
- [ ] Add success, Claude failure, stale evidence, rotated log, verification failure, and signal cleanup assertions to `--self-test`.

**Test Strategy**

The self-test runs every case under its own controlled executable temp root, asserts no nested raw capture remains, asserts failed cases leave no final/partial output, and confirms the valid case publishes exactly one schema-valid manifest.

**Verification**

Run `make test-single-request-claude-smoke-self-test`; it passes and reports cleanup/publication coverage without network or installed binaries.

## Modified Files Summary

| File | Items |
|------|-------|
| `scripts/e2e-single-request-claude.sh` | TEST-1, TEST-2, TEST-3, TEST-4 |
| `scripts/fixtures/single-request-claude-smoke-manifest.schema.json` | TEST-1, TEST-2, TEST-3 |
| `agent-task/m-iop-owned-single-request-agent-execution/24+22_claude_smoke_harness/CODE_REVIEW-cloud-G10.md` | TEST-1, TEST-2, TEST-3, TEST-4 |

## 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 the good fake and every model/config/preflight/workspace/redaction/cleanup/publication contradiction 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"` — proves the actual model input is used and verification success is not hard-coded.
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`.
