Skip to content

Index transcript reads by their actual ordering keys

Context

Message pagination filters by thread and orders by the canonical SQLite rowid cursor, while the existing (thread_id, created_at) index requires a temporary sort. Dashboard date windows and agent thread lists also lack matching indexes. Synchronous scans and sorts can delay other requests in the single Node process.

Decision

Add idempotent indexes on messages(thread_id), messages(created_at), and threads(updated_at DESC). Ordinary SQLite secondary indexes include rowid as their final ordering key, so the thread-only message index supports both directions of cursor pagination. Keep the existing thread/timestamp index for time-window conversation reads. No tables, message cursors, or stored values change.

Also index latest usage by (thread_id, created_at DESC), memory lists by (namespace, updated_at DESC) and (updated_at DESC), and runtime operation backlogs by (thread_id, state). Add signal-event indexes on thread_id and operation_id for backlog counts, pruning, and foreign-key cascades. Index background-result expiry for retention cleanup.

The embedding-job and signal-delivery indexes on (state, available_at...) cannot bound the separate lease_until range. Add (state, lease_until_ms) for embedding jobs and (state, lease_until) for signal deliveries. Compute the next embedding wakeup from two indexed minima rather than scanning all active jobs through a CASE expression. Preserve due-work ordering and lease semantics.

Do not add another agent/thread index: the existing unique threads(agent_id) index already limits an agent lookup to one row. Checkpoint reads already use the composite primary keys on checkpoint and write identifiers; no extra checkpoint index is justified by those hot reads.

Validation

Migration tests must verify index creation on upgrade, repeatability, and query plans without table scans or temporary ordering trees for the targeted reads. Existing pagination tests remain the source of truth for message ordering.

Consequences

Reads avoid unnecessary scans and sorting at the cost of extra index storage, write maintenance, and a one-time index build when an existing database opens. No user action or database-file relocation is required.