Config Reference
verfix.config.json is the Flow Library for your project. It lives in your project root and
is created by verfix init.
Full annotated example
Section titled “Full annotated example”{ "baseUrl": "http://localhost:3000", "mode": "assisted", "selectors": { "emailInput": "input[type=email]", "submitBtn": "#login-form button[type=submit]" }, "sourceCodePolicy": "warn", "flows": [ { "id": "homepage-smoke", "steps": [ { "action": "navigate", "url": "/" } ], "assertions": [ { "type": "page_loaded" }, { "type": "no_console_errors" } ] }, { "id": "login", "mode": "strict", "clearState": true, "saveState": "auth", "steps": [ { "action": "type", "selector": "emailInput", "value": "${TEST_EMAIL}" }, { "action": "type", "selector": "password", "testId": "password", "value": "${TEST_PASSWORD}" }, { "action": "click", "selector": "submitBtn" } ], "assertions": [ { "type": "network_request_success", "value": "/api/auth/login", "acceptStatuses": [200, 409] } ] }, { "id": "dashboard-load", "useState": "auth", "steps": [ { "action": "navigate", "url": "/dashboard" } ], "assertions": [ { "type": "text_visible", "value": "Total Declarations", "selector": "[data-testid=stats-card]" } ] } ]}Top-level fields
Section titled “Top-level fields”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
baseUrl | string | Yes | — | Base URL of your app. All navigate steps with relative URLs are resolved against this. Supports ${VAR} interpolation. |
mode | "strict" | "assisted" | "exploratory" | No | "strict" | Default execution mode for all flows. Individual flows can override this. |
task | string | No | — | Natural language task, required when mode is exploratory and no flows are given. |
flows | Flow[] | Yes | — | Array of verification flows. |
assertions | AssertionDefinition[] | No | — | Global assertions applied to every flow if the flow has none. |
selectors | Record<string, string> | No | {} | Alias map of logical name → real selector. Steps referencing an alias are resolved at run time — this is the core of config-first target resolution. |
sourceCodePolicy | "warn" | "block" | "off" | No | "warn" | What happens when project source is edited during a verify loop. See Config-First Verification. |
browser | { channel?, headless? } | No | {} | { "channel": "chrome" } reuses your installed Chrome/Edge and skips the Chromium download. { "headless": false } shows the browser window. |
metadata | object | No | — | Arbitrary metadata passed through to run output (e.g. { "framework": "nextjs" }). |
timeout | number | No | 15000 | Default timeout in milliseconds for steps and assertions. |
retries | number | No | 2 | Number of retries on failure before declaring the flow failed. |
Flow fields
Section titled “Flow fields”Each item in the flows array:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for this flow. Used with --flow <id>. |
name | string | No | Alternative to id. If both provided, id takes precedence. |
mode | "strict" | "assisted" | No | Overrides the global mode for this flow only. "exploratory" is not valid per-flow — it only applies as the top-level mode. |
steps | FlowStep[] | Yes | Ordered list of browser actions. |
assertions | AssertionDefinition[] | No | Assertions to check after all steps complete. |
clearState | boolean | No | Clears cookies and localStorage/sessionStorage before this flow runs. Use on a flow that must start logged-out. Does not clear IndexedDB or service workers. |
saveState | string | No | After this flow’s steps and assertions pass, saves the browser’s cookies (including httpOnly), localStorage, and IndexedDB under this name in .verfix/state/ (never committed). Put it on your login flow. sessionStorage is not captured. |
useState | string | No | Restores the named state before the run navigates, so the flow starts already logged in. Fails normally if the state doesn’t exist yet or has expired — rerun the saveState flow to recreate it. One state name per run. |
skip | boolean | No | Quarantines the flow — excluded from a full verfix run, but still runs if named explicitly via --flow <id>. |
skipReason | string | No | Human-readable reason shown alongside a skipped flow in verfix flows. |
Step actions
Section titled “Step actions”Steps are the browser actions executed in order before assertions run. Any step accepts:
optional: true— if the step fails for any reason within itstimeout, it’s skipped (reported inskipped_optional_steps) instead of aborting the flow. Use it for a UI branch that may or may not appear.frame: "<css selector>"— resolves the step’s target inside an<iframe>matching that selector (payment widgets, embedded editors). One frame level; AI selector-healing does not apply inside frames.
navigate
Section titled “navigate”{ "action": "navigate", "url": "/login" }| Field | Type | Required |
|---|---|---|
url | string | Yes — absolute or relative (resolved against baseUrl), supports ${VAR} |
{ "action": "click", "selector": "[data-testid=submit]" }{ "action": "click", "text": "Logout previous session", "optional": true, "timeout": 2000 }| Field | Type | Required |
|---|---|---|
selector / testId / text | string | One of — target element (testId is shorthand for [data-testid=<value>]) |
timeout | number | No — step-level timeout override |
| Field | Type | Required |
|---|---|---|
selector / testId / text | string | One of — target element |
value | string | Yes — text to type, supports ${VAR} |
{ "action": "press", "key": "Enter" }{ "action": "press", "selector": "searchInput", "key": "Escape" }Sends a keyboard key (Playwright key name — "Enter", "Escape", "Tab", etc.) on a target
locator if given, otherwise at the page level. For UIs that submit or react to keydown rather
than form submission.
select_option
Section titled “select_option”{ "action": "select_option", "selector": "countrySelect", "value": "US" }Picks a <select> option by value or visible label.
check / uncheck
Section titled “check / uncheck”{ "action": "check", "selector": "acceptTerms" }{ "action": "uncheck", "selector": "newsletterOptIn" }Sets a checkbox/radio to a known state. Idempotent — unlike click on a checkbox, so reruns are
deterministic regardless of the current state.
{ "action": "hover", "selector": "userMenu" }Hovers to reveal hover-only UI (dropdown menus, tooltips).
upload_file
Section titled “upload_file”{ "action": "upload_file", "selector": "avatarInput", "file": "fixtures/avatar.png" }{ "action": "upload_file", "selector": "csvInput", "file": { "name": "note.csv", "content": "a,b\n1,2", "mimeType": "text/csv" } }| Field | Type | Description |
|---|---|---|
file (path) | string | Project-relative fixture path (${VAR} substitution supported) |
file (inline) | { name, content, mimeType, encoding? } | CI-safe inline content materialized at run time, no filesystem dependency. encoding: "base64" for binary. verfix validate warns above 64KB — use a fixture path for larger files. |
Targets the <input type=file> by attachment, not visibility, since real UIs hide it behind
styled buttons.
wait_for_selector
Section titled “wait_for_selector”{ "action": "wait_for_selector", "selector": "[data-testid=dashboard]" }Waits for an element to appear before proceeding.
wait_for_url
Section titled “wait_for_url”{ "action": "wait_for_url", "value": "/dashboard" }Waits until the URL contains a substring — same semantics as the url_contains assertion. For
client-side redirects.
wait_for_network_idle
Section titled “wait_for_network_idle”{ "action": "wait_for_network_idle" }Waits for network activity to settle. For background-loaded data that doesn’t block a selector.
Assertion types
Section titled “Assertion types”Assertions are checked after all steps complete. Each has a type field.
page_loaded
Section titled “page_loaded”{ "type": "page_loaded" }Verifies the page loaded without a navigation error.
selector_visible
Section titled “selector_visible”{ "type": "selector_visible", "selector": "[data-testid=dashboard]" }| Field | Type | Required |
|---|---|---|
selector | string | Yes |
timeout | number | No |
text_visible
Section titled “text_visible”{ "type": "text_visible", "value": "Welcome back" }{ "type": "text_visible", "value": "Total Declarations", "selector": "[data-testid=stats-card]" }Verifies a text string appears on the page. Passes if any visible occurrence matches. Add the
optional selector field to scope the search to matches inside a specific element — useful when
the text is repeated elsewhere on the page (a stats-card title also appearing in a nav item, say).
| Field | Type | Required |
|---|---|---|
value | string | Yes |
selector | string | No — scopes the search |
timeout | number | No |
url_contains
Section titled “url_contains”{ "type": "url_contains", "value": "/dashboard" }Verifies the current URL contains a substring.
title_contains
Section titled “title_contains”{ "type": "title_contains", "value": "Dashboard" }Verifies the page <title> contains a substring.
no_console_errors
Section titled “no_console_errors”{ "type": "no_console_errors" }{ "type": "no_console_errors", "exclude": ["ACTIVE_SESSION_EXISTS"] }Fails if any console.error() calls were detected during the flow. exclude is an array of
regex patterns — matching console errors are ignored instead of failing the assertion, for
known/expected warnings (e.g. a third-party script notice), without silencing every error.
network_request_success
Section titled “network_request_success”{ "type": "network_request_success", "value": "/api/user" }{ "type": "network_request_success", "value": "/api/auth/login", "acceptStatuses": [200, 409] }Verifies a network request to a URL pattern returned a 2xx status. acceptStatuses replaces the
default 200–399 range entirely when set — useful for endpoints with more than one valid outcome
(e.g. 200 on login success, 409 when a session is already active) without branching the flow.
Both no_console_errors and network_request_success failures name the concrete matched request
(method, URL, status) or console error text in detail/fix_hint, so an agent can tell whether to
add an exception above or treat it as a real bug.
${VAR} environment variable interpolation
Section titled “${VAR} environment variable interpolation”Step value/url, assertion value, and baseUrl may reference ${VAR_NAME}, resolved from
process.env at run time (including anything set in .verfix/.env) — so secrets never need to be
committed in verfix.config.json. An unset variable fails the run immediately, naming the missing
variable and its field path, rather than typing the literal ${VAR_NAME} string into a form field.
{ "action": "type", "selector": "emailInput", "value": "${TEST_EMAIL}" }Where flows come from
Section titled “Where flows come from”verfix init writes verfix.config.json with flows: [] — empty. There’s no preset library and
no scaffolding step, because Verfix has no way to know your app’s routes, forms, or components
ahead of time. Flows are meant to be written by the coding agent, on demand, as part of the
edit → verify → fix loop: it reads the component it just touched, finds the real selector, and
adds (or updates) a flow — see Config-First Verification and the
Agent Integration guide for the exact decision hierarchy an agent follows.