# QA Plan: Feedback Loop UX ## Scope Verify that `submit_feedback` and `feedback_state` APIs work correctly for all 11 feedback action variants, that the immediate reflection guarantee holds, and that undo actions properly reverse effects. ## Test Strategy ### 1. Unit Tests (21 tests in `db/feedback.rs`) | Test | Validates | |------|-----------| | `submit_like_updates_liked_bitmap` | Like adds to liked bitmap | | `submit_unlike_removes_from_liked_bitmap` | Unlike removes from liked bitmap | | `submit_hide_adds_hard_neg_and_hidden` | Hide writes signal + hard-neg + hidden | | `submit_unhide_removes_hard_neg_and_hidden` | Unhide reverses Hide | | `submit_mute_creator_blocks_creator` | MuteCreator adds to blocked creators | | `submit_unmute_creator_unblocks_creator` | UnmuteCreator reverses | | `submit_dislike_adds_hard_neg` | Dislike adds to hard-neg bitmap | | `submit_undo_dislike_removes_hard_neg` | UndoDislike removes from hard-neg | | `submit_save_and_unsave` | Save/Unsave toggle saved bitmap | | `submit_not_interested_marks_seen` | NotInterested marks seen + writes skip signal | | `skip_escalation_to_hard_neg` | 3 skips on same item = hard-neg | | `skip_below_threshold_no_hard_neg` | 2 skips = no hard-neg | | `feedback_state_after_like` | feedback_state returns is_liked=true | | `feedback_state_after_hide` | feedback_state returns is_hidden=true | | `feedback_state_after_dislike` | feedback_state returns is_disliked=true | | `feedback_state_after_mute_creator` | feedback_state with creator_id returns is_creator_muted=true | | `feedback_state_default_all_false` | Untouched item returns all false | | `unlike_on_never_liked_is_noop` | Unlike on non-liked item = no panic | | `unhide_on_never_hidden_writes_signal_ok` | Unhide on non-hidden item = Ok | | `like_then_dislike_both_states_coexist` | Both liked and disliked can be true | | `submit_feedback_with_write_item` | Feedback works on items with metadata | ### 2. Integration Tests (10 tests in `p1_feedback_loop.rs`) | Test | Validates | |------|-----------| | `like_and_unlike_roundtrip` | Like/Unlike toggle via feedback_state | | `hide_and_unhide_roundtrip` | Hide/Unhide toggle via feedback_state | | `dislike_and_undo_dislike_roundtrip` | Dislike/UndoDislike toggle via feedback_state | | `mute_and_unmute_creator` | MuteCreator/UnmuteCreator toggle | | `save_and_unsave_roundtrip` | Save/Unsave toggle | | `skip_escalation_at_threshold` | 3 NotInterested = hard-neg (shows as disliked) | | `feedback_state_default_for_unknown_pair` | Unknown user-entity = all-false state | | `multiple_users_independent_feedback` | User A like + User B hide = independent | | `negative_preference_update_on_dislike` | Dislike with embedding triggers preference update | | `combined_actions_feedback_state` | Like + Save = both is_liked and is_saved | ### 3. Build Quality | Check | Command | Pass criteria | |-------|---------|---------------| | Format | `cargo fmt --check` | No diffs | | Clippy | `cargo clippy -- -D warnings` | Zero warnings from feedback files | | All lib tests | `cargo test --lib` | 1345 passed, 0 failures | | Integration tests | `cargo test --test p1_feedback_loop` | 10 passed, 0 failures | ### 4. Regression Full lib test suite (1345 tests) must pass with no regressions.