Milestone 12: Search
Purpose: Agents can search tickets and messages by subject/body (full-text); sender and tag as filters; search integrated into ticket list.
Exit state: FTS on tickets and messages; tRPC search with filters; ticket list search input and results.
Spec reference: §2.11 Search, §5.22 GIN indexes (tickets, messages tsv), §6.10 SearchInput.
Prerequisites: M11. FTS columns and GIN indexes from M01.
12.1 Full-text search backend
Tasks
-
Search procedure
- tRPC search: input query (string), optional filters (sender/from_address, tag_ids), tenantId from context. Use websearch_to_tsquery('english', query) for safety; search tickets.tsv and messages.tsv with ts_rank; WHERE tenant_id = ? and (tickets match or messages match). Return unified results: ticket id, ticket_number, subject, snippet (highlight), inbox name; optionally message snippet. Section 2.11. Sender and tag as WHERE filters (not FTS). Pagination (limit/offset or cursor).
-
Indexes
- Verify GIN on (tenant_id, tsv) for tickets and messages (M01). Section 5.22.
-
Ranking
- Order by ts_rank(tsv, query) desc, then created_at desc. Limit results (e.g. 50).
Acceptance criteria
| Criterion | Status |
|---|---|
| Search returns tickets (and optionally messages) matching query; tenant-scoped; sender and tag filters apply. | Pending |
| Query with special characters (quotes, negation) handled via websearch_to_tsquery; no SQL injection. | Pending |
| Performance acceptable with GIN indexes (test with 10k+ tickets). | Pending |
12.2 Search UI in ticket list
Tasks
-
Search input
- In ticket list (M07): search field (debounced, e.g. 300ms). On submit or debounce: call search tRPC; display results in same list UX (replace or merge with filter results). Section 6.10. Alternatively: search drives list query (ticket list tRPC accepts search param and uses FTS in WHERE). Prefer single list that can be filtered by search + filters.
-
Combine with filters
- When search is active, list shows only tickets matching FTS + status/assignee/inbox/tag/date filters. URL can include q= for shareable search link.
-
Empty state
- "No results for your search" when search returns empty; suggest clearing search or filters.
Acceptance criteria
| Criterion | Status |
|---|---|
| User can type in search; results update (debounced); list shows matching tickets; filters still apply. | Pending |
| Clear search or empty query shows normal list (or last filters). | Pending |
| URL reflects search query for sharing (optional). | Pending |
| Search results and ticket list stay in sync when tickets change (SSE: ticket.updated, ticket.message_added invalidate list/search queries; Section 4.11). | Pending |
Milestone 12 sign-off
| Criterion | Status |
|---|---|
| All tasks in 12.1–12.2 complete. | Pending |
| All acceptance criteria met. | Pending |
| Search by subject/body works; sender and tag as filters; UI integrated. | Pending |
| E2E: Search and filters (see INDEX — Testing strategy). | Pending |
| Ready for M13 (Reporting). | Pending |