QA that reads the database back
A green screen is not a passing feature. Real QA drives the UI, then reads the persisted state back at multiplicity 0/1/N and recomputes the logic against the spec — checking the thing, not the picture of the thing.
- AI agents
- Quality
- Testing
- Verification
Most UI tests confirm that a button exists and a toast appeared. That tells you the screen rendered — not that the feature worked. You actually want to know that the write persisted, that the number on the page is the number the spec says it should be, and that it still holds at zero, one, and many records. qa-e2e-pilot is a QA agent built to check exactly that.
A UI can lie
The screen is the last place to look for the truth, because it has every incentive to look fine:
| What the UI shows | What might actually be true |
|---|---|
| A success toast | The write never reached the database |
A total of 1,240 | The client summed it; the backend disagrees |
| An updated row | A stale cache; a refresh reverts it |
| An empty list | A filter bug, not an empty table |
A test that only reads the screen inherits every one of those lies. The fix is to treat the UI as an input device, then verify against the system of record.
The loop
drive → bake(0, 1, N) → recompute vs spec → report (evidence, not vibes)- Drive the feature through the real browser, the way a user would.
- Bake — read the persisted state back from the backend at multiplicity 0, 1, and N: empty, a single record, and many. Those three cases are where off-by-one, aggregation, and pagination bugs hide.
- Recompute the feature’s numbers and business rules independently, with the spec as the oracle, and compare them to what the UI claimed.
- Probe the backend directly when the UI and the data disagree, so a failure says which layer lied.
Evidence over assertions
Every finding ships with the persisted value, the independently recomputed value, and the spec rule it violates. That turns a QA report from “looks broken, here’s a screenshot” into something a developer can act on without re-deriving the bug. And because a thorough pass outlives a single context window, it checkpoints a resumable trail so a long run survives interruption.
What it costs
This is heavier than asserting on the DOM. You need a way to read the backend state, and you need the spec to be explicit enough to act as an oracle — vague acceptance criteria make a poor judge. The upside is that the bugs it catches are the ones that reach production precisely because the screen looked fine.
It’s the same discipline I bring to debugging by hand: don’t claim “fixed” or “passing” until you’ve read the state back and seen it yourself. The agent just does it at multiplicity 0/1/N, every time, without getting bored.