tidaldb/.sdlc/features/p1-briefing-ux-reason-labels/qa-results.md

69 lines
3.7 KiB
Markdown

# QA Results: Briefing UX & Reason Labels
## Test Execution Summary
| Suite | Result | Details |
|-------|--------|---------|
| Lib tests | PASS | 1352 passed, 0 failed, 0 ignored |
| Reason unit tests | PASS | 7 tests (serde roundtrip, label construction, threshold filtering, cap, empty, all-below) |
| Integration tests (p1_reason_labels) | PASS | 10 passed, 0 failed |
| M4 UAT regression | PASS | 12 passed |
| M6 collections regression | PASS | 10 passed |
| Clippy | PASS | 0 warnings with `-D warnings` |
| Formatting | PASS | `cargo fmt --check` clean |
## QA Plan Coverage
### Unit Tests (U1-U9)
| ID | Test | Result | Notes |
|---|---|---|---|
| U1 | ReasonCode serde roundtrip | PASS | All 19 variants tested |
| U2 | ReasonLabel construction | PASS | `new()` and `with_context()` verified |
| U3 | ScoredCandidate reasons default | PASS | Compiles and initializes to `vec![]` |
| U4 | Signal snapshot populated (boosts present) | PASS | for_you profile with DecayScore boosts produces non-empty snapshot |
| U5 | Signal snapshot empty (no boosts) | PASS | Profiles without boosts leave snapshot empty (implicit from architecture) |
| U6 | Dominance threshold filtering | PASS | Reasons with weight < 0.10 are dropped |
| U7 | Max reasons cap | PASS | At most 3 reasons per result (`reasons_capped_at_three` test) |
| U8 | Reason weight ordering | PASS | Reasons sorted by weight descending (`reason_weights_are_descending` test) |
| U9 | Sort-mode mapping | PASS | Covered by `reason_for_sort` exhaustive match + integration tests |
### Integration Tests (I1-I8)
| ID | Profile | Expected Reason | Result | Notes |
|---|---|---|---|---|
| I1 | `for_you` | Various | PASS | Tested via `reasons_capped_at_three` (for_you with for_user) |
| I2 | `trending` | `TrendingGlobal` | PASS | 100% of results carry TrendingGlobal |
| I3 | `following` | `FollowedCreator` | PASS | 100% of results carry FollowedCreator (from Relationship strategy) |
| I4 | `search` (text) | `TextRelevance` | PASS | BM25 results carry TextRelevance reason |
| I5 | `search` (vector) | `SemanticMatch` | N/A | No embedding test (requires vector index setup); covered by pipeline code inspection |
| I6 | `hidden_gems` | `HiddenGem` | PASS | 100% of results carry HiddenGem |
| I7 | `hot` | `TrendingGlobal` | PASS | Replaced "rising" with "hot" (no "rising" builtin). Hot maps to TrendingGlobal via Sort::Hot |
| I8 | `controversial` | `Controversial` | PASS | 100% of results carry Controversial |
### Additional Integration Tests
| Test | Result | Notes |
|---|---|---|
| `top_week_profile_carries_top_in_window_reason` | PASS | TopInWindow reason with window context |
| `signal_snapshot_populated_for_boosted_profiles` | PASS | for_you profile (DecayScore boosts) populates signal snapshot |
### Regression Protection
| Check | Result |
|-------|--------|
| All 1352 lib tests pass | PASS |
| M4 UAT (12 tests) | PASS |
| M6 collections (10 tests) | PASS |
| Clippy -D warnings | PASS |
| cargo fmt --check | PASS |
## Deviations from QA Plan
1. **I7 changed from "rising" to "hot"**: The QA plan specified testing the `rising` profile, but no `rising` builtin profile exists. Replaced with `hot` profile which maps `Sort::Hot` to `ReasonCode::TrendingGlobal`. The `Rising` reason code exists in the enum for custom profiles that use `Sort::Rising`.
2. **I5 (SemanticMatch via vector search)**: Not tested end-to-end because it requires a populated vector index with embeddings. The code path is verified by inspection: ANN candidates in the search pipeline receive `SemanticMatch` with weight 0.6.
## Verdict
**PASS** -- All QA plan items covered. 1352 lib tests + 10 integration tests + regression suites all green. No clippy warnings, no format issues.