# Verification Report

**Change**: provider-vision-gating
**Issue**: #268
**Date**: 2026-03-27
**Version**: v1

---

## Completeness

| Metric           | Value                       |
|------------------|-----------------------------|
| Tasks total      | 12 (Phase 1: 5, Phase 2: 7) |
| Tasks complete   | 12                          |
| Tasks incomplete | 0                           |

Phase 3 tasks (3.1–3.4) are explicitly deferred and out of scope for this change.

---

## Build & Tests Execution

**Build**: ✅ Passed

```shell
cargo clippy --manifest-path clients/agent-runtime/Cargo.toml --all-targets -- -D warnings
→ 0 errors, 0 warnings
cargo fmt --manifest-path clients/agent-runtime/Cargo.toml --check
→ no formatting issues
```

**Tests**: ✅ 64 passed / ❌ 0 failed / ⚠️ 0 skipped

```shell
cargo test --manifest-path clients/agent-runtime/Cargo.toml -- anthropic
test result: ok. 64 passed; 0 failed; 0 ignored; 0 measured; finished in 0.03s
(run across lib + main binary targets)
```

**Coverage**: ➖ Not measured (coverage instrumentation not run; threshold configured at 60%)

---

## Spec Compliance Matrix

### REQ-1: Provider Vision Capability Matrix

| Scenario                                               | Test                                                    | Result      |
|--------------------------------------------------------|---------------------------------------------------------|-------------|
| Vision capability matrix matches provider declarations | `anthropic::tests::capabilities_declares_image_support` | ✅ COMPLIANT |

### REQ-2: Capability Declaration Contract

| Scenario                                             | Test                                                                                          | Result                                                                                                         |
|------------------------------------------------------|-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| Provider declares image support with transport forms | `anthropic::tests::capabilities_declares_image_support` + `supports_image_input_returns_true` | ✅ COMPLIANT                                                                                                    |
| Provider declares image_input but no transport forms | (trait-level test, not Anthropic-specific)                                                    | ⚠️ PARTIAL — behavior is enforced by `supports_image_input()` in `traits.rs`; no dedicated test in this change |
| Provider uses trait default capabilities             | (trait-level test, not Anthropic-specific)                                                    | ⚠️ PARTIAL — default is `image_input: false`; verified by existing Ollama/other provider tests                 |

### REQ-3: Fail-Closed Gating (existing behavior — no code changes)

| Scenario                                                     | Test                                           | Result                                                               |
|--------------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------|
| Trait default rejects image turn on unoverridden provider    | (existing tests in `traits.rs` / default impl) | ⚠️ PARTIAL — existing behavior, not re-tested in this change's scope |
| Router rejects image turn before dispatch                    | (existing tests in `router.rs`)                | ⚠️ PARTIAL — existing behavior formalized as spec                    |
| Reliable wrapper skips text-only providers for image turns   | (existing tests in `reliable.rs`)              | ⚠️ PARTIAL — existing behavior formalized as spec                    |
| Reliable wrapper fails when no image-capable provider exists | (existing tests in `reliable.rs`)              | ⚠️ PARTIAL — existing behavior formalized as spec                    |

### REQ-4: Provider-Specific Image Format Contracts

| Scenario                                               | Test                                                                                                                  | Result                                   |
|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------|
| OpenAI-compatible formats image as data URL            | (existing tests in `compatible.rs`)                                                                                   | ⚠️ PARTIAL — not in scope of this change |
| Anthropic formats image as base64 source content block | `anthropic::tests::image_content_block_serializes_to_anthropic_format` + `image_blocks_attached_to_last_user_message` | ✅ COMPLIANT                              |
| Anthropic source.data has no data: URL prefix          | `anthropic::tests::image_content_block_no_data_url_prefix`                                                            | ✅ COMPLIANT                              |
| Gemini formats image as inline_data part               | (existing tests in `gemini.rs`)                                                                                       | ⚠️ PARTIAL — not in scope of this change |
| Image blocks attached to last user message             | `anthropic::tests::image_blocks_attached_to_last_user_message`                                                        | ✅ COMPLIANT                              |

### REQ-5: Error Behavior for Non-Vision Providers (existing behavior — no code changes)

| Scenario                                          | Test                                          | Result                                            |
|---------------------------------------------------|-----------------------------------------------|---------------------------------------------------|
| Non-vision provider rejects image before API call | (existing router tests)                       | ⚠️ PARTIAL — existing behavior formalized as spec |
| Image parts are never silently stripped           | (existing behavior at router/reliable layers) | ⚠️ PARTIAL — existing behavior formalized as spec |

### REQ-6: Config Integration for Vision Routing (existing behavior — no code changes)

| Scenario                                             | Test                           | Result                                            |
|------------------------------------------------------|--------------------------------|---------------------------------------------------|
| vision_model_hint resolves to image-capable route    | (existing config/router tests) | ⚠️ PARTIAL — existing behavior formalized as spec |
| vision_model_hint resolves to non-image route        | (existing config/router tests) | ⚠️ PARTIAL — existing behavior formalized as spec |
| Router selects vision route over default text route  | (existing config/router tests) | ⚠️ PARTIAL — existing behavior formalized as spec |
| Multiple vision-capable providers with explicit hint | (existing config/router tests) | ⚠️ PARTIAL — existing behavior formalized as spec |

**Compliance summary**: 6/20 scenarios fully COMPLIANT (test exists AND passed in this run), 14/20
PARTIAL (existing behavior formalized as spec — tests exist in other modules but were not
re-executed as part of this targeted verification).

**Note**: The 14 PARTIAL scenarios document **existing, already-shipping behavior** that this change
formalizes into spec without modifying code. The 6 COMPLIANT scenarios cover all **new**
functionality introduced by this change (Anthropic adapter).

---

## Correctness (Static — Structural Evidence)

| Requirement                              | Status        | Notes                                                                                                                                              |
|------------------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| REQ-1: Provider Vision Capability Matrix | ✅ Implemented | `AnthropicProvider::capabilities()` declares `image_input: true`, `image_transport_forms: [InlineBytes]`                                           |
| REQ-2: Capability Declaration Contract   | ✅ Implemented | `capabilities()` override follows contract; `supports_image_input()` returns true                                                                  |
| REQ-3: Fail-Closed Gating                | ✅ Existing    | No code changes — three-layer gating already in `traits.rs`, `router.rs`, `reliable.rs`                                                            |
| REQ-4: Anthropic Image Format            | ✅ Implemented | `NativeContentOut::Image` variant, `ImageSource` struct, image injection via `attach_images_to_last_user_message()` helper called from `chat()`    |
| REQ-4: OpenAI/Gemini Format              | ✅ Existing    | No code changes — already implemented in `compatible.rs` and `gemini.rs`                                                                           |
| REQ-5: Error Behavior                    | ✅ Existing    | No code changes — router rejects at `router.rs`; `attach_images_to_last_user_message()` fails closed if last non-system message is not a user turn |
| REQ-6: Config Integration                | ✅ Existing    | No code changes — `vision_model_hint` routing already in place                                                                                     |

---

## Coherence (Design)

| Decision                                  | Followed? | Notes                                                                                                                                                                                   |
|-------------------------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ADR-1: Trait-Level Capability Declaration | ✅ Yes     | `AnthropicProvider::capabilities()` override matches design exactly                                                                                                                     |
| ADR-2: Three-Layer Fail-Closed Gating     | ✅ Yes     | No changes to gating layers — existing behavior preserved                                                                                                                               |
| ADR-3: Anthropic Image Adapter Design     | ✅ Yes     | `NativeContentOut::Image` variant, `ImageSource` struct, `attach_images_to_last_user_message()` helper + `chat()` call-site, `apply_cache_to_last_message` Image arm — all match design |
| ADR-4: Provider Format Translation        | ✅ Yes     | Anthropic format `{type:"image", source:{type:"base64", media_type, data}}` matches design and Anthropic API docs                                                                       |
| ADR-5: Ollama Deferral                    | ✅ Yes     | Not implemented; correctly deferred to Phase 3 tasks                                                                                                                                    |

### File Changes vs Design Table

| Design Table Entry                                             | Actual                                                                                      | Match? |
|----------------------------------------------------------------|---------------------------------------------------------------------------------------------|--------|
| `anthropic.rs` — Add `NativeContentOut::Image` + `ImageSource` | `NativeContentOut::Image` variant + `ImageSource` struct (lines 94-104)                     | ✅      |
| `anthropic.rs` — Override `capabilities()`                     | `AnthropicProvider::capabilities()` (line 586)                                              | ✅      |
| `anthropic.rs` — Extend `chat()` for image injection           | `attach_images_to_last_user_message()` helper (line 399) + call-site in `chat()` (line 553) | ✅      |
| `anthropic.rs` — Update `apply_cache_to_last_message`          | `Image` match arm (line 226)                                                                | ✅      |
| `anthropic.rs` (tests) — Unit + integration tests              | Vision tests (lines 1213-1427)                                                              | ✅      |

---

## Issues Found

**CRITICAL** (must fix before archive):
None

**WARNING** (should fix):

1. **Coverage not measured**: `coverage_threshold: 60` is configured in `openspec/config.yaml`, but
   coverage instrumentation was not run. The targeted Anthropic tests (64 passed) provide strong
   behavioral evidence, but quantitative coverage data is missing. Recommend running
   `cargo llvm-cov` or equivalent before final archive.

**SUGGESTION** (nice to have):

1. **REQ-2 edge case test**: No dedicated test for "provider declares `image_input: true` but empty
   `image_transport_forms`" specific to Anthropic. The trait-level `supports_image_input()` logic
   handles this, but an explicit negative test would strengthen the spec compliance evidence.
2. **Full test suite run**: This verification targeted `-- anthropic` tests (64 tests). A full
   `cargo test` run would provide additional regression confidence for REQ-3/REQ-5/REQ-6
   existing-behavior scenarios.

---

## Proposal → Spec Traceability

| Proposal Item                                            | Spec Coverage                                                                                                         |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| Anthropic declares `image_input: true` + `[InlineBytes]` | REQ-1, REQ-2 scenario 1                                                                                               |
| Anthropic `chat()` builds correct image content blocks   | REQ-4 Anthropic scenario                                                                                              |
| Existing tests pass (no regressions)                     | PARTIAL — Anthropic-focused verification passed; full cross-provider regression suite was not re-run in this report   |
| Router/reliable route image turns to Anthropic           | REQ-3 scenarios (existing behavior)                                                                                   |
| Provider capability matrix documented                    | REQ-1 matrix table                                                                                                    |
| `cargo test` + `cargo clippy` pass                       | PARTIAL — `cargo clippy` passed and `cargo test -- anthropic` passed; full `cargo test` was not re-run in this report |
| Fail-closed gating formalized                            | PARTIAL — formalized in REQ-3/REQ-5, but this report's targeted validation remained Anthropic-scoped                  |
| Config integration documented                            | REQ-6 (4 scenarios)                                                                                                   |

All 7 proposal success criteria are traceable to spec requirements. ✅

## Spec → Design Traceability

| Spec Requirement           | Design Coverage                                 |
|----------------------------|-------------------------------------------------|
| REQ-1 Capability Matrix    | ADR-1, ADR-5                                    |
| REQ-2 Declaration Contract | ADR-1                                           |
| REQ-3 Fail-Closed Gating   | ADR-2                                           |
| REQ-4 Format Contracts     | ADR-3, ADR-4                                    |
| REQ-5 Error Behavior       | ADR-2                                           |
| REQ-6 Config Integration   | ADR-1 (operator control via `ModelRouteConfig`) |

All 6 spec requirements are traceable to design ADRs. ✅

## Design → Tasks Traceability

| Design Element                           | Task(s) |
|------------------------------------------|---------|
| ADR-3: `NativeContentOut::Image` variant | 2.1     |
| ADR-1: `capabilities()` override         | 2.2     |
| ADR-3: `chat()` image injection          | 2.3     |
| ADR-3: `apply_cache_to_last_message`     | 2.4     |
| Design testing strategy (unit)           | 2.5     |
| Design testing strategy (integration)    | 2.6     |
| Proposal success criteria (validation)   | 2.7     |

All design elements are traceable to implementation tasks. ✅

---

## Verdict

**PASS WITH WARNINGS**

All new functionality (Anthropic image adapter) is correctly implemented, tested, and compliant with
specs. All 12 tasks complete. Design decisions were followed. Code compiles, clippy is clean, fmt is
clean, and 64 Anthropic-focused tests pass. One minor warning remains: coverage was not
quantitatively measured. This does not block archive.
