GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

20. Let the index hold what the schema declares

Context and problem

ADR-0019 made the parser keep every frontmatter key and let groove-schema.toml name any of them, and stopped there: retained keys reached groove validate and nothing else. The same user who asked for that (#289) showed where the rest of the cost sits. In a 74-document corpus with four metadata axes, 434 of 1,036 tag occurrences exist only to mirror status, environment, team and source into tags, because tags is the only frontmatter the search filters can see. The mirror needs an external validator to stay honest, and topic ends up spent on document type. They asked for one thing: filter on a retained key, exact string match, no operators, only on keys the schema declares.

The question this answers: who brings a declared key into the index, and when — the indexer reading the schema, or the query reading it?

Decision drivers

Options considered

  1. The indexer reads the schema and stores the declared keys’ values in their own table; search filters on the table and never reads the schema. Taken.
  2. Store every scalar the parser retained; validate the key against the schema at query time. Rejected: serve and groove search would both need to load the schema, a knowledge base without one would need a separate rule, and the index would carry values nobody declared.
  3. A JSON column on documents, filtered in Rust after the query, like tags. Rejected: a separate table lets the query itself narrow, and a list value is a second row rather than a second encoding.
  4. Exclusion as an operator (status!=deprecated). Rejected: the reporter asked for exact match and no operators. A second flag, --field-not / fields_not, gives the exclusion the same exact-match shape and leaves the value grammar alone.

Decision

Consequences

References