Skip to content

Incremental indexing

Indexing converts append-oriented JSONL session files into structured, searchable records. Re-reading every file on every change would waste work, so LocalLore checkpoints each source by path, filesystem identity, size, modified time, byte offset, and last complete line.

flowchart TD
    A[Poll JSONL metadata] --> B{Snapshot changed?}
    B -- No --> A
    B -- Yes --> C[Debounce burst]
    C --> D[Queue one refresh]
    D --> E[Acquire index lock]
    E --> F[Discover current sources]
    F --> G{Source state}
    G -- New or appended --> H[Read from checkpoint]
    G -- Truncated or replaced --> I[Rebuild that source]
    G -- Deleted or renamed --> J[Remove old source records]
    H --> K[Parse complete JSONL lines]
    I --> K
    J --> L[Embed pending messages]
    K --> L
    L --> M[Commit SQLite transaction]
    M --> A

Change handling

  • Unchanged file: skip it using identity, size, and modification time.
  • Append: seek to the stored byte offset and parse only new complete lines.
  • Incomplete tail: leave the byte offset before the partial line so a later refresh can import it safely.
  • Truncation or replacement: delete records associated with that source and rebuild it from byte zero.
  • Deletion or rename: remove the missing source; foreign-key cascades clean up its messages, file operations, FTS rows, and embeddings. A renamed file is then discovered as new.
  • Malformed line: record the error and continue with other lines.

Only one worker refreshes the index. File bursts coalesce through a debounce, and refresh requests arriving during indexing queue another pass. Failures retry with bounded exponential backoff.