Project Configuration File (.pixmoat.yaml)
Manage project-level visual regression settings as code by checking a .pixmoat.yaml file into your repository root. Settings become PR-reviewable, reproducible across projects, and self-documenting per build.
Overview
The config file controls two categories of settings:
| Category | When applied | Mechanism |
|---|---|---|
| Build-scoped | Every build, from the commit being tested | Capture client reads the file and sends settings with POST /api/builds |
| Project-global | Only from the default branch | pixmoat config apply CLI command writes to the project via PUT /api/projects/:slug |
Settings not in the file (secrets, org membership, instance config) remain in the UI or environment variables.
The feature is fully opt-in. Projects without a .pixmoat.yaml file behave exactly as before.
Quick start
- Create
.pixmoat.yamlat your repository root:
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
version: 1
default_tolerance: 0.001
blocking_mode: blocking
-
Commit and push. The capture client reads the file automatically on the next build.
-
(Optional) Apply project-global settings from your default branch:
export PIXMOAT_URL=https://app.pixmoat.com
export PIXMOAT_PROJECT=my-project
export PIXMOAT_TOKEN=pxg_...
pixmoat config apply
Full example
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
version: 1 # schema version — must be 1
# ── Build-scoped settings ─────────────────────────────────────────────
default_tolerance: 0.001 # 0.0–1.0, mismatch ratio threshold
blocking_mode: blocking # blocking | report_only
diff:
threshold: 0.1 # YIQ color sensitivity (0.0–1.0)
detect_aa: true # anti-aliasing detection
diff_color: [255, 0, 255, 255] # RGBA highlight color
dpr: 1 # device pixel ratio (1, 2, or 3)
viewports: # named viewport presets
desktop: 1280x720
tablet: 768x1024
mobile: 375x812
browsers: # expected browser identifiers
- chromium
- firefox
- webkit
flakiness:
captures: 2 # captures per snapshot (1–5)
flakiness_threshold: 0.002 # inter-capture mismatch ratio
quarantine_after: 3 # flaky builds before quarantine
recovery_after: 5 # stable builds before recovery
performance:
enabled: true
gating: advisory # advisory | blocking
regression_threshold: 0.15 # allowed variance from baseline (0.0–1.0)
thresholds:
lcp: { soft_fail: 2500, hard_fail: 4000 }
cls: { soft_fail: 0.10, hard_fail: 0.25 }
inp: { soft_fail: 200, hard_fail: 500 }
fcp: { soft_fail: 1800, hard_fail: 3000 }
ttfb: { soft_fail: 800, hard_fail: 1800 }
total_transfer_size: { soft_fail: 2000000, hard_fail: 5000000 }
total_js_size: { soft_fail: 500000, hard_fail: 1000000 }
removal_gating: advisory # advisory | blocking
# ── Project-global settings (applied only from default branch) ────────
retention:
keep_builds: 50 # keep N most recent builds per branch
delete_after_days: 90 # delete builds older than N days
branch_rules: # per-branch overrides
- branch: "release/*"
keep_builds: 100
delete_after_days: 0 # use plan maximum; unlimited only on an unlimited plan
agent_auto_approve: intent_only # none | intent_only | all
webhook:
url: https://ci.example.com/hook
provider: gitlab # generic | github | gitlab
on_approved: true
on_rejected: true
# secret: set via UI only — never in this file
Settings reference
Build-scoped settings
These travel with the commit and are evaluated per-build. Each build stores a snapshot of its resolved config, so you can always inspect what settings were in effect.
| Field | Type | Default | Description |
|---|---|---|---|
default_tolerance | number | 0.001 | Mismatch ratio threshold (0.0–1.0). Per-snapshot overrides in test code take precedence. |
blocking_mode | string | blocking | blocking or report_only. Whether visual differences block the pipeline. |
diff.threshold | number | 0.1 | YIQ color sensitivity (0.0–1.0). Lower = more sensitive. |
diff.detect_aa | boolean | true | Anti-aliasing detection. |
diff.diff_color | [R,G,B,A] | [255,0,255,255] | RGBA highlight color for changed pixels. |
diff.dpr | integer | 1 | Device pixel ratio (1, 2, or 3). |
viewports | map | — | Named viewport presets. Values must match WIDTHxHEIGHT (e.g. 1280x720). |
browsers | string[] | — | Expected browser identifiers: chromium, firefox, webkit. |
flakiness.captures | integer | 2 | Captures per snapshot (1–5). |
flakiness.flakiness_threshold | number | 0.002 | Inter-capture mismatch ratio above which a snapshot is flaky. |
flakiness.quarantine_after | integer | 3 | Consecutive flaky builds before quarantine. |
flakiness.recovery_after | integer | 5 | Consecutive stable builds before recovery. |
performance.enabled | boolean | false | Enable Core Web Vitals collection. |
performance.gating | string | advisory | advisory or blocking. |
performance.regression_threshold | number | 0.15 | Allowed variance from baseline (0.0–1.0). |
performance.thresholds.* | object | — | Per-metric { soft_fail, hard_fail } pairs. Metrics: lcp, cls, inp, fcp, ttfb, total_transfer_size, total_js_size. |
removal_gating | string | advisory | advisory or blocking. How removed screenshots gate the review. |
Project-global settings
These are applied only from the default branch via pixmoat config apply. Feature branches cannot change these settings.
| Field | Type | Default | Description |
|---|---|---|---|
retention.keep_builds | integer | 50 | Keep N most recent builds per branch. |
retention.delete_after_days | integer | 90 | Delete builds older than N days. On a finite plan, 0 means use the plan maximum and values above the plan limit are clamped. |
retention.branch_rules | array | [] | Per-branch retention overrides. Each entry has branch (glob pattern), keep_builds, and delete_after_days; age values are also clamped by the plan. |
agent_auto_approve | string | intent_only | AI agent auto-approve mode: none, intent_only, or all. |
webhook.url | string | — | Webhook endpoint URL. |
webhook.provider | string | generic | generic, github, or gitlab. |
webhook.on_approved | boolean | true | Send webhook on build approval. |
webhook.on_rejected | boolean | true | Send webhook on build rejection. |
Excluded settings (never in this file)
| Setting | Reason |
|---|---|
webhook.secret | Secret — set via UI or environment variable |
| GitLab PAT / tokens | Secret |
| Project access tokens | Secret |
| Org membership, roles, billing | Org-scoped, not project-scoped |
| Instance/admin settings | Operator-scoped (pixmoat.toml) |
The parser rejects the file with a clear error if any known secret field is present.
Precedence
Settings are resolved most-specific-wins:
per-snapshot override (in test code via SnapshotOptions)
> .pixmoat.yaml build-scoped defaults
> project DB settings (from last "pixmoat config apply" or UI edit)
> server defaults
Plan quotas always clamp: retention cannot exceed your plan’s limits.
CLI commands
All commands use the same environment variables as the capture client (PIXMOAT_URL, PIXMOAT_PROJECT, PIXMOAT_TOKEN).
pixmoat config validate
Parse and validate .pixmoat.yaml without contacting the server.
pixmoat config validate # validates .pixmoat.yaml in CWD
pixmoat config validate --file custom.yaml # validates a specific file
pixmoat config validate --json # structured JSON output
Exit codes: 0 = valid, 1 = invalid or not found.
pixmoat config diff
Compare the local file against the project’s current settings in the database.
pixmoat config diff
Output format:
~ default_tolerance: 0.001 → 0.005
~ diff.threshold: 0.1 → 0.15
+ viewports.tablet: 768x1024
(3 changes)
Exit codes: 0 = file matches DB, 1 = differences found.
pixmoat config apply
Validate the file, show the diff, and write project-global settings to the project.
pixmoat config apply # gated to default branch
pixmoat config apply --force # bypass branch check (local dev only)
Branch gating: the command detects the current branch from $CI_COMMIT_BRANCH (GitLab), $GITHUB_REF_NAME (GitHub), or git rev-parse --abbrev-ref HEAD, and compares it to the project’s default_branch. The apply is rejected if they don’t match (unless --force is used).
Exit codes: 0 = applied, 1 = validation error / auth error / branch gate rejection.
CI integration
GitLab CI
pixmoat-test:
script:
- npx playwright test # capture client reads .pixmoat.yaml
- pixmoat check --wait
pixmoat-config:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- pixmoat config validate
- pixmoat config apply
GitHub Actions
jobs:
pixmoat-test:
steps:
- uses: actions/checkout@v4
- run: npx playwright test
- run: pixmoat check --wait
pixmoat-config:
if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
steps:
- uses: actions/checkout@v4
- run: pixmoat config validate
- run: pixmoat config apply
Editor support
The JSON Schema at docs/schemas/pixmoat-config.schema.json enables autocompletion and validation in editors that support YAML Language Server.
Add this comment to the top of your .pixmoat.yaml:
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
VS Code users: install the YAML extension for automatic schema validation.
UI behavior
When a project’s most recent default-branch build included a .pixmoat.yaml file, the settings page shows a banner:
These settings are managed by
.pixmoat.yaml. Changes made here will be overwritten on the nextpixmoat config apply.
Settings remain editable in the UI for quick fixes and experimentation. The banner makes the consequence clear: the next pixmoat config apply from CI will reconcile settings back to the file’s values.
Backwards compatibility
- Projects without a
.pixmoat.yamlfile are completely unaffected. - Builds created before this feature have
NULLforresolved_configand fall back to reading settings from the project, identical to previous behavior. - The
configfield in the build creation API is optional. Omitting it preserves existing behavior. - Unknown top-level keys in the YAML file produce warnings, not errors, for forward compatibility.