# How the Reasonix History Catalog Powers Session Search: Architecture & Implementation

> Discover how the Reasonix history catalog, a disposable SQLite FTS5 index, powers fast cross-session search. Learn about its architecture and implementation for efficient session searching.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: architecture
- Published: 2026-08-13

---

**The Reasonix history catalog is a disposable SQLite FTS5 full-text index that projects searchable fields from every chat turn across all session files, enabling fast cross-session search with pagination and context extraction.**

The `esengine/DeepSeek-Reasonix` project maintains every conversation turn—system prompts, user messages, assistant replies, and tool calls—in append-only JSONL session files. To make this chat history searchable without scanning raw logs, Reasonix implements a **history catalog**: a lightweight, rebuildable full-text search layer that sits between the on-disk session storage and the desktop UI's search interface.

## What the History Catalog Is: A Projection, Not a Source of Truth

The catalog is intentionally **disposable**. It does not store complete messages; rather, it projects only the searchable fields from each turn—role, text content, and tool-call data—while preserving a reference back to the original session file and the turn's position within it. This design keeps the index small, fast to rebuild, and separate from the durable session store.

The core implementation lives in [`internal/historycatalog/catalog.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/historycatalog/catalog.go), which manages the SQLite FTS5 index, rebuild orchestration, and query execution.

## Registering Catalog Roots at Startup

Before the catalog can index anything, Reasonix registers **roots** that define where session files live on disk. Each root is represented by `historycatalog.Root` as defined in [`internal/historycatalog/types.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/historycatalog/types.go):

- **Global sessions**: The default session directory
- **Project-scoped sessions**: Session files tied to specific workspaces
- **Archive**: Older sessions moved to cold storage

```go
roots := []historycatalog.Root{
    {Path: config.SessionDir(), Source: "global", Scope: "global"},
    {Path: config.ArchiveDir(), Source: "archive", Scope: "global", Archive: true},
}

```

The [`desktop/history_catalog.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/history_catalog.go) file wraps this root construction and exposes it to the desktop layer. When Reasonix launches, these roots are passed to `historycatalog.Rebuild` to populate or refresh the index.

## Rebuilding the Index: Background Processing with Status Updates

The `historycatalog.Rebuild` function—also in [`internal/historycatalog/catalog.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/historycatalog/catalog.go)—walks every registered root, parses each JSONL session file, and inserts extracted messages into SQLite FTS5. This runs asynchronously and publishes `historycatalog.Status` updates that the UI displays as progress indicators.

For manual control, the CLI command `reasonix catalog reindex history` (implemented in [`internal/cli/catalogs_history.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/cli/catalogs_history.go)) triggers the same rebuild pipeline on demand.

## Searching the History Catalog: Filters, Pagination, and Relevance

The desktop UI initiates searches through `catalog.Search` with a `historycatalog.SearchRequest`. This request supports multiple filtering dimensions:

- **Full-text query**: Arbitrary search terms against message content
- **Role filtering**: Restrict to specific turn kinds (`user`, `assistant`, `system`, `tool`)
- **Tool name filtering**: Find turns that invoked specific tools

```go
req := historycatalog.SearchRequest{
    Query:    "authentication bug",
    Kinds:    []string{"user", "assistant"},
    ToolName: "",
    Limit:    20,
}
hits, err := catalog.Search(ctx, req)

```

Results return as `[]historycatalog.Candidate`, each containing:
- `SessionPath`: Location of the source JSONL file
- `MessageIndex`: Position of the turn within that session
- Relevance rank for result ordering

### Cursor-Based Pagination

Large result sets stream through `historycatalog.SearchCursor`. The UI requests subsequent pages by passing the cursor's rank and session location, allowing efficient batch retrieval without re-scanning the entire index. This mechanism lives alongside the core search implementation in [`internal/historycatalog/catalog.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/historycatalog/catalog.go).

## Context Extraction: Bridging Index Hits to Full Conversations

The catalog stores only minimal data. To show meaningful search results, Reasonix fetches surrounding turns from the original session file using [`desktop/history_search_collect.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/history_search_collect.go). Given a `Candidate`, this code:

1. Opens the session file at `Candidate.SessionPath`
2. Parses the JSONL into an in-memory slice
3. Extracts a window around `Candidate.MessageIndex` (typically ±3 turns)

```go
cand := hits[0]
sessionPath := cand.SessionPath
msgIdx := cand.MessageIndex

sessionMsgs, _ := readSessionFile(sessionPath)
context := sessionMsgs[max(0, msgIdx-3) : min(len(sessionMsgs), msgIdx+3)]

```

The resulting `[]HistorySearchContextLine` gives the UI a navigable snippet with full conversational context.

## Lifecycle Management: Shared Instances and Observers

The [`internal/history/indexed.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/history/indexed.go) file manages the shared catalog instance, coordinates observers for change notifications, and handles asynchronous updates when sessions are modified. This ensures that long-running Reasonix processes keep their search indexes reasonably fresh without blocking the main application loop.

## Summary

- **The history catalog** is a disposable SQLite FTS5 projection of searchable message fields, not a primary data store.
- **Roots** (`historycatalog.Root`) define where session files live; `historycatalog.Rebuild` indexes them at startup or on demand.
- **Search** (`catalog.Search`) supports full-text queries, role filters, and tool name filters, returning ranked `Candidate` results.
- **Pagination** uses `historycatalog.SearchCursor` for efficient streaming of large result sets.
- **Context extraction** in [`desktop/history_search_collect.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/history_search_collect.go) retrieves surrounding turns from source JSONL files to populate the UI.

## Frequently Asked Questions

### How is the history catalog different from the session files themselves?

The session files are the durable source of truth—append-only JSONL logs containing complete turn data. The history catalog is a disposable, searchable projection that extracts only fields needed for full-text search (role, text, tool data) plus location references. If the catalog is deleted, `historycatalog.Rebuild` regenerates it from the session files without data loss.

### Can I trigger a catalog rebuild manually?

Yes. The CLI command `reasonix catalog reindex history` (in [`internal/cli/catalogs_history.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/cli/catalogs_history.go)) initiates a rebuild on demand. The desktop application also rebuilds automatically at startup when roots are registered.

### What search filters does the history catalog support?

The `historycatalog.SearchRequest` accepts three filter types: a full-text `Query` string against message content, a `Kinds` slice restricting to specific roles (`user`, `assistant`, `system`, `tool`), and a `ToolName` string to find specific tool invocations. These can be combined arbitrarily.

### How does Reasonix handle pagination for large search results?

Results stream through `historycatalog.SearchCursor`. The client requests the next batch by passing the cursor's rank and session position, allowing the query to resume efficiently without re-executing the full search.