Skip to content

Fixing

A rule that can mechanically repair its violation declares a fix: block. alint check only reports; alint fix applies the repairs. Evaluation runs in parallel across files, but the fix pass is a single sequential loop, one rule at a time, because each fixer writes to the tree that the next rule will read.

alint evaluates files in parallel, then applies fixes one rule at a time Three files are evaluated in parallel and produce fixable violations. Those violations feed a single sequential lane where fixes are applied one rule at a time. evaluate parallel, all files at once README.md Cargo.toml main.rs apply sequential, one rule at a time fix 1 fix 2 fix 3 two families of op content edits (7)trim, newline, line endings, BOM, bidi, zero-width, blanks path + content (5)create, remove, rename, prepend, append

A rule is auto-fixable only if it declares a fix: block; otherwise its violation is report-only and you repair it by hand. alint check --format human marks the fixable ones, and the summary counts them. alint fix applies them, alint fix --dry-run prints what it would change without writing, alint fix --only <rule-id> applies just one rule’s fixers (the command the agent surface emits), and alint fix --changed restricts the pass to the diff (cross-file and existence rules still see the whole tree). A violation with no fixer still fails the gate; fixing is an accelerant, not an escape hatch.

The fix pass runs rule by rule in sequence (evaluate the rule, then apply its fixers) rather than in parallel like check, because a fixer mutates files on disk and a later rule must see the result, not race it.

Seven ops edit content in place: file_trim_trailing_whitespace, file_append_final_newline, file_normalize_line_endings, file_strip_bom, file_strip_bidi, file_strip_zero_width, and file_collapse_blank_lines. Five more work at the path or prepend/append level: file_create, file_remove, file_rename, file_prepend, and file_append.

The three content-providing ops, file_create, file_prepend, and file_append, take either an inline content: string or a content_from: <path> that reads the bytes from a file (exactly one of the two must be set). This is how boilerplate that is awkward to inline stays under version control: a real Apache-2 LICENSE is ~10 KB, and pasting it into YAML is fragile (escape rules, indentation drift, stray code-search hits), so stash the canonical bytes under .alint/templates/ and point content_from: at them. The path resolves against the lint root (and, like every fix op that writes a path, stays confined to that root unless allow_out_of_root is set) and is read at fix-apply time, so the template need not exist when alint check runs, only when alint fix writes the target; a missing source is reported as Skipped, never a half-written file. In a monorepo with nested_configs: true, a sub-config’s content_from: still resolves against the workspace root, so one root .alint/templates/ supplies every package.

These ops are careful about repeat runs: file_prepend and file_append are idempotent (a no-op when the content is already present) and file_prepend preserves a leading byte-order mark, while file_create skips a target that already exists.

fix_size_limit (default 1 MiB) bounds the file a fixer rewrites in place: if that target is over the cap it is reported Skipped rather than rewritten. It covers the seven content edits and file_prepend / file_append, all of which read the target to splice content. The path ops fall outside it: file_remove and file_rename read no content at all, and while file_create does read its content_from: template, that read is not itself bounded by fix_size_limit.

Trim trailing whitespace across Markdown, and create a missing LICENSE from a template:

version: 1
rules:
- id: md-trim
kind: no_trailing_whitespace
paths: "**/*.md"
level: info
fix:
file_trim_trailing_whitespace: {}
- id: license-present
kind: file_exists
paths: [LICENSE]
root_only: true
level: error
fix:
file_create:
content_from: ".alint/templates/LICENSE-MIT.txt"

alint fix prints a block per rule and a final count:

error [license-present]:
✓ created LICENSE
info [md-trim]:
✓ trimmed trailing whitespace in docs/guide.md
2 applied, 0 skipped, 0 unfixable.

Each applied line names the file in its summary; skipped and unfixable lines carry it as a prefix. A file over fix_size_limit, or a content_from: source that is missing, shows on its rule as (skipped: <reason>) and lands in the skipped count instead.

  • Configuration documents fix_size_limit and the per-rule fix: field.
  • Rules lists which kinds ship a fixer and the options each op takes.
  • Changed mode covers alint fix --changed.