# Test Specification — C3.5 AdHoP-Conditional Refinement

Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`.

## Acceptance Criteria Traceability

| AC ID | Acceptance Criterion (one-line) | Test IDs | Coverage |
|-------|---------------------------------|----------|----------|
| AC-2.2 (cross-domain MRE on hard frames) | MRE < 2.5 px after refinement | FT-P-06, **C3.5-IT-01** | Covered |
| AC-4.1 | E2E latency budget under conditional invocation | NFT-PERF-01, **C3.5-PT-01** | Covered |

---

## Component-Internal Tests

### C3.5-IT-01: residual reduction under refinement

**Summary**: when `refine_if_needed` is invoked (residual > threshold), the post-refinement residual is strictly lower than the pre-refinement residual on ≥90% of invocations.

**Traces to**: AC-2.2 (hard-frame portion)

**Description**: prepare a fixture of "hard frames" (synthetic perspective skew that pushes the C3 residual above the threshold); run C3 → C3.5; assert `MatchResult.per_candidate[best].per_candidate_residual_px` post-refinement < pre-refinement value in ≥90% of invocations. The remaining ≤10% are accepted (refinement can legitimately fail to improve a frame; passthrough is the documented fallback).

**Input data**: `synthetic_matcher/hard_frames_perspective_skew_50f/`.

**Expected result**: improvement rate ≥ 0.90.

**Max execution time**: 5 min on Tier-1.

---

### C3.5-IT-02: passthrough fall-through on `RefinerBackboneError`

**Summary**: if the AdHoP backbone fails, C3.5 returns C3's original `MatchResult` unchanged with `refinement_label = "passthrough"`.

**Traces to**: defensive (no AC trace; backstops the conditional-refinement design contract)

**Description**: monkey-patch the AdHoP runtime to raise `RefinerBackboneError`; run 20 hard frames; assert (a) every frame produces a `MatchResult`, (b) `refinement_label == "passthrough"`, (c) error logged at ERROR level, (d) inlier coordinates equal C3's input coordinates bit-for-bit (no silent corruption).

**Input data**: as C3.5-IT-01.

**Expected result**: 20/20 passthrough; bit-identical correspondences.

**Max execution time**: 60 s.

---

### C3.5-IT-03: invocation rate stays under target on the steady-state path

**Summary**: on the Derkachi normal segment, the AdHoP invocation rate is below the 25% target threshold; if it exceeds 30%, the test fails (signals a threshold mis-tune).

**Traces to**: NFT-PERF-01 invocation-rate budget partition

**Description**: replay Derkachi normal segment; count `was_invoked()` true returns over 60 s rolling windows; assert max invocation rate < 0.30 (warning at 0.25 per the component spec § 9 logging table).

**Input data**: `flight_derkachi/normal_segment_60_stills/`.

**Expected result**: max invocation rate < 0.30.

**Max execution time**: 90 s.

---

## Performance Tests

### C3.5-PT-01: refinement-added latency budget

**Traces to**: AC-4.1

**Load scenario**: 3 Hz, mixed normal + hard frames (25% hard) replicating realistic invocation rate.

**Expected results**:

| Metric | Target | Failure Threshold |
|--------|--------|-------------------|
| `refine_if_needed` p95 (when invoked) | ≤ 90 ms (one extra backbone forward + perspective remap) | 150 ms |
| `refine_if_needed` p95 (passthrough) | ≤ 0.5 ms | 2 ms |
| Frame-aggregated added latency p95 | ≤ 25 ms (≤25% × 90 ms invocation; ≥75% × 0.5 ms passthrough) | 40 ms |

---

## Security Tests

C3.5 has no externally-reachable surface.

---

## Acceptance Tests

Covered transitively via FT-P-06.

---

## Test Data Management

| Data Set | Source | Size |
|----------|--------|------|
| `synthetic_matcher/hard_frames_perspective_skew_50f/` | generated, deterministic | ~50 MB |
| `flight_derkachi/normal_segment_60_stills/` | shared | shared |
| AdHoP TRT engine | C7-built artifact | shared |

**Setup**: AdHoP engine must be compiled (deterministic; cached after first build).
**Teardown**: read-only.
**Data isolation**: per-test temp dirs.
