Skip to main content
An agent fails less for lack of memory than for stating what it did not consult: a price nobody quoted, a deadline three days off, a promise with no action behind it, a citation of an article that says something else. The claim contract declares what an agent may state and the evidence each kind of claim needs in the turn record. The check runs without a model, in the agent’s process, at the last point before the customer or before a document is saved; Niadra only receives the verdicts and adds them up. The contract rewrites nothing by default, never rewrites an immutable output, and comes with a versioned list of phrases of the trade that must never trigger it.

Turning it on

The contract is the space’s claims feature, off by default and turned on in the features document by the security role. The contract itself lives in the claim-contract document, of the integration and security roles, and reaches the SDK through the SDK profile, without the phrases of the negative corpus (only its version), which only your CI’s niadra contract test reads, from your own copy. The claims the SDK checks enter the turn record, so the turns feature is what carries them to Niadra and to context use. Without a contract, nothing is checked.

The document

Detection is lexical: numbers of certain classes (with terms, a number counts only in a sentence that holds one of the terms), roles (the words that give a number its role: “was” and “before” for the list price, “now” and “with discount” for the sale price), terms of the trade on their own (“I reserved”, “I sent the link”), named patterns (article_citation, precedent_citation) or document_sections, in which every sentence is a claim of fact. Evidence is exactly one of: value (a value with provenance in the turn, from a tool’s result or a state read, with same_role, fresh_for: claim to require the type’s claim age, and must_state_gaps to require the value’s declared gaps to be said with it), tool (a call of this tool in the turn: the boundary of what the agent may know, and an agent without the tool always fails), tool_any (a call of any of these tools: a promise of an action needs the action) or anchor (the text cites a source through an anchor that matches it, at 0.90 or more). An example category, from a retail contract:

The numbers

The parser reads every number of an output, in one language, and each number lands in at most one mention: first the labels, by shape (a case number, a postal code, a phone, a time of day) and by the word before (order, protocol, article, size, page); then the dates, day first in Portuguese and Spanish, month first in English; then the amounts, by what comes before or after (a currency, %, business days, mg, kg, 10x); then codes and ordinals, which are labels; and the rest (five digits or more and a year are labels, two decimals are money without a currency, an integer before a word is a count). A number in words counts only before a unit (“fifteen business days”), because “one” is also an article. Two amounts joined by “to”, “until” or a hyphen make a range. The classes are money, percent, date, duration, quantity, count, dosage and label; a label names instead of measuring, no category detects it and nothing ever rewrites it. When a number has both . and ,, the last one is the decimal mark; “R$ 511.06” in Portuguese is 511.06 all the same. A role counts when its term stands within 6 words of the number, in the same sentence; the nearest term gives the role, and two tied roles leave the number ambiguous, which is never approved.

The three natures

The validator that drops every amount absent from the history is one category of class money with model: block.

Verdicts and actions

matched, quoted_found and anchored take no action; not_checked is counted. Every other verdict takes the category’s action for the output’s context (actions.contexts, or actions.default), and unsupported takes natures.model when set: A rewrite is unequivocal only when all hold: the output is mutable; the class is money, percent, date or duration (a dose, a technical quantity, a size, a position and an identifier are never rewritten, in any context); the number is a single value, not a range; the verdict is stale and the number is a literal copy of one field of one object, whose fresh value in the turn is different; and no other number of its class is in the sentence. A contract may not name rewrite_if_unequivocal as its default, nor for a context it does not list as mutable. The principle: correct, annotate or count; never block with a generic message, and never rewrite what is immutable. Stating the gap is an acceptable answer.

The negative corpus

Every contract with categories comes with the versioned list of phrases of the trade that a naive lexicon would catch and that must never trigger (“we are not sure about the deadline”, “the deadline to contest is 15 business days”, “Is your postal code 01310-100?”). A phrase triggers when, read in any of the contract’s languages and for any of its agents, a category finds a claim in it. The SDKs’ niadra contract test command fails when one does, and it is made for your CI: a false positive that blocks a document with a deadline running is the contract’s main risk, and the test is part of it.
The command reads your copy of the contract (or the profile’s, with the corpus in --corpus) and files of example turns, and exits with 0 when everything holds, 1 when it does not, 2 when it could not run.

Internal text

Your company may register fingerprints of its own prompt: hashes of every n consecutive words (8 by default), computed by the SDK, never the prompt. An output that repeats one gives way to redact: the passage becomes a claim of the reserved category internal_text, with the verdict internal_text_found and the action block (in an immutable output, the whole output goes to a person), and the turn is flagged guard_acted. The record never holds the redacted text.

The text anchor

A tool or the agent emits anchors: where in the output, the quoted text and the document it cites. Both texts are normalized the same way (lower case, accents removed, every run of anything but letters and digits made one space), and the edit distance between the quote and some passage of the document gives the score 1 - d / len(quote). An anchor holds at 0.90 or above; below it is discarded and counted, never repaired. A number inside an anchored passage is still checked as a number: the score alone would let a changed amount through. coverage keeps law and fact apart, and they never add up.

Where it runs

In the SDK, at the stream bridge, before the customer (a candidate is held at most 150 ms and a message at most 300 ms; past that, the text goes as it is, annotated guard_budget_exceeded), or before a document is saved. The server never runs the check in a turn.
Each claim becomes one entry of claims in the turn record: the category, a number’s class, nature and role, the span in code points, the normalized value, the evidence (the call and field, the object and field, or the document and score), the verdict and the action taken (none, block, warn, count, rewrite or discard_anchor). A value that is not safe to claim can be read again by your resolver, inside your company and within 300 ms, before the answer leaves: conversation.verify_claim() in Python, convo.verifyClaim() in TypeScript. See The resolver worker.

Reading the numbers

GET /v1/context-use, with the claims feature on, brings in claims, per source and agent, the claims of its outputs by category and verdict, and how many stand on evidence (evidenced). It is the rate of what the agent states without consulting. A contract with categories, a negative corpus your CI maintains and that rate in the Console are what your company measures; Niadra adds up, and never runs one more model per turn.

Next steps

Object types and state

each field’s claim age and the prohibitions.

Turn records

where the verdicts live.

Retail agents

a contract of price, availability and action promises.

Legal agents

deadlines with declared gaps and anchored citations.