|
| 1 | +# Implementation Plan: pi_terraphim_router No-Match Fallback Evidence |
| 2 | + |
| 3 | +**Status**: Draft |
| 4 | +**Research Doc**: `.docs/research-pi-rust-terraphim-router-no-match-fallback-evidence.md` |
| 5 | +**Author**: AI Agent |
| 6 | +**Date**: 2026-05-26 |
| 7 | +**Estimated Effort**: 45-75 minutes |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +### Summary |
| 12 | + |
| 13 | +Close the remaining P2 verification gap by making no-match fallback evidence accurate and testable. The implementation should not alter public APIs or runtime behaviour; it should align private execution-selection tests with the behaviour they claim to prove. |
| 14 | + |
| 15 | +### Approach |
| 16 | + |
| 17 | +Make the fallback metadata a private, testable `ExecutionSelection`, then adjust tests so each scenario is explicit: |
| 18 | + |
| 19 | +1. Keep `select_execution_route()` returning `None` for no taxonomy match. |
| 20 | +2. Add a private `no_match_fallback_selection()` helper that returns the existing Anthropic fallback metadata. |
| 21 | +3. Use that helper in `route_and_execute_with_registry()` so fallback metadata has one source of truth. |
| 22 | +4. Rename the current misleading test to describe matched primary selection when no registry route is ready. |
| 23 | +5. Add a true no-match planner test and a fallback metadata test. |
| 24 | + |
| 25 | +### Scope |
| 26 | + |
| 27 | +**In Scope:** |
| 28 | + |
| 29 | +- Private helper for no-match fallback metadata. |
| 30 | +- Test rename for accurate behaviour description. |
| 31 | +- New test proving no-match planner returns `None`. |
| 32 | +- New test proving no-match fallback metadata. |
| 33 | +- Focused quality gates for router tests and formatting. |
| 34 | + |
| 35 | +**Out of Scope:** |
| 36 | + |
| 37 | +- Public API changes. |
| 38 | +- Real RPC/provider process tests. |
| 39 | +- Mocks or fake `RpcClient` implementations. |
| 40 | +- Changing fallback provider/model. |
| 41 | +- Addressing unrelated baseline clippy/UBS findings. |
| 42 | + |
| 43 | +**Avoid At All Cost** (from 5/25 analysis): |
| 44 | + |
| 45 | +- Adding test-only production abstractions around `RpcClient`. |
| 46 | +- Refactoring broader execution flow. |
| 47 | +- Converting no-match fallback into an error. |
| 48 | +- Loading auth/model registry inside pure route helpers. |
| 49 | + |
| 50 | +## Architecture |
| 51 | + |
| 52 | +### Component Diagram |
| 53 | + |
| 54 | +```text |
| 55 | +RouterInput + Router + optional ModelRegistry |
| 56 | + | |
| 57 | + v |
| 58 | +select_execution_route() |
| 59 | + |-- explicit provider/model --> ExecutionSelection |
| 60 | + |-- taxonomy match ----------> ExecutionSelection |
| 61 | + '-- no taxonomy match -------> None |
| 62 | + | |
| 63 | + v |
| 64 | + no_match_fallback_selection() |
| 65 | + | |
| 66 | + v |
| 67 | + ExecutionSelection |
| 68 | + | |
| 69 | + v |
| 70 | + execute_selection() |
| 71 | +``` |
| 72 | + |
| 73 | +### Data Flow |
| 74 | + |
| 75 | +```text |
| 76 | +Non-matching prompt |
| 77 | + -> select_execution_route(input, router, Some(registry)) |
| 78 | + -> None |
| 79 | + -> no_match_fallback_selection() |
| 80 | + -> ExecutionSelection { |
| 81 | + provider: "anthropic", |
| 82 | + model: "claude-sonnet-4-6", |
| 83 | + capabilities: [], |
| 84 | + confidence: 0.0, |
| 85 | + reason: "fallback: no kg route matched", |
| 86 | + fallback_used: true |
| 87 | + } |
| 88 | + -> execute_selection(input, selection) |
| 89 | +``` |
| 90 | + |
| 91 | +### Key Design Decisions |
| 92 | + |
| 93 | +| Decision | Rationale | Alternatives Rejected | |
| 94 | +|----------|-----------|----------------------| |
| 95 | +| Keep `select_execution_route()` returning `None` for no match | Preserves separation between route planning and execution fallback policy. | Making planner always return fallback selection. | |
| 96 | +| Add `no_match_fallback_selection()` | Makes fallback metadata testable without RPC spawn and avoids string drift. | Duplicating fallback strings in tests and execution. | |
| 97 | +| Rename the misleading test | Test names should describe the exercised behaviour. | Keeping a misleading test name and adding comments. | |
| 98 | +| Do not test public async fallback by spawning RPC | Avoids external process dependency and mocks. | Mocking `RpcClient`; requiring provider process in unit tests. | |
| 99 | + |
| 100 | +### Eliminated Options (Essentialism) |
| 101 | + |
| 102 | +| Option Rejected | Why Rejected | Risk of Including | |
| 103 | +|-----------------|--------------|-------------------| |
| 104 | +| Mock `RpcClient` | Project instruction says no mocks in tests. | Brittle test-only abstraction. | |
| 105 | +| Change fallback execution behaviour | Runtime behaviour is not the problem. | New regression risk. | |
| 106 | +| Add integration harness for RPC fallback | Too large for a P2 evidence gap. | Slow, environment-dependent test. | |
| 107 | +| Fix unrelated scanner baselines | Outside this review gap. | Scope creep. | |
| 108 | + |
| 109 | +### Simplicity Check |
| 110 | + |
| 111 | +> "Minimum code that solves the problem. Nothing speculative." |
| 112 | +
|
| 113 | +The simplest correct design is one private helper and two targeted tests. It proves the actual no-match condition and the exact fallback metadata without changing public API or requiring a provider process. |
| 114 | + |
| 115 | +**Senior Engineer Test**: This is not overcomplicated if the helper has no parameters and simply returns the existing fallback metadata as `ExecutionSelection`. |
| 116 | + |
| 117 | +**Nothing Speculative Checklist**: |
| 118 | + |
| 119 | +- [x] No features the user did not request |
| 120 | +- [x] No abstractions in case we need them later |
| 121 | +- [x] No flexibility just in case |
| 122 | +- [x] No new runtime error handling |
| 123 | +- [x] No premature optimisation |
| 124 | + |
| 125 | +## File Changes |
| 126 | + |
| 127 | +### New Files |
| 128 | + |
| 129 | +| File | Purpose | |
| 130 | +|------|---------| |
| 131 | +| None | Existing router module is sufficient. | |
| 132 | + |
| 133 | +### Modified Files |
| 134 | + |
| 135 | +| File | Changes | |
| 136 | +|------|---------| |
| 137 | +| `src/pi_terraphim_router/mod.rs` | Add private fallback metadata helper, update registry-aware fallback branch, rename/split tests. | |
| 138 | +| `.docs/research-pi-rust-terraphim-router-no-match-fallback-evidence.md` | Phase 1 research for this evidence gap. | |
| 139 | +| `.docs/design-pi-rust-terraphim-router-no-match-fallback-evidence.md` | This implementation plan. | |
| 140 | + |
| 141 | +### Deleted Files |
| 142 | + |
| 143 | +| File | Reason | |
| 144 | +|------|--------| |
| 145 | +| None | No deletion required. | |
| 146 | + |
| 147 | +## API Design |
| 148 | + |
| 149 | +### Public Types |
| 150 | + |
| 151 | +No public type changes. |
| 152 | + |
| 153 | +### Public Functions |
| 154 | + |
| 155 | +No public function changes. |
| 156 | + |
| 157 | +### Private Functions |
| 158 | + |
| 159 | +Add: |
| 160 | + |
| 161 | +```rust |
| 162 | +fn no_match_fallback_selection() -> ExecutionSelection { |
| 163 | + ExecutionSelection { |
| 164 | + provider: "anthropic".to_string(), |
| 165 | + model: "claude-sonnet-4-6".to_string(), |
| 166 | + capabilities: vec![], |
| 167 | + confidence: 0.0, |
| 168 | + reason: "fallback: no kg route matched".to_string(), |
| 169 | + fallback_used: true, |
| 170 | + } |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +Use in `route_and_execute_with_registry()`: |
| 175 | + |
| 176 | +```rust |
| 177 | +let selection = select_execution_route(&input, &router, Some(registry)) |
| 178 | + .unwrap_or_else(no_match_fallback_selection); |
| 179 | +execute_selection(input, selection).await |
| 180 | +``` |
| 181 | + |
| 182 | +Do not change `route_and_execute(input)` unless a separate design decision approves sharing fallback metadata there too. Optional minimal sharing is allowed only if it does not increase diff size meaningfully. |
| 183 | + |
| 184 | +### Error Types |
| 185 | + |
| 186 | +No error type changes. |
| 187 | + |
| 188 | +## Test Strategy |
| 189 | + |
| 190 | +### Unit Tests |
| 191 | + |
| 192 | +| Test | Location | Purpose | |
| 193 | +|------|----------|---------| |
| 194 | +| `test_execution_selection_with_registry_uses_primary_when_none_ready` | `src/pi_terraphim_router/mod.rs` | Renamed current test; proves matched route with no ready registry still selects primary. | |
| 195 | +| `test_execution_selection_with_registry_returns_none_without_match` | `src/pi_terraphim_router/mod.rs` | Uses a non-matching prompt and proves planner returns `None`. | |
| 196 | +| `test_no_match_fallback_selection_uses_default_anthropic_route` | `src/pi_terraphim_router/mod.rs` | Proves fallback metadata without launching RPC. | |
| 197 | + |
| 198 | +### Integration Tests |
| 199 | + |
| 200 | +No new integration test required. The public async function still spawns RPC, so its fallback branch is covered through private metadata helper plus compile-time checks rather than external execution. |
| 201 | + |
| 202 | +### Coverage Expectations |
| 203 | + |
| 204 | +- No test should claim no-match fallback while using a matching prompt. |
| 205 | +- Fallback metadata should have one assertion each for provider, model, capabilities, confidence, reason, and `fallback_used`. |
| 206 | +- Existing first-ready fallback and explicit preference tests remain unchanged. |
| 207 | + |
| 208 | +## Implementation Steps |
| 209 | + |
| 210 | +### Step 1: Add Private Fallback Selection Helper |
| 211 | + |
| 212 | +**Files:** `src/pi_terraphim_router/mod.rs` |
| 213 | +**Description:** Add `no_match_fallback_selection() -> ExecutionSelection` beside `select_execution_route()`. It must return the same fallback metadata currently hardcoded in `route_and_execute_with_registry()`. |
| 214 | +**Tests:** Add `test_no_match_fallback_selection_uses_default_anthropic_route`. |
| 215 | +**Estimated:** 15 minutes |
| 216 | + |
| 217 | +### Step 2: Use Helper in Registry-Aware Execution |
| 218 | + |
| 219 | +**Files:** `src/pi_terraphim_router/mod.rs` |
| 220 | +**Description:** Replace the manual no-match fallback branch in `route_and_execute_with_registry()` with `unwrap_or_else(no_match_fallback_selection)` and `execute_selection(input, selection).await`. This preserves runtime behaviour while making metadata single-source. |
| 221 | +**Tests:** Existing tests plus fallback metadata helper test. |
| 222 | +**Dependencies:** Step 1 |
| 223 | +**Estimated:** 15 minutes |
| 224 | + |
| 225 | +### Step 3: Fix Misleading Test Coverage |
| 226 | + |
| 227 | +**Files:** `src/pi_terraphim_router/mod.rs` |
| 228 | +**Description:** Rename `test_execution_selection_with_registry_uses_no_match_fallback` to `test_execution_selection_with_registry_uses_primary_when_none_ready`. Add `test_execution_selection_with_registry_returns_none_without_match` using a prompt that does not contain any configured synonym. |
| 229 | +**Tests:** The renamed and new tests. |
| 230 | +**Dependencies:** None |
| 231 | +**Estimated:** 15 minutes |
| 232 | + |
| 233 | +### Step 4: Quality Gates |
| 234 | + |
| 235 | +**Files:** All changed files |
| 236 | +**Description:** Run focused and standard checks. |
| 237 | +**Tests:** |
| 238 | + |
| 239 | +```bash |
| 240 | +rch exec -- cargo check --all-targets --features terraphim-routing |
| 241 | +rch exec -- cargo test --features terraphim-routing -- pi_terraphim_router |
| 242 | +cargo fmt --check |
| 243 | +``` |
| 244 | + |
| 245 | +Run clippy and UBS as evidence, but document known repository baseline noise if unchanged-file findings dominate: |
| 246 | + |
| 247 | +```bash |
| 248 | +rch exec -- cargo clippy --all-targets --features terraphim-routing -- -D warnings |
| 249 | +ubs --only=rust src/pi_terraphim_router/mod.rs src/main.rs |
| 250 | +``` |
| 251 | + |
| 252 | +If committing staged Rust changes, prefer changed-line UBS gate when full-file scan is noisy: |
| 253 | + |
| 254 | +```bash |
| 255 | +python3 scripts/check_ubs_staged_delta.py |
| 256 | +``` |
| 257 | + |
| 258 | +**Dependencies:** Steps 1-3 |
| 259 | +**Estimated:** 20-30 minutes |
| 260 | + |
| 261 | +## Rollback Plan |
| 262 | + |
| 263 | +If issues are discovered: |
| 264 | + |
| 265 | +1. Revert the no-match evidence follow-up commit. |
| 266 | +2. Keep `91b16777` intact because it added the registry-aware execution API. |
| 267 | +3. If helper extraction proves undesirable, use the minimal test-only approach: assert `select_execution_route()` returns `None` for a non-matching prompt and leave runtime code unchanged. |
| 268 | + |
| 269 | +## Migration |
| 270 | + |
| 271 | +No migration required. No public API changes. |
| 272 | + |
| 273 | +## Dependencies |
| 274 | + |
| 275 | +### New Dependencies |
| 276 | + |
| 277 | +| Crate | Version | Justification | |
| 278 | +|-------|---------|---------------| |
| 279 | +| None | N/A | Existing private types are sufficient. | |
| 280 | + |
| 281 | +### Dependency Updates |
| 282 | + |
| 283 | +| Crate | From | To | Reason | |
| 284 | +|-------|------|----|--------| |
| 285 | +| None | N/A | N/A | No dependency changes needed. | |
| 286 | + |
| 287 | +## Performance Considerations |
| 288 | + |
| 289 | +### Expected Performance |
| 290 | + |
| 291 | +| Metric | Target | Measurement | |
| 292 | +|--------|--------|-------------| |
| 293 | +| Route planning latency | No material change | One private helper only used on no-match branch. | |
| 294 | +| RPC execution latency | No change | Same provider/model fallback and same `execute_selection()` path. | |
| 295 | +| Memory | No material change | One short-lived `ExecutionSelection`. | |
| 296 | + |
| 297 | +### Benchmarks to Add |
| 298 | + |
| 299 | +No benchmark required. This is a testability/evidence fix with no meaningful hot-path change. |
| 300 | + |
| 301 | +## Open Items |
| 302 | + |
| 303 | +| Item | Status | Owner | |
| 304 | +|------|--------|-------| |
| 305 | +| Decide whether to also use `no_match_fallback_selection()` in compatibility `route_and_execute(input)` | Optional, pending implementation judgement | Implementer | |
| 306 | +| Decide whether baseline scanner findings need separate issue tracking | Outside scope | User/repo owner | |
| 307 | + |
| 308 | +## Approval |
| 309 | + |
| 310 | +- [ ] Technical review complete |
| 311 | +- [ ] Test strategy approved |
| 312 | +- [ ] Performance targets agreed |
| 313 | +- [ ] Human approval received |
0 commit comments