# How Section Order Validation Works in the DESIGN.md Linter

> Understand how the DESIGN.md linter validates section order by comparing headings to a canonical sequence using pairwise index comparison. Ensure your design documents follow the correct structure.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: internals
- Published: 2026-06-28

---

**The DESIGN.md linter validates section order by comparing parsed headings against a canonical sequence defined in [`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml), using pairwise index comparison to detect any sections that appear out of order.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) linter enforces structured documentation standards by ensuring DESIGN.md files follow a specific section hierarchy. Section order validation is a core rule that checks whether headings appear in the canonical sequence defined by the specification configuration, catching documentation errors before they propagate.

## The Three-Stage Validation Pipeline

Section order validation operates through a precise three-step process that maps file content against the specification's canonical structure.

### Loading Canonical Order and Aliases

The validation begins in [`packages/cli/src/linter/spec-config.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/spec-config.ts), where the system reads [`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml) and exports three critical components: `CANONICAL_ORDER`, `SECTION_ALIASES`, and the `resolveAlias` function. These exports define the expected sequence of sections and establish mappings for common variations or alternate spellings. The configuration parsing occurs between lines 49-61, creating a centralized source of truth for valid section names.

### Building the Order Map

In [`packages/cli/src/linter/linter/rules/section-order.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/section-order.ts) (lines 26-28), the rule constructs a `Map` called `ORDER_MAP` that assigns each canonical section name its expected position index. This data structure enables O(1) lookup during validation, converting the canonical order array into a queryable format where each section maps to its integer position.

### Pairwise Order Validation

The core validation logic resides in the `sectionOrder(state)` function (lines 28-56 of [`section-order.ts`](https://github.com/google-labs-code/design.md/blob/main/section-order.ts)). The process follows these steps:

1. **Extract sections** from the parsed design system (`state.sections`)
2. **Normalize headings** using `resolveAlias` to convert aliases to canonical names
3. **Filter to known sections** by checking membership in `ORDER_MAP` (creating `knownSections`)
4. **Walk pair-wise** through adjacent sections in `knownSections`
5. **Compare indices** by checking if the later section has a smaller index than the preceding one in `ORDER_MAP`

If the linter detects an inversion—where a section appears before its predecessor in the canonical order—it immediately records a finding with a descriptive message listing the expected order and stops further scanning of that file.

## Code Implementation Walkthrough

### Running the Validation Programmatically

You can invoke the section order rule directly in TypeScript:

```typescript
import { loadSpec } from '@design-md/cli';
import { sectionOrder } from '@design-md/cli/src/linter/linter/rules/section-order';

// Parse a DESIGN.md file (returns DesignSystemState)
const state = await loadSpec('path/to/DESIGN.md');

// Run the section-order check
const findings = sectionOrder(state);

if (findings.length) {
  console.warn('Section order problems:');
  findings.forEach(f => console.warn(f.message));
}

```

### Expected Output Format

When the linter detects a violation, it generates a finding with a clear message:

```

Section 'Colors' appears before 'Tokens', which is out of order. Expected order: Tokens, Colors, Typography, Components, ...

```

### Configuring Custom Aliases

The validation system recognizes alternate section names through the alias resolution mechanism. To treat `Palette` as equivalent to `Colors`, modify your [`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml):

```yaml

# spec-config.yaml (excerpt)

sections:
  - canonical: Colors
    aliases: [Palette, Colour]

```

The `resolveAlias` function automatically normalizes these variations during validation, ensuring that `## Palette` is evaluated as `Colors` when checking against the canonical order.

## Key Source Files

Understanding section order validation requires familiarity with these specific files in the [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository:

- **[`packages/cli/src/linter/spec-config.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/spec-config.ts)** – Loads and validates the specification configuration; exports `CANONICAL_ORDER`, `SECTION_ALIASES`, and `resolveAlias`
- **[`packages/cli/src/linter/linter/rules/section-order.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/section-order.ts)** – Implements the `sectionOrder` function and `sectionOrderRule` descriptor
- **[`packages/cli/src/linter/linter/rules/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/index.ts)** – Aggregates all rule descriptors for the linter runner
- **[`packages/cli/src/linter/linter/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/handler.ts)** – Coordinates rule execution against parsed `DesignSystemState` objects
- **[`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml)** – Source YAML defining canonical sections and their aliases

## Summary

- **Section order validation** enforces canonical heading sequences defined in [`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml) across all DESIGN.md files
- The system uses a **three-stage pipeline**: loading configuration, building an order map, and performing pairwise index comparisons
- **Alias resolution** via `resolveAlias` ensures flexible heading recognition while maintaining strict ordering requirements
- Validation **stops at the first violation** found, providing immediate feedback with the expected canonical sequence
- The rule is implemented in [`section-order.ts`](https://github.com/google-labs-code/design.md/blob/main/section-order.ts) and integrated into the linter through the rule descriptor system in [`rules/index.ts`](https://github.com/google-labs-code/design.md/blob/main/rules/index.ts)

## Frequently Asked Questions

### What happens if a section is missing from the DESIGN.md file?

The section order validator only checks the relative ordering of sections that are actually present. Missing sections do not trigger order violations because the validation logic filters headings against `ORDER_MAP` and only evaluates `knownSections`. However, other linter rules may flag missing required sections separately.

### Can I customize the canonical order for my organization?

Yes. The canonical order is defined in [`spec-config.yaml`](https://github.com/google-labs-code/design.md/blob/main/spec-config.yaml) and loaded through [`spec-config.ts`](https://github.com/google-labs-code/design.md/blob/main/spec-config.ts). Modifying the `sections` array in that YAML file changes the expected order globally. You must rebuild or reinstall the linter package after modifying the configuration to ensure the TypeScript exports reflect your changes.

### How does the linter handle unknown or custom sections?

The `sectionOrder` function filters the parsed sections against `ORDER_MAP` before validation. Any headings not defined in the canonical order or aliases are excluded from `knownSections` and do not participate in order validation. This allows documents to contain custom sections without triggering false positives, provided those sections do not interfere with the ordering of canonical sections.

### Where is the section order rule registered in the linter?

The `sectionOrderRule` descriptor is exported from [`packages/cli/src/linter/linter/rules/section-order.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/section-order.ts) and aggregated in [`packages/cli/src/linter/linter/rules/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/index.ts). The linter runner ([`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)) loads all rule descriptors from this index and executes them against the parsed `DesignSystemState` when users run `design-md lint <file>`.