--- # claudbg-4bms title: Filter query parser status: completed type: task priority: normal created_at: 2026-03-31T00:33:08Z updated_at: 2026-03-31T05:04:41Z parent: claudbg-2vwx --- Implement the filter query language: **Syntax:** - `key:value` — substring match (e.g. `model:haiku` matches `claude-haiku-4-5`) - `key:*` — field is non-empty - `key>value` / `key Result` — hand-rolled recursive-descent parser, no new dependencies - **`SessionRow` trait** with fields: `model`, `project`, `id`, `agents`, `messages`, `tokens`, `date` - **`impl SessionRow for SessionListItem`** — wires to all `SessionListItem` fields; `tokens()` returns `None` (token count not yet tracked in that struct) - **`Filter::matches(&self, row: &R) -> bool`** evaluates the parsed AST against any `SessionRow` - **`pub use filter::Filter`** re-exported from `src/lib.rs` ### Parser details - Tokenizer splits on whitespace; identifies `AND`/`OR` keywords and key-op-value atoms - Recursive descent: `or_expr → and_expr ( OR and_expr )*`, `and_expr → primary ( AND primary )*` - AND binds tighter than OR (standard precedence) - String keys (`model`, `project`, `id`): `:` only; case-insensitive substring match; `*` matches any non-empty - Numeric keys (`agents`, `messages`, `tokens`): `:` (equality), `>`, `<` - Date key (`date`): ISO 8601 `YYYY-MM-DD`; `:` (equality), `>`, `<` - Unknown keys and malformed syntax produce `AppError::Parse` with user-readable messages ### Not-yet-wired fields - `tokens` — `SessionListItem` does not carry a token count; `tokens:`/`tokens>`/`tokens<` always returns `false` until the field is added ### Tests 39 new unit tests in `filter::tests` covering: tokenizer, atom parser, `Filter::parse` (valid + error cases), and `Filter::matches` for all key types and logical operators. All 234 project tests pass.