GrooveSeek

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

View the Project on GitHub alphabet-h/grooveseek

13. Compile in one grammar and load the rest

Context and Problem Statement

ADR-0012 settles how a source file becomes chunks. It needs a grammar to do it, and grammars are not small: a generated parse table is a large C source file per language, and a tool that carries every language carries all of them.

groove’s value proposition has been one binary you place and run. Nothing else to install, no runtime, no package manager. A code parser that ships twenty grammars would break that promise by weight alone; one that ships none would not parse anything out of the box.

The question this decision answers: which grammars does the binary carry, and how does a user who wants another language get it?

Decision Drivers

Considered Options

  1. Every grammar compiled in. Simple, and the size is the problem: an editor that took this route found grammars dominating its installed footprint. The user who indexes only Markdown pays all of it.
  2. Cargo features, so each user compiles the set they want. Works only for people who build from source. The release pipeline produces one artifact per platform and cannot produce a matrix of feature combinations, so the published binary would still have to pick one set — which is option 1 or option 5 wearing a different hat.
  3. One grammar compiled in, the rest as dynamic libraries groove publishes (chosen).
  4. WebAssembly plugins. Sandboxed, which is the attractive part. The embedded runtime costs several megabytes on its own — paid by everyone, including the people who never load a plugin, which is the cost option 1 was rejected for. It also raises the toolchain floor, and on macOS a just-in-time compiler needs entitlements a signed application must request.
  5. Nothing compiled in; every language is a plugin. The same code path as option 3 with the feature turned off, so it stays available. As a default it means the tool cannot parse a single line of code until the user goes and fetches something.
  6. Download a language bundle on first use. Convenient, and it spends the network property for a convenience. The one bundle available is also large per platform and does not carry the queries.

Decision Outcome

Chosen: option 3. Rust is compiled in, behind a feature that is on by default. Every other language is a separate dynamic library, published by groove’s own release pipeline, that the user places in a directory groove reads.

Rust rather than some other first language for two reasons: it is what groove is written in, so pointing groove at its own repository works with nothing extra; and its grammar was measured at just over a megabyte of binary — small enough to hand to everyone.

The upstream tree-sitter project publishes source archives and WebAssembly builds, not native libraries, so the libraries are groove’s to build and sign for. They are produced by the same release job as the binaries, from a crate that depends on the grammar directly — which is what keeps a grammar and its tags query at versions that were built together.

Consequences

More Information

The grammar contract lives in crates/groove-grammar-abi. The chunking decision this one supports is ADR-0012.

This decision ships before its mechanism. v1.2.0 carries the compiled-in Rust grammar and the shared contract; the loader, the directory setting and the published libraries arrive in v1.3.0. A reader of v1.2.0 who wants to place a plugin will not find anywhere to put it yet.