# How the design.md Lint Command Reports Validation Results and Exit Codes

> Learn how the design.md lint command reports validation results. Understand exit codes 0, 1, 2 and get detailed findings for successful runs or errors.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-07-04

---

**The `lint` command in the design.md CLI validates DESIGN.md documents and returns exit code 0 for success, 1 for validation errors, and 2 for file read failures, outputting a structured report containing detailed findings and aggregated severity counts.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository provides a command-line interface for validating design system documentation. When you run the **lint command**, it performs comprehensive validation of your DESIGN.md file and reports results through both structured output and process exit codes. Understanding how validation results are reported and which exit codes are used enables reliable integration into CI/CD pipelines and automated workflows.

## Validation Workflow and Reporting

The lint command follows a four-stage pipeline implemented across the CLI source tree. Each stage contributes to the final report and exit behavior.

### Input Reading and Error Handling

In [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts), the `readInput` utility attempts to read the specified file path or stdin. If the file cannot be accessed, it throws a `FileReadError` and the command immediately terminates with **exit code 2**. This distinguishes I/O failures from validation failures.

### The Lint Report Structure

Once input is successfully read, the raw markdown passes to the `lint(content)` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts). This function parses the document, builds a design-system model, runs all lint rules, and generates a Tailwind CSS configuration. The returned **LintReport** object contains:

- **`findings`** – An array of individual issues, each specifying severity (error, warning, or info), message, and optional path.
- **`summary`** – Aggregated counts in the format `{ errors, warnings, infos }`.
- **Additional metadata** – Includes `designSystem`, `sections`, and other data not printed by default.

### Output Formatting

The `formatOutput` utility (also in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts)) serializes the report based on the `--format` flag. By default, it outputs **JSON**, but it can also render **markdown** or plain text for human-readable reports.

## Exit Code Behavior

After printing the formatted report, the command sets the process exit code in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts) using this logic:

```typescript
process.exitCode = report.summary.errors > 0 ? 1 : 0;

```

The exit codes follow this specification:

- **0** – No lint errors detected. Warnings and informational messages may be present, but the document is considered valid.
- **1** – One or more lint errors were found in the document.
- **2** – The input file could not be read (I/O error or missing file).

This behavior allows shell scripts and CI systems to distinguish between validation failures and system errors reliably.

## Practical Usage Examples

### Basic JSON Validation

Run the linter against a DESIGN.md file to receive machine-readable output:

```bash
design-md lint ./examples/totality-festival/DESIGN.md

```

Typical JSON output structure:

```json
{
  "findings": [
    {
      "severity": "error",
      "message": "Token \"brand-primary\" is undefined",
      "path": "colors.brand-primary"
    },
    {
      "severity": "warning",
      "message": "Unused token \"spacing.large\"",
      "path": "spacing.large"
    }
  ],
  "summary": {
    "errors": 1,
    "warnings": 1,
    "infos": 0
  }
}

```

### Exit Code Handling in Scripts

Use the exit code in automated workflows to trigger different actions:

```bash
#!/usr/bin/env bash
design-md lint ./DESIGN.md
case $? in
  0) echo "✅ Lint passed – no errors." ;;
  1) echo "❌ Lint failed – errors were reported." ;;
  2) echo "⚠️ Unable to read the DESIGN.md file." ;;
esac

```

### Human-Readable Markdown Reports

For review during development, request a markdown formatted report:

```bash
design-md lint ./DESIGN.md --format markdown

```

This produces structured output like:

```

# Lint Report

**1 errors**, **1 warnings**, **0 infos**

## Findings

- **error** `colors.brand-primary`: Token "brand-primary" is undefined
- **warning** `spacing.large`: Unused token "spacing.large"

```

## Summary

- The **lint command** validates DESIGN.md documents through a pipeline involving input reading, model validation, and report generation.
- **Exit code 0** indicates success (no errors), **exit code 1** indicates validation errors found, and **exit code 2** indicates file read failures.
- Validation reports include a **`findings`** array with detailed issue descriptions and a **`summary`** object aggregating error, warning, and info counts.
- Output formats include **JSON** (default) and **markdown**, controlled via the `--format` flag.
- Core implementation resides in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts), [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts), and [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts).

## Frequently Asked Questions

### What exit code does the lint command return when only warnings are present?

The command returns **exit code 0** when only warnings or informational messages are present. According to the source code in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts), the process only exits with code 1 when `report.summary.errors > 0`. Warnings and infos do not trigger a failure status.

### How does the lint command handle file read errors?

When the input file cannot be read, the `readInput` function in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) throws a `FileReadError`. This exception causes the command to exit with **exit code 2** before any validation logic executes, distinguishing I/O problems from document validation issues.

### Can the lint command output be formatted as markdown?

Yes. The `--format` flag accepts `markdown` as an option. When specified, the `formatOutput` utility in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) serializes the **LintReport** into a human-readable markdown document containing a summary header and a bulleted list of findings with their severity levels.

### Where is the exit code logic implemented in the source code?

The exit code assignment is implemented in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts). After formatting and printing the output, the command explicitly sets `process.exitCode` based on the error count from the report summary, while file read errors handled earlier in the pipeline use code 2.