Open a matter in our product and you get three cross-document views of it. Facts is a citation-backed ledger of who, what, when, and how much. Summary is a narrative with every claim tied to a source. Chronology is a dated timeline of what happened.
The obvious way to build those three is also the wrong way. The obvious way is to let each view read every document in the matter and pull out what it needs. Facts re-reads the whole matter to find facts. Summary re-reads the whole matter to write prose. Chronology re-reads the whole matter to find dates.
That design has three problems, and the third is the one that hurts a lawyer. It is slow, because a matter with fifty documents gets read three times. It is expensive, for the same reason. And it is inconsistent, because three separate reads of the same document can quietly disagree. Facts says the agreement was signed on one date, Summary implies another, and Chronology plots a third. Now the tool has three tabs that contradict each other about the same file, and the person paying for it has to figure out which one to trust.
This post is about the design we chose instead. Extract each document once into a shared evidence spine, then make every feature a reader of that spine rather than its own extractor. There are two small snippets, simplified for readability, because the interesting parts are decisions you can see.
What one extraction produces
When a document enters a matter, we read it once and turn it into a set of small, atomic, verified units of evidence. Each unit is one fact, in plain English, with a verbatim quote that supports it, and a pointer back to exactly where in the document that quote lives.
The verbatim quote is not decoration. Before a unit is allowed to exist, its quote is checked to be a literal substring of the text the extractor actually read. If the quote is not found in the source, the unit is dropped. This is the same anti-fabrication rule we use elsewhere: the tool is never allowed to invent supporting text, because a quotation a lawyer cannot find in the document is worse than no quotation at all.
Here is the shape of one unit, simplified.
# Simplified: one row in the shared evidence spine.
{
"document_id": "...",
"quote": "verbatim text copied out of the document", # verified as a literal substring of the source
"normalized_fact": "The MSA was executed on 2024-03-01 by Acme Corp and Beta LLC.",
"fact_type": "agreement_execution",
"entities": [{"role": "party", "name": "Acme Corp"}],
"chunk_id": "...", "page_start": 4, # where it came from, so a reader can open the source
"confidence": 0.9
}
In plain English: a unit carries the plain-language fact, the exact words that prove it, the parties and dates and amounts involved, and the coordinates (document, page, character offsets) to click straight back to the source. That last part is what turns every downstream fact, claim, and timeline event into something clickable rather than something you have to take on faith.
Mechanically, a document is read once, its full text is reassembled and split into overlapping windows of a few thousand characters each, and every window is scanned in parallel for atomic facts. The overlap exists so a fact that straddles a window boundary is still caught whole, and the surviving facts are de-duplicated across the overlap by normalized text before they are written.
These units land in one shared table, and a companion table records, per document, that the extraction ran and what it produced. That second table is a cache, and it is the whole reason "extract once" is literally true rather than aspirational.
Here is the shape of the whole thing: one extraction per document feeds a shared pool that Facts, Summary, and the chat all read, while Chronology runs its own extraction into a separate store.
The cache that makes "once" real
The per-document extraction is keyed, not run blindly. The key is the document, its version, a hash of its text, and the versions of the extraction logic and model that produced the evidence.
# Simplified: the per-document cache key.
key = (tenant_scope, document_id, document_version, text_hash, prompt_version, model_version)
# Key already marked extracted -> return instantly, no model call.
# Otherwise: read the document once, extract, store, mark it done.
In plain English: if a document has already been turned into evidence, and nothing about the document or the extractor has changed, the second request for that evidence is free and instant. It does not re-read the document. It does not call a model. It returns what is already there.
Two consequences fall out of that key, and both matter to a buyer. Because the text hash is in the key, editing or re-uploading a document produces a different key, so its evidence is re-extracted rather than served stale. Because a full regeneration only re-extracts the documents that actually changed since the last one, adding a single file to a large matter is cheap: the fifty unchanged documents keep their cached evidence, and only the new one is read. Concretely, an incremental refresh diffs the documents against the timestamp of the last completed generation and only re-reads the ones touched since, so the per-document text fetch is paid for changed files, not the whole matter.
Two more fields in that key are quiet but load-bearing. The prompt version means that improving the extractor invalidates every document's evidence at once, transparently, the next time it is asked for, so there is no stale-schema evidence lingering from an old prompt. The model version means a deeper, stronger extraction and a standard one cache side by side rather than overwriting each other, so upgrading a matter to the deep pass does not silently discard the cheaper evidence you already have.
Reader one: the fact ledger
The Facts tab does not read your documents. It reads the shared evidence pool.
Its whole job is to take the pile of verified units and organize them: group the ones that state the same fact, attach the supporting quotes as citations, and flag contradictions. The grouping is deterministic, on the fact's own signal (its type, the entities it names, its date, its amount), so identical facts stated across several documents collapse into one fact carrying several citations, while genuinely different facts stay separate.
Conflict detection is deliberately narrow. Only single-valued facts are eligible to conflict, so an amendment that changes the governing law or the execution date surfaces as a flagged contradiction, while many different payment amounts about the same company stay what they are, which is many distinct facts rather than a fight. Every fact in the ledger is a real, verified quote from a real document, and every citation opens the passage it came from.
Reader two: the summary that cannot disagree with the ledger
The Summary tab is where the payoff shows. It is built on a hard internal rule: the summary does not run its own extractor.
It reuses the exact same per-document extraction that Facts uses, populating the exact same shared pool, and then it reads that pool to write its narrative. When it drafts a section, every claim has to cite one or more of those shared evidence units by number, or it does not survive.
Sit with what that buys you. Facts and Summary are not two engines that happen to agree most of the time. They are two readers of one set of verified quotes. The Summary cannot assert something about a document that the Facts ledger does not also have a unit for, because both are drawing from the same well. The two tabs agree because there is only one underlying source of truth, not because we ran a reconciliation pass to paper over two.
This is the difference between "our views are usually consistent" and "our views are consistent by construction." Only the second one is safe to sell to someone who will act on it.
The same discipline, one table over
Chronology is the third view, and it is honest to say it is not identical in plumbing. The dated timeline is extracted into its own store of events, because a timeline needs things the fact ledger does not, like approximate dates, date ranges, and event types.
But it obeys the same principle. Each document's events are extracted once and persisted, and every consumer of the timeline reads those persisted events rather than re-deriving them: the tab that renders it, the export that ships it, and, importantly, the chat.
That last consumer is the quiet proof the principle generalizes. When you chat about a matter, the assistant does not re-analyze your documents turn after turn. It reads a compact digest of the already-extracted analysis (the Facts ledger, the Summary, the Chronology) and answers from the matter's established record. That reader is strictly read-only over the shared analysis: it never writes and never deletes. It is one more feature that consumes the spine instead of rebuilding it.
Why "never delete a unit" is a rule, not a preference
There is a discipline that holds all of this together, and it is worth stating plainly because it is easy to get wrong. The evidence spine is read-only to its consumers. Summary never deletes from the shared pool, because Facts co-owns it. The chat reader never writes to it at all.
The reason is provenance safety. A rendered summary snapshots the quote it cited and holds a soft reference to the underlying evidence unit. If Facts later re-extracts a document (because you edited it, say), and the set of units changes, the reference is allowed to go null, but the snapshotted quote the reader already saw does not vanish or silently change under them. A view that was built and shown stays faithful to what it showed. If any feature were allowed to reach into the shared pool and delete units it thought it no longer needed, it would be yanking the ground truth out from under every other feature that cited them. So the pool only grows and refreshes; it is never quietly pruned by a consumer.
What this is really buying the buyer
Strip out the engineering and three things are true for the person using the product.
The three views of your matter agree with each other, because they read one extraction, not three. Every fact in the ledger, every claim in the summary, and every event on the timeline traces back to a specific quote in a specific document, and you can click it open. And building those views is fast and stays fast as the matter grows, because a document is read once and its analysis is cached, so adding the fifty-first file does not re-read the first fifty.
The failure mode this design exists to prevent is the one that erodes trust the fastest: a tool that shows you a confident summary, a separate list of facts, and a separate timeline, and lets them quietly contradict each other while every tab wears the same authoritative font.
A test you can run, and questions to ask a vendor
You do not need our architecture to check whether a tool has this discipline or is faking it.
Run this. Load a handful of related documents into a matter, then generate every cross-document view the tool offers. Find one concrete fact that appears in more than one view, a signing date or a dollar amount, and check that the views agree on it. Click the citation behind it in each view and confirm each one opens the same underlying source language, not a vague "based on your documents." Then add one more document and regenerate. If the tool grinds through the entire matter again from scratch, it is re-extracting per request; if it comes back fast, it is reading a cache.
And ask the vendor these.
Do your different views of my matter read from one extraction, or does each feature analyze my documents independently? If it is the second, ask how they keep the views from disagreeing, and listen for whether the answer is "by construction" or "we try to."
Does every fact, summary claim, and timeline event link to a specific passage I can open, or only to the document as a whole? A pointer to a source you can read beats a confidence score you cannot.
When I edit or add a document, do you re-extract everything or only what changed? The answer tells you whether the tool was built to live in a real matter that grows, or to look good in a one-shot demo.
A tool whose views agree because they share one verified source, and whose every assertion opens the passage behind it, is doing the boring work correctly. That boring work is the entire difference between an answer you can file behind and an answer you have to double-check by hand.
