A fast, configurable linter for MADR (Markdown Architectural Decision Records). It validates the things a plain Markdown linter can't: required sections, a valid status, ISO-8601 dates, filename convention, and cross-file integrity like unique numbering and non-broken links — across MADR v2, v3, and v4.
Why madr-lint
MADR ships no official linter, and the general-purpose tools each cover only part of what an ADR collection needs:
| Tool | Markdown style | Inter-doc links | ADR numbering | Status enum | Date format | Supersedes graph | v2 bold-list |
|---|---|---|---|---|---|---|---|
| markdownlint-cli2 | — | — | — | — | — | n/a | |
| lychee | — | — | — | — | — | n/a | |
| adrs (rust) | — | — | (init) | — | — | ~ | — |
| madr-lint | —¹ |
¹ Deliberately not Markdown style — pair it with markdownlint-cli2 if you want both. madr-lint owns the ADR-specific semantics.
- MADR v2 / v3 / v4 aware — reads YAML frontmatter and v2 body-list metadata (both
- **Status**:and canonical* Status:), or auto-detects per file. - ESLint-style rules — named
madr/*rules witherror/warn/offand per-rule options validated by a JSON Schema. - Per-file & cross-file — fast single-pass checks plus project rules for unique numbering, the supersedes graph, and link rot.
- CLI, library & CI —
text/json/sarifreporters, a programmatic API, and a drop-in GitHub Actions step.
Install
npm install --save-dev madr-lint # or: pnpm add -D madr-lint / yarn add -D madr-lint
Node.js 22+. ESM-only. Ships TypeScript types.
Quick start
# Lint the configured adrDir (default: docs/adr)
npx madr-lint
# Explicit paths (files or directories; directories are searched recursively)
npx madr-lint docs/adr libs/x/adr
# Machine-readable output for CI
npx madr-lint --format sarif > madr-lint.sarif
Exit code: 0 clean · 1 on error-level diagnostics · 2 on a config problem.
Configure
A madr-lint.config.ts (or .madrlintrc.json) at your project root:
import { defineConfig } from 'madr-lint';
export default defineConfig({
extends: ['madr-lint:recommended'],
madrVersion: 'auto',
adrDir: 'docs/adr',
ignorePatterns: ['**/template.md', '9999-*'],
rules: {
'madr/filename-format': ['error', { pattern: '^[0-9]{4}-.+\\.md
Rule values are a severity ('error' | 'warn' | 'off') or a [severity, options] tuple. Options are validated — an invalid option fails fast with exit code 2. Full reference: Configuration.
Rules
8 rules — 7 enabled by recommended, 1 opt-in. Each page documents its options, examples, and MADR-version compatibility.
Rule
Type
Default
Checks
madr/required-sections
per-file
error
Required heading sections are present
madr/status-enum
per-file
error
status is one of the allowed values
madr/date-iso8601
per-file
error
date is a valid ISO-8601 calendar date
madr/filename-format
per-file
error
Filename matches the ADR convention
madr/no-broken-links
project
error
Relative links resolve to existing files
madr/no-duplicate-numbering
project
error
ADR numbers are unique
madr/supersedes-bidirectional
project
error
supersedes / superseded-by agree
madr/no-numbering-gap
project
off
ADR numbers are contiguous (opt-in)
Use in CI
# .github/workflows/adr-lint.yml
jobs:
madr-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx madr-lint
The sarif reporter integrates with GitHub code scanning — see GitHub Action.
Programmatic API
import { runRulesOnFile, buildProjectFile, rules } from 'madr-lint';
const diagnostics = runRulesOnFile(
[rules.requiredSections, rules.statusEnum],
{ path: '0001-x.md', content },
{ severity: 'error' },
);
Full surface: Programmatic API.
Pairs well with
madr-lint intentionally skips Markdown style — compose it:
markdownlint-cli2 'docs/adr/**/*.md' # Markdown style
lychee 'docs/adr/**/*.md' # external link rot
madr-lint docs/adr # ADR structure + inter-ADR links
Status
Alpha. Self-dogfooded (this repo lints its own ADRs) and validated against an external repository. Until 1.0, treat each minor bump as potentially breaking; the 1.0 gate is adoption feedback.
Contributing
Issues and PRs welcome. See CONTRIBUTING.md for the dev setup, rule-shape guide, and TDD/changeset workflow. The project dogfoods its own linter against its own ADRs in docs/adr/.
License
MIT t.kaneko
}],
'madr/no-numbering-gap': 'off',
},
});
Rule values are a severity (__INLINE_CODE_17__) or a __INLINE_CODE_18__ tuple. Options are validated — an invalid option fails fast with exit code __INLINE_CODE_19__. Full reference: Configuration.
Rules
8 rules — 7 enabled by __INLINE_CODE_20__, 1 opt-in. Each page documents its options, examples, and MADR-version compatibility.
| Rule | Type | Default | Checks |
|---|---|---|---|
| __INLINE_CODE_21__ | per-file | __INLINE_CODE_22__ | Required heading sections are present |
| __INLINE_CODE_23__ | per-file | __INLINE_CODE_24__ | __INLINE_CODE_25__ is one of the allowed values |
| __INLINE_CODE_26__ | per-file | __INLINE_CODE_27__ | __INLINE_CODE_28__ is a valid ISO-8601 calendar date |
| __INLINE_CODE_29__ | per-file | __INLINE_CODE_30__ | Filename matches the ADR convention |
| __INLINE_CODE_31__ | project | __INLINE_CODE_32__ | Relative links resolve to existing files |
| __INLINE_CODE_33__ | project | __INLINE_CODE_34__ | ADR numbers are unique |
| __INLINE_CODE_35__ | project | __INLINE_CODE_36__ | __INLINE_CODE_37__ / __INLINE_CODE_38__ agree |
| __INLINE_CODE_39__ | project | __INLINE_CODE_40__ | ADR numbers are contiguous (opt-in) |
Use in CI
__CODE_BLOCK_3__The __INLINE_CODE_41__ reporter integrates with GitHub code scanning — see GitHub Action.
Programmatic API
__CODE_BLOCK_4__Full surface: Programmatic API.
Pairs well with
__INLINE_CODE_42__ intentionally skips Markdown style — compose it:
__CODE_BLOCK_5__Status
Alpha. Self-dogfooded (this repo lints its own ADRs) and validated against an external repository. Until 1.0, treat each minor bump as potentially breaking; the 1.0 gate is adoption feedback.
Contributing
Issues and PRs welcome. See CONTRIBUTING.md for the dev setup, rule-shape guide, and TDD/changeset workflow. The project dogfoods its own linter against its own ADRs in __INLINE_CODE_43__.
License
MIT t.kaneko