Architecture
Storage, search, behavior, rendering, and terminal ownership remain separate so the core can be tested without a live terminal.
SQLite
│
▼
history.rs → immutable entries + indexes
│
├── scope.rs → global / pwd / git-root semantics
│
▼
search.rs → incremental fuzzy candidate sets + bounded top-K
│
▼
app.rs → pure state transitions + modal state
│
├── config.rs / keymap.rs → typed TOML validation
│
▼
tui.rs → render-only Ratatui projection
│
▼
terminal.rs → terminal lifetime + blocking event loop
History and refresh
The store is loaded as an immutable snapshot. LiveHistory fingerprints the SQLite file and requests a fresh snapshot after metadata changes. The model reapplies scope and query and rebinds selection by stable history identity where possible.
Search
Search layers narrow candidate streams as the query grows. Backspace can restore a cached prefix layer. A bounded heap retains only the best result set, avoiding a full sort of every fuzzy match.
Modal state
Menus are stacked. Windows are explicit modal surfaces. The event loop resolves local modal bindings before the base keymap and never treats ordinary text as input while a modal is active.
Testing
Pure model/store tests cover behavior without a terminal. SQLite refresh integration uses deterministic temporary databases. Render tests use fixed-size buffers, and the repository fixture is exercised through the real-db smoke command.