Linting & Enforcing a Style Guide
Converting a document is only half the job. A .docx that looks tidy in Word
routinely carries straight quotes, -- where an em-dash belongs, double
spaces left over from manual justification, heading levels that skip because
someone styled an H3 to “look like” an H2, and figures with no alt text.
None of that is visible until you publish — and then it is everywhere.
The all2md linter turns those latent problems into a checklist you can read, gate CI on, and in many cases fix automatically. Because it runs on the parsed AST, the same rules apply whether the document started life as DOCX, PDF, HTML, EPUB, or Markdown.
This guide walks the end-to-end workflow — convert, inspect, fix, and lock in a house style with a profile. For the exhaustive rule reference (all 47 codes and their severities) and the full CLI flag list, see the “Lint Command” section of the CLI reference.
The four-step workflow
Cleaning up a converted document is always the same loop: convert → inspect → preview fixes → apply, then optionally pin a profile so every future document gets the same treatment.
# 1. Convert the source document to Markdown
all2md report.docx --out report.md
# 2. Inspect — what's wrong, and where?
all2md lint report.md
# 3. Preview the automatic fixes without touching the file
all2md lint --fix --dry-run report.md
# 4. Apply the safe fixes in place
all2md lint --fix report.md
Step 2 prints a report like this:
report.md:1:1: STR003 error: Heading level 3 follows level 1
suggestion: Use heading level 2 instead
report.md:12:1: TYP003 info: Text uses straight quotes around a word
suggestion: Replace with curly quotes (“” or ‘’)
report.md:12:40: TYP004 info: Text contains '--' (should be em-dash)
report.md:31:1: IMG001 warning: Image is missing alt text
Found 4 violations (1 error, 1 warning, 2 info) in 1 file
Why this matters for DOCX in particular
Word is a reliable source of exactly the issues the typography and structure rules catch. A converted DOCX is the linter’s ideal first customer:
Symptom Word leaves behind |
Rule that catches it |
|---|---|
Straight |
|
|
|
|
|
Double spaces after periods |
|
Trailing spaces on lines |
|
Visually-styled heading levels |
|
Inconsistent heading capitalization |
|
Pasted images with no description |
|
Several of these — TYP006, TYP007, and a handful of others — carry
safe auto-fixes, so --fix resolves them with no manual editing. Run
all2md lint --fix --dry-run first to see exactly which violations will be
rewritten; only fixes classified SAFE are ever applied.
Profiles: a named house style
Hand-assembling a long --rule / --disable / --severity invocation
gets old fast, and it does not travel between projects. A profile packages a
coherent set of rules and severities behind a single flag:
all2md lint --profile prose report.md
List the built-in profiles and what each one is for:
all2md lint --list-profiles
Profile |
Use it when… |
|---|---|
|
Polishing long-form writing (articles, reports, a converted DOCX bound for publication). Enforces typographic niceties, consistent heading style, and high-quality link text; promotes the curly-quote / em-dash / ellipsis rules to warning. |
|
Accessibility is the priority. Enforces image alt text, descriptive link text, table headers, and a clean heading hierarchy at error severity, and deliberately skips purely stylistic typography. |
|
Engineering / API documentation. Enforces structure, valid links, and
resolvable images, but relaxes the prose-typography rules that fight
code, CLI flags ( |
Profiles ship as data, built entirely from the existing rules — they are a convenience, not a separate engine. The same three are available from Python:
from all2md.linter import (
LintConfig,
available_profiles,
get_profile_config,
lint_document,
)
from all2md import to_ast
print(available_profiles()) # ['accessibility', 'prose', 'technical-docs']
config = LintConfig.from_dict(get_profile_config("prose"))
result = lint_document(to_ast("report.docx"), config=config)
print(result.total, "violations")
Tuning a profile
A profile is a base, not a straitjacket. Configuration layers on top of it in a fixed precedence — lowest to highest:
--profilebundlethe project’s
[tool.all2md.lint]config fileexplicit CLI flags (
--rule/--disable/--severity)
So you can adopt prose wholesale but silence one rule you disagree with, and
the flag wins:
# Everything prose enforces, minus the curly-quote rule
all2md lint --profile prose --disable TYP003 report.md
Or bake the same idea into pyproject.toml so every contributor and CI run
inherits it. The config file refines the profile; --disable lists are
unioned, while severity and per-rule options are merged with the more
specific layer winning:
[tool.all2md.lint]
# Layered on top of `--profile prose`
disable = ["TYP003"] # added to anything the profile already disables
[tool.all2md.lint.severity]
IMG001 = "error" # stricter than the profile's default
Gating CI on a clean document
all2md lint is built to gate a pipeline. It exits non-zero when violations
remain after the severity filter, so a CI step needs no extra glue:
# Fail the build on any warning-or-worse, machine-readable report for logs
all2md lint --profile accessibility --severity warning \
--format json --output lint-report.json docs/
Exit codes:
0— no violations remain after the severity filter3— one or more violations remain (EXIT_VALIDATION_ERROR)4— an input file was not found
--severity filters both the printed report and the exit code, so the
mental model is “what you see is what fails CI.” Pair a profile with a severity
threshold to express a precise gate: --profile accessibility --severity error
fails only on genuine accessibility blockers, while --severity warning holds
the line on style too.
See also
CLI reference (“Lint Command”) — every flag and the full 47-rule table with default severities.
Lint Options — the
[tool.all2md.lint]schema in depth.Python API Workflows (“Document Linting”) — the runner, rule registry, and how to ship your own rules via the
all2md.lint_rulesentry point.