<!-- rumdl-disable MD007 MD073 MD051 MD022 MD026 MD031 MD032 MD064 -->
<!-- This documentation contains example TOCs and formatting that would trigger these rules -->

# MD073 - Table of Contents Validation

## Overview

**Rule ID:** MD073
**Alias:** toc-validation
**Category:** Other
**Fixable:** Yes (marker-based TOCs only)

This rule validates that Table of Contents (TOC) sections match the actual document headings. It detects missing entries, stale entries, and text mismatches.

## Configuration

```toml
[MD073]
# Enable the rule (opt-in, disabled by default)
enabled = true

# Minimum heading level to include (default: 2)
min-level = 2

# Maximum heading level to include (default: 4)
max-level = 4

# Whether TOC order must match document order (default: true)
enforce-order = true

# Spaces per indentation level (default: from MD007, or 2)
# indent = 4
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | boolean | `false` | Whether to enable this rule (opt-in) |
| `min-level` | integer | `2` | Minimum heading level to include in TOC |
| `max-level` | integer | `4` | Maximum heading level to include in TOC |
| `enforce-order` | boolean | `true` | Whether TOC entry order must match heading order |
| `indent` | integer | MD007's indent, or `2` | Spaces per indentation level for nested TOC entries |

### Indentation Configuration

By default, MD073 reads the `indent` value from [MD007](md007.md) to ensure TOC indentation matches your list indentation style. This means if you configure:

```toml
[MD007]
indent = 4
```

Then MD073 will automatically use 4-space indentation for nested TOC entries:

```markdown
<!-- toc -->
- [Installation](#installation)
    - [npm](#npm)
    - [yarn](#yarn)
- [Usage](#usage)
<!-- tocstop -->
```

You can override this by setting `indent` explicitly in MD073:

```toml
[MD073]
enabled = true
indent = 2  # Use 2-space indent regardless of MD007 config
```

## TOC Detection Methods

### Marker-Based Detection (Recommended)

Use HTML comments to mark the TOC region explicitly:

```markdown
<!-- toc -->

- [Installation](#installation)
- [Usage](#usage)

<!-- tocstop -->
```

Alternative stop marker: `<!-- /toc -->`

Marker detection is case-insensitive and allows whitespace variations:
- `<!-- toc -->`
- `<!--toc-->`
- `<!-- TOC -->`

**Auto-fix only works for marker-based TOCs** because markers provide clear, unambiguous boundaries.

### Heading-Based Detection

Detects TOC by looking for headings like "Table of Contents", "Contents", or "TOC":

```markdown
## Table of Contents

- [Installation](#installation)
- [Usage](#usage)

## Installation

...
```

The TOC region ends at the next heading or after two consecutive blank lines.

**Note:** Auto-fix is disabled for heading-based TOCs because the boundaries are fuzzy and could lead to unintended changes.

## What This Rule Checks

### Missing Entries

Headings that exist in the document but don't have corresponding TOC entries:

```markdown
<!-- toc -->
- [Installation](#installation)
<!-- tocstop -->

## Installation
Content.

## Usage      <-- Missing from TOC!
More content.
```

### Stale Entries

TOC entries for headings that no longer exist:

```markdown
<!-- toc -->
- [Installation](#installation)
- [Deleted Section](#deleted-section)  <-- Heading doesn't exist!
<!-- tocstop -->

## Installation
Content.
```

### Text Mismatches

TOC entry text differs from the actual heading:

```markdown
<!-- toc -->
- [Install](#installation)  <-- Should be "Installation"
<!-- tocstop -->

## Installation
Content.
```

### Order Mismatches (when enforce-order = true)

TOC entries in wrong order compared to document headings:

```markdown
<!-- toc -->
- [Usage](#usage)           <-- Should come after Installation
- [Installation](#installation)
<!-- tocstop -->

## Installation
...

## Usage
...
```

## Auto-Fix Behavior

When auto-fix is enabled, this rule:

1. **Only fixes marker-based TOCs** - heading-based TOCs are not auto-fixed
2. Generates a new TOC from all headings after the TOC region
3. Respects `min-level` and `max-level` filters
4. Uses GitHub-style anchors for link generation
5. Uses nested indentation based on heading level
6. Preserves markers (`<!-- toc -->...<!-- tocstop -->`)

The fix is idempotent - running it multiple times produces the same output.

## Examples

### Correct TOC with Markers

```markdown
# My Project

<!-- toc -->

- [Installation](#installation)
  - [npm](#npm)
  - [yarn](#yarn)
- [Usage](#usage)

<!-- tocstop -->

## Installation

### npm

npm install my-project

### yarn

yarn add my-project

## Usage

import MyProject from 'my-project';
```

### Correct TOC with Heading

```markdown
# My Project

## Contents

- [Installation](#installation)
- [Usage](#usage)

## Installation

...

## Usage

...
```

## Rationale

Table of Contents sections are valuable for navigation in documentation, but they easily drift out of sync with actual headings when documents are refactored. This rule:

- Catches missing TOC entries when new sections are added
- Identifies stale entries when sections are removed or renamed
- Ensures TOC accurately reflects document structure
- Provides auto-fix to regenerate correct TOC

## Special Cases

### Headings in Code Blocks

Headings inside fenced code blocks are ignored:

```markdown
## Real Heading    <-- Included in TOC

\`\`\`markdown
## Example Heading    <-- Ignored (in code block)
\`\`\`
```

### Headings with Code Spans

Headings containing code spans work correctly:

```markdown
#### \`check [PATHS...]\`
```

Generates anchor: `#check-paths`

### Duplicate Headings

Duplicate headings get numbered anchors:

```markdown
## FAQ           <-- anchor: #faq
## FAQ           <-- anchor: #faq-1
## FAQ           <-- anchor: #faq-2
```

### Custom Anchors

Custom IDs using `{#custom-id}` syntax are respected:

```markdown
## My Section {#my-custom-anchor}
```

Generates anchor: `#my-custom-anchor` instead of `#my-section`

## See Also

- [MD051](md051.md) - Link fragments should be valid
- [MD041](md041.md) - First line in file should be a top-level heading
