"If the index disagrees with the files, the index is wrong. Purge it and replay — never patch it into agreement."
Indexes Are Derived, Never Masters
Rekindle grows real databases — a note index, full-text search (the corpus side is Lantern's). None of them are the truth. Every one is a derived mirror of the Markdown files, and its entire contents could be thrown away and rebuilt from disk without losing anything. That status is decided up front, not discovered later: an index is a fast cache of what the files already say.
Purge and Replay, Never Diff and Patch
The discipline that keeps it honest is the recovery rule. If the index and the files ever disagree — an external edit, a crash mid-write, a bug — the answer is never "figure out the delta and patch the index into agreement." It's purge the derived rows and replay from the files. Diff-and-patch reconciliation is exactly where corruption lives: every patch is a small guess about what happened, and guesses accumulate. A rebuild has no guesses in it.
// Recovery is always this shape — never a reconcile.
fn rebuild_index(vault: &Path) -> Result<(), Error> {
purge_index()?; // throw away ALL derived rows
for file in markdown_files(vault) { // replay from ground truth
index_note(&read_to_string(&file)?)?;
}
Ok(())
}
This is cwkPippa's discipline, carried one domain over. There, the JSONL log is ground truth and SQLite plus the vector store are derived mirrors, rebuilt by purge-and-replay. Here, the Markdown files are ground truth and every index is the mirror. Same rule, same reason: one authority, rebuildable derivatives, no reconciliation logic anywhere.
Meaning Lives in Frontmatter
The rule extends to metadata, with one line drawn carefully. What the document means — its title, its tags, its date — lives in the file's frontmatter, not in a database. If tags lived only in a DB, that DB would become a second master (lose it and you've lost real data), and the whole file-canonical claim would be a half-truth. What the app remembers about a note — a pin, a lock, a workflow lane — is a different kind of thing, and v4 keeps it in small sparse sidecar files beside the library, on purpose (the note above). Lose a sidecar and every document is still whole; only the app's arrangement of it is gone. The index, either way, stays what it should be: a fast, disposable view of what the files already contain.