Skip to content

Documentation Linter Guide

Date: 2026-01-17
Status: ✅ Active

Overview

The documentation linter automatically checks that all markdown (.md) files are:

  1. Organized - Located in allowed directories per documentation structure guidelines
  2. Non-duplicated - Exist in only one location (single source of truth principle)

This prevents rogue documentation files from being scattered throughout the repository and catches unintended copies that create maintenance problems.

See .github/copilot-instructions.md for documentation organization guidelines.

Quick Start

Run the linter

bash
# Check documentation organization
npm run lint:docs

# Strict mode (fails if issues found)
npm run lint:docs:strict

# Integrated with main linter
npm run lint

What It Checks

The documentation linter validates that all .md files satisfy two requirements:

1. Location Check

All files must be in allowed locations:

✅ Root-level files (allowed)

  • README.md - Main project README
  • CHANGELOG.md - Root changelog (synced with docs/CHANGELOG.md)

✅ Directory patterns (allowed)

  • docs/**/*.md - Primary documentation location (all user-facing docs)
  • .github/**/*.md - GitHub-specific documentation (workflows, actions, etc.)
  • discord-bot/**/*.md - Discord bot documentation (if added)
  • functions/**/*.md - Firebase Cloud Functions documentation (if added)

Script documentation should live in docs/engineering/guides/SCRIPTS_GUIDE.md (not in scripts/README.md).

❌ Not allowed

  • ./*.md (except README.md and CHANGELOG.md)
  • Markdown files scattered in arbitrary directories
  • Feature docs outside docs/features/
  • Engineering docs outside docs/engineering/

2. Duplicate Files Check

All markdown files must be unique by filename - no file should exist in multiple locations with the same name. This enforces the "single source of truth" principle.

Examples of duplicates found:

  • README.md in multiple directories → Keep only in appropriate location
  • CHANGELOG.md in both root and docs/ → Consolidate to one location
  • IMPLEMENTATION_SUMMARY.md in 3+ locations → Consolidate and link to single source

When duplicates are found:

  • Linter reports all locations where the file appears
  • You must identify which is the authoritative version
  • Delete duplicates and update cross-references
  • Use links instead of copying content

Documentation Organization

When creating new documentation, follow these rules:

Feature Documentation

docs/features/{feature-name}/
  ├── QUICK_START.md          # 5-minute quick start
  ├── {FEATURE_NAME}.md       # Complete spec
  ├── IMPLEMENTATION.md       # Developer guide (optional)
  └── TESTING_GUIDE.md        # QA procedures (optional)

Examples: docs/features/wave/, docs/features/lantern-hub/, docs/features/profile/

Engineering Documentation

docs/engineering/{subdomain}/
  └── {TOPIC}_{TYPE}.md

Subdomains:

  • architecture/ - System design, tech stack, patterns, APIs
  • deployment/ - Deployment guides, CI/CD, infrastructure
  • security/ - Encryption, security architecture, incident response
  • guides/ - Onboarding, troubleshooting, utility setup
  • testing/ - Testing strategies, debugging guides
  • mobile/ - Mobile-specific optimizations

Business & Governance

docs/business/
  ├── BUSINESS.md
  ├── PILOT_STRATEGY.md
  ├── COFOUNDER_FEEDBACK_POA.md
  └── ...

docs/governance/
  ├── GOVERNANCE.md
  ├── GOVERNANCE_QUICK_REFERENCE.md
  ├── IMMUTABLE_RIGHTS.md
  └── ...

Handling Duplicates

When the linter detects duplicate filenames:

1. Identify the Source of Truth

The linter output shows all locations where a file appears:

📄 IMPLEMENTATION_SUMMARY.md
   • docs/archive/worklog-historical/IMPLEMENTATION_SUMMARY.md
   • docs/engineering/github/workflows/ai-triage/IMPLEMENTATION_SUMMARY.md
   • docs/engineering/github/workflows/discord/IMPLEMENTATION_SUMMARY.md

Determine which is the canonical/authoritative version.

2. Update Cross-References

If other files link to duplicates, update them to point to the source:

Before:

markdown
[See implementation](../../engineering/github/workflows/discord/IMPLEMENTATION_SUMMARY.md)

After:

markdown
[See implementation](../../engineering/github/workflows/IMPLEMENTATION_SUMMARY.md)

3. Delete Duplicate Files

bash
rm docs/archive/worklog-historical/IMPLEMENTATION_SUMMARY.md
rm docs/engineering/github/workflows/discord/IMPLEMENTATION_SUMMARY.md

4. Common Duplicate Patterns & Solutions

README.md in multiple directories

  • Keep: Module-level READMEs (e.g., discord-bot/README.md)
  • Remove: Duplicates in subdirectories
  • Rule: One README per module, none in subdirectories; scripts are documented in docs/engineering/guides/SCRIPTS_GUIDE.md

CHANGELOG.md (Root vs docs/)

  • Keep: docs/CHANGELOG.md as primary
  • Sync/Remove: Root version should mirror docs/ or be removed
  • Best Practice: Single source in docs/CHANGELOG.md

IMPLEMENTATION_SUMMARY.md

  • Issue: Multiple copies document same thing differently
  • Solution: Keep in one location (e.g., per feature area)
  • Link: From other locations, use relative links instead

QUICK_START.md & DEPLOYMENT.md

  • Rule: One per feature/module in appropriate docs/ subdirectory
  • Link: Use cross-references between docs instead of copying

Configuration

The linter is configured using .docs-linter-config.js which controls all linting behavior.

Main Configuration File

File: .docs-linter-config.js

javascript
module.exports = {
  // Root-level files that are allowed
  rootFiles: ['README.md'],

  // Directory patterns where markdown is allowed
  allowedPaths: [
    'docs/**/*.md',
    '.github/**/*.md',
    // ... more patterns
  ],

  // Directories to completely ignore
  excludePatterns: [
    'node_modules/**/*.md',
    '.git/**/*.md',
    'dist/**/*.md',
    // ... more patterns
  ],

  // Files allowed to exist in multiple locations (duplicate exclusions)
  duplicateExclusions: [
    'README.md',        // Per-module README files are expected
    'CHANGELOG.md',     // Per-directory changelogs are acceptable
    '.gitignore',
    '.prettierignore',
    '.eslintignore',
  ],

  // Error reporting modes
  strictMode: false,
  showSuggestions: true,
};

Script Implementation

File: scripts/lint-docs.js

  • Loads configuration from .docs-linter-config.js
  • Applies exclusions when checking for duplicates
  • Falls back to safe defaults if config is missing
  • Provides helpful error messages with resolution steps

Modifying Configuration

To change allowed locations or duplicate exclusions:

  1. Edit .docs-linter-config.js

    • Add/remove paths from allowedPaths
    • Add/remove filenames from duplicateExclusions
    • Update exclusion patterns as needed
  2. Test changes

    bash
    npm run lint:docs
  3. Document in changelog

    bash
    npm run changelog:consolidate  # or update docs/CHANGELOG.md

Exclusion Lists

Directory Exclusions

These directories are automatically ignored by the linter:

  • node_modules/**/*.md - Dependencies
  • .git/**/*.md - Git history
  • dist/**/*.md - Build artifacts
  • build/**/*.md - Build output
  • .next/**/*.md - Next.js artifacts
  • .github/workflows/test-data/**/*.md - Test data

Duplicate Exclusions

These files are allowed to exist in multiple locations:

  • README.md - Per-module documentation
  • CHANGELOG.md - Per-directory change logs
  • .gitignore - Per-directory git ignore rules
  • .prettierignore - Per-directory prettier config
  • .eslintignore - Per-directory eslint config

Adding New Allowed Paths

If you need to allow markdown files in a new location (e.g., for a new module):

  1. Edit .docs-linter-config.js:

    javascript
    allowedPaths: [
      'docs/**/*.md',
      '.github/**/*.md',
      'new-module/**/*.md',  // ← Add here
    ]
  2. Test the change

    bash
    npm run lint:docs
  3. Document in changelog

    markdown
    - Add support for new-module documentation in linter

Adding Duplicate Exclusions

If you have a file that should exist in multiple locations:

  1. Edit .docs-linter-config.js:

    javascript
    duplicateExclusions: [
      'README.md',
      'CHANGELOG.md',
      'MY_FILE.md',  // ← Add here
    ]
  2. Test the change

    bash
    npm run lint:docs
  3. Document why this file legitimately has duplicates

Integration with Main Linter

The documentation linter is automatically included when you run:

bash
npm run lint

It runs before ESLint and reports organization issues alongside code linting issues.

Exit codes:

  • 0 - All documentation is properly organized and has no duplicates
  • 1 - Issues found (if using --strict flag)

Exit Codes & Modes

Warning Mode (default)

bash
npm run lint:docs
# Always exits with 0, even if issues found
# Use this for informational checks

Strict Mode

bash
npm run lint:docs:strict
# Exits with 1 if issues found
# Use this in CI/CD pipelines to enforce compliance

Example Output

✅ Success

✅ All markdown files are in the correct locations!
✅ No duplicate markdown files found!

❌ Organization Issue

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️  DOCUMENTATION ORGANIZATION ISSUE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Found 1 markdown file(s) outside allowed locations:

  ❌ API_GUIDE.md

📍 ALLOWED LOCATIONS:

  Root-level:
    • README.md
    • CHANGELOG.md

  Path patterns:
    • docs/**/*.md
    • .github/**/*.md
    • discord-bot/**/*.md
    • functions/**/*.md

📚 ORGANIZATION RULES:

  • All user-facing documentation → docs/
  • Feature docs → docs/features/{feature-name}/
  • Engineering docs → docs/engineering/{subdomain}/
  • Business docs → docs/business/
  • Governance docs → docs/governance/
  • GitHub workflow docs → .github/workflows/docs/
  • Script documentation → docs/engineering/guides/SCRIPTS_GUIDE.md (no README in scripts/)

  See docs/DOCS_INDEX.md and .github/copilot-instructions.md for details.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

❌ Duplicate Files

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️  DUPLICATE MARKDOWN FILES DETECTED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Documentation should exist in only one location (single source of truth):

  📄 README.md
      • .github/workflows/README.md
      • README.md
      • discord-bot/README.md

  📄 IMPLEMENTATION_SUMMARY.md
     • docs/engineering/github/workflows/ai-triage/IMPLEMENTATION_SUMMARY.md
     • docs/engineering/github/workflows/discord/IMPLEMENTATION_SUMMARY.md
     • docs/features/frens/IMPLEMENTATION_SUMMARY.md

🎯 RESOLUTION:

  1. Identify which copy is the "source of truth"
  2. Move or delete the duplicate copies
  3. Update links in other files to point to the single source
  4. Consider using VitePress with proper linking instead of copying

📚 BEST PRACTICES:

  ✓ Keep README.md files in module/package directories
  ✓ Keep detailed docs in appropriate docs/ subdirectories
  ✓ Link between docs using relative paths (no copying)
  ✓ Use VitePress for cross-referencing between docs

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Fix Examples

Move documentation into docs/

Before:

lantern_app/
├── API_GUIDE.md              ❌ Not allowed
├── INTEGRATION.md            ❌ Not allowed
└── docs/
    └── (proper location)

After:

lantern_app/
└── docs/
    └── engineering/
        ├── API_GUIDE.md      ✅ Allowed
        └── INTEGRATION.md    ✅ Allowed

Relocate feature documentation

Before:

lantern_app/
├── WAVE_FEATURES.md          ❌ Not allowed

After:

lantern_app/
└── docs/
    └── features/
        └── wave/
            └── WAVE_FEATURES.md  ✅ Allowed (rename to feature-appropriate name)

Troubleshooting

"Documentation file X is not allowed"

Solution: Move the file to the appropriate docs/ subdirectory per the organization rules above.

"I need markdown files in a new location"

Solution: Update scripts/lint-docs.js with the new pattern and document it in docs/CHANGELOG.md.

Linter is too strict

Solution: Use warning mode for informational checks:

bash
npm run lint:docs    # Warning mode (doesn't fail)
npm run lint:docs:strict  # Strict mode (fails on issues)

See Also

Built with VitePress