Business context — browse guides

Business context & the rule book

Tell Vera what your product is supposed to do, so generated tests can assert an outcome instead of asserting that something is visible.

Vera can generate tests from your pages. Reading a page tells it what a user can do; it cannot tell it what the app is supposed to do. A DOM-grounded generator can only ever assert that something is visible — and "the total is visible" passes whether the total is £90 or £900.

The business context and the rule book are how you tell it the difference.

The two things you write down

Facts describe what the product is: its purpose, the capabilities it provides, the personas who use it, the objects it manages. They give generation the vocabulary of your domain, so a planned test is called "Reject an order below the minimum" instead of "Test form 3".

Rules describe what the product must do: "a loyalty order receives a 20% discount", "only an administrator may void a balance". A rule is the thing a test can be wrong about.

Nothing is in force until you approve it

Every fact and every rule lands proposed. Nothing that is merely proposed reaches generation — a single function returns approved context, and it is the only place the gate exists.

This matters more than it sounds. A proposal is a suggestion; an approval is a statement your team is making about its own product, and every test written afterwards inherits it. If a rule says 20% and your product does 10%, the tests generated from that rule will fail — which is the point.

Where a rule comes from decides how much to trust it

A rule's source is not decoration:

SourceWhat it means
manualA human typed it into Vera. Asserting it is the approval.
openapi, postmanRead from an API contract — versioned with the API.
trackerAcceptance criteria off a Jira/Linear/GitHub ticket.
documentA requirements specification — an RSD, BRD or PRD.
source-codeRead out of your implementation.
ai-inferredInferred by a model from surrounding logic.

The bottom two are read out of the thing you are testing, so they agree with it by construction. If your code shipped a 10% discount where the requirement said 20%, an inferred rule inherits that bug, and every test generated from it confirms the bug — sensitively, forever.

Only the middle four come from something written independently of the code, which is why only they can contradict a running app. That is their entire value.

Vera calls those three groups asserted (a human said so), artifact (read from something written independently) and implementation (read from the code), and it orders a review queue by them so the most trustworthy rule is the one you read first. That ordering decides nothing: a higher-ranked rule does not win an argument, is not preferred at generation time, and cannot approve itself. Generation reads approved rules unranked.

Importing rules you already have

Vera reads rules out of artifacts you have already written, deterministically:

  • API contracts — an OpenAPI document states its constraints formally.
  • Tracker tickets — Gherkin blocks, checklist items, and bullets under an "Acceptance criteria" heading.
  • Requirements documents — numbered clauses, shall/must/should sentences, Given/When/Then blocks, criteria lists, and requirements tables. Upload a .docx or .pdf, or paste the text. The upload is dispatched on the file's content type rather than on its name, so those two formats are the whole list and anything else is refused before it is opened. A scanned PDF with no text layer is refused by name too — Vera does not run OCR.

The core of each of these is deterministic. A deterministic reader cannot invent a requirement your document does not contain, and its evidence is your own sentence rather than a paraphrase — so reviewing a proposed rule is checking "is this still true?", not "is this what the document said?".

Free prose yields nothing from the deterministic pass, on purpose. A document written as flowing paragraphs with no clause numbering, no modal verbs and no lists produces no rules that way, and the import says so and names the forms it does read. Reading prose is a genuinely different act: it needs a model, so it needs you to review a guess rather than a quote, and folding it into the deterministic pass would hand you an invention while you believed you were approving a citation. So it is kept apart — a judged reading runs only when the deterministic one found nothing, it must clear a confidence floor, and what it produces is a proposed rule like everything else. Where it is switched off, an import of free prose returns nothing, exactly as before.

The document import also reports what it did not read — how many lines were prose, how many bullets sat outside a criteria heading, which sections produced nothing, and whether a page or size cap truncated the file. An import that silently covers 40% of a document and shows a green count is worse than one that refuses, because the missing 60% now looks handled. (The contract and tracker importers report only what they created; they do not yet account for what they skipped.)

Re-importing never un-approves anything. A second import of the same ticket or contract may attach new evidence to an existing rule, and may promote its recorded source when the new one is more independent than the old — but it never writes a rule's status. An approval is yours and only you withdraw it.

When two rules disagree

Vera detects contradictions between rules about the same thing — the "20% requirement, 10% implementation" case — and raises them as a review item, never a silent merge.

A contradiction is not a rule to accept or reject. It is a question about which of two rules your product actually follows, so the resolutions are exactly four: keep A, keep B, both are wrong, or this is not actually a conflict. Recording a decision does not retire the losing rule; it records what you decided. A conflict is also not a fourth rule status — an open conflict does not block approving either side, it just sits there asking.

Detection is deliberately conservative, and it runs in two passes with a strict order. First the deterministic one: two rules about the same entity whose statements differ only in a number, or only in a negation. That pass is pure text comparison and always runs. Only the pairs it could not settle are put to a judged second pass, where a model is asked whether they genuinely contradict; a pair needs to clear a high bar to be raised at all, and each conflict records which pass found it.

What the judged pass may never do is decide. It proposes a conflict exactly as the deterministic pass does, it lands unresolved, and a person picks one of the four answers. Turn the judged pass off — or run without a key for it — and you get the deterministic behaviour, unchanged.

Related, and governed by the same restraint: where Vera suspects two rules are duplicates rather than contradictions, it will never fold an independently sourced rule into a code-derived one. If the two really do say the same thing in different words, the wording that survives is the one your application did not write.

Resolving a conflict takes the same permission as approving context, not a higher one — deciding which of two rules is true is context work.

Risk

A rule or a journey can carry a risk band — p0, p1, p2 or p3, most severe first — derived from three things you assess:

InputWhat you are judging
Blast radiusHow bad is the worst case — financially, for security and privacy, for regulation, for data loss? The worst of those, not their average.
ReachHow many users, how often.
IrreversibleCan the damage be undone?

The band is computed from those, never typed directly — so two people assessing the same thing the same way get the same band, and the reasoning stays visible. An irreversible answer raises the band by one. A catastrophic failure on a path nobody walks lands at p1 rather than p0, which is the matrix saying that reach is real and not a tiebreaker.

Unassessed is not low risk. Something nobody has looked at reports as unassessed, never as p3. It is its own bucket everywhere it is counted, and a default band would be a claim nobody made.

What counts as covering a rule

Approved rules are the denominator of the coverage board, and four verdicts are possible per rule. The distinctions are the whole point:

VerdictWhat it means
CoveredAt least one test that has run and is known to be sensitive to this rule.
PartialTests exist, but none has both run and been proved sensitive — including the common case where nobody has run the negative control yet.
InsensitiveIts tests were checked and found not to notice the rule breaking.
UncoveredNo test claims the rule at all.

Two traps live in that table. insensitive is worse than uncovered, not better — an uncovered rule at least looks like work; an insensitive one looks finished. And "no verdict" is not "insensitive": a rule whose tests were never put through a negative control reads partial, because nobody has checked, which is different from having checked and found nothing.

One more honest limit. The link from a test to the rules it covers is only written by the paths that generated the test from a rule — the planner, the fleet generator, edge cases, the authoring tools. A test you wrote by hand carries no rule links, so it contributes nothing to this board until you attach them. On a real project the board is usually reporting on a subset, and it is worth knowing which.

A test still awaiting review is also excluded — an unreviewed generated test must not close a coverage gap.

A judged opinion about a test is not coverage

Where a test was generated from a stated goal, Vera can additionally ask a model two questions about the step list: does it test the goal it claims, and would its checks notice the feature breaking? That reading is stored on the test and shown beside it.

It is advisory, and it is fenced off deliberately. It never becomes the test's sensitivity verdict, never enters the coverage board above, and never feeds any health number — because it is a reading of a step list by a model that never ran it, while sensitivity is what actually happened when the app was broken on purpose. The opinion also carries a fingerprint of the steps it judged, so editing the test makes it visibly stale by itself rather than silently vouching for something else.

What history you get

The approved profile is a set, and it changes whenever a fact is approved, rejected or revised. Vera records every one of those transitions and replays the log rather than reading each fact's latest timestamp, so an edit shows up as one revision — the old wording out, the new one in — instead of an unrelated add and remove.

You can compare the profile between any two of those moments and see what was added, what was removed, and what was reworded, plus which AI-generated tests predate the change. Hand-written tests are deliberately left out of that list: their author was not reading the profile. And the list is a prompt to re-read those tests, not a verdict that they are wrong.

One boundary is stated rather than hidden: the history begins where the transition log begins. That is a fact about the record, not a warning about your data.

See also

  • AI planning — what approved rules are actually used for, and the negative control that produces a sensitivity verdict
  • vera check — reading the coverage board from CI
  • GitHub integration — reading rules out of a linked repository
  • Issues & triage — where a tracker ticket's acceptance criteria come from