Your agent cannot assert here without quoting the source.
Extraction pipelines produce knowledge graphs that assert. Acme ACQUIRED Beta, as a fact. That holds until the source only implies it, attributes it to
someone, or hedges it — and then the graph has quietly turned a rumour into a
fact, and nothing downstream can tell.
xrad stores claims: who said it, with what commitment, and the verbatim span that proves it. The contract lives in the tooling, not in the prompt.
uv tool install xrad # the CLI, isolated, on your PATH
uv pip install xrad # or as a library, in your projectgit clone https://github.com/narimannemo/xrad && cd xrad
python examples/demo.py-- 1. an agent paraphrases ---------------------------------
REFUSED | quote_not_found
The contract says 'hereby acquires'. The paraphrase is plausible,
would pass any human review, and is not in the document.
-- 2. it quotes verbatim -----------------------------------
STORED | contract:c00001 at characters [31, 64]
-- 3. it invents a relation --------------------------------
REFUSED | unknown relation 'SORT_OF_RELATED_TO' — use one of: ACQUIRED, OWNS
-- 4. it credits a source the quote does not name -----------
STORED, with a warning:
the quote does not name 'Reuters'...
A paraphrase is refused. A model quotes; xrad finds the quote in the source. Exact, case-insensitive, whitespace-insensitive — and deliberately nothing fuzzy, because a near-miss means the model paraphrased, and a paraphrase pointing at real offsets is the corruption this exists to prevent.
A relation outside the vocabulary is refused, with the valid options in the error so an agent can retry. Vocabularies are YAML; the prompt text is generated from the same file the code enforces, so the two cannot drift.
A claim with no evidence cannot be constructed. A claim you cannot check is indistinguishable from one that was invented.
$ xrad path Acme Gamma
1. Acme --ACQUIRED--> Beta the filing
2. Beta --OWNS--> Gamma (reported) Reuters
2 hops · 1 asserted · 1 reported or hedged
$ xrad path Acme Gamma --modality assertion
no chain — the connection may exist when reported claims are allowedThe argument does not survive on the filing's own authority. It is a shortest-path query by any other name, but what comes back is a chain of claims: every hop carries who committed to it, how strongly, and whether its evidence held.
$ xrad disagreements
Acme ACQUIRED Beta the filing (assertion) vs the auditor (rejected)Two sources contradicting each other is the most interesting object in a corpus. An edge store would have kept one and lost the other.
| Concepts | why claims and not edges, and what follows from it |
| MCP | wiring it to Claude Code, Cursor or your own agent |
| Vocabularies | designing the closed set of types and relations |
| Auditing | having a second model try to refute every claim |
| Reference | every command and the Python API |
| Packaging | uv, Homebrew, and which to use |
Extracted from a pipeline that put 5,646 claims from a 1652 folio into a graph where every one resolves to a rectangle on a scanned page.
The audit is the reason to trust any of it: a second model, shown only the cited span, tries to refute each claim. 23% did not survive. The largest single failure was an attribution the quote itself did not contain — the source names Strabo once and writes for three paragraphs, and the extractor quotes paragraph three while still crediting him. One prompt rule fixed it, refutation fell to 11.5%, and it replicated on a second volume (z = 5.05).
That finding ships as the warning record_claim emits when a reported claim
credits a source its own quote does not name.
Not a vector index. No embeddings, no similarity search — a claim store you traverse.
Not an extraction model. Bring your own agent; xrad decides what it is allowed to record.
Not a fact database. Sources are wrong. A source being wrong is recorded, with the modality showing how it held the statement.
Apache-2.0. Contributions welcome — CONTRIBUTING.md says what will and will not be accepted before you spend an evening.