# How to Configure the Linter Rules Programmatically in Instagit

> Configure linter rules programmatically in Instagit by passing a custom RuleDescriptor array to the lint function. Modify, disable, or inject rules at runtime.

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

---

**You configure the linter rules programmatically by passing a custom `RuleDescriptor[]` array to the `lint` function, bypassing the default `DEFAULT_RULE_DESCRIPTORS` to disable, modify severity, or inject custom rules at runtime.**

The Instagit design-linting engine from the [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository exposes a composable, in-process API that lets you configure the linter rules programmatically without touching external configuration files. By supplying your own rule descriptors to the main `lint` helper, you gain full control over which validations run and how violations are reported.

## Understanding the Rule Configuration Architecture

The programmatic configuration flow relies on three core components working together. First, the **spec configuration** 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) defines canonical sections and units via `loadSpecConfig` and `getSpecConfig`. Second, the **rules index** at [`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) exports `DEFAULT_RULE_DESCRIPTORS` containing rule logic and default severities. Third, the **lint runner** in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts) exposes the public `lint` function that accepts your custom rule array, while [`packages/cli/src/linter/linter/runner.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/runner.ts) handles the actual execution.

## Loading the Spec Configuration

Before customizing rules, the system reads the DESIGN.md specification lazily. The `getSpecConfig` function in [`spec-config.ts`](https://github.com/google-labs-code/design.md/blob/main/spec-config.ts) provides access to canonical definitions that most rules reference during execution. This step happens automatically during the first rule execution, but you can preload it explicitly if your workflow requires early validation.

## Customizing the Rule Set

Because the linter accepts a plain JavaScript array of descriptors, you manipulate the rule set using standard array operations. The helper `toLintRule` in the rules index converts descriptors into executable `LintRule` objects that the runner iterates over.

### Disabling Default Rules

To remove a specific rule, filter it from the `DEFAULT_RULE_DESCRIPTORS` array before passing it to the linter.

```typescript
import { DEFAULT_RULE_DESCRIPTORS } from '@/linter/rules/index';
import { lint } from '@/linter';

const customRuleSet = DEFAULT_RULE_DESCRIPTORS.filter(
  d => d.name !== 'unknownKey'
);

const findings = await lint(state, { rules: customRuleSet });

```

### Modifying Rule Severity

Copy an existing descriptor and replace its `severity` field to change how violations are reported without altering the underlying logic.

```typescript
import { DEFAULT_RULE_DESCRIPTORS } from '@/linter/rules/index';

const adjustedDescriptors = DEFAULT_RULE_DESCRIPTORS.map(d => {
  if (d.name === 'contrastCheck') {
    // Downgrade from “error” to “warning”
    return { ...d, severity: 'warning' as const };
  }
  return d;
});

const findings = await lint(state, { rules: adjustedDescriptors });

```

### Adding Custom Rules

Create a new `RuleDescriptor` object adhering to the required shape—including `name`, `description`, `severity`, and a `run` function—and prepend or append it to your array.

```typescript
import { DEFAULT_RULE_DESCRIPTORS, type RuleDescriptor } from '@/linter/rules/index';
import { lint } from '@/linter';

const dangerTokenRule: RuleDescriptor = {
  name: 'dangerToken',
  description: 'Disallow the use of a token called “danger”.',
  severity: 'error',
  run: (state) => {
    const findings = [];
    for (const token of Object.keys(state.tokens)) {
      if (token === 'danger') {
        findings.push({
          path: [`tokens`, token],
          message: 'Token “danger” is prohibited.',
        });
      }
    }
    return findings;
  },
};

const customRuleSet: RuleDescriptor[] = [
  dangerTokenRule,
  ...DEFAULT_RULE_DESCRIPTORS,
];

const findings = await lint(state, { rules: customRuleSet });

```

## Executing the Linter with Custom Rules

The public `lint` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts) accepts a `DesignSystemState` and an optional configuration object. If you omit the `rules` property, the linter uses `DEFAULT_RULES`; otherwise, it executes your custom list in the exact order provided.

```typescript
import { missingPrimaryRule, sectionOrderRule } from '@/linter/rules/index';
import { lint } from '@/linter';

// Run only a subset of rules
const minimalSet = [missingPrimaryRule, sectionOrderRule];
const findings = await lint(state, { rules: minimalSet });

```

The CLI entry point in [`packages/cli/src/commands/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/lint.ts) demonstrates this pattern by forwarding command-line flags to the same programmatic API, confirming that all configuration happens in-process without environment variables.

## Summary

- **Rule descriptors** are plain JavaScript objects exported from `DEFAULT_RULE_DESCRIPTORS` 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).
- **Configuration** happens at runtime by passing a `RuleDescriptor[]` array to the `lint` function in [`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts).
- **Customization** supports disabling rules via array filtering, changing severity by modifying the `severity` property, and adding custom logic with new descriptor objects.
- **Execution** preserves the order of your array and requires no external configuration files.

## Frequently Asked Questions

### How do I disable a specific linter rule without modifying the source code?

Filter the `DEFAULT_RULE_DESCRIPTORS` array using the standard JavaScript `filter` method to exclude the rule by name, then pass the resulting array to the `lint` function. This happens entirely in memory and does not affect the default configuration file.

### Can I change a rule severity from error to warning programmatically?

Yes. Map over `DEFAULT_RULE_DESCRIPTORS` and return a new object with the `severity` property changed to `'warning'` for the specific rule you want to adjust. The linter uses this value during execution to determine how to report violations.

### What is the difference between `RuleDescriptor` and `LintRule`?

A **RuleDescriptor** is the static configuration object containing metadata and a `run` function, while a **LintRule** is the executable version created internally by the `toLintRule` helper. You typically pass descriptors to the `lint` function, which converts them automatically before the runner in [`runner.ts`](https://github.com/google-labs-code/design.md/blob/main/runner.ts) executes them.

### Can I run the linter with only my custom rules and ignore the defaults?

Absolutely. Instead of spreading `DEFAULT_RULE_DESCRIPTORS`, construct an array containing only your custom rule descriptors (or imported individual rules) and pass that directly to the `lint` function. The linter will execute only the rules you provide, in the order you specify.