--- title: Rules description: Understand Archgate's TypeScript rule system that turns Architecture Decision Records into automated compliance checks with file-level violation reporting. --- Rules are the executable side of an ADR. They live in companion `.rules.ts` files alongside the ADR document and export a plain object typed with `satisfies RuleSet`. When you run `archgate check`, the CLI loads each ADR that has `rules: true`, imports its companion rules file, and executes every check against your codebase. ## Defining Rules A rules file is a TypeScript module that default-exports a plain object conforming to the `RuleSet` type. The type is provided by the local shim auto-generated by `archgate init` (no npm install needed): ```typescript /// export default { rules: { "rule-key": { description: "What this rule checks", severity: "error", async check(ctx) { // Inspect files and report violations }, }, }, } satisfies RuleSet; ``` Each key in the `rules` object becomes the rule ID. The full rule identifier shown in check output combines the ADR ID and the rule key, for example `ARCH-004/no-barrel-files`. ## Rule Structure Every rule has three parts: | Property | Type | Required | Description | | ------------- | -------- | -------- | --------------------------------------------- | | `description` | string | Yes | A short summary of what the rule checks | | `severity` | string | No | `"error"` (default), `"warning"`, or `"info"` | | `check` | function | Yes | Async function receiving a `RuleContext` | ### Severity Levels Severity determines what happens when a rule finds a problem: | Severity | Exit Code | Effect | | --------- | --------- | ----------------------------------------- | | `error` | 1 | Violation is reported and the check fails | | `warning` | 0 | Warning is logged but the check passes | | `info` | 0 | Informational message, check passes | When `archgate check` runs, exit code 1 means at least one `error`-severity violation was found. Exit code 0 means no errors (warnings and info messages are logged but do not block). ## The RuleContext The `check` function receives a `RuleContext` object that provides everything a rule needs to inspect the codebase and report findings. ### Project Information | Property | Type | Description | | ------------------ | ---------- | -------------------------------------------------------------------------------- | | `ctx.projectRoot` | `string` | Absolute path to the project root directory | | `ctx.scopedFiles` | `string[]` | Files matching the ADR's `files` globs, or all project files if no globs are set | | `ctx.changedFiles` | `string[]` | Files changed in git (populated when running with `--staged`) | ### File Operations | Method | Returns | Description | | -------------------- | ------------------- | ---------------------------------- | | `ctx.glob(pattern)` | `Promise` | Find files matching a glob pattern | | `ctx.readFile(path)` | `Promise` | Read a file's content as a string | | `ctx.readJSON(path)` | `Promise` | Read and parse a JSON file | ### Search Operations | Method | Returns | Description | | ---------------------------------- | ---------------------- | -------------------------------------------- | | `ctx.grep(file, pattern)` | `Promise` | Search a single file with a regex pattern | | `ctx.grepFiles(pattern, fileGlob)` | `Promise` | Search across multiple files matching a glob | Both `grep` and `grepFiles` return an array of `GrepMatch` objects: ```typescript interface GrepMatch { file: string; // Relative path from project root line: number; // 1-based line number column: number; // 1-based column number content: string; // The full line content } ``` ### Reporting The `ctx.report` object provides three methods for reporting findings: ```typescript ctx.report.violation({ message, file?, line?, fix? }); ctx.report.warning({ message, file?, line?, fix? }); ctx.report.info({ message, file?, line?, fix? }); ``` Each method accepts an object with: | Property | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------ | | `message` | string | Yes | What the problem is | | `file` | string | No | Relative path to the offending file | | `line` | number | No | Line number where the problem occurs | | `fix` | string | No | Suggested fix for the violation | Use `ctx.report.violation()` for problems that must block merges. Use `ctx.report.warning()` for issues worth flagging but not blocking. Use `ctx.report.info()` for purely informational output. ## Rule Timeout Each rule has a 30-second execution timeout. If a rule's `check` function does not complete within 30 seconds, it is terminated and reported as an error. This prevents runaway rules from blocking the pipeline indefinitely. ## Complete Example Here is a complete rules file that checks for a banned import pattern. It enforces that no source file imports directly from `node:fs` (the project requires using a wrapper instead). ```typescript /// export default { rules: { "no-direct-fs-import": { description: "Source files must not import directly from node:fs; use the fs wrapper", severity: "error", async check(ctx) { const sourceFiles = ctx.scopedFiles.filter( (f) => f.endsWith(".ts") && !f.endsWith(".test.ts") ); for (const file of sourceFiles) { const matches = await ctx.grep(file, /from ["']node:fs["']/); for (const match of matches) { ctx.report.violation({ message: `Direct import from "node:fs" is not allowed. Use the fs wrapper from "src/helpers/fs" instead.`, file: match.file, line: match.line, fix: 'Replace the import with: import { readFile, writeFile } from "../helpers/fs"', }); } } }, }, }, } satisfies RuleSet; ``` When this rule runs against a file containing `import { readFileSync } from "node:fs"`, the output looks like: ``` ARCH-007/no-direct-fs-import ERROR src/services/config.ts:3 — Direct import from "node:fs" is not allowed. Use the fs wrapper from "src/helpers/fs" instead. Fix: Replace the import with: import { readFile, writeFile } from "../helpers/fs" ``` ## Execution Model Rules execute with the following guarantees: - **Parallel across ADRs** -- Rules from different ADRs run concurrently for faster execution. - **Sequential within an ADR** -- Rules belonging to the same ADR run one after another, so earlier rules can establish context for later ones. - **Scoped files are pre-resolved** -- The `ctx.scopedFiles` array is populated before your `check` function is called, based on the ADR's `files` globs. - **Changed files for staged mode** -- When running `archgate check --staged`, `ctx.changedFiles` contains only the files staged in git, letting rules skip unchanged files for faster feedback. :::tip[Run checks automatically with editor plugins] The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) run `archgate check` automatically after every code change. The agent reads the applicable ADRs, writes compliant code, and validates -- no manual check commands needed. [Sign up for beta access](https://plugins.archgate.dev). :::