# Web App: the Traces page scales like the session sidebar

The trace list used to fetch everything at once: every agent auto-expanded on load, each expansion walked the agent's entire trace tree server-side with a stat per file, every session row and file pill rendered, and any session the paged sidebar store hadn't loaded fell back to its raw id in mono type — long-lived Projects froze the page.

`GET /api/projects/:p/agents/:a/traces` now takes optional `offset`/`limit`. Without them the response shape is byte-identical to before; with them the server returns newest-first session groups plus a total, and only the returned page is stat-ed (sizes written back to the index).

Discovery itself no longer touches the tree per request. A SQLite-derived **trace index** (`trace_files` plus per-session facts read once at registration: origin, workspace, a first-prompt title fallback, model ref) serves every consumer. The hot path is two stats — an mtime gate on the traces root and the newest date dir; a moved gate triggers an incremental readdir of only the changed date dirs, each new session classified once with a bounded head-read; server-side writes (import, session/agent/project deletion) update the index synchronously; and every consumer's miss forces one full reconcile before it may 404, so a stale index costs one extra scan, never a lie. The index is a rebuildable cache — the on-disk Trace files stay the single source of truth, and the first request after an upgrade or restart simply rebuilds it. The same index now feeds the agents-list 30-day activity sparkline (previously a full-history walk on every agents-list request), the `/messages` subagent resolver (previously a project-wide scan), trace locate/download/delete, CLI-session discovery — adoption reads no file anymore — and session resume. Listings do zero head-reads; titles come from the stored facts: DB session title, else the registered first-prompt fallback.

The page now mirrors the sidebar's structure outright, not just its pure helpers. The sidebar's group header, folder section, "more" rows and group-mode toggle were extracted verbatim from its inner closures into shared `components/ui/group-list.tsx` — and the sidebar itself was refactored onto them, so the two surfaces render from one implementation. The traces tree gains both grouping modes (by Workspace or by Agent, on the same persisted preference as the sidebar — flipping the mode on either surface flips both) and the three lazy folders per group (subagent, schedule, archived), each paging independently. Server support is additive: paged session groups carry `category` and `workspace`, the response carries per-category and per-workspace counts, and a `category=` filter pages within one folder — all answered from the trace index, with classification exact on the first request since facts are stored at registration.

Elsewhere the earlier fixes stand: the page-size split for load-more (traces-specific steps — 20 sessions, 15 groups, larger than the sidebar's 10s since the full-height page holds more), group caps with a "more groups" row, pinned-first ordering for a deep-linked agent, only the focused (or first) agent expanded by default, capped file pills, and a memoized title lookup. CLI-origin sessions follow the user's "show CLI sessions" preference here too, defaulting to hidden; a "View trace" deep link to a CLI session still resolves and opens the detail pane regardless of the preference. Session ids no longer render anywhere on the page — an untitled session shows the standard "New chat" label, and the detail subtitle keeps date · size only. The chat page links back: its session info dropdown gains a "View trace" action deep-linking to the traces page with the agent focused and the session selected, falling back to a one-agent full fetch when the session sits beyond the first page or inside a folder.
