Skip to main content

Gates

Gates are quality checks that code must pass before proceeding. They're deterministic, composable, and run automatically.

What is a Gate?

A gate is a validation checkpoint. Code enters, gets checked, and either passes or fails:

┌─────────┐     ┌─────────┐     ┌─────────┐
│ Code │ ──▶ │ Gate │ ──▶ │ Pass │
│ Changes │ │ Checks │ │ Fail │
└─────────┘ └─────────┘ └─────────┘

Gates are:

  • Deterministic — same input, same result
  • Composable — multiple checks per gate
  • Configurable — enable/disable per project
  • Fast — run in milliseconds

Built-in Gate Checks

Architecture Check

Validates that imports respect defined boundaries.

{
"architecture": {
"enabled": true,
"boundaries": [
{
"name": "api-layer",
"pattern": "src/api/**",
"deny": ["src/repositories/**"]
}
]
}
}

Catches:

  • Layer violations (UI importing data layer directly)
  • Domain violations (one domain importing another's internals)
  • Circular dependencies

Anti-Pattern Check

Detects known problematic patterns:

IDPatternWhy it matters
AP-001Broad eslint-disableHides multiple issues
AP-003Explicit anyDefeats type safety
AP-004@ts-ignoreMasks type errors
AP-006Empty catch blockSwallows errors
AP-007Console in productionDebug code leaked

Secret Detection

Finds potential secrets in code:

  • API keys (pattern matching)
  • Passwords (entropy analysis)
  • Credentials in git history

ESLint Check

Runs ESLint as a gate check, failing on errors:

{
"eslint": {
"enabled": true,
"failOn": "error"
}
}

Coverage Check

Validates code coverage thresholds:

{
"coverage": {
"enabled": true,
"threshold": {
"lines": 80,
"branches": 75
}
}
}

Policy Check

Custom rules via OPA/Rego:

package anvil.policy

deny[msg] {
input.file.path == "src/index.ts"
count(input.file.lines) > 500
msg := "src/index.ts exceeds 500 lines"
}

Gate Results

Each gate check produces a result:

interface GateResult {
status: 'pass' | 'fail' | 'warn' | 'skip';
check: string;
message: string;
details?: {
file: string;
line: number;
column: number;
suggestion?: string;
}[];
}

Status Levels

StatusMeaningDefault behaviour
passCheck passedContinue
warnIssue found, not blockingContinue (log warning)
failBlocking issueStop execution
skipCheck not applicableContinue

Configuring Severity

Override default severity:

{
"antiPatterns": {
"patterns": {
"AP-003": { "severity": "error" },
"AP-007": { "severity": "off" }
}
}
}

Gate Execution

Run Order

Gates run in dependency order:

  1. Lint — fast syntax checks
  2. Architecture — import analysis
  3. Anti-patterns — pattern matching
  4. Secrets — security scan
  5. Tests — if configured
  6. Coverage — if configured

Parallel Execution

Independent checks run in parallel. Dependent checks wait:

lint ────────┐
├──▶ architecture ──▶ anti-patterns
secrets ─────┘

Caching

Gate results are cached by file hash. Unchanged files skip re-validation:

File: src/utils.ts
Hash: abc123
Cached result: PASS (from 2 minutes ago)
Skipping re-check.

Suppressions

When a gate check should be bypassed, use suppressions:

// @anvil-ignore AP-003 Using any for legacy API compatibility
const legacyData: any = fetchLegacyApi();

Rules:

  • Suppressions require explanations
  • Suppressions are tracked in evidence
  • Unexplained suppressions trigger warnings

File-Level Suppression

// @anvil-ignore-file AP-007
// This file legitimately uses console for CLI output

Configuration Suppression

{
"suppressions": [
{
"pattern": "scripts/**",
"checks": ["AP-007"],
"reason": "Scripts may use console"
}
]
}

Next: Sessions and runs →