How it works
loam.dev reads your Dart/Flutter project semantically and across the whole codebase — not file by file — and turns it into a deterministic pass/fail decision in CI. A baseline tolerates the code you already have, so only newly introduced problems block the build. It does this in a six-stage pipeline, without guessing, without regex shortcuts, without false positives from generated code.
The pipeline
The ProjectLoader feeds your entire project into
Dart's own AnalysisContextCollection.
Every file is parsed and fully resolved into a typed
element model — the same model the Dart SDK uses internally.
This is the foundation that makes everything downstream semantic,
not syntactic.
A set of Rules runs over the resolved model. Each Rule is a self-contained analysis unit behind a shared interface; adding a new capability never changes the pipeline — only the rule list. Rules are either deterministic (pure AST/element-model, reproducible offline) or LLM-backed (planned, always verdict-cached before hitting CI).
Each Rule emits Findings — structured results of
the form { ruleId, severity, location, message, fingerprint }.
Every Finding carries a stable Fingerprint
(a position-robust hash) that survives reformats and line-number
shifts. This is the key that makes baseline diffing exact.
The Baseline is a frozen snapshot of accepted
Findings stored in baseline.json. When you onboard an
existing project, you run a full audit, clean up what you can, and
then freeze the rest with loam baseline --write.
From that point on the Gate only cares about new Findings
— not the ones you already know about.
The Gate is the CI decision point: it compares the current Findings against the Baseline and returns a structured exit code. Zero regressions → exit 0. New Finding → exit 1. No surprises at merge time.
The Reporter formats results for humans or
machines: human for terminal output,
json / sarif / markdown
for agent pipelines, and html for an interactive
self-contained report file with a built-in fix-prompt builder.
loam scan vs. loam gate
Two commands, two purposes. Knowing which to reach for keeps your workflow clean.
loam scan
Full audit · baseline-independent
Runs every active Rule over the entire project and shows
all Findings regardless of what the Baseline says.
Use this when you first bring loam.dev into an existing repo —
see the full picture, clean up what you can, then freeze the rest
with loam baseline --write.
loam gate
CI ratchet · Baseline-aware
The everyday CI command. By default it runs in
Ratchet mode: only Findings that are
new (not in the Baseline) fail the build. The score can
only improve over time — your codebase ratchets forward, never
backward. For greenfield projects or pipelines that require zero
findings, pass --absolute to enforce a hard threshold
instead.
Typical onboarding sequence: loam scan → fix what
matters → loam baseline --write → loam gate
in CI from then on.
Semantics over syntax
The most important architectural decision in loam.dev is what the analysis runs on.
dart analyze reports type errors, missing imports, and
style lint violations — all things that can be caught at the
declaration level. What it does not do is reason about the
graph of references across your entire project: which public
API members are actually reachable from outside their own library, which
class boundaries are being crossed, which helpers exist in two copies
that happen to look the same.
Catching those things with regex or string matching is brittle. A regex that looks for an identifier name has no way to distinguish a definition from a reference, a local variable from a public export, or a generated file from hand-written code.
loam.dev uses the resolved element model: every symbol is a typed element with a canonical identity, every reference is a resolved pointer. Rules operate on that model, not on raw text. This means:
- No false positives from coincidentally similar names.
- Generated files are excluded automatically (the same way the SDK knows about them).
- Renaming a class is invisible to the rule — what matters is whether the element is referenced, not what it is called.
- Cross-library and cross-package references are tracked correctly.
This is what "semantics over syntax" means in practice: the Rules see your code the way the compiler sees it, not the way a text editor does.
Going deeper
The Developer Guide
covers the full CLI reference, output formats, configuration, and
worked examples — including the complete baseline onboarding sequence
and how to integrate loam gate into GitHub Actions.