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.
Fixable versus report-only
Section titled "Fixable versus report-only"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.
The fix ops
Section titled "The fix ops"A fix: block names exactly one op. Twenty-six ship, grouped by what they touch:
- Text hygiene (7) rewrite bytes in place:
file_trim_trailing_whitespace,file_append_final_newline,file_normalize_line_endings,file_strip_bom,file_strip_bidi,file_strip_zero_width,file_collapse_blank_lines. - Boilerplate (4) add declared content:
file_create(a whole file),file_prepend/file_append(at start / end), andinsert_header(after a leading shebang or XML declaration, so it does not push either off line 1). - Path, metadata, and VCS (7):
file_remove,file_rename,dir_create,relocate(move a stray file such as a nested lockfile back to the repo root),chmod,git_untrack(git rm --cached), andcommand(run a user-supplied fix tool such aseslint --fix). - Located value edits (3) splice one span of a structured file:
replace(a regex match),set_value(write a*_path_equalsvalue), andremove_value(drop a*_path_absentnode). - Cross-file (2):
sync_from(mirror a drifted file from its canonical source) andcreate_and_register(create a workspace member and add it to a manifest list such asworkspace.members). - Ordering (3):
sort(keep a marked block or a whole markerless file ordered under its comparator),insert_line(splice a missingrequire:d line at its sorted position), andindent_style(reindent tabs to spaces).
Rules lists which kind ships which op and the options each takes.
Safe and Unsafe fixes
Section titled "Safe and Unsafe fixes"Every op has a tier. Safe fixes are behavior-preserving -- normalizing whitespace, adding a missing header, creating a file from a template -- so alint fix applies them by default. Unsafe fixes can change what a file means or delete content -- removing a file, rewriting a matched span, reindenting an indent-significant file, inserting a line at a computed position -- so a bare alint fix holds them back as suggestions and applies them only with --unsafe-fixes. A per-rule applicability: overrides the default either way: safe opts an Unsafe op in, suggestion (or never) holds a Safe one back. alint check --format human marks a Safe fix as auto-fixable and an Unsafe one as a suggestion. And a content-injecting op (one that writes ruleset-authored bytes, such as file_prepend or insert_header) pulled in through an untrusted extends: URL is demoted to a suggestion whatever its tier, so a third-party ruleset can never silently write into your files.
content_from
Section titled "content_from"The five content-providing ops -- file_create, file_prepend, file_append, insert_header, and create_and_register -- 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, file_append, and insert_header are idempotent (a no-op when the content is already present); file_prepend and insert_header preserve a leading byte-order mark; and file_create skips a target that already exists.
fix_size_limit
Section titled "fix_size_limit"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 whole-file ops that read a target to splice or rewrite its content -- the seven text-hygiene ops, file_prepend / file_append / insert_header, sort / insert_line / indent_style, and sync_from. The pure path and metadata ops read no target content and fall outside it (file_remove, file_rename, relocate, dir_create, chmod, git_untrack); file_create reads its content_from: template, but that read is not itself bounded by fix_size_limit.
In practice
Section titled "In practice"Trim trailing whitespace across Markdown, and create a missing LICENSE from a template:
version: 1rules: - 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 LICENSEinfo [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.
A finding that check does not tag as fixable even though its rule declares a fix (a UTF-16 BOM, a bidi control in a binary-looking file, an ambiguous reindent) is listed as unfixable without running the fixer, and lands in the unfixable count.
Going deeper
Section titled "Going deeper"- Configuration documents
fix_size_limitand the per-rulefix:field. - Rules lists which kinds ship a fixer and the options each op takes.
- Changed mode covers
alint fix --changed.