# m6p4 — User Collections + Watch History + Saved Searches (✅ COMPLETE 2026-02-23) Phase spec and acceptance criteria: [ROADMAP · Milestone 6 · Phase 4](../ROADMAP.md). Milestone index: [README.md](README.md). Backfilled record. ## What shipped 1. **Collections** (`tidal/src/entities/collection.rs`, `tidal/src/db/collections.rs`). `create_collection(owner, name, visibility)` with `Visibility::{Private, Shared, Public}`, plus `add_to_collection` (idempotent), `remove_from_collection`, and `list_collections`. Membership is a `RoaringBitmap` per collection for O(1) checks, persisted to fjall. 2. **`FilterExpr::InCollection`** (`tidal/src/storage/indexes/filter/expr.rs`, evaluated in `filter/evaluator.rs`) constrains candidates to a collection's bitmap. 3. **Watch history / in-progress.** The `in_progress` user-state filter selects items with a partial-completion signal, closing the "continue watching" surface. 4. **Saved searches** (`tidal/src/session/saved_search.rs`). `save_search`, `list_saved_searches`, `retrieve_saved_search` (re-executes with a `created_after` bound so a saved search behaves as a persistent feed), and `delete_saved_search`. 5. **Cross-session preference aggregation — closes an M4 deferral.** On `close_session`, every entity that received a session signal has its stored content embedding blended into the user's global preference vector (`tidal/src/db/sessions.rs:532-574`). Entities without stored embeddings are skipped, and a dimension mismatch is **logged at WARN rather than silently dropped**, because a silent drop hides a schema/embedding mismatch that quietly degrades personalization. This is the M4 "Deferred to Later Milestones" item "session signal influence on global user preference vector", delivered here rather than deferred further. ## Evidence - `tidal/tests/m6p4_collections.rs` — 10 `#[test]` functions, incl. persistence across restart. - `tidal/tests/m6_crash_surfaces.rs`, `tidal/tests/m7_crash_m6.rs` — collections and saved searches under crash injection. - `tidal/tests/session_durability.rs` — the session half of the aggregation path. ## Note on the "< 10 ms `in_collection` at 100 items" criterion Marked `[x]` with no recorded measurement. Functional behaviour is proven by `m6p4_collections.rs`; the latency figure is not evidenced.