Baseline mode
Turning a linter on for an established repo has a chicken-and-egg problem: the rules go red on thousands of pre-existing violations the current change never introduced. Baseline mode breaks it. You record today’s violations into a committed baseline file, and from then on alint check reports and gates on only the findings that are new relative to that snapshot. It is the ratchet: stop the bleeding now, pay the backlog down on your own schedule.
The two-command workflow
Section titled “The two-command workflow”1. Record the baseline. Run once when you adopt alint (and again when you deliberately pay down or accept debt):
alint baselineThis runs the same whole-tree evaluation as check, then writes every current violation to .alint-baseline.json. Commit that file.
2. Enforce the delta. In CI and locally:
alint check --baseline .alint-baseline.jsonEvery violation whose fingerprint is in the baseline is suppressed (up to its recorded count); only new violations are reported, and only they drive the exit code. Persist the path in .alint.yml (baseline: .alint-baseline.json) so CI need not repeat the flag. A --baseline flag overrides the config key, and there is no silent auto-detect: suppression is always an explicit, committed decision.
Fingerprints, not line numbers
Section titled “Fingerprints, not line numbers”The crux of a usable baseline is a stable identity for each violation. alint fingerprints a violation as a SHA-256 over its rule, its path, and a content discriminator, chosen in priority order: a rule may supply its own key (a structured-query rule keys on its query plus the operator and the value it matched, so two different bad values at one path like $.license stay distinct findings; a whole-file rule keys on the path alone), otherwise the offending line’s text is used, and a path-bearing finding with no line keys on (rule, path) with the message deliberately left out of the hash. The line number is never part of it.
So inserting or deleting unrelated lines never churns the baseline. Editing the offending line re-keys a line-anchored finding, so it counts as new and the gate catches it, but a structured-query or whole-file finding keeps its identity across unrelated edits. The baseline survives ordinary refactoring without stale-entry noise, and never masks a genuinely new problem.
The baseline file
Section titled “The baseline file”.alint-baseline.json is JSON Lines: a header, then one sorted entry per grandfathered finding.
{"schema_version":1,"alint_version":"0.16.1"}{"rule_id":"no-todo-comments","path":"src/legacy/api.ts","fingerprint":"<64-hex>","count":3,"message":"TODO without an owner"}{"rule_id":"lockfiles-only-one","path":null,"fingerprint":"<64-hex>","count":1,"message":"Multiple lockfiles found"}One entry per line (not a JSON array) is deliberately merge-friendly, and the sorted order makes an unchanged tree regenerate byte-for-byte. Only fingerprint and count are matched; rule_id, path, and message are advisory, there so a reviewer reading the diff can see what is being grandfathered. The count is a budget: identical findings collapse into one entry, and if the tree later holds more occurrences than recorded, the excess is reported as new; if fewer, the remainder is pruned as stale.
Keeping the baseline honest
Section titled “Keeping the baseline honest”Re-running alint baseline on a repo that already has one will not silently grandfather new debt. If the re-run would add any new fingerprint (or a higher count), it refuses to write and tells you to fix them or opt in:
regenerating .alint-baseline.json would grandfather 2 new violation(s) (+2 / -1);fix them, or pass --accept-new to accept them into the baselinePruning stale entries (findings you have since fixed) is always safe and happens without a flag, so a pure-cleanup re-run just rewrites the file. Accepting new debt is always explicit, with --accept-new. Separately, at enforcement time, alint check --baseline warns about stale entries by default, and --strict-baseline turns those warnings into a hard failure so a baseline cannot quietly rot.
Output formats
Section titled “Output formats”Suppression marks violations rather than deleting them, so sarif emits suppressed results with baselineState: "unchanged" (keeping GitHub Code Scanning alerts open-but-dismissed instead of flapping) and json carries a summary.baselined_suppressed count. Only sarif and json are baseline-aware; the other formats receive the already-filtered live report. The --show-baselined flag lists the suppressed findings in any format, and the exit code is gated on the live (new) findings only, unless --strict-baseline also fails the run on stale entries.
In practice
Section titled “In practice”Adopt alint on a repo with a backlog of no-todo-comments hits, then gate on the delta:
alint baseline # records today's violations, commit italint check --baseline .alint-baseline.json # CI: only NEW findings failA pull request that adds one fresh TODO, while the legacy ones stay suppressed, fails on exactly that one:
error no-todo-comments TODO without an ownerGoing deeper
Section titled “Going deeper”baseline:configuration key persists the path.- The walker and git is the whole-tree evaluation a baseline records.
- Changed mode is the
--changedsubset that baseline recording refuses. - Severity and exit codes is the gate the live (non-suppressed) findings drive.
- Suggesting rules pairs with baseline mode when adopting on an established repo.