Assert Mind reviews your pull requests and runs tests against your app. Results arrive as a single PR comment plus a check run — nothing new to log into.
About 15 minutes, three stepsYou need admin rights on the repository and the subscription credentials sent with your welcome email.
Open the installation link from your welcome email, then:
| Permission | Why |
|---|---|
| Pull requests | Read the diff, post the analysis comment, apply labels |
| Checks | Publish the assertmind check run and its annotations |
| Issues | React 👀 to acknowledge your /qa commands |
| Contents (read) | Read your test definitions and app description |
| Metadata | Required by GitHub for every App |
Assert Mind never pushes commits and never modifies your source.
Assert Mind runs inside your own CI. Your code and credentials stay on your infrastructure — the App only sends a signal saying "a PR needs QA".
Add this as .github/workflows/assertmind-qa.yml:
name: Assert Mind QA
on:
repository_dispatch:
types: [xqa-triage]
permissions:
contents: read
pull-requests: write
checks: write
concurrency:
group: assertmind-qa-${{ github.event.client_payload.prNumber }}
cancel-in-progress: true
jobs:
qa:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.client_payload.headSha }}
- uses: actions/setup-node@v4
with:
node-version: 22
- name: PR review analysis
if: contains(fromJSON('["review", "all"]'), github.event.client_payload.mode)
run: |
npx @assertmind/qa@1.0.0 triage \
--pr ${{ github.event.client_payload.prNumber }} \
--repo ${{ github.repository }} \
--commit-sha ${{ github.event.client_payload.headSha }} \
--comment \
--apply-labels
env:
XQA_LLM_MODE: ${{ secrets.XQA_LLM_MODE }}
XQA_GATEWAY_URL: ${{ secrets.XQA_GATEWAY_URL }}
XQA_SUBSCRIPTION_TOKEN: ${{ secrets.XQA_SUBSCRIPTION_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Pin the version. The @1.0.0 above stops a new release changing behaviour
mid-sprint. Bump it deliberately.
Adding test runs? The review lane above runs anywhere. Running your actual test suites needs a runner that can reach your app — a simulator, a device cloud, or a browser. See Adding test runs once the review lane is working.
Repository → Settings → Secrets and variables → Actions → New repository secret.
Most customers are on managed mode, where we handle model access:
| Secret | Value |
|---|---|
XQA_LLM_MODE |
managed |
XQA_GATEWAY_URL |
From your welcome email |
XQA_SUBSCRIPTION_TOKEN |
From your welcome email — treat as a password |
Prefer to use your own Anthropic account? Set XQA_LLM_MODE to byok and add
ANTHROPIC_API_KEY instead of the gateway pair. Usage is then billed by Anthropic directly, and you
also add ANTHROPIC_API_KEY to the workflow's env: block.
GITHUB_TOKEN needs no setup — GitHub provides it automatically.
Assert Mind reads two things from your repository to understand what it's testing. Create them by running this once at the repo root and committing the result:
npx @assertmind/qa@1.0.0 init
This creates a .xqa/ directory. Two files matter:
.xqa/config.yamlTurn on the PR reviewer — it's off by default:
agents:
triager:
enabled: true
.xqa/app.mdA plain-English description of your product: what it does, its main screens or routes, and what "working correctly" means.
This is the highest-leverage file in the setup — the quality of the review tracks the quality of this description.
A useful shape:
# Checkout
Customers build a cart, sign in, and pay. Anonymous carts must survive
sign-in by merging into the saved cart, never overwriting it.
## Screens
- **Cart** — line items, quantity steppers, promo field
- **Checkout** — address, payment, order summary
- **Confirmation** — order number, receipt email
## Rules that must always hold
- A logged-out user never sees another user's cart
- Prices shown at checkout match prices charged
Commit .xqa/ to your repository.
Once the workflow is on your default branch, Assert Mind reviews every new pull request automatically. You can also drive it from a PR comment:
| Comment | What happens |
|---|---|
/qa |
Review the PR and run tests |
/qa review |
Review only — no test run |
/qa test |
Run tests only — no review |
A 👀 reaction on your comment confirms the command was accepted; results follow in the PR comment. Only repository owners, members, and collaborators can trigger runs, so outside contributors cannot consume your quota.
Everything lands in one comment that updates in place — it never spams the thread, and re-running replaces the previous content.
Findings that point at a specific line also appear as annotations in the Files changed tab,
next to the code. The assertmind check run reports pass or fail on the PR — use it in branch
protection when you're ready to make QA blocking.
The review lane analyses your diff and needs no special infrastructure. Running real tests requires a runner that can reach your application.
First, define what to test. xqa init created .xqa/specs/ and
.xqa/suites/: specs are Markdown files describing a user journey, and suites group specs into a
named set such as smoke. Then add a test step to your workflow on a runner appropriate to your
platform:
- name: Test run
if: contains(fromJSON('["test", "all"]'), github.event.client_payload.mode)
run: |
npx @assertmind/qa@1.0.0 run \
--suite "${{ github.event.client_payload.commandArgs || 'smoke' }}"
env:
XQA_LLM_MODE: ${{ secrets.XQA_LLM_MODE }}
XQA_GATEWAY_URL: ${{ secrets.XQA_GATEWAY_URL }}
XQA_SUBSCRIPTION_TOKEN: ${{ secrets.XQA_SUBSCRIPTION_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Report test results
if: always() && contains(fromJSON('["test", "all"]'), github.event.client_payload.mode)
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
findings_path="$(cat .xqa/last-findings-path 2>/dev/null || true)"
if [[ -z "$findings_path" || ! -f "$findings_path" ]]; then
echo "::warning::no findings to report"
exit 0
fi
npx @assertmind/qa@1.0.0 report "$findings_path"
/qa test checkout passes checkout through as the suite name, so one workflow covers
every suite you define.
Platform notes: iOS needs a macOS runner with Xcode and a simulator, or a device cloud such as BrowserStack. Android needs an emulator or a device cloud. Web runs on a standard Linux runner — see below. For mobile device clouds, talk to us during onboarding.
Web needs no simulator, emulator, or device cloud — it runs on ubuntu-latest with the Chrome that
GitHub's runner image already ships. Add --device web to the test step:
- name: Test run
if: contains(fromJSON('["test", "all"]'), github.event.client_payload.mode)
run: |
npx @assertmind/qa@1.0.0 run \
--suite "${{ github.event.client_payload.commandArgs || 'smoke' }}" \
--device web
env:
XQA_LLM_MODE: ${{ secrets.XQA_LLM_MODE }}
XQA_GATEWAY_URL: ${{ secrets.XQA_GATEWAY_URL }}
XQA_SUBSCRIPTION_TOKEN: ${{ secrets.XQA_SUBSCRIPTION_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Make it the default in .xqa/config.yaml so you can drop the flag:
defaults:
device: web
agents:
explorer:
enabled: true
Specs are the same .xqa/specs/*.test.md files as every other platform — steps name what the user
does, not CSS selectors:
---
id: checkout-smoke
feature: Checkout
timeout: 180
---
## Steps
1. Open https://staging.example.com/cart → the cart lists the saved line items
2. Press "Checkout" → the address form is shown
## Assertions
- The order summary total matches the cart subtotal plus shipping
Two web-specific behaviours worth knowing:
Confirm the workflow file is on your repository's default branch —
repository_dispatch only ever runs the default-branch copy of a workflow. Then check the App is
installed on this specific repository (org → Settings → GitHub Apps → Configure).
/qa gets no reaction.The command must be the first thing in the comment (/qa review, not "please /qa review"), and you
must be an owner, member, or collaborator on the repository.
Open the run in the Actions tab. A missing XQA_SUBSCRIPTION_TOKEN or XQA_GATEWAY_URL
fails there with a config error. Also confirm agents.triager.enabled: true is set in
.xqa/config.yaml and committed.
Almost always .xqa/app.md. Add the specific rules and flows the reviewer should care about — it
reasons from that file plus the diff, and cannot infer intent you haven't written down.
Shouldn't happen — Assert Mind maintains a single comment per PR and absorbs older ones. If you see duplicates, send us the PR link.
Your source is read inside your own CI runner and is never stored by us. In managed mode, the diff and app description are sent to the model provider through our gateway for analysis; we meter usage but do not retain your code. In BYOK mode, the traffic goes directly from your runner to Anthropic and never touches our infrastructure.