Quickstart
Install agent-qa, connect a model and run your first tests in about ten minutes.
This page takes you from an empty folder to a first run. The examples test Komazon, a small shop at https://playground.vostride.dev/shop/ that exists for this purpose. One of its pages has a deliberate bug, and one of the examples catches it.
Working with a coding agent? The agent quickstart hands this whole page to it with one prompt.
Before you start
- Node.js 24 or newer.
- A model. A Claude or Codex subscription works, and so does an API key for an OpenAI-compatible, Anthropic-compatible or Gemini endpoint. Local models work through the compatible endpoints.
- Network access to
playground.vostride.devand to your model provider. - Docker, but only for tests that use hooks.
1. Install
Run these from the folder that will hold your tests. In an existing app, that is usually the repository root.
npm install -D agent-qaWith a Claude or Codex subscription, also install the sign-in plugin:
npm install -D @vostride/agent-qa-subscription-auth2. Install a browser
The examples run in Chromium. agent-qa installs its own copy, which leaves your everyday browsers alone:
npx agent-qa install-browsers --chromiumOn Linux, add --with-deps to install the system libraries Chromium needs.
3. Create the project
npx agent-qa initIn a terminal, init asks which platform you test and how agent-qa reaches a model. Every question has a default, and each one has a flag if you would rather skip it:
--llm | Model entry it writes | Default model | Signs in with |
|---|---|---|---|
claude-code (default) | claude-code | claude-sonnet-5-5 | Claude subscription |
codex | codex | gpt-6.1-sol | Codex subscription |
openai-compatible | default | gpt-5.5 | API key |
anthropic-compatible | default | claude-sonnet-4-6 | API key or bearer token |
gemini | default | gemini-3-flash-preview | API key |
--model <id> replaces the default model. --base-url <url> points the two compatible providers at another endpoint, such as a local model server. For example:
npx agent-qa init --platform web --llm openai-compatible --model my-model --base-url http://127.0.0.1:1234/v1init writes a config, three example tests, a suite that runs two of them, a hook example and the knowledge folder. Select a file to see what it contains:
- agent-qa-knowledge
- policies
- global.md
- .gitignore
- AGENTS.md
- README.md
- scripts
- examples
- make-shipping-address.mjs
- suites
- examples.suite.yaml
- tests
- examples
- komazon-cart-total-bug.yaml
- komazon-checkout.yaml
- komazon-shipping-hook.yaml
- .env.secrets.local
- .gitignore
- agent-qa.config.yaml
- agent-qa.local.yaml
- hooks.yaml
- package.json
# agent-qa Configuration# File discovery and project settingsworkspace: testMatch: - tests/**/*.yaml suiteMatch: - suites/**/*.suite.yaml hooksFile: hooks.yaml envFile: .env secretsFile: .env.secrets.local # Its values are shown as [secret:<name>] and never restored; nothing else is masked.# Optional: ignore archived or generated tests. # testPathIgnore: # - tests/archive/**/*.yaml# Infrastructure services (dashboard, cache, auth state, logging)services: dashboard: port: 3100 artifactsDir: .agent-qa/artifacts # Optional: persist dashboard state to a custom SQLite path. # dbPath: .agent-qa/dashboard.sqlite cache: dir: .agent-qa/cache ttl: 7d authState: dir: .agent-qa/auth-states vision: screenshotSize: 256k effectiveResolution: 768 # Accessibility checks run once at the end of each test; results show in the dashboard's run detail. accessibility: enabled: true standard: wcag2aa runAfter: test-end failOnViolation: false recording: enabled: true logging: level: warn# Committed knowledge root (optional block; these defaults apply when absent)# knowledge:# dir: agent-qa-knowledge# commitArtifacts: none# Named resource definitions (LLM configs, app targets)registry: # Optional: local and cloud device/provider profiles. # devices: # android-emu: # platform: android # transport: local # providers: # browserstack: # username: ${BROWSERSTACK_USERNAME} # accessKey: ${BROWSERSTACK_ACCESS_KEY} llms: - name: claude-code provider: anthropic-subscription model: claude-sonnet-5-5 targets: # Optional: add more web or mobile targets here. # my-mobile: # platform: android # appPackage: com.example.app # appActivity: .MainActivity # app: # path: apps/example.apk komazon: platform: web url: https://playground.vostride.dev/shop/ komazon-cart-total-bug: platform: web url: https://playground.vostride.dev/shop/?bug=cart-total# Optional subscription auth plugin declarationsplugins: auth: - package: "@vostride/agent-qa-subscription-auth"# Execution settings (cascades: global -> suite -> test -> CLI flags)use: browser: name: chromium headless: true viewport: width: 1280 height: 720 mobile: appState: preserve timeout: step: 20m test: 90m navigation: 2m # Per-step limits: maxActions (actions) and maxPlanningTurns (planner calls); a step that needs more stops with the limit reason planner: maxActions: 30 maxPlanningTurns: 30 previousStepCount: 5 logCapture: console: true network: true parallel: false llm: claude-code # Optional: bind mobile runs to a configured device profile. # device: android-emu# agent-qa init:start# Runtime, credential, dependency, and local-only paths managed by agent-qa init..agent-qa/.agent-qa/auth-states/tests/.agent-qa-drafts/node_modules/agent-qa.local.yaml.env.env.secrets.local.*.agent-qa-init-*.tmp# agent-qa init:end# The Komazon playground's public demo password (printed on its sign-in page); the examples type it as {{secret:KOMAZON_PASSWORD}}.KOMAZON_PASSWORD=komazon123# This file is for machine-specific device, app, and provider bindings.# Keep it out of git.# Add agent-qa.local.yaml to .gitignore.devices: # Example local device binding: # android-emu: # avd: Pixel_8_API_35apps: # Example app install binding: # example-android: # path: apps/example.apkproviders: # Example cloud provider credentials: # browserstack: # username: ${BROWSERSTACK_USERNAME} # accessKey: ${BROWSERSTACK_ACCESS_KEY}hooks: - id: h_phi-arts-vis-band-aira-streng-twig-roi-mino-fold name: Make a shipping address runtime: node file: scripts/examples/make-shipping-address.mjs timeout: 30s network: false{ "private": true, "devDependencies": { "@vostride/agent-qa-subscription-auth": "0.2.0" }}test-id: t_packer-staff-asha-lak-spill-usher-pele-allen-bid-donename: Komazon checkout journeytarget: komazonsteps: - Add Everyday Ceramic Mug to the bag, then open the shopping bag and verify it holds one Everyday Ceramic Mug. - Continue to checkout. Sign in with email alex@example.com and password {{secret:KOMAZON_PASSWORD}}, then verify the shipping step is shown. - Enter street address 24 Cedar Lane, city Portland and postal code 97205. Continue to delivery. - Select Express delivery and continue with Review order. Verify the review shows one Everyday Ceramic Mug, the entered address, Express delivery and a total of $36.00. - Place the order using Place order · $36.00. Verify Order confirmed and that the bag is empty. - Open View orders. Verify Order FW-1001 contains one Everyday Ceramic Mug and the $36.00 total.test-id: t_reduce-eva-elding-reset-lai-iban-rum-hin-parent-changname: Komazon cart total (fails on the deliberate cart-total bug)target: komazon-cart-total-bugsteps: - Navigate to https://playground.vostride.dev/shop/?bug=cart-total - Add Everyday Ceramic Mug to the bag, open the shopping bag, and increase that product's quantity to two. - Verify the bag contains exactly two Everyday Ceramic Mugs and the order total is $53.00.test-id: t_ayin-dual-chunk-lou-tablet-ticket-brace-lak-kite-abuname: Komazon checkout with a shipping address from a hooktarget: komazonsetup: - h_phi-arts-vis-band-aira-streng-twig-roi-mino-folduse: cache: falsesteps: - Add Everyday Ceramic Mug to the bag, then open the shopping bag and verify it holds one Everyday Ceramic Mug. - Continue to checkout. Sign in with email alex@example.com and password {{secret:KOMAZON_PASSWORD}}, then verify the shipping step is shown. - Type {{env:SHIPPING_ADDRESS}} into the Street address field, enter city Portland and postal code 97205, then continue to delivery. - Select Express delivery and continue with Review order. Verify the review shows {{env:SHIPPING_ADDRESS}} as the street address.suite-id: s_dice-ego-veer-warp-ionic-trying-tur-cal-stride-battlename: Komazon examplestarget: komazontests: - test: tests/examples/komazon-checkout.yaml id: t_packer-staff-asha-lak-spill-usher-pele-allen-bid-done - test: tests/examples/komazon-cart-total-bug.yaml id: t_reduce-eva-elding-reset-lai-iban-rum-hin-parent-chang// Runs before the test with no network and writes SHIPPING_ADDRESS for {{env:SHIPPING_ADDRESS}}.import { writeFile } from 'node:fs/promises'import { randomBytes } from 'node:crypto'const address = `${100 + (randomBytes(1)[0] % 900)} Cedar Lane`await writeFile('/tmp/agent-qa.env', `SHIPPING_ADDRESS="${address}"\n`, 'utf-8')# agent-qa-knowledgeThe committed knowledge root for this project's QA agent. Everything the agentlearns and everything you author for it lives under this one directory.## Two shelves- **Learned shelf** (`memory/`, created by the first record write): durable records the agent distills from runs: facts, cautions, selectors, flows. Each record is a self-sufficient bundle directory: the fact, its evidence, and its provenance travel together.- **Authored shelf** (`policies/` now; `skills/` arrives with its feature): content people write: QA rules, agent skills.Only `policies/` exists after init; the other directories are created onfirst use.## How to read a knowledge change- `record.md` is the fact: what the agent now believes, in plain prose.- `evidence/outcomes.jsonl` is the evidence: the append-only outcome log the belief rests on.- Trust is derived by code from the outcome log.## Changing knowledgeNever edit the learned shelf by hand: not the fact, the evidence, trustcounters or status fields. Learned knowledge changes only through agent-qa: arun mark, or `agent_qa_change_knowledge` to accept, reject or forget arecord. Each change applies at once and shows up here. Wrong knowledge iscorrected by new feedback (a run mark) or by forgetting the record. The oneshelf people write by hand is `policies/` (below).## Committed vs. gitignored`git diff` shows exactly what the agent learned. The `.gitignore` in thisdirectory enforces the split:- `memory/index/`: derived search index; rebuildable from records, never committed.- `memory/queue/`, `.lease`, `.epoch`: machine-local curation runtime state.- `.writer-authority.sqlite*`: the local writer-authority database and its -wal/-shm/-journal files.- `memory/*/bundles/.staging/`: a bundle being published; present only if a publish was interrupted.- `**/artifacts/`: binary blobs (screenshots, DOM snapshots, HAR). Excluded from git by default; artifact *inventories* are text, live outside `artifacts/`, and are committed.- `skills/.staging/`: machine-local scratch space for skill writes, never committed.Everything else, every `*.md` and `*.json`/`*.jsonl` of distilled orauthored text, is committed, so `git diff` and `git log` show it.## How to write policiesQA rules live in `policies/` as plain markdown: one rule per line, inimperative English. Start with `policies/global.md` (its header commentteaches the format); add `policies/<product>.md` siblings for product-scopedrules. Policies are sovereign: served to the agent verbatim on every run.## Reading knowledge`AGENTS.md` in this directory lists the agent-qa commands and MCP tools thatanswer questions about runs and records. Answer from them rather than from a raw`record.md`: trust, validity and supersession are worked out when you ask.# agent-qa knowledge: for coding agentsGenerated by agent-qa init when it creates the workspace.This folder holds what agent-qa learned from test runs and the QA policies people wrote.Read it through agent-qa's commands, never through SQLite or index files.## What's here- `memory/<product>/bundles/<id>/`: one learned record per bundle: `record.md` and its evidence.- `policies/`: QA rules people wrote; the runner applies them on every run.- `memory/index/`, `memory/queue/`, `.lease`, `.epoch` and any SQLite file are generated runtime state. Never read or edit them.Answer questions with the commands below: trust, validity and supersession are worked outwhen you ask, so a raw `record.md` can be stale or superseded. Opening a bundle to lookaround is fine.## Questions → commands| Question | CLI (add `--json` for JSON) | MCP tool ||---|---|---|| What happened in a run, and where is everything it recorded? | `npx agent-qa runs overview <runId>` | `agent_qa_get_run_overview` || Which runs exist, newest first? | `npx agent-qa runs list` | `agent_qa_list_runs` || One step's console, network, a11y, aria or variables | `npx agent-qa runs evidence <runId> --test <n> --step <n> --kind <kind>` | `agent_qa_get_run_evidence` || What knowledge did a run use and learn? | `npx agent-qa runs knowledge <runId>` | `agent_qa_get_run_knowledge` || Why did a step's brief say what it said? | `npx agent-qa knowledge brief-why <runId> --step <n>` | `agent_qa_explain_brief` || One record, with its provenance | `npx agent-qa knowledge show <recordId>` | `agent_qa_show_knowledge_record` || Which knowledge bundles exist? | `npx agent-qa knowledge bundles` | `agent_qa_list_knowledge_bundles` || Records by product, kind or scope | `npx agent-qa knowledge inspect` | `agent_qa_inspect_knowledge_records` || What changed since a build? | `npx agent-qa knowledge diff <build>` | `agent_qa_diff_knowledge_records` || How did a record pass its gates? | `npx agent-qa knowledge gates <recordId>` | `agent_qa_list_knowledge_gates` |With `--json` each verb prints what its MCP tool answers; `npx agent-qa knowledge inspect` and`npx agent-qa knowledge diff <build>` (`<build>` is a build ordinal or tag) print only the `records` and `changes` array of that answer.Test and step numbers are zero-based, as `runs overview` prints them. A screenshot:`npx agent-qa runs evidence <runId> --screenshot <sha> --out <file>`.## ProvenanceEvery record names where it came from: the runs and steps that taught it and the outcomesits trust rests on. `knowledge show` returns them; cite the record id when you rely on one.## Changing knowledgeNever edit files under agent-qa-knowledge/ yourself; knowledge changes only through agent-qa.`agent_qa_change_knowledge` accepts, rejects or forgets a record, and the change applies at once.Wrong knowledge is corrected by new feedback (a run mark) or by forgetting the record.The `agent-qa-knowledge` skill has the workflow, with the depth in its references/ folder;`npx agent-qa skills` prints where it is.Connecting Claude Code or Codex: https://vostride.com/docs/agent-qa/coding-agents<!--agent-qa policies: global scopeWrite one rule per line, in plain imperative English. Every active (non-comment)line in this file becomes a QA rule the agent follows.Example (inactive: it lives inside this comment):Always dismiss the cookie banner before asserting page contentScoping: this file is the global scope. Add sibling files named <product>.mdnext to it for product-scoped rules.Sovereignty: policies are human-asserted and win every conflict. Once policyserving lands, every active line here is served to the agent verbatim on everyrun: never summarized, never budgeted out: so keep rules few and deliberate.HTML comments are stripped at serve time: a comment-only file like this one isan empty policy. Add rules below this comment block to activate them.--># agent-qa-knowledge/.gitignorememory/index/memory/queue/memory/*/bundles/.staging/.lease.epoch.writer-authority.sqlite*# binary blobs; artifact inventories are committed and live OUTSIDE artifacts/**/artifacts/skills/.staging/# INIT-TOOL OPERATIONAL: outside the committed/derived split above. agent-qa init publishes# every file through a temporary sibling that carries the destination's own content; this# keeps that temporary out of a commit even when this tree is its own repository..*.agent-qa-init-*.tmpIf the folder is not a git repository yet, make it one and commit. Later you will read what agent-qa learned as a git diff against this commit.
git init
git add .
git commit -m "Add agent-qa"The .gitignore that init wrote keeps .agent-qa/, .env, .env.secrets.local and agent-qa.local.yaml out of the commit.
4. Connect the model
With a subscription, sign in. Name the model entry init wrote:
npx agent-qa auth login --config claude-codeIt prints a sign-in URL and opens it in your browser. For Claude, paste the code the page shows back into the terminal. Use --config codex for a Codex subscription.
With an API key, save it for the default entry:
npx agent-qa auth set --config default --type api-keyauth set asks for the key at a hidden prompt, so it never lands in your shell history. agent-qa keeps credentials in ~/.agent-qa/auth.json, outside the project. Models and credentials covers every provider.
5. Check the setup
npx agent-qa validatevalidate reads the config and every test and suite, and ends with All files valid when they are. For a wider check, npx agent-qa doctor also looks at Node.js, the browser, Docker and your credentials, and sends one short request to your model.
6. Run the examples
npx agent-qa run suites/examples.suite.yamlExpect one pass, one failure and exit code 1:
- The checkout journey signs in, buys a mug and checks the order. It passes.
- The cart-total test catches the playground's deliberate bug. Two mugs should cost $53.00, and the bag shows $29.00.
The summary ends with Tests: 1 of 2 passed. After the verdict, agent-qa spends up to a minute analyzing the run and recording what it learned. That work never changes the exit code.
7. Look around
Open the dashboard
npx agent-qa dashboard --openIt prints Dashboard running at and a URL. Each run shows its tests, steps and evidence. Accessibility results appear here too: open a test's last step and its A11y tab. Stop the dashboard with Ctrl+C.
Read what agent-qa learned
Runs write what they learn into agent-qa-knowledge/. After the first runs every record is a new file, so ask git to include new files in the diff:
git add --intent-to-add agent-qa-knowledge
git diff -- agent-qa-knowledgeCommit the records you want to keep. memidex explains what each file means.
See the action cache
agent-qa remembers the action that completed a step and replays it next time without asking the model, as long as the screen looks the same. On your default branch it only saves when asked, so the first of these two runs passes --save-cache:
npx agent-qa run tests/examples/komazon-checkout.yaml --save-cache
npx agent-qa run tests/examples/komazon-checkout.yamlSteps that check something, or that type a secret, are never cached, so in this journey only the address step can replay. The Cache: line in the summary counts hits and misses. Caching has the details.
Connect your coding agent
Give Claude Code, Codex or another MCP client the agent-qa skills and tools: Coding agents.
When something is missing
| Situation | What agent-qa prints | Fix |
|---|---|---|
| Node.js older than 24 | agent-qa needs Node.js 24 or newer. | Install Node.js 24 or newer from nodejs.org. |
| No browser | agent-qa browser support is not installed for Chromium. | npx agent-qa install-browsers --chromium |
| Subscription not signed in | Sign in to "Anthropic subscription" for the "claude-code" config | npx agent-qa auth login --config claude-code |
| Subscription plugin missing | needs the subscription-auth plugin | npm install -D @vostride/agent-qa-subscription-auth |
| No API key | Save an API key for the "default" config | npx agent-qa auth set --config default --type api-key |
| A config error | Config validation failed, then the key | Fix or remove that key, then run npx agent-qa validate. |
| A config from v0.1 | This config was written for agent-qa before v2. | Follow v0.1.x to v0.2.x. |