Skip to main content
VertaaUX Docs

Policy-as-Code

Configure branch-specific quality gates with vertaa.policy.yml

Policy-as-Code

Policy-as-code lets you define UX quality standards as a YAML configuration file in your repository. Different branches can have different thresholds, and the CLI automatically enforces the right policy based on the current branch.

What is Policy-as-Code?

Instead of passing flags like --threshold 80 --fail-on error every time, define policies once:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 85
      max_new_errors: 0

Then simply run:

# Policy automatically applies based on current branch
vertaa audit -u $PREVIEW_URL

Benefits:

  • Consistency - Same rules apply regardless of who runs the audit
  • Visibility - Quality standards are visible in the repository
  • Flexibility - Different standards for different contexts
  • Version control - Policy changes are tracked with code changes

File Location

The policy file must be named vertaa.policy.yml and placed in your repository root:

your-repo/
  vertaa.policy.yml   # Policy file here
  src/
  package.json

The CLI automatically detects and uses this file when running in a Git repository.

Schema Reference

Top-Level Structure

vertaa.policy.yml
version: 1               # Required. Only version 1 is currently supported
branches:                # Required. Map of branch configurations
  main:                  # Branch name (for display/organization)
    pattern: "^main$"    # Regex pattern to match branch names
    assertions:          # Quality gate rules
      # ... assertion options

Branch Pattern Syntax

The pattern field uses regular expressions to match branch names:

PatternMatches
"^main$"Exactly "main"
"^develop$"Exactly "develop"
"^feature/.*$"Any branch starting with "feature/"
"^release/v\\d+\\.\\d+$"Release branches like "release/v1.0"
"^hotfix/.*$"Any branch starting with "hotfix/"
".*"Any branch (catch-all)

Patterns are evaluated in order. The first matching pattern wins. Use a catch-all pattern at the end for defaults.

Assertion Options

AssertionTypeDescription
fail_on'error' | 'warning' | 'none'Minimum severity to fail the audit
overall_scorenumber (0-100)Minimum overall score required
max_new_errorsnumberMaximum new errors compared to baseline
max_new_warningsnumberMaximum new warnings compared to baseline
max_regressionsnumberMaximum rules that got worse vs baseline

Assertion behavior:

  • fail_on: error - Fail if any error-severity issue exists
  • fail_on: warning - Fail if any warning or error exists
  • fail_on: none - Never fail based on severity (still checks other assertions)
  • overall_score: 85 - Fail if score < 85
  • max_new_errors: 0 - Fail if any new errors since baseline
  • max_new_warnings: 5 - Allow up to 5 new warnings
  • max_regressions: 0 - Fail if any rule scores got worse

Example Configurations

Strict Main Branch

Enforce high standards on production code:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90
      max_new_errors: 0
      max_new_warnings: 0
      max_regressions: 0

Relaxed Feature Branches

Allow work-in-progress on feature branches:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90
      max_new_errors: 0

  feature:
    pattern: "^feature/.*$"
    assertions:
      fail_on: error
      overall_score: 70
      max_new_errors: 3
      max_new_warnings: 10

Staging Environment

Different rules for staging vs production:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90
      max_new_errors: 0

  staging:
    pattern: "^staging$"
    assertions:
      fail_on: error
      overall_score: 80
      max_new_warnings: 5

  develop:
    pattern: "^develop$"
    assertions:
      fail_on: error
      overall_score: 70

Release Branches

Strict rules for release candidates:

vertaa.policy.yml
version: 1
branches:
  release:
    pattern: "^release/.*$"
    assertions:
      fail_on: error
      overall_score: 95
      max_new_errors: 0
      max_new_warnings: 0

  hotfix:
    pattern: "^hotfix/.*$"
    assertions:
      fail_on: error
      overall_score: 85
      max_new_errors: 0

  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90

  default:
    pattern: ".*"
    assertions:
      fail_on: error
      overall_score: 70

Monorepo with Path-Based Rules

For monorepos, combine with --paths flag:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 85

  feature:
    pattern: "^feature/.*$"
    assertions:
      fail_on: error
      overall_score: 70

Then in CI:

# Audit only changed paths
- run: |
    CHANGED_PATHS=$(git diff --name-only origin/main | xargs dirname | sort -u)
    for path in $CHANGED_PATHS; do
      vertaa audit -u "$PREVIEW_URL/$path" --paths "$path/**"
    done

Progressive Improvement

Start lenient and tighten over time:

vertaa.policy.yml (week 1)
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 60
      max_new_errors: 0
vertaa.policy.yml (month 1)
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 75
      max_new_errors: 0
      max_new_warnings: 0
vertaa.policy.yml (month 3)
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90
      max_new_errors: 0
      max_new_warnings: 0
      max_regressions: 0

Policy vs CLI Flags

When both policy file and CLI flags are present, CLI flags take precedence:

SourcePriority
CLI flagsHighest (always wins)
vertaa.policy.ymlMedium
Built-in defaultsLowest

Example:

vertaa.policy.yml
version: 1
branches:
  main:
    pattern: "^main$"
    assertions:
      overall_score: 85
# CLI flag overrides policy
vertaa audit -u $URL --threshold 70
# Uses threshold 70, not 85

This allows one-off overrides without changing the policy file.

Using with Baselines

The max_new_errors, max_new_warnings, and max_regressions assertions require a baseline file:

# First, create baseline
vertaa baseline -u $PRODUCTION_URL --update baseline.json

# Then audit with baseline comparison
vertaa audit -u $PREVIEW_URL --baseline baseline.json

Without a baseline, these assertions are ignored:

With BaselineWithout Baseline
max_new_errors: 0 enforcedmax_new_errors: 0 ignored
max_new_warnings: 5 enforcedmax_new_warnings: 5 ignored
max_regressions: 0 enforcedmax_regressions: 0 ignored

See Baseline Management for CI workflows.

Validating Policy Files

Check your policy file for errors:

vertaa policy validate

Output:

vertaa.policy.yml: valid
  - 3 branch patterns defined
  - main: overall_score=85, fail_on=error
  - feature: overall_score=70, fail_on=error
  - default: overall_score=60, fail_on=error

If invalid:

vertaa.policy.yml: invalid
  Error at line 5: 'fail_on' must be 'error', 'warning', or 'none'

Viewing Active Policy

See which policy applies to the current branch:

vertaa policy show

Output:

Current branch: feature/add-checkout
Matched pattern: ^feature/.*$

Active assertions:
  fail_on: error
  overall_score: 70
  max_new_errors: 3
  max_new_warnings: 10

Best Practices

Start Lenient, Tighten Over Time

Don't block all builds on day one:

  1. Start with overall_score: 60 and max_new_errors: 0
  2. Monitor baseline improvements
  3. Gradually increase thresholds
  4. Add warnings enforcement after errors are controlled

Use Baselines for Fairness

Without baselines, PRs inherit all existing issues. With baselines:

  • Existing issues don't block PRs
  • Only new issues are enforced
  • Teams can improve incrementally

Different Rules for Different Contexts

ContextRecommended Policy
Production (main)Strictest: high score, no new issues
StagingModerate: medium score, limited warnings
Feature branchesLenient: lower score, more warnings allowed
HotfixesStrict for safety, but skip baseline checks

Keep Patterns Specific

Avoid patterns that match unexpectedly:

# Bad: too broad
branches:
  all:
    pattern: ".*main.*"  # Matches "main", "domain", "maintenance"

# Good: specific
branches:
  main:
    pattern: "^main$"    # Only matches exactly "main"

Document Your Policies

Add comments explaining the rationale:

version: 1
branches:
  # Production branch - highest standards
  main:
    pattern: "^main$"
    assertions:
      fail_on: error
      overall_score: 90
      max_new_errors: 0     # No new errors ever
      max_regressions: 0    # No rule scores can get worse

  # Feature branches - work in progress allowed
  feature:
    pattern: "^feature/.*$"
    assertions:
      fail_on: error        # Block critical errors
      overall_score: 70     # Lower threshold for WIP
      max_new_errors: 3     # Some flexibility for iteration

Full Schema

version: 1                    # Required, integer
branches:                     # Required, object
  <branch-name>:              # String, for organization only
    pattern: <regex>          # Required, string (regex pattern)
    assertions:               # Required, object
      fail_on: <severity>     # Optional, 'error' | 'warning' | 'none'
      overall_score: <number> # Optional, 0-100
      max_new_errors: <number>    # Optional, >= 0
      max_new_warnings: <number>  # Optional, >= 0
      max_regressions: <number>   # Optional, >= 0

Was this page helpful?

On this page