--- id: "0004" date: 2026-06-03 status: Superseded supersedes: superseded-by: "0010" affected-paths: [] affected-components: [] migration_confidence: medium migration_source: "docs/memory-system/03-architecture-decisions.md### ADR-004 — SQLite + Sequel (Ruby) tag index as the knowledge-layer cache" --- # 0004 — SQLite + Sequel (Ruby) tag index as the knowledge-layer cache ## Context The AI can't use Obsidian tags directly; tag filtering needs a machine-queryable index. A previous `~/Documents/SecondBrain/` tag database was lost track of. ## Decision A small **Ruby program using the Sequel ORM over SQLite**, exposed as a **CLI**. Schema: `files(path, mtime, summary, scope)`, `tags(name)`, `files_tags` join (`many_to_many`). The summary is a **column on `files`** (an attribute), not a join. - **Rationale**: Normalized `tags` table makes enumerating the vocabulary a first-class cheap query (the "virtual index" goal). The `summary` column is what turns the index from a *finder* into a *router* — the AI sees enough to pick a file without opening it (progressive disclosure, low tokens). Ruby + Sequel + CLI keeps the contract clean and the DB swappable; the AI never touches SQLite directly. - **Failure-mode guard (the lost-SecondBrain lesson)**: **markdown is always authoritative; the SQLite file is a disposable cache** that is never synced and can be rebuilt from frontmatter anytime (`index update --rebuild`). ## Consequences A Ruby/Sequel/SQLite CLI tag index was built as the machine-queryable cache over the vault, with markdown treated as always-authoritative and the SQLite file as a disposable, rebuildable cache — never synced. This bespoke index and its CLI were later dropped and superseded by ADR-010's Graphify graph, though the summary + namespaced-tag frontmatter it introduced was retained as note metadata. ## Alternatives rejected Plain-markdown generated `INDEX.md` (must regenerate; grep-at-scale is token-heavy). Frontmatter grep on demand (scales badly). Milvus/Postgres for knowledge (overkill; QMD/memsearch prove SQLite is enough — see ADR-006/008). - **Query output**: returns **path + summary + matched tags** (option C) — tags are cheap and show *why* a result matched, useful for cross-client queries.