# m6p1 — Cohort Engine + Cohort-Scoped Trending (✅ COMPLETE 2026-02-23) Phase spec and acceptance criteria: [ROADMAP · Milestone 6 · Phase 1](../ROADMAP.md). Milestone index: [README.md](README.md). Spec: [docs/specs/05-cohorts.md](../../specs/05-cohorts.md). Backfilled record. ## What shipped 1. **Cohort definitions as data** (`tidal/src/cohort/types.rs`). `CohortDef { name, predicate }` with a five-arm `Predicate` enum — `Eq { field, value }`, `Any { field, values }`, `Range { field, lo, hi }` (inclusive, `f64`-parsed), `And(Vec)`, `Or(Vec)`. Both types are `Serialize`/`Deserialize`, which is what makes cohort state checkpointable rather than rebuild-only. 2. **`db.define_cohort(CohortDef)`** (`tidal/src/db/cohorts.rs:19`) registers a named cohort at runtime, not only at schema build time. 3. **Membership resolution at write time** (`tidal/src/cohort/resolver.rs`). User metadata is evaluated against every registered predicate; the result is cached and invalidated when the user's metadata is rewritten, so a signal write pays a lookup rather than a predicate sweep. 4. **Per-cohort signal aggregation** (`tidal/src/cohort/ledger.rs` — `CohortSignalLedger`). Same decay / windowed-count / velocity semantics as the global ledger, keyed by cohort as well as entity and signal type, updated on the same write. 5. **Query surface.** `RetrieveBuilder::cohort(name)` scopes signal reads to the cohort ledger; `cohort_predicate(Predicate)` answers ad-hoc cohorts by resolving matching users at query time (slower, no pre-materialized state). A `cohort_trending` built-in profile ships in `tidal/src/ranking/builtins.rs`. 6. **Durability** (`tidal/src/cohort/checkpoint.rs`). Cohort ledger state is checkpointed alongside the global ledger and restored on open. ## Evidence - `tidal/tests/m6_cohort.rs` — 12 `#[test]` functions, including the divergence case the ROADMAP calls for: cohort-scoped trending must differ from global trending when cohort velocity diverges from global velocity. - `tidal/tests/m6_crash_surfaces.rs` and `tidal/tests/m7_crash_m6.rs` — M7 crash-fences the cohort state this phase introduced. - `tidal/src/testing/crash_injector.rs` references `CohortSignalLedger`, i.e. the cohort tier is a first-class target of fault injection, not an untested side structure. ## Note on the "< 50 ms at 500 items / 20 users" criterion Marked `[x]` in the ROADMAP with no recorded run. The functional criteria are proven by `m6_cohort.rs`; the latency figure has no in-repo measurement. Unlike M11/M12, this milestone published no `docs/profiling/` record for its own gates.