Most AI products treat a citation as a formatting concern. The model writes "Article 30 GDPR," the renderer makes it a footnote, and everyone moves on. That works right up until someone with authority — an auditor, a regulator, opposing counsel, the insurer underwriting your cyber policy — reads the citation as a fact and acts on it. At that moment the question stops being "is it formatted correctly" and becomes "does this provision exist, does it say what the sentence claims, and can I check that myself." A string cannot answer those questions. A contract can.
This is the engineering post behind every marketing claim on this site. When we say a gap analysis is "cited" or a DPIA carries "article-level citations," we mean something specific and mechanical: a citation is a provision URI that resolves against a live corpus, a text that compares true against the claim it supports, and a refusal when either step fails. Not a string. A contract with three obligations.
A citation is a URI, not a link#
Start with the data model, because the whole pipeline follows from it.
A document link points at a thing you can open — a PDF, a consolidated-text page, a Eur-Lex URL. It tells you where the law lives. It does not tell you which provision governs your question, and it gives a downstream reader no way to verify that the paragraph you relied on is the paragraph you cited.
A provision URI points at a specific addressable unit of law: a jurisdiction, an instrument, and a provision number that together resolve to one piece of text. "GDPR Article 30" is not a link to the regulation — it is an address the gateway can dereference to the exact records-of-processing obligation, return as structured text, and re-dereference next quarter to confirm it has not moved. The same shape works across every corpus despite wildly different numbering conventions: Sweden's kap. §, Germany's § Abs., France's article numbering, the EU's Article(paragraph). The surface differs; the contract is identical — every provision is addressable, every result carries its own citation metadata.
For uploaded documents the address goes one level finer. A customer's policy or contract is segmented to the paragraph level and addressed by a segment URI of the form doc://{uuid}/segment/paragraph/{ref}. A citation into that document does not point at the file — it points at the paragraph. That distinction is the entire basis for the tamper-evidence guarantee, and we will come back to it.
The reason this matters is that you cannot validate a link. You can only open it. You can validate a URI: dereference it and check what comes back. Every property we care about — does it exist, does it match, did it change — depends on the citation being an address, not a pointer to a blob.
The pipeline: cite, resolve, compare#
Three steps, run on every regulatory claim before it lands in a deliverable. None of them is "ask the model to be careful."
Cite. The customer's agent — Claude, Copilot in VS Code or Studio, Cursor, any MCP client — produces a structured citation, not a sentence with a citation embedded in it. Jurisdiction, law, article. The agent is asserting "this claim rests on this provision," and it has to name the provision in a form the gateway can act on. A free-text "(see Article 30)" buried in prose is not a citation in this model; it is narration.
Resolve. The gateway dereferences the citation against the live corpus. get_provision(jurisdiction, law, article) returns the exact current text of that provision with its citation metadata. validate_citation does the same and additionally reports whether the provision has been amended since it was last cited. This is a deterministic lookup against a segmented corpus, not a retrieval over an embedding index — the same input returns the same provision every time, or a clean "no such provision" when the address does not resolve. A fabricated article number dies here. There is nothing to return, and the gateway says so rather than handing back the nearest neighbour and letting the model narrate around it.
Compare. A provision that exists is necessary but not sufficient. A model can cite a real article for a proposition the article does not support — "Article 30 GDPR" is real, but if the surrounding sentence claims it sets a breach-notification deadline, the citation is wrong even though it resolves. So the resolved text is compared against the claim it is attached to. The provision that comes back has to actually support the sentence in front of it. A real article cited for the wrong obligation fails at compare.
A citation has to clear all three to ship. The first two are mechanical and the gateway owns them. The third is where the agent and the workflow's grounding rules do their job, and it is the one RAG pipelines have no equivalent for — they generate the citation string and the prose in the same token stream, with no checkpoint that separates "the model retrieved this" from "the model recalled this."
agent emits: cite(jurisdiction="EU", law="GDPR", article="30")
gateway: resolve -> get_provision / validate_citation
-> provision text + metadata, or "no such provision"
workflow: compare -> resolved text supports the claim? yes / no
result: ship the citation | fail closed -> unresolved
Three independent ways for a citation to be wrong, three independent checks. The article number may not exist (caught at resolve). The obligation may live in a different instrument (the cited instrument resolves but the text does not support the claim — caught at compare). A figure may be recalled rather than retrieved (the provision carries no such figure — caught at compare). None of those errors leaves a trace in a RAG output, because the citation looks identical whether it is grounded or fabricated. Here each one has a step that catches it.
Refusal is a feature, not a failure mode#
The step most product teams quietly skip is the one where the pipeline returns nothing.
When a claim cannot be grounded — the address does not resolve, or the resolved text does not support the claim after the workflow's enrichment passes have run — the correct output is not a confident guess. It is a refusal. On our platform this is a hard rule: no silent fallbacks. If a corpus tool is unavailable, the answer is a data-source-unavailable error, never a fluent paragraph reconstructed from training memory. If a requirement produces no validated provision, it is marked regulatory_basis_unresolved rather than fitted with a plausible-looking article number.
This makes the demo look weaker and the product stronger. A language model handed an underspecified context will produce something, because that is what the architecture does — and the fluency that makes the guess read well is exactly what makes it dangerous. A compliance officer reading a clean paragraph cannot see that the grounding failed and the model improvised. So we inverted the default. A wrong citation in a compliance deliverable does not merely waste time; it manufactures a false record, and false records are precisely what audit regimes exist to catch. We would rather force a human to look at an honest gap than hand over a sentence nobody knows to question. Availability is not the goal. Correctness is.
The grounding check is enforced at the gateway, not left to the model's good intentions. There is a minimum-grounding ratio below which a synthesised answer is refused outright — the answer has to be sufficiently anchored in tool results, or it does not ship. Fail-closed, by design.
Why paragraph-level beats whole-document for tamper-evidence#
Now the part that makes the difference concrete.
Citing a whole document is citing a moving target. A document can be edited, re-paginated, have a clause inserted three pages above the one you relied on, or be silently replaced with a newer version at the same URL. A whole-document citation has no anchor inside the text. It still "points" at the document, and it gives a reader no signal that the content behind the claim shifted. You are trusting that the file is the file you cited, with no way to check.
Paragraph-level addressing removes the trust. Because a citation points at a specific paragraph by URI, the gateway can hash that paragraph's content. At report time — when a workflow assembles the final deliverable — generate_report re-resolves every citation and runs a paragraph-hash drift check: it re-fetches each cited provision or document segment and compares the current hash against the hash captured when the citation was made. If the underlying text moved or changed, the report says so. The citation does not silently carry a claim that was true last quarter and is stale today; it carries a drift flag the auditor can see.
That is the property whole-document citation cannot offer. There is nothing stable to re-hash in a blob you only have a link to. The granularity is the guarantee: a citation you can pin to a paragraph is a citation you can prove was either unchanged or changed, and "I can prove it was unchanged" is the entire content of tamper-evidence. The same mechanism runs over law corpora (the published provision changed because the legislature amended it) and over a customer's uploaded documents (the paragraph changed because someone edited the file). One contract, two corpora.
This is why the workflow standard requires uploaded-document citations to use segment URIs rather than whole-document references — a whole-doc citation loses the tamper-evidence guarantee by construction. The fine-grained address is not pedantry. It is the thing that lets a citation survive a year in a file and still be checkable.
How the obligations line up with the law#
The contract is not an abstraction we imposed on the law. The major EU regimes are written in a way that presupposes you can produce the provision behind a claim on demand, which is exactly what cite-resolve-compare delivers.
The GDPR's accountability principle requires the controller to be able to demonstrate compliance — Article 5(2) makes "demonstrate" an operative verb, not an aspiration. Its records-of-processing obligation under Article 30 presupposes the same: you maintain a record you can produce. A pipeline that cannot tell you whether its own citation is real is structurally unable to support "demonstrate" — it can produce prose that describes compliance but not a citation that proves the obligation it claims to meet. DORA's ICT third-party risk regime carries this further: the contractual requirements in Article 28 assume a register of information you can put in front of a supervisor, with each obligation traceable to its source. NIS2 frames its security duties as risk-management measures an entity must be able to evidence. Across all of them, the regulator's recurring demand is the same — show me the basis. A citation that resolves and compares true is the basis, produced in a form a third party can re-check. A formatted string is not.
What you get when the contract holds#
The payoff is reproducibility, and it compounds.
Because every citation resolves to a stable address, you — or your auditor, or the regulator — can run the same lookup a year after the report shipped and get the same provision back, or a clean signal that it changed. Because the validation is deterministic, the same input produces the same result on every run; there is no embedding drift, no re-retrieval that returns a different chunk this quarter, no citation string regenerated from scratch each time. Because grounding is fail-closed, an ungrounded claim is visibly absent rather than invisibly fabricated. Those three properties are what turn "cited" from a marketing adjective into an audit-defensible fact.
This is the layer underneath every deliverable the gateway produces — the gap analysis, the threat model that doubles as compliance evidence, the AI Act readiness assessment whose obligations depend on getting the system's risk classification grounded against the actual text rather than a model's recollection of the categories. The same search, get_provision, and validate_citation tools sit behind all of them, across 28 audited law jurisdictions live, out of 119 law corpora built, an EU regulations corpus of 102 instruments with article-level addressable provisions, and 262 security frameworks. The breadth is on /coverage; the mechanics are on /how-it-works.
The architecture is deliberately boring at the seam. The gateway speaks MCP over OAuth 2.1, and the whole thing is EU-hosted with no server-side model holding your data — your agent does the reasoning, the gateway supplies the law with a citation that re-validates. Quickstart is at /docs/quickstart, and the tier matrix — free at 100 searches a day, Premium at €249 a month — is on /pricing.
The whole post reduces to one sentence. A citation in a compliance deliverable is a contract with three clauses — it resolves to a real provision, its text matches the claim, and it refuses when neither holds — and the day someone with authority reads your citation as a fact is the day you find out whether you shipped a contract or a string.