Architecture
Sextant is a Rust Cargo workspace with nine crates. The dependency graph is a tree: a thin core type crate at the bottom, single-purpose crates that depend on it for their domain (rules, diff acquisition, language parsing, LLM judges, config), an engine that orchestrates them, and two binaries — CLI and MCP server — that share the engine.
Crate dependency graph
Section titled “Crate dependency graph”The shape is deliberately flat. There’s exactly one engine; the two binaries are thin shells around it. New surfaces (a language server, say, or a webhook) plug in at the binary level without touching the shared crates.
What each crate does
Section titled “What each crate does”sextant-core— Type definitions (Rule,Finding,Report,Verdict,Evaluatortrait). No I/O. Every other crate depends on it.sextant-config— Reads.sextant/config.tomlinto typed structs and merges with defaults.sextant-rules— Built-in rule evaluators (the seven shipped rules) plus rule discovery for.sextant/rules/**/*.md.sextant-diff— Git diff acquisition viagit2. Resolves base refs, walks blob contents, returns changed line ranges.sextant-lang— Tree-sitter parsers and queries for Rust, Python, Go, Java, TypeScript / TSX / JavaScript. Used by built-in evaluators that need ASTs.sextant-judge— LLM-as-judge providers (Anthropic, OpenAI), cache (BLAKE3), tool-use schema enforcement.sextant-engine— Grading orchestration. Loads config and rules, picks files via diff or paths, runs evaluators, builds theReport. Exposed by both binaries.sextant-cli— Thesextantbinary. Wraps the engine in a clap CLI.sextant-mcp— Thesextant-mcpbinary. JSON-RPC 2.0 server (stdio + HTTP) wrapping the engine.
See Per-crate summary for one paragraph each.
Why nine crates
Section titled “Why nine crates”Splitting along dependency boundaries pays off in three places:
- Compile time. Touching the LLM provider doesn’t recompile the tree-sitter grammars; touching a parser doesn’t recompile the MCP protocol layer.
- Surface evolution. The MCP binary and the GitHub Action share
no transitive deps beyond
sextant-engine— their wire formats can evolve independently. - Test isolation. Each crate has its own integration tests
focused on its concern.
cargo test -p sextant-rulesruns in a second.
Data flow
Section titled “Data flow”A grade follows the same path on every surface:
- Surface (CLI / MCP / Action) parses arguments and calls
sextant_engine::grade(cwd, GradeMode). - Engine loads config (
sextant-config) and rules (sextant-rules). - Engine acquires the file set — diff (
sextant-diff) or directory walk for whole-file mode. - Engine parses files with
sextant-langif any rule needs an AST. - Engine runs each evaluator against each in-scope file. LLM
evaluators go through
sextant-judge. - Engine assembles a
Report(sorted, summary line computed, verdict from[verdict]thresholds). - Surface renders the
Report(sextant-cli/src/commands/format/for CLI; JSON-string-in-MCP-envelope for the server).
See Data model for the specific types each step exchanges.
See also
Section titled “See also”- Per-crate summary — one paragraph per crate.
- Data model — Rule → Finding → Report → Verdict.
- The
crates/directory in the repo.