settles judgment from match a payment to its invoice.
Inspect an unexpected answer
fresh says the answer agrees with each document it read, at their current revisions. It does not say that the engine read the invoice you expected. When a pick surprises you, open its evaluation with GET /namespaces/{ns}/evaluations/{id}:
related_documentslists each invoice the engine read, with therelationand therevision. An invoice not in it was not a candidate then.context_truncatedistruewhen the context passedmax_tokensand Brussle cut part of it. Brussle drops a relation’s records before the payment’s own fields, so a cut can remove candidates. See what is cut first.relations_cappedistruewhen a relation stopped before it considered every matching document, so a candidate can be missing.
To see the exact text the engine read, get the payment with
include=history,context (include_history and include_context in Python, includeHistory and includeContext in TypeScript), or open the payment in the dashboard. Inspect one evaluation before you connect the queue to your workflow. See a fresh answer can have incomplete inputs.
Count the misses by cause
One evaluation shows one payment. Across all your outcomes, the calibration report’smatch_misses counts the outcomes that named an invoice the evaluation did not read, by cause:
The oldest open invoice
The relation reads the newest candidates first, so in a large block the oldest open invoices go unread. In receivables, the invoice a payment settles is often the oldest. Make the key finer first: add the currency, the amount band or the month the invoice is due, so that each block holds only the invoices a payment could settle. When you import history, or Brussle judges a payment long after it arrived, the newest invoices in the block can be ones issued after the payment. Add"relative": "before" and remove window. Each payment then reads the last_n invoices of its block created immediately before it, by created_at, whatever came later, and a new invoice judges again only the payments whose last_n it is in. See records in sequence.
If match_misses.beyond_last_n grows, the key is too coarse, or the payments do not read the invoices they settle.
Freshness
A write to a candidate makes each payment in its blockpending from the moment Brussle acknowledges it. Once the block has been quiet for fanout.debounce_ms (10 minutes by default), or fanout.max_wait_ms (1 hour) has passed, Brussle reads the block once and compares it with what it showed before:
- Nothing the payments read changed, such as an edit to a field the relation does not show: the payments are
freshagain, with no engine call. - Something changed: a new open invoice, a settled one, or one replaced by a different invoice with the same fields. Brussle judges again each payment in scope whose own candidates changed. A payment whose candidates did not change keeps its answer.
Cost and limits
Size class. A matcher’s question includes each candidate with its label fields, so it counts toward the size class with the context. Ten candidates with three short labels each come to about a thousand tokens: a standard judgment, which counts as 1. The same payment as pair documents is ten yes/no judgments. Long labels or more candidates can make the question large, which counts as 4, so label with the fields that tell candidates apart, not every field you have. A month of re-judges is the block changes in a month multiplied by the payments in scope in each block. The create response’s replay estimate counts the writes to the payments and each invoice created in the last 30 days, because a new invoice changes the candidates of every payment in its block. It cannot see invoices edited or deleted in that time, so it says"excludes": ["candidate_edits", "candidate_deletes"] and shows its figures as “at least”. In receivables a settled invoice is an edit, so each invoice changes its block at least twice: when you create it and when it is settled. Expect about twice the estimate.
The limits sit in freshness.fanout, and a PATCH changes them:
block_capis the most payments in one block, 1,000 by default. Brussle refuses a create when your data already has a larger block, naming the key value indetails.key, so you find a coarse key before its first fan-out. A block that grows past it later waits: its payments readstale(limit_reached), and the events feed recordsjudgment.limit_reachedwithlimitblock_capand the key value inkey. A higher cap starts it again.rolling_limitis the most re-judges the block changes can cause in any 30 days, 300,000 by default. Past it, the rest wait asstale(limit_reached) until the window has room. When the window is short, the busiest blocks wait first, so a few busy counterparties cannot make every other counterparty’s payments stale. See the rolling limit.