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

# Choosing a freshness policy

> Compute answers when documents change, when they are read, or on a schedule.

Nothing is computed continuously. An answer is computed when its policy says so and not again until the document changes. A document that sits untouched for a year costs storage only. The cost driver is changes per month, not rows, and the policy decides which changes you pay for.

## Pick by how the answer is read

| You read the answer... | Policy |
| - | - |
| In queries that filter or sort across the namespace ("every ticket over 0.85") | `on_change` |
| One document at a time, when someone opens it | `on_read` |
| As a prediction that goes stale with time, whether or not the document changes ("churn within 30 days") | `periodic` with an `interval` |
| Only in occasional analyses you start yourself | `manual`, with a backfill |

* **`on_change`** is the policy to filter or rank on, because every document's answer stays current. It is also the most expensive: every change to every document is judged. You can filter or rank on `periodic` and `manual` answers too, as they are stored, but not on `on_read` ones, which exist only once something reads them.
* **`on_read`** is the default. The answer is computed the first time a get or query includes it, then cached until the document changes. Cold namespaces with `on_read` judgments cost nothing until someone looks. The first read returns `pending`, unless it passes `wait_ms`.
* **`periodic`** recomputes every document whose answer is older than the interval. Over a large namespace that is a recurring backfill, billed as one. The namespace's stats show its projected monthly cost.
* **`manual`** answers change only when you run a backfill.

## Switching to `on_change`

The policy is a setting, so changing it creates no new version:

```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
ns.judgments.update("needs_escalation", freshness={"policy": "on_change"})
```

Switching to `on_change` needs an answer for every document. Without `confirm=True` the call changes nothing and returns a backfill estimate for the documents that have no current answer, with its cost and duration. Call it again with `confirm=True` to switch and start the backfill.

For a judgment that reads [related documents](/guides/related-documents), the estimate also carries `replay`: what it would cost a month from then on, replayed from your last 30 days of writes. Creating such a judgment with `on_change` returns the same replay estimate and creates nothing until you pass `confirm=True`. See [the replay estimate](/guides/related-documents#the-replay-estimate), and what "at least" means there.

## Chatty documents: debounce

Any policy can set `debounce_ms`. A document that changed more recently than that is not judged until it settles, and then only its newest revision is judged. For live conversations, 2,000 ms turns a burst of 50 messages into one evaluation, about two seconds after the burst ends.

```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
ns.judgments.update("needs_escalation", freshness={"policy": "on_change", "debounce_ms": 2000})
```

Rapid writes are coalesced anyway: the engine only ever sees the newest revision at the time it judges. Debounce adds a deliberate wait on top.

## Documents that never settle: the ceiling

A debounce never ends for a document that changes more often than the debounce: an account whose tickets arrive every few minutes, or a conversation that never pauses. `max_wait_ms` is the ceiling. Such a document is judged once it has been quiet for `debounce_ms`, or once `max_wait_ms` has passed since its oldest unjudged change, whichever comes first.

```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
ns.judgments.update("churn_risk", freshness={"policy": "on_change", "debounce_ms": 600000, "max_wait_ms": 3600000})
```

With a 10-minute debounce and a 1-hour ceiling, a burst of 100 writes in 5 minutes costs one evaluation, and a document written every minute for 3 hours costs 3.

* **Defaults.** For a judgment with [related documents](/guides/related-documents) and a `debounce_ms` above 0, the ceiling defaults to 12 × `debounce_ms`; with no debounce there is nothing to wait for, so there is no ceiling. For any other judgment it defaults to none, so a debounce behaves as it always has. `GET` returns the value in effect.
* **Bounds.** It must be at least `debounce_ms`. `null` means no ceiling.
* **A setting.** Like the debounce, changing it creates no version. A lower ceiling means fresher answers and more evaluations.

For a judgment with related documents, "changes" includes writes to the documents that point at the judged one, so the ceiling is what keeps a busy account's answer from lagging for ever. Its answer lags by the debounce, typically minutes, and at most by the ceiling.

## Fan-out: when a referenced document changes

A judgment can read the one document each judged document points at: its [referenced document](/guides/context-recipes#the-document-the-judged-one-points-at). A change to what the relation shows of it re-judges every judged document that points at it and is inside the **re-judge scope**. That is a fan-out, and `freshness.fanout` decides which judged documents it re-judges and when. A change to a [blocking relation](/guides/entity-matching)'s block fans out the same way, to the documents in the block. Like the rest of `freshness`, these are settings: a `PATCH` changes the keys it sends, merges `scope` per key, and creates no version.

```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
ns.judgments.update("needs_review", freshness={"fanout": {"scope": {"created_within": "30d", "where": {"attributes.status": "open"}}}})
```

| Setting | Default | What it does |
| - | - | - |
| `scope.created_within` | `"30d"` | Re-judge only judged documents created no more than this long before the change. `null` means all of them |
| `scope.where` | none | Narrow the scope to judged documents whose attributes match, in the shape of `applies_to`. `null` removes it |
| `debounce_ms` | 600,000 (10 minutes) | Fan out once the referenced document has had no change for this long, so a burst of changes costs one fan-out |
| `max_wait_ms` | 6 × `debounce_ms` (1 hour) | Fan out a referenced document that keeps changing once this long has passed since its oldest change not yet fanned out. At least `debounce_ms`; `null` means no ceiling |
| `rolling_limit` | 300,000 | The most judged documents this judgment's fan-outs re-judge in any 30 days, confirmed when you create the judgment. A fan-out past it is deferred until there is room. `null` removes the limit: every change then re-judges every in-scope document, with no deferral, and only the namespace budget caps the spend |
| `block_cap` | 1,000 | For a [blocking relation](/guides/entity-matching#cost-and-limits): the most judged documents one key value's block may hold. A block past it is deferred like a fan-out past the rolling limit |

* **The scope counts from the change,** not from now, so one change's scope stays fixed however long its fan-out waits. A judged document outside it is not re-judged: its answer reads `stale`, with `stale_reason` `referenced_changed`, so `answers: "fresh_only"` leaves it out. Its watermark says which version of the referenced document it read, and its next own write re-judges it.
* **Only a change to what the relation shows counts.** An edit to a field it leaves out, or a move inside a [band](/guides/context-recipes#bands), neither fans out nor starts the debounce.
* **The fan-out debounce is its own.** The judgment's `debounce_ms` and `max_wait_ms` still govern the judged documents' own writes, so a new judged document is judged within seconds.
* **Only `on_change` fans out.** Under the other policies a change makes the in-scope answers `stale`.
* **The namespace** sets `share`, the most of its in-flight engine requests fan-out may use (half by default). See [namespaces](/concepts/namespaces#settings).

In-scope answers are `pending` from the change until they are re-judged. While a fan-out is deferred past the rolling limit they read `stale`, with `stale_reason` `limit_reached`, and the events feed records `judgment.limit_reached`. A deferred fan-out runs whole, never in part, once the window has room for all of it or a `PATCH` raises `rolling_limit`, with no confirm; one larger than the whole limit stays deferred until you raise the limit above it or set it to `null`. No event marks the catch-up: each answer reads `stale` (`limit_reached`) until it is re-judged, then `fresh`. [Judge a document with the document it points at](/guides/referenced-document#the-controls-in-order-of-effect) works through what each setting saves.

## Budgets

A namespace budget caps judgment compute per [billing period](/pricing#billing-periods). Each request to the engine is priced before it is sent, and when the next one would not fit what is left, evaluation pauses rather than overspending, and your organization's owners and admins get an email. Changed documents read `stale`, and documents never judged read `unavailable`. A large write is judged up to the last document that fits, and the rest waits until you raise the budget or the next billing period starts. Writes continue, unless the budget says `"on_exceeded": "reject"`. Every backfill is checked against the remaining budget before it starts. See [namespaces](/concepts/namespaces#settings).


## Related topics

- [Freshness](/concepts/freshness.md)
- [Judgments](/concepts/judgments.md)
- [Simulate a policy change](/guides/policy-simulation.md)
- [Templates](/guides/templates.md)
- [Starter judgments](/guides/starter-judgments.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.