<!-- task=m-iop-hot-path-one-shot-execution/09+06,08_artifact_pair plan=2 tag=REVIEW_API milestone-task=artifact-pair -->

# Consume Artifact Dispositions and Close the Direct Frontier Lifecycle

## For the Implementing Agent

Implement every checklist item, run every verification command exactly as written, and fill the implementation-owned sections of `CODE_REVIEW-cloud-G08.md` with actual notes and stdout/stderr. Keep both active artifacts in place and report ready for review; finalization belongs only to the code-review agent. If blocked, record 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`.

## Background

The artifact store now validates prepare and exact Plan/Review receipts, but its disposition is written only to metadata that no production consumer reads. Both public handlers therefore submit the selector again after pair success, and the post-prepare selector turn can be reclassified as direct. Separately, a successful no-tool direct completion terminals only the logical coordinator and leaves the pinned artifact record behind until the bounded store rejects later valid traffic.

## Archive Evidence Snapshot

- Prior artifacts after review finalization: `agent-task/m-iop-hot-path-one-shot-execution/09+06,08_artifact_pair/plan_cloud_G09_1.log` and `agent-task/m-iop-hot-path-one-shot-execution/09+06,08_artifact_pair/code_review_cloud_G09_1.log`.
- Prior verdict: FAIL with 2 Required, 0 Suggested, and 0 Nit findings; `review_rework_count=2` and `evidence_integrity_failure=false`.
- Required findings: consume a typed artifact disposition at the Chat/Messages handler boundary so prepare alone resumes the selector, pair success reaches a no-selector local-stage handoff, and `pair_ready` cannot downgrade to direct; release the pinned artifact record when a no-tool direct turn completes successfully.
- Affected files: `apps/edge/internal/openai/artifact_pair.go`, `apps/edge/internal/openai/request_identity_ingress.go`, `apps/edge/internal/openai/chat_handler.go`, `apps/edge/internal/openai/anthropic_handler.go`, `apps/edge/internal/openai/hot_path_dispatch.go`, `apps/edge/internal/openai/hot_path_direct.go`, `apps/edge/internal/openai/artifact_pair_test.go`, and `apps/edge/internal/openai/hot_path_direct_test.go`.
- Fresh review evidence: predecessor checks, the named artifact test, focused and shared `-race -count=1` suites, `go vet`, `gofmt -d`, and `git diff --check` all passed. Static call-site tracing proved `iop_artifact_disposition` and `iop_artifact_local_eligible` have no production reader, while Chat and Messages call `SubmitProviderPool` unconditionally; direct-terminal tracing proved the artifact record is not removed on successful no-tool direct completion.
- Roadmap carryover: approved SDD scenario S06 and Evidence Map row `artifact-pair` remain the sole scope. Actual local/review model execution belongs to later milestone children, so this child must expose a typed fail-closed local-stage handoff without starting that worker.

## Dependencies and Execution Order

- Predecessor 06 remains satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/complete.log`.
- Predecessor 08 remains satisfied by `agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/08+02,04,06_workspace_binding/complete.log`.
- Implement REVIEW_API-1 before REVIEW_API-2 so direct cleanup uses the same terminal lifecycle proven by the integrated handler regressions.

## Analysis

### Files Read

- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-hot-path-one-shot-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-contract/outer/anthropic-compatible-api.md`
- `agent-test/local/rules.md`
- `agent-test/local/domains/edge-smoke.md`
- `apps/edge/internal/openai/artifact_pair.go`
- `apps/edge/internal/openai/artifact_pair_test.go`
- `apps/edge/internal/openai/request_identity_ingress.go`
- `apps/edge/internal/openai/chat_handler.go`
- `apps/edge/internal/openai/anthropic_handler.go`
- `apps/edge/internal/openai/hot_path_dispatch.go`
- `apps/edge/internal/openai/hot_path_direct.go`
- `apps/edge/internal/openai/hot_path_direct_test.go`
- `apps/edge/internal/openai/request_coordinator.go`
- `apps/edge/internal/openai/request_lineage.go`
- `apps/edge/internal/openai/workspace_tool_codec.go`
- `apps/edge/internal/openai/hot_path_selector.go`
- `apps/edge/internal/openai/server.go`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-hot-path-one-shot-execution/SDD.md`, approved with its lock released.
- First-line milestone contribution: `milestone-task=artifact-pair`.
- Target: S06 and Evidence Map row `artifact-pair`.
- S06 fixes `light` after the first classification, permits only the pair-authoring selector subphase after prepare, and crosses the local-stage frontier only after the exact successful Plan/Review result set. REVIEW_API-1 makes those transitions observable and consumed at the production handler boundary; REVIEW_API-2 prevents a separate pinned-record lifecycle from exhausting the same route.

### Verification Context

- Handoff: none supplied.
- Verification sources read: local Edge test rules, the approved SDD, both endpoint contracts, the matching implementation spec, and all source/test files this plan modifies.
- Fresh reviewer commands: both predecessor checks, `TestArtifactPairFrontierMatrix` listing, focused and shared race suites, vet, formatting, and diff checks exited 0 under Go 1.26.2.
- Static evidence: `applyArtifactDisposition` writes metadata at `artifact_pair.go:319-324`; repository-wide references show only tests read those keys. Chat reaches `SubmitProviderPool` at `chat_handler.go:330` and Messages at `anthropic_handler.go:61` after the join methods. `runDirectTurn` calls only `requestCoordinator.terminal` after a successful no-tool response at `hot_path_direct.go:71-74`.
- Constraints: preserve unrelated dirty-worktree changes; do not start a workspace tool, local model, external provider, or later milestone worker; use endpoint-native deterministic fakes and fresh uncached race output.
- Confidence: high. Both failures follow a single production call path and are reproducible without external services.

### Test Coverage Gaps

- `TestArtifactPairFrontierMatrix` calls the ingress/store boundary directly, so it cannot detect the unconditional provider submissions in the real Chat and Messages handlers.
- No test proves that `pair_ready` rejects a selector response classified as direct.
- Existing direct handler tests assert logical-request terminal state but do not assert artifact-store removal or bounded-capacity reuse.

### Symbol References

- `joinPresetChatIngress` has production call sites in `chat_handler.go` and test call sites in `artifact_pair_test.go`.
- `joinPresetAnthropicIngress` has a production call site in `anthropicPoolRequest` and test call sites in `artifact_pair_test.go`.
- `applyArtifactDisposition` is called only by those two join methods; its metadata keys have no production consumer.
- `terminalPresetRequest` is the existing coordinator-plus-artifact cleanup primitive and is already used by all direct error paths.

### Split Judgment

The handler disposition and pair-phase gate form one boundary invariant across Chat and Messages. Direct success cleanup is a small adjacent lifecycle correction in the same pinned store and must be verified with that invariant. Splitting either part would leave valid preset traffic capable of selector redispatch or capacity exhaustion, so the two-item follow-up is the smallest independently PASS-verifiable scope.

### Scope Rationale

Exclude binding compilation, receipt cryptography, workspace execution, actual local/review model dispatch, contracts/specs, cleanup manifests, and sibling milestone tasks. For local eligibility, add only a typed handoff seam that performs no provider submission and fails closed with the endpoint-native response until the later local-flow child supplies execution.

### Final Routing

`evaluation_mode=isolated-reassessment`; finalizer `finalize-task-policy.sh pair`. Build closure checks for algorithm, interface, schema, control flow, and test contract are all true. Build scores `(2,2,2,1,1)` give G08; `review_rework_count=2` selects the `recovery-boundary`, so the build route is cloud G08 at `PLAN-cloud-G08.md`. Review closure checks are all true; review scores `(2,2,2,1,1)` route by `official-review` to cloud G08 at `CODE_REVIEW-cloud-G08.md`. `large_indivisible_context=false`. Positive loop risks are `temporal_state`, `concurrent_consistency`, `boundary_contract`, `structured_interpretation`, and `variant_product` (5). `evidence_integrity_failure=false`; no capability gap exists.

## Implementation Checklist

- [ ] Implement REVIEW_API-1 so the real Chat and Messages handlers consume typed prepare/local dispositions and enforce pair-only post-prepare output.
- [ ] Implement REVIEW_API-2 so successful no-tool direct completion releases its pinned artifact frontier and bounded capacity remains reusable.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Consume artifact dispositions at the public handler boundary

#### Problem

`applyArtifactDisposition` stores `resume_selector` or `local_eligible` only in metadata. Neither public handler reads it, so both call `SubmitProviderPool` after pair success. After prepare, `dispatchPresetTurn` runs the general classifier again and accepts `direct`, even though the artifact record is already `pair_ready` and S06 permits only the exact pair-authoring selector response.

#### Solution

Replace the metadata-only control signal with an explicit typed ingress result returned through `joinPresetChatIngress` and `joinPresetAnthropicIngress`. Keep logical request, call, and retained selector stage IDs in trusted metadata for downstream selector work, but make the disposition itself impossible to ignore at the handler call site.

For `resume_selector`, continue through exactly one existing provider-pool selector submission using the retained stage. For `local_eligible`, short-circuit before pool request construction/submission and invoke a small typed local-stage handoff boundary. This child must not synthesize a model completion or start the later local worker; its default handoff must fail closed with the endpoint-native not-implemented response while preserving the consumed local-eligible frontier for the later owner. General non-artifact continuations remain unchanged.

Expose a lock-safe artifact phase query or equivalent store-owned guard and use it in `dispatchPresetTurn`: when the request is `pair_ready`, any classifier result other than `light` must terminal/reject before `runDirectTurn`; repeated prepare or malformed pair outputs continue to be rejected by `artifactFrontierStore.issue` without local eligibility.

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/artifact_pair.go` — define the typed handler disposition/local-stage handoff result and expose the lock-safe pair-required phase guard.
- [ ] `apps/edge/internal/openai/request_identity_ingress.go` — return the typed artifact disposition from both join methods instead of publishing an unconsumed metadata-only signal.
- [ ] `apps/edge/internal/openai/chat_handler.go` — branch on the typed disposition before provider-pool dispatch and invoke the no-selector local-stage handoff.
- [ ] `apps/edge/internal/openai/anthropic_handler.go` — propagate the typed disposition out of pool-request preparation, branch before `SubmitProviderPool`, and invoke the same endpoint-native handoff contract.
- [ ] `apps/edge/internal/openai/hot_path_dispatch.go` — reject a `pair_ready` classifier downgrade to direct before direct execution.
- [ ] `apps/edge/internal/openai/artifact_pair_test.go` — retain the full receipt matrix while adapting helper calls to the typed result and add pair-ready direct-downgrade coverage.
- [ ] `apps/edge/internal/openai/hot_path_direct_test.go` — add `TestArtifactPairHandlerDisposition` for real Chat/Messages service-call sequences: prepare submits exactly once more, pair success submits zero additional selector calls, and the local handoff is endpoint-native and fail-closed.

#### Test Strategy

Use the existing in-package provider fake and HTTP helpers. For each endpoint, drive initial selection and continuation through `srv.routes()` rather than calling only the store helper. Capture pool submission counts around prepare and pair continuations, verify the retained stage, assert no selector call after local eligibility, and assert a post-prepare direct-shaped selector output is rejected before a direct response. Keep the existing reversed-order, malformed receipt, and concurrent replay matrix passing.

#### Verification

Run the named-test listing and focused race command from Final Verification. Expect both endpoint variants to prove exact service-call counts and no race, external service, workspace operation, or local model execution.

### [REVIEW_API-2] Release artifact state on successful direct completion

#### Problem

Every preset that permits `light` pins an artifact record at initial ingress, including requests later classified `direct`. The successful no-tool branch of `runDirectTurn` terminals only `requestCoordinator`, so those records accumulate until `defaultArtifactFrontierCapacity` rejects new valid admissions.

#### Solution

After a successful no-tool direct response, close the request through `terminalPresetRequest` rather than coordinator-only terminal logic. Preserve current error and tool-waiting behavior: errors already use the combined terminal, while a direct response that issued ordinary caller tools must retain its logical frontier for continuation.

#### Modified Files and Checklist

- [ ] `apps/edge/internal/openai/hot_path_direct.go` — use the combined preset terminal on successful no-tool direct completion.
- [ ] `apps/edge/internal/openai/hot_path_direct_test.go` — add `TestDirectTurnReleasesArtifactFrontier`, including repeated admissions/completions beyond the store capacity and a control proving tool-waiting direct turns remain pinned.

#### Test Strategy

Exercise the production `runDirectTurn` lifecycle with a small-capacity store or more than `defaultArtifactFrontierCapacity` sequential requests. Assert every no-tool request completes, the artifact store does not grow, later admission remains available, and the ordinary-tool direct branch remains waiting with its record intact.

#### Verification

Run the focused race command and full shared race suite. Expect no capacity error, no lost waiting frontier, and no data race.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/edge/internal/openai/artifact_pair.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/request_identity_ingress.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/chat_handler.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/anthropic_handler.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/hot_path_dispatch.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/hot_path_direct.go` | REVIEW_API-2 |
| `apps/edge/internal/openai/artifact_pair_test.go` | REVIEW_API-1 |
| `apps/edge/internal/openai/hot_path_direct_test.go` | REVIEW_API-1, REVIEW_API-2 |
| `agent-task/m-iop-hot-path-one-shot-execution/09+06,08_artifact_pair/CODE_REVIEW-cloud-G08.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

```bash
test -f agent-task/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/complete.log || test -f agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/06+04,05_request_identity_ingress/complete.log
test -f agent-task/m-iop-hot-path-one-shot-execution/08+02,04,06_workspace_binding/complete.log || test -f agent-task/archive/2026/08/m-iop-hot-path-one-shot-execution/08+02,04,06_workspace_binding/complete.log
go test ./apps/edge/internal/openai -list 'Test(ArtifactPairHandlerDisposition|DirectTurnReleasesArtifactFrontier)' | rg 'Test(ArtifactPairHandlerDisposition|DirectTurnReleasesArtifactFrontier)'
go test -race -count=1 ./apps/edge/internal/openai -run 'Test(Workspace|ArtifactPair|DirectTurnReleasesArtifactFrontier)'
go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./apps/edge/internal/service
go vet ./apps/edge/internal/openai
gofmt -d apps/edge/internal/openai/artifact_pair.go apps/edge/internal/openai/artifact_pair_test.go apps/edge/internal/openai/request_identity_ingress.go apps/edge/internal/openai/chat_handler.go apps/edge/internal/openai/anthropic_handler.go apps/edge/internal/openai/hot_path_dispatch.go apps/edge/internal/openai/hot_path_direct.go apps/edge/internal/openai/hot_path_direct_test.go
git diff --check
```

Expected: every command exits 0 with fresh uncached race results; both named regressions are present, prepare alone redispatches the retained selector, pair success never redispatches it, `pair_ready` never becomes direct, direct terminal cleanup keeps the bounded artifact store reusable, and ordinary tool continuations remain intact. Actual local/review model execution remains deferred to its mapped milestone children.

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