How the `diff` Command Detects Regressions in DESIGN.md Files

The diff command detects regressions by comparing lint summary statistics between two versions of a DESIGN.md file, flagging a regression only when the after version contains more errors or warnings than the before version.

The diff command in the google-labs-code/design.md repository serves as an automated gatekeeper for design system changes. Unlike traditional diff tools that only compare text, this command parses both files into design system maps and evaluates whether modifications introduced new validation failures. Understanding how it detects regressions requires examining its four-stage pipeline implemented across the CLI utilities.

The Regression Detection Pipeline

The command orchestrates file reading, linting, token comparison, and summary analysis to determine regression status. Each stage relies on specific utility functions defined in the source code.

Reading Input Files with readInput

The process begins in packages/cli/src/commands/diff.ts by loading both versions of the design file. The readInput function (defined in packages/cli/src/utils.ts) handles file paths or stdin (indicated by -) and returns the raw content without throwing parsing errors.

const before = await readInput(beforePath);
const after = await readInput(afterPath);

This utility ensures the command can compare local files against previous versions or piped content from version control systems.

Linting Both Versions

Both file contents pass through the lint function exported from packages/cli/src/linter/index.ts. This function parses the markdown, validates tokens, and returns a LintReport object containing two critical properties:

  • designSystem – Parsed token maps for colors, typography, spacing, rounded corners, and components
  • summary – Counts of errors, warnings, and infos found during validation
const beforeReport = lint(beforeContent);
const afterReport = lint(afterContent);

The linting step establishes the baseline and comparison metrics for regression detection.

Diffing Token Maps with diffMaps

While token changes are tracked for reporting purposes, they do not directly trigger regression flags. The diffMaps function (lines 58-84 in packages/cli/src/utils.ts) compares the designSystem maps from both reports and categorizes changes into three arrays:

  • added – Keys present only in the after map
  • removed – Keys present only in the before map
  • modified – Keys present in both but with different JSON-stringified values

The command calls diffMaps for each token category:

tokens: {
  colors: diffMaps(beforeReport.designSystem.colors, afterReport.designSystem.colors),
  typography: diffMaps(beforeReport.designSystem.typography, afterReport.designSystem.typography),
  rounded: diffMaps(beforeReport.designSystem.rounded, afterReport.designSystem.rounded),
  spacing: diffMaps(beforeReport.designSystem.spacing, afterReport.designSystem.spacing),
  components: diffMaps(
    serializeComponents(beforeReport.designSystem.components),
    serializeComponents(afterReport.designSystem.components)
  )
}

For components, serializeComponents converts the nested Map<string, ComponentDef> structure into a plain object to enable structural comparison.

Determining Regression Status

The actual regression detection occurs by comparing the summary objects from both lint reports. As implemented in packages/cli/src/commands/diff.ts (lines 68-70), a regression is defined strictly by an increase in lint findings:

regression: afterReport.summary.errors > beforeReport.summary.errors
  || afterReport.summary.warnings > beforeReport.summary.warnings,

The command calculates the delta between versions and sets the process exit code accordingly:

process.exitCode = diff.regression ? 1 : 0;

If the after file contains more errors or warnings than the before file, the command exits with status 1, signaling a regression. Otherwise, it exits with 0.

Practical Usage Examples

Command Line Interface

Invoke the diff command to compare two design files directly:

npx @google/design.md diff DESIGN.md DESIGN-v2.md

The command outputs a JSON structure showing token changes and findings:

{
  "tokens": {
    "colors": { "added": [], "removed": [], "modified": ["tertiary"] },
    "components": { "added": [], "removed": [], "modified": [] }
  },
  "findings": {
    "before": { "errors": 0, "warnings": 1, "infos": 1 },
    "after": { "errors": 1, "warnings": 2, "infos": 1 },
    "delta": { "errors": 1, "warnings": 1 }
  },
  "regression": true
}

Because the after state introduced additional errors and warnings, the regression flag is true and the process exits with code 1.

Programmatic Integration

You can replicate the regression logic in Node.js or TypeScript applications:

import { readInput } from '@google/design.md/cli/src/utils.js';
import { lint } from '@google/design.md/cli/src/linter/index.js';
import { diffMaps } from '@google/design.md/cli/src/utils.js';

async function diffDesigns(beforePath: string, afterPath: string) {
  const before = await readInput(beforePath);
  const after = await readInput(afterPath);

  const beforeReport = lint(before);
  const afterReport = lint(after);

  const diff = {
    tokens: {
      colors: diffMaps(beforeReport.designSystem.colors, afterReport.designSystem.colors),
      typography: diffMaps(beforeReport.designSystem.typography, afterReport.designSystem.typography),
      spacing: diffMaps(beforeReport.designSystem.spacing, afterReport.designSystem.spacing),
    },
    regression:
      afterReport.summary.errors > beforeReport.summary.errors ||
      afterReport.summary.warnings > beforeReport.summary.warnings,
  };

  console.log(diff);
  process.exitCode = diff.regression ? 1 : 0;
}

This implementation mirrors the CLI behavior, enabling custom CI/CD integrations or automated design system monitoring.

Key Source Files

Understanding the regression detection mechanism requires familiarity with these specific files in the google-labs-code/design.md repository:

  • packages/cli/src/commands/diff.ts – Orchestrates the entire pipeline, including the regression logic at lines 68-70 that compares error and warning counts.
  • packages/cli/src/utils.ts – Contains readInput and diffMaps (lines 58-84), which classify token changes into added, removed, or modified categories.
  • packages/cli/src/linter/index.ts – Re-exports the lint function that produces the LintReport and summary statistics.
  • packages/cli/src/linter/lint.ts – Implements the parsing and rule evaluation that populates summary.errors and summary.warnings.
  • packages/cli/src/linter/rules/* – Individual lint rules (such as broken-ref and contrast-ratio) whose counts drive the regression detection algorithm.

Summary

  • The diff command detects regressions by comparing lint summary counts, not token semantic changes, between two DESIGN.md versions.
  • Regression definition: A regression occurs when the after version has more errors or warnings than the before version, as implemented in packages/cli/src/commands/diff.ts.
  • The command uses readInput to load files and diffMaps to categorize token modifications, but these serve reporting rather than regression detection.
  • Exit codes indicate status: 1 for regression detected, 0 for no regression.
  • The lint function generates the summary statistics that ultimately determine whether a design change constitutes a regression.

Frequently Asked Questions

Does the diff command consider token changes as regressions?

No. The diff command does not flag semantic changes to design tokens (such as color value modifications or new component definitions) as regressions. According to the source code in packages/cli/src/commands/diff.ts, regressions are determined solely by comparing lint summary counts. Token diffs generated by diffMaps are provided for human review but do not influence the regression boolean or exit code.

What exit codes does the diff command return?

The command returns exit code 1 when a regression is detected (meaning the after file has more errors or warnings than the before file), and exit code 0 when no regression is found. This behavior, implemented via process.exitCode = diff.regression ? 1 : 0, makes the command suitable for CI/CD pipelines where non-zero exit codes fail builds automatically.

How does diffMaps determine if a token was modified?

The diffMaps function in packages/cli/src/utils.ts (lines 58-84) determines modifications by comparing JSON-stringified values of keys present in both the before and after maps. If JSON.stringify(beforeValue) !== JSON.stringify(afterValue), the key is classified as modified rather than added or removed. This approach handles complex nested objects and ensures structural equality checks.

Can the diff command compare files from stdin?

Yes. The readInput utility in packages/cli/src/utils.ts accepts - as a file path indicator, allowing the command to read from stdin. This enables piping scenarios such as git show HEAD:DESIGN.md | npx @google/design.md diff DESIGN.md - to compare the current working file against the previous Git version.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →