GrooveSeek

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

View the Project on GitHub alphabet-h/grooveseek

5. Record each document’s size in the index

Context and Problem Statement

ADR-0004 established that a resource read is bounded by the index, and that resources/list must therefore offer only what a read will accept. It also recorded one place where that property did not hold:

a document above GET_DOCUMENT_MAX_BYTES (1 MiB of text) is indexed, because indexing accepts 50 MiB, and then refused on read. That one is knowable, but only by stat-ing every indexed file on every listing […] The durable fix is to record the size at index time or to reconcile the two caps; until then this is a known limitation, not a claim.

So a Markdown or plain-text document between 1 MiB and 50 MiB is indexed, appears in a topic listing, carries a uri on its search hits — and is refused when a client follows that link. Binary formats are unaffected: they are read under the 50 MiB cap and their extracted text is truncated with a notice rather than refused, which ADR-0004 already covers.

Nothing forced the question now. What made it the moment to answer is that kb-mcp doctor was being added, and both changes are about what the index knows about itself; deciding later would mean a second migration of the same table.

Decision Drivers

Considered Options

  1. Stat every indexed file on every listing. The option ADR-0004 named and declined.
  2. Reconcile the two caps — refuse to index text above what a read can return.
  3. Record the byte size at index time, in the documents table, and use it in the predicate that decides what is offered.

Decision Outcome

Option 3.

Option 1 turns an offer from a property of the index into a live filesystem probe, which is precisely the boundary ADR-0004 drew, and it does not even close the gap: a file can cross the cap between the listing and the read.

Option 2 is backwards-incompatible in the worst direction. GET_DOCUMENT_MAX_BYTES is 1 MiB for a reason that has nothing to do with indexing — it is how much text kb-mcp is willing to hand an MCP client in one response — so reconciling the two means lowering the index limit to it, and documents that are indexed and searchable today would silently stop being indexed. A retrieval system that drops a document because it is long is worse than one that finds it and declines to inline the whole thing.

Option 3 keeps ADR-0004 intact: size becomes part of what the index knows, so the offer stays a statement about the index. It costs one nullable column, and the query that consults it asks only for rows past the smallest read cap — on a corpus with no oversized document, that returns nothing.

ServableRules is now the single predicate behind both resources/list and the uri on a search hit. It applies the per-extension cap through max_bytes_for, the same chooser load_document_blocking passes to read_checked, so the listing and the read cannot come to enforce different limits. Before this change the two surfaces called the registry check separately; that was harmless only while the predicate was a single call, and adding a second condition to one of them is exactly what would have made a search hand out a link resources/read refuses.

Consequences