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:
| ID | Pattern | Why it matters |
|---|---|---|
| AP-001 | Broad eslint-disable | Hides multiple issues |
| AP-003 | Explicit any | Defeats type safety |
| AP-004 | @ts-ignore | Masks type errors |
| AP-006 | Empty catch block | Swallows errors |
| AP-007 | Console in production | Debug 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
| Status | Meaning | Default behaviour |
|---|---|---|
pass | Check passed | Continue |
warn | Issue found, not blocking | Continue (log warning) |
fail | Blocking issue | Stop execution |
skip | Check not applicable | Continue |
Configuring Severity
Override default severity:
{
"antiPatterns": {
"patterns": {
"AP-003": { "severity": "error" },
"AP-007": { "severity": "off" }
}
}
}
Gate Execution
Run Order
Gates run in dependency order:
- Lint — fast syntax checks
- Architecture — import analysis
- Anti-patterns — pattern matching
- Secrets — security scan
- Tests — if configured
- 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 →