Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Instruction on check_adr.py script


Owner: Vadim Rudakov, rudakow.wadim@gmail.com Version: 0.3.0 Birth: 2026-01-30 Last Modified: 2026-02-02


1. Architectural Overview: The SVA Principle

This script validates ADR (Architecture Decision Record) files in architecture/adr/ for format compliance and synchronization with the index at architecture/adr_index.md.

It ensures:

  • Index Sync: Every ADR file has a corresponding entry in the index

  • No Orphans: No index entries point to non-existent files

  • Correct Links: Links in the index point to the correct file paths

  • Numerical Order: Index entries are in numerical order

  • Valid Status: Status values match allowed values from config

  • Required Frontmatter: YAML frontmatter contains id, title, date, status, tags

  • Date Format: Date field matches YYYY-MM-DD (ISO 8601)

  • Valid Tags: Tags are from predefined list in config

  • Required Sections: Document contains Context, Decision, Consequences, Alternatives, References, Participants

  • Term References: MyST {term} cross-references use correct hyphen format ({term}ADR-26001``)

All validation rules are defined in adr_config.yaml (Single Source of Truth).

This tool is designed to serve as a quality gate in CI/CD, ensuring consistent ADR format and index synchronization.

2. Key Capabilities & Logic

A. ADR File Discovery

The script discovers ADR files by:

StepDescription
Glob patternFinds files matching architecture/adr/adr_*.md
Template exclusionExcludes adr_template.md
Header parsingExtracts number and title from # ADR NNNNN: Title header
SortingReturns files sorted by ADR number ascending

B. Index Parsing

The script parses the MyST glossary format:

:::{glossary}
ADR 26001
: [Title](path/to/adr_26001_slug.md)

ADR 26002
: [Another Title](path/to/adr_26002_slug.md)
:::
FieldExtraction
NumberFrom ADR NNNNN line
TitleFrom markdown link text [Title]
LinkFrom markdown link path (/path/to/file.md)

C. Validation Rules

Index Synchronization Errors:

Error TypeDescription
missing_in_indexADR file exists but has no index entry
orphan_in_indexIndex entry points to non-existent file
wrong_linkIndex link path doesn’t match actual file path
wrong_orderIndex entries are not in numerical order
duplicate_numberMultiple files have the same ADR number
wrong_sectionADR placed in wrong index section (based on status)

Format Validation Errors:

Error TypeDescription
invalid_statusStatus not in allowed list from config
title_mismatchFrontmatter title differs from header title
missing_fieldRequired frontmatter field missing (id, title, date, status, tags)
invalid_dateDate doesn’t match YYYY-MM-DD format
invalid_tagTag not in allowed list from config
empty_tagsTags list is empty (at least one required)
missing_sectionRequired document section not found
broken_term_reference{term}ADR 26001 should use hyphen: `{term}`ADR-26001

D. Auto-Fix Mode (--fix)

When run with --fix, the script:

  1. Fixes invalid statuses: Prompts to correct typos (e.g., “prposed” → “proposed”)

  2. Fixes title mismatches: Prompts to update frontmatter title to match header

  3. Regenerates partitioned index: Groups ADRs by status into sections:

    • Active Architecture: accepted ADRs

    • Evolutionary Proposals: proposed ADRs

    • Historical Context: rejected, superseded, deprecated ADRs

  4. Sorts entries by ADR number within each section

  5. Removes orphan entries pointing to non-existent files

  6. Reports all changes made

E. Migration Mode (--migrate)

When run with --migrate, the script adds YAML frontmatter to legacy ADRs:

  1. Scans all ADR files without YAML frontmatter

  2. Extracts title from # ADR NNNNN: Title header

  3. Extracts status from ## Status section (or uses default “proposed”)

  4. Corrects status typos using the corrections map

  5. Generates frontmatter with id, title, date (from file mtime), status, tags

  6. Writes updated file with frontmatter prepended

F. Configuration (adr_config.yaml)

All validation rules are defined in adr_config.yaml:

# Required frontmatter fields
required_fields: [id, title, date, status, tags]

# Date format (regex)
date_format: "^\\d{4}-\\d{2}-\\d{2}$"

# Valid tags
tags: [architecture, documentation, hardware, model, workflow, ...]

# Required document sections
required_sections: [Context, Decision, Consequences, Alternatives, References, Participants]

# Valid statuses
statuses: [proposed, accepted, rejected, superseded, deprecated]

# Status to section mapping for partitioned index
sections:
  Active Architecture: [accepted]
  Evolutionary Proposals: [proposed]
  Historical Context: [rejected, superseded, deprecated]

# Typo corrections (typo → correct)
status_corrections:
  proposed: [prposed, draft, pending, wip]
  accepted: [acepted, approved, active]
  ...

3. Operational Guide

CLI Options

OptionDescription
--verbose, -vShow detailed output including counts
--fixAutomatically fix index and prompt to fix status/title issues
--migrateAdd YAML frontmatter to legacy ADRs without it
--check-stagedOnly check staged ADR files (for pre-commit)
--check-termsValidate {term} references use hyphen format (ADR-26001)
--fix-termsAuto-fix broken term references (space → hyphen)

Basic Usage

cd ../../../
# Validate ADR index synchronization (default)
env -u VIRTUAL_ENV uv run tools/scripts/check_adr.py
# Verbose output
env -u VIRTUAL_ENV uv run tools/scripts/check_adr.py --verbose
Checking ADR index synchronization...
Found 13 ADR files
Found 13 index entries
All ADRs are synchronized with the index.
# Auto-fix issues
env -u VIRTUAL_ENV uv run tools/scripts/check_adr.py --fix --verbose
Checking ADR index synchronization...
Running in fix mode...
No changes needed.
# Check only staged files (for pre-commit)
env -u VIRTUAL_ENV uv run tools/scripts/check_adr.py --check-staged --verbose
Checking ADR index synchronization...
No staged ADR files to check.

Exit Codes

CodeMeaning
0All ADRs are synchronized with the index
1One or more synchronization errors found

4. Validation Layers

Pre-commit Hook

The script runs automatically via pre-commit when ADR or index files change:

- id: check-adr-index
  name: Check ADR Index
  entry: uv run --active tools/scripts/check_adr.py
  language: python
  files: ^architecture/(adr/adr_.*\.md|adr_index\.md)$
  pass_filenames: false
  stages: [pre-commit, manual]

GitHub Actions

The script runs in CI via the adr-index job in quality.yml:

adr-index:
  runs-on: ubuntu-latest
  steps:
    - name: Run ADR Index Check
      run: uv run tools/scripts/check_adr.py --verbose

5. Test Suite

The test suite provides 110+ tests with 98% coverage:

Test ClassCoverage
TestGetAdrFilesADR file discovery, template exclusion, sorting
TestParseIndexGlossary parsing, entry extraction, section detection
TestValidateSyncSync validation (missing, orphan, wrong link, order)
TestAutoFixIndexFix mode (add entries, maintain order, remove orphans)
TestCliCLI integration (exit codes, verbose, fix, migrate flags)
TestValidateFrontmatterFieldsRequired field validation
TestValidateDateFormatISO date format validation
TestValidateTagsTag validation against allowed list
TestValidateSectionsRequired section detection
TestMigrateLegacyAdrLegacy ADR migration
TestCliMigrateMode--migrate CLI mode
TestStatusValidationStatus value validation
TestTitleMismatchHandlingTitle mismatch detection and fix
TestPartitionedIndexStatus-based index partitioning
TestEdgeCasesSpecial characters, whitespace, edge cases
TestTermReferenceDetectionBroken term reference pattern matching
TestTermReferenceValidationTerm reference error generation
TestTermReferenceFixTerm reference auto-fix
TestTermReferenceCliFlags--check-terms and --fix-terms CLI

Run tests with:

uv run pytest tools/tests/test_check_adr.py -v
env -u VIRTUAL_ENV uv run pytest tools/tests/test_check_adr.py -q
.................................................                        [100%]
49 passed in 0.09s
env -u VIRTUAL_ENV uv run pytest tools/tests/test_check_adr.py --cov=tools.scripts.check_adr --cov-report=term-missing -q
.................................................                        [100%]
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.13.11-final-0 _______________

Name                                  Stmts   Miss  Cover   Missing
-------------------------------------------------------------------
tools/scripts/check_adr_index.py        183      2    99%   353-354
tools/tests/test_check_adr_index.py     395      1    99%   47
-------------------------------------------------------------------
TOTAL                                   578      3    99%
49 passed in 0.13s

6. Common Scenarios

Scenario 1: Adding a New ADR

Goal: Create a new ADR and add it to the index.

# 1. Copy template
cp architecture/adr/adr_template.md architecture/adr/adr_26018_my_new_decision.md

# 2. Edit the file (update frontmatter and content)
# - Set id: 26018
# - Set title: "My New Decision"
# - Set date: 2026-02-01
# - Set status: proposed
# - Set tags: [architecture]
# - Fill in Context, Decision, Consequences, etc.

# 3. Validate (will show "missing_in_index" error)
uv run tools/scripts/check_adr.py --verbose

# 4. Auto-fix to add to index
uv run tools/scripts/check_adr.py --fix

# 5. Stage changes
git add architecture/adr/adr_26018_my_new_decision.md architecture/adr_index.md

Scenario 2: Migrating Legacy ADRs

Goal: Add YAML frontmatter to old ADRs that only have markdown format.

# 1. Check which ADRs need migration (look for "missing_field" errors)
uv run tools/scripts/check_adr.py --verbose

# Example output:
# - ADR 26001 missing required field: 'id'
# - ADR 26001 missing required field: 'date'
# ...

# 2. Run migration to add frontmatter automatically
uv run tools/scripts/check_adr.py --migrate

# Example output:
# Migrated: adr_26001_use_of_python.md
# Migrated: adr_26002_adoption_of_pre_commit.md
# Migrated 2 ADR file(s).

# 3. Regenerate the partitioned index
uv run tools/scripts/check_adr.py --fix

# 4. Review and commit
git diff architecture/adr/
git add architecture/adr/ architecture/adr_index.md

Scenario 3: Fixing a Status Typo

Goal: Correct a misspelled status value.

# 1. Validation shows invalid status
uv run tools/scripts/check_adr.py --verbose

# Output:
# - ADR 26005 has invalid status: 'prposed' (valid: accepted, deprecated, proposed, rejected, superseded)

# 2. Run fix mode (will prompt for correction)
uv run tools/scripts/check_adr.py --fix

# Interactive prompt:
# ADR 26005 has invalid status: 'prposed'
# Valid statuses: accepted, deprecated, proposed, rejected, superseded
# Suggested correction: 'prposed' -> 'proposed'
# Apply suggested fix 'proposed'? [Y/n/custom]:
# (Press Enter to accept)

# 3. Stage the fixed file
git add architecture/adr/adr_26005_*.md

Scenario 4: Fixing Title Mismatch

Goal: Sync frontmatter title with header title.

# 1. Validation shows title mismatch
uv run tools/scripts/check_adr.py --verbose

# Output:
# - ADR 26003 has mismatched titles: header='Adoption of Gitlint', frontmatter='Old Title'

# 2. Run fix mode (will prompt for confirmation)
uv run tools/scripts/check_adr.py --fix

# Interactive prompt:
# ADR 26003 title mismatch:
#   Header title:      'Adoption of Gitlint'
#   Frontmatter title: 'Old Title'
# The header title is authoritative. Update frontmatter to match?
# Apply fix? [y/N]: y

# 3. Stage the fixed file
git add architecture/adr/adr_26003_*.md

Scenario 5: Renaming an ADR File

Goal: Rename an ADR file and update the index.

# 1. Rename the file
mv architecture/adr/adr_26001_old_name.md architecture/adr/adr_26001_new_name.md

# 2. Validation detects wrong link
uv run tools/scripts/check_adr.py --verbose

# Output:
# - ADR 26001 has wrong link: /architecture/adr/adr_26001_old_name.md (expected /architecture/adr/adr_26001_new_name.md)

# 3. Auto-fix updates the index
uv run tools/scripts/check_adr.py --fix

# 4. Stage changes
git add architecture/adr/adr_26001_new_name.md architecture/adr_index.md
git rm architecture/adr/adr_26001_old_name.md

Scenario 6: Changing ADR Status

Goal: Move an ADR from “proposed” to “accepted”.

# 1. Edit the ADR file and change status in frontmatter
# From: status: proposed
# To:   status: accepted

# 2. Validation detects wrong section placement
uv run tools/scripts/check_adr.py --verbose

# Output:
# - ADR 26010 is in section 'Evolutionary Proposals' but should be in 'Active Architecture'

# 3. Auto-fix regenerates the partitioned index
uv run tools/scripts/check_adr.py --fix

# 4. Stage changes
git add architecture/adr/adr_26010_*.md architecture/adr_index.md

Scenario 7: Adding a New Valid Tag

Goal: Use a new tag that’s not in the allowed list.

# 1. Validation shows invalid tag
uv run tools/scripts/check_adr.py --verbose

# Output:
# - ADR 26015 has invalid tag: 'performance' (valid: architecture, ci, documentation, ...)

# 2. Option A: Use an existing tag instead
# Edit the ADR and change to a valid tag

# 3. Option B: Add the new tag to config (if it should be allowed)
# Edit architecture/adr/adr_config.yaml:
# tags:
#   - architecture
#   - ...
#   - performance  # Add new tag

# 4. Re-run validation
uv run tools/scripts/check_adr.py --verbose

Scenario 8: Pre-commit Validation

Goal: Validate ADRs before committing.

# The pre-commit hook runs automatically when you commit ADR files
git add architecture/adr/adr_26018_new_feature.md
git commit -m "feat: Add ADR 26018"

# If validation fails, you'll see:
# Check ADR Index...................................................Failed
# - ADR 26018 (adr_26018_new_feature.md) not in index
# Run with --fix to update the index automatically.

# Fix and retry:
uv run tools/scripts/check_adr.py --fix
git add architecture/adr_index.md
git commit -m "feat: Add ADR 26018"

7. Quick Reference

Command Cheat Sheet

TaskCommand
Validate all ADRsuv run tools/scripts/check_adr.py
Verbose validationuv run tools/scripts/check_adr.py --verbose
Auto-fix issuesuv run tools/scripts/check_adr.py --fix
Migrate legacy ADRsuv run tools/scripts/check_adr.py --migrate
Check staged onlyuv run tools/scripts/check_adr.py --check-staged
Check term referencesuv run tools/scripts/check_adr.py --check-terms
Fix term referencesuv run tools/scripts/check_adr.py --fix-terms
Run testsuv run pytest tools/tests/test_check_adr.py -v
Run tests + coverageuv run pytest tools/tests/test_check_adr.py --cov=tools.scripts.check_adr

Typical Workflow

# 1. Create/edit ADR
cp architecture/adr/adr_template.md architecture/adr/adr_NNNNN_slug.md
# Edit the file...

# 2. Validate
uv run tools/scripts/check_adr.py --verbose

# 3. Fix any issues
uv run tools/scripts/check_adr.py --fix

# 4. Commit
git add architecture/adr/ architecture/adr_index.md
git commit -m "feat: Add ADR NNNNN"

Key Files

FilePurpose
tools/scripts/check_adr.pyMain validation script
tools/tests/test_check_adr.pyTest suite (110+ tests, 98% coverage)
architecture/adr/adr_config.yamlSSoT for validation rules
architecture/adr/adr_template.mdTemplate for new ADRs
architecture/adr_index.mdPartitioned index (auto-generated)