How alint works
alint reads one declarative config, makes a single parallel pass over your repository, and emits one report in the format your pipeline wants. The diagram below traces the whole run top to bottom: the command, the config it assembles, the walk that indexes the repository, the facts and when: filter that decide which rules survive, the scanner that reads each file once, and the report that comes back with an exit code.
The design goal is one config, one pass, one report: predictable, fast, and easy to wire into CI. The stages below trace the run in order.
1. Assemble the config
Section titled “1. Assemble the config”alint discovers the .alint.yml at the repository root and builds the effective config: it resolves every extends: source (a local file, an https:// URL pinned by a SHA-256 hash, or a bundled ruleset resolved offline), caches and cycle-checks them, and field-merges each layer by rule id. This is also the trust boundary: a process-spawning rule (kind: command and its siblings) or a custom: fact that arrives through extends: is rejected at load, so adopting someone else’s ruleset can never make your machine run their commands. The config model covers assembly and precedence in full.
2. Walk the repository
Section titled “2. Walk the repository”alint walks the tree once, in parallel, honoring .gitignore (and .ignore, git excludes, and your ignore: globs), and builds one deterministic, sorted FileIndex. Sorting the index up front is what makes a run reproducible: the same repository produces byte-identical output every time. Everything downstream reads this one index, facts included, so the walk comes first.
3. Evaluate facts, once
Section titled “3. Evaluate facts, once”Any facts: you declared are evaluated a single time, in order, against the walked index, and the results are reused for the rest of the run. Facts answer questions about the repository (does a file exist, how many match a glob, what does a command print) that rules gate on.
4. Filter rules by when:
Section titled “4. Filter rules by when:”Each rule’s when: expression is evaluated against the facts. A rule whose condition is false is dropped before its files are scanned, so a config that layers in the Rust, Node, Python, and Go rulesets costs almost nothing in a repository that is only one of them.
5. Dispatch: scan each file once
Section titled “5. Dispatch: scan each file once”Rules split into two classes, and both run over that single index:
- Per-file rules run file-major. alint reads each matched file’s bytes at most once, no matter how many rules apply to it, and hands that one read to every matching rule (
no_bidi,filename_case, and the rest). This read-coalescing is why adding more per-file rules barely changes a run’s cost. - Cross-file rules run rule-major: each one scans the whole index itself to assert a relationship no single file can (every crate has a README, no two manifests disagree, a reference graph is acyclic).
Both classes run in parallel across cores; their violations are merged and re-sorted so the output stays deterministic.
6. Aggregate and emit
Section titled “6. Aggregate and emit”The violations collect into one Report. alint renders it in your chosen format (human, JSON, SARIF, and five more) and returns an exit code your pipeline can gate on. With alint fix, alint evaluates each rule and applies its fixers to the working tree one rule at a time, so a later rule sees the edits an earlier one made.
Going deeper
Section titled “Going deeper”- The config model is the language this pipeline evaluates.
- The interactive model below lets you explore every component and edge of the run:
- Architecture covers the engine, the crate-level design, and the security boundaries.
- Architecture diagrams is the interactive gallery of every flow: config load, fix, facts, the walker, the LSP, CI, and more.