> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vainona.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Judge what changed since the last verdict

> Show the engine a document as the judgment last saw it, beside the current version, to moderate edits and watch records for material change.

export const productName = "Vainona";

Some questions are about a change, not a document: "did this listing change materially after we approved it?", "did the renewal clause change between these two versions of the contract?", "is this edit trying to get past the moderation the original passed?". The usual answer is a pipeline: a diff job, an edit lock during review, a re-review queue. In {productName} it is a judgment whose context recipe has `previous`: the engine sees the document as the judgment's last successful evaluation saw it, beside the version it is judging now. It is the one [relation](/concepts/relations) that reads no other document.

## Moderate edits to approved listings

A marketplace approves a listing, and the seller edits it afterwards. The moderator writes `attributes.approved_at` when they approve, which they do anyway. This judgment asks whether an edit changed the listing materially since it was approved:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "name": "material_edit",
  "type": "bool",
  "applies_to": {"attributes.kind": "listing"},
  "question": "Compared with the previous version, does this edit change what is being sold, its condition or its price enough that a moderator should review it again?",
  "criteria": "Typo fixes, reworded descriptions and small price drops are not material. A different item, a new condition, contact details or payment instructions are.",
  "context": {
    "fields": ["state.title", "state.price", "state.description"],
    "previous": {
      "fields": ["state.title", "state.price", "state.description"],
      "anchor": "attributes.approved_at"
    }
  },
  "engine": {"name": "jev", "version": "current"},
  "freshness": {"policy": "on_change"},
  "thresholds": {"review": 0.5}
}
```

The engine sees both versions, each path once as it is now and once as it was:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "state.title": "Road bike, 54cm - message me on WhatsApp",
  "state.price": 480,
  "state.description": "Barely used. Pay by bank transfer before viewing.",
  "previous.state.title": "Road bike, 54cm",
  "previous.state.price": 520,
  "previous.state.description": "Barely used, collection only."
}
```

Query on the threshold to build the re-review queue, with no queue of your own: `["answers.material_edit.thresholds.review", "Eq", true]`. A [subscription](/guides/webhooks) on the same filter tells you as each edit lands.

## What "previous" is

`previous.fields` names the document's own paths, `state.*` or `attributes.*`, rendered exactly as `fields` renders them, `last_n` and `window` included. Each renders as a `previous.<path>` entry after the `fields` entries. It never reads another document: for those, see [related documents](/guides/related-documents).

It is not simply the revision before the write. The worker judges only the newest revision of a document, so if two edits land close together, or inside the judgment's `debounce_ms`, nobody judged the one in between. Comparing with it would hide the first edit. Instead, with each successful evaluation, {productName} stores two renderings of the document beside the answer: the one the verdict compared against, and the one of the revision it judged. When a new revision is judged:

| This revision's rendering | What the engine sees under `previous` | After a success |
| - | - | - |
| The document's first evaluation, or a re-created id | nothing | this rendering is stored as the current |
| Equal to the stored current | the stored previous: the context the last verdict saw | nothing changes |
| Different from the stored current | the stored current: what the last verdict judged | the stored current becomes the previous, and this rendering the current |

So two quick edits are compared with the last verdict, not with each other, and a failed evaluation changes nothing: its retry compares with what the last success saw.

## A re-upsert costs nothing

Many systems re-send whole records every night, whether they changed or not. A re-upsert with the same values renders equal to the stored current, so the engine would see exactly the context the last verdict saw. The [context hash](/concepts/evaluations) matches, the answer is kept for the new revision, and nothing is billed. The next real edit pays once, as any change does. An edit to a path neither `fields` nor `previous.fields` names changes nothing the engine sees either, so it costs nothing too.

## Since approved: the anchor

Without an anchor, the previous moves on with every verdict. An edit the moderator rejected becomes the previous for the next edit, and a seller can walk a listing a small step at a time, each edit only slightly different from the one before, far from what was approved.

With `anchor`, an attribute such as `attributes.approved_at`, the stored previous moves on only when that attribute's value changes, and then to the revision that carries the new value: the one approved. Every later edit is compared with it, however many rejected edits come between, until the next approval. Before the anchor is first set, the first revision the judgment judged stands in for it.

| Revision | Write | Compared with |
| - | - | - |
| 1 | listing created | nothing (`first_revision: true`) |
| 2 | moderator approves: `approved_at` set | revision 2, the approved one |
| 3 | seller edits the title; the moderator rejects it | revision 2 |
| 4 | seller edits again | revision 2 |
| 5 | moderator approves: `approved_at` changes | revision 5 |
| 6 | seller edits | revision 5 |

The anchor costs one attribute value beside the stored renderings. The approval write itself is judged once, and its answer compares the approved revision with itself.

## Where it shows

* **The answer** carries `previous_revision`: the revision its previous rendering came from, absent when it showed nothing. A `failed` answer keeps the last success's, with its numbers.
* **The evaluation** records `previous_revision`, or `first_revision: true` when the document had no stored rendering in its incarnation, and its stored context holds both renderings. Fetch it with `include=history,context` on a get, or open the document in the dashboard.
* **A re-created id** (deleted, then written again) starts a new incarnation, and the old one's renderings count as none: its first evaluation shows nothing.

## Cost and limits

`previous` roughly doubles the context of the paths it names, and context size is the cost: see [size classes](/guides/context-recipes#context-size-is-the-cost). Name only the paths a change of which matters, and prefer short ones: a title and a price, not a whole description history. Over `max_tokens`, the compiler cuts the `previous.<path>` entries, the last first, before it cuts any field, so the current version is always shown whole first.

The renderings are stored as part of the namespace's data, at most two contexts' worth per document for each judgment with `previous`.

## Other uses

* **Contracts.** A clause-level question over `state.clauses.renewal` and `state.clauses.termination`: "did the renewal terms change against the customer's interest?", anchored on `attributes.signed_at`.
* **Change monitoring.** Supplier records, product catalogues or policy pages re-imported nightly: only real changes are judged, and each is judged against the last verdict.
* **Profile edits.** "Does this profile edit look like an account takeover?" over the email, payout details and display name, without an anchor, so each edit is compared with the last one judged.


## Related topics

- [Relations](/concepts/relations.md)
- [Writing a context recipe](/guides/context-recipes.md)
- [Re-send every failed or skipped delivery since a time](/api-reference/webhooks/re-send-every-failed-or-skipped-delivery-since-a-time.md)
- [Freshness](/concepts/freshness.md)
- [Export evaluation history](/api-reference/documents/export-evaluation-history.md)
