Autonomous adversarial QA agent

Your tests check what you remembered.
Invariant attacks what you forgot.

Point it at a running web app. It explores the interface, infers the business rules that must always hold, then relentlessly tries to break each one — and writes the regression test the moment it succeeds.

invariant · run #A-1042 · demo-shop.invariant.devrunning
Agent feed
Browser · /cart
INV-02 · qty ≥ 1
Aero75 Keyboard
$179.99
-$359.98
Subtotal-$320.00
F-01 · Critical · contradiction recorded
What it does

Not a scanner. A QA engineer that argues with your product.

Invariant does black-box business-logic testing — no source access, no network exploits. It reasons about what your app claimsto be true, then proves it isn't.

Explores like a user

Drives a real Playwright browser — reading the DOM, taking screenshots, and mapping every form and route the way a person would.

Infers the invariants

Claude turns the interface into explicit rules that must always hold — “a coupon applies once”, “quantity ≥ 1”, “no PAID order without payment”.

Attacks until something breaks

It generates adversarial sequences, executes them, and watches for contradictions — then captures the repro and writes a regression test.

How it works

One URL in. Reproducible findings out.

01

Explore

Launch a headless browser, capture DOM + screenshots, map the surface.

02

Hypothesize

Claude states the business rules the app must never violate.

03

Adversarial

Execute edge-case interactions designed to break each invariant.

04

Report

On a contradiction: capture evidence and emit a Playwright regression test.

The output

Every finding ships with the test that locks it shut.

No vague “looks suspicious”. You get the violated invariant, expected vs actual, a step-by-step repro, and a runnable spec.

CriticalF-01INV-02
Negative quantity manipulates order total
EXPECTED
Field rejects values < 1.
ACTUAL
-2 accepted → subtotal -$320.00.
tests/invariant-02.spec.ts
import { test, expect } from '@playwright/test';

// INV-02 — quantity must be an integer >= 1
test('cart rejects negative quantity', async ({ page }) => {
  await page.goto('/cart');
  const qty = page.getByTestId('qty-input');
  await qty.fill('-2');
  await expect(qty).toHaveValue('1');
});

Watch it find a bug in 38 seconds.

The live demo runs against a store with three planted invariant violations. It finds all three, every time.