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.

vadocs v0.1.0 - Proof of Concept

Motivation

The ai_engineering_book repository contains several validation scripts in tools/scripts/ that share common patterns: YAML frontmatter parsing, configuration loading, validation error reporting. Each script duplicates parsing logic, and the validation functionality cannot be reused in other repositories.

Solution: Extract common validation logic into a reusable Python package (vadocs) that can be installed via uv add.

Goal

Create a minimal viable package that proves:

  1. Installability as a uv dependency from GitHub

  2. YAML frontmatter validation works correctly

  3. ADR-specific validation (fields, status, tags, sections)

  4. Configuration loading from YAML files

Implementation Summary

  • Core models: Document, ValidationError, SyncField, SyncResult

  • Validators: FrontmatterValidator, AdrValidator, AdrTermValidator

  • Fixers: AdrFixer, SyncFixer

  • Config: load_config() for YAML files

  • Tests: 79 passing

What’s NOT in v0.1.0

  • CLI interface (library-only)

  • pyproject.toml [tool.vadocs] config loading

  • Project scaffolding (vadocs init)

  • Index sync validation

Further Steps

  • v0.2.0: CLI + pyproject.toml config

  • v0.3.0: Index sync validation

  • v0.4.0: Additional validators (broken links, jupytext)

  • v1.0.0: PyPI release, stable API


PoC Validation Checklist

Execute each step manually. Mark [x] when passed.

1. Installation

cd /tmp/test-vadocs
ls -a
.  ..  adr  adr_index.md  packages  post-mortems  README.md  what_is_an_adr.md
uv init
env -u VIRTUAL_ENV uv add "vadocs @ git+https://github.com/lefthand67/vadocs.git"
Initialized project `test-vadocs`
Using CPython 3.12.11 interpreter at: /usr/local/bin/python3.12
Creating virtual environment at: .venv
Resolved 11 packages in 703ms                                        
░░░░░░░░░░░░░░░░░░░░ [0/9] Installing wheels...                                 warning: Failed to hardlink files; falling back to full copy. This may lead to degraded performance.
         If the cache and target directories are on different filesystems, hardlinking may not be supported.
         If this is intentional, set `export UV_LINK_MODE=copy` or use `--link-mode=copy` to suppress this warning.
Installed 9 packages in 12ms                                
 + coverage==7.13.3
 + iniconfig==2.3.0
 + packaging==26.0
 + pluggy==1.6.0
 + pygments==2.19.2
 + pytest==9.0.2
 + pytest-cov==7.0.0
 + pyyaml==6.0.3
 + vadocs==0.1.0 (from git+https://github.com/lefthand67/vadocs.git@f742e6ccabb27e6fc55c60b26166014ba3ac0498)
env -u VIRTUAL_ENV uv tree
Resolved 11 packages in 0.64ms
test-vadocs v0.1.0
└── vadocs v0.1.0
    ├── pytest v9.0.2
    │   ├── iniconfig v2.3.0
    │   ├── packaging v26.0
    │   ├── pluggy v1.6.0
    │   └── pygments v2.19.2
    ├── pytest-cov v7.0.0
    │   ├── coverage v7.13.3
    │   ├── pluggy v1.6.0
    │   └── pytest v9.0.2 (*)
    └── pyyaml v6.0.3
(*) Package tree already displayed
cat pyproject.toml
[project]
name = "test-vadocs"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "vadocs",
]

[tool.uv.sources]
vadocs = { git = "https://github.com/lefthand67/vadocs.git" }
ls -a
.    adr_index.md  main.py       pyproject.toml   uv.lock
..   .git          packages      .python-version  .venv
adr  .gitignore    post-mortems  README.md        what_is_an_adr.md
  • PASS: Command completes without errors

2. Import verification

env -u VIRTUAL_ENV uv run python -c "from vadocs import AdrValidator, Document, load_config, parse_frontmatter; print('OK')"
OK
  • PASS: Prints OK

3. Config loading

env -u VIRTUAL_ENV uv run python -c "
from pathlib import Path
from vadocs import load_config
config = load_config(Path('adr/adr_config.yaml'))
print('statuses:', config.get('statuses'))
print('required_fields:', config.get('required_fields'))
"
statuses: ['proposed', 'accepted', 'rejected', 'superseded', 'deprecated']
required_fields: ['id', 'title', 'date', 'status', 'tags']
  • PASS: Prints statuses list (proposed, accepted, etc.)

  • PASS: Prints required_fields list (id, title, date, status, tags)

4. Valid ADR produces no errors

Pick a known valid ADR file and run:

env -u VIRTUAL_ENV uv run python -c "
from pathlib import Path
from vadocs import AdrValidator, Document, load_config, parse_frontmatter

config = load_config(Path('adr/adr_config.yaml'))
adr_path = Path('adr/adr_26017_adr_format_validation_workflow.md')  # <-- change to your ADR
content = adr_path.read_text()
doc = Document(path=adr_path, content=content, frontmatter=parse_frontmatter(content), doc_type='adr')
errors = AdrValidator().validate(doc, config)
print(f'Errors: {len(errors)}')
for e in errors: print(f'  [{e.error_type}] {e.message}')
"
Errors: 0
  • PASS: Prints Errors: 0

5. Missing field detected

Create a test file /tmp/test_adr.md:

cat > /tmp/test_adr.md <<EOF
---
id: 99999
title: Test ADR
status: accepted
---

# ADR-99999: Test ADR
EOF

Run validation:

env -u VIRTUAL_ENV uv run python -c "
from pathlib import Path
from vadocs import AdrValidator, Document, load_config, parse_frontmatter

config = load_config(Path('adr/adr_config.yaml'))
content = Path('/tmp/test_adr.md').read_text()
doc = Document(path=Path('/tmp/test_adr.md'), content=content, frontmatter=parse_frontmatter(content), doc_type='adr')
errors = AdrValidator().validate(doc, config)
print(f'Errors: {len(errors)}')
for e in errors: print(f'  [{e.error_type}] {e.message}')
"
Errors: 10
  [missing_field] ADR 99999 missing required field: 'date'
  [missing_field] ADR 99999 missing required field: 'tags'
  [missing_section] ADR 99999 missing required section: '## Date'
  [missing_section] ADR 99999 missing required section: '## Status'
  [missing_section] ADR 99999 missing required section: '## Context'
  [missing_section] ADR 99999 missing required section: '## Decision'
  [missing_section] ADR 99999 missing required section: '## Consequences'
  [missing_section] ADR 99999 missing required section: '## Alternatives'
  [missing_section] ADR 99999 missing required section: '## References'
  [missing_section] ADR 99999 missing required section: '## Participants'
  • PASS: Reports missing_field for date

  • PASS: Reports missing_field for tags

6. Invalid status detected

Create /tmp/test_adr_status.md:

cat > /tmp/test_adr_status.md <<EOF
---
id: 99999
title: Test ADR
date: 2024-01-15
status: invalid_status
tags: [architecture]
---

# ADR-99999: Test ADR
EOF

Run validation:

env -u VIRTUAL_ENV uv run python -c "
from pathlib import Path
from vadocs import AdrValidator, Document, load_config, parse_frontmatter

config = load_config(Path('adr/adr_config.yaml'))
content = Path('/tmp/test_adr_status.md').read_text()
doc = Document(path=Path('/tmp/test_adr_status.md'), content=content, frontmatter=parse_frontmatter(content), doc_type='adr')
errors = AdrValidator().validate(doc, config)
for e in errors: print(f'  [{e.error_type}] {e.message}')
"
  [invalid_status] ADR 99999 has invalid status: 'invalid_status' (valid: accepted, deprecated, proposed, rejected, superseded)
  [missing_section] ADR 99999 missing required section: '## Date'
  [missing_section] ADR 99999 missing required section: '## Status'
  [missing_section] ADR 99999 missing required section: '## Context'
  [missing_section] ADR 99999 missing required section: '## Decision'
  [missing_section] ADR 99999 missing required section: '## Consequences'
  [missing_section] ADR 99999 missing required section: '## Alternatives'
  [missing_section] ADR 99999 missing required section: '## References'
  [missing_section] ADR 99999 missing required section: '## Participants'
  • PASS: Reports invalid_status error

7. Invalid tag detected

Create /tmp/test_adr_tag.md:

cat > /tmp/test_adr_tag.md <<EOF
---
id: 99999
title: Test ADR
date: 2024-01-15
status: accepted
tags: [nonexistent_tag]
---

# ADR-99999: Test ADR
EOF

Run validation:

env -u VIRTUAL_ENV uv run python -c "
from pathlib import Path
from vadocs import AdrValidator, Document, load_config, parse_frontmatter

config = load_config(Path('adr/adr_config.yaml'))
content = Path('/tmp/test_adr_tag.md').read_text()
doc = Document(path=Path('/tmp/test_adr_tag.md'), content=content, frontmatter=parse_frontmatter(content), doc_type='adr')
errors = AdrValidator().validate(doc, config)
for e in errors: print(f'  [{e.error_type}] {e.message}')
"
  [invalid_tag] ADR 99999 has invalid tag: 'nonexistent_tag' (valid: architecture, ci, context_management, devops, documentation, governance, hardware, model, security, testing, workflow)
  [missing_section] ADR 99999 missing required section: '## Date'
  [missing_section] ADR 99999 missing required section: '## Status'
  [missing_section] ADR 99999 missing required section: '## Context'
  [missing_section] ADR 99999 missing required section: '## Decision'
  [missing_section] ADR 99999 missing required section: '## Consequences'
  [missing_section] ADR 99999 missing required section: '## Alternatives'
  [missing_section] ADR 99999 missing required section: '## References'
  [missing_section] ADR 99999 missing required section: '## Participants'
  • PASS: Reports invalid_tag error


Result

Date: 2026-02-04

Tester: rudakow.wadim@gmail.com

All criteria passed:

  • YES

  • NO

Notes: Before conducting the PoC, these actions have been done: Creating Spoke Packages.