Getting started

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 steps

You need admin rights on the repository and the subscription credentials sent with your welcome email.

  1. Install the GitHub App — 2 minutes
  2. Add the workflow and secrets — 5 minutes
  3. Describe your app — 10 minutes

1 Install the GitHub App

Open the installation link from your welcome email, then:

  1. Choose the organisation that owns the repository.
  2. Select Only select repositories and pick the repositories you want QA on.
  3. Click Install.

Permissions the App requests

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.

2 Add the workflow

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".

2a. Create the workflow file

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.

2b. Add your secrets

Repository → SettingsSecrets and variablesActionsNew 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.

3 Describe your app

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.yaml

Turn on the PR reviewer — it's off by default:

agents:
  triager:
    enabled: true

.xqa/app.md

A 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.

Using it

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.

Reading the results

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.

Adding test runs

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

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:

Troubleshooting

Nothing happens when I open a PR.

Confirm the workflow file is on your repository's default branchrepository_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.

The workflow runs but posts nothing.

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.

The review is vague or misses obvious things.

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.

Two comments appeared.

Shouldn't happen — Assert Mind maintains a single comment per PR and absorbs older ones. If you see duplicates, send us the PR link.

Privacy

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.