the manual

The ontology

What the record is made of, and how a document becomes it. A small set of first-class objects — sources, chunks, claims, evidence, entities, questions, patterns — one typed graph between them, and everything else recomputed from those as they change. Every mechanism here is what the runtime does, with the runtime's own numbers.

the objects, and the edges between them
sourcesnapshot · sha256 · rightschunk~1,200 chars · offsetsevidencequote · offsets · verifiedclaimepistemic · statuspagespan · digest · tagsentitycanonical · kind · aliasesquestionorigin · prioritypatternkind · supportsurfaceaxis · cell · densityrelation linkkind · lineagesanswerrung · confidencebreakkind · tierchunksquote inbacksspansmentions → resolved_tosurveyborn_ofmaterialisedactor · place cellsstandingdocument cellskinship · cites (source ↔ source)notifiedsolid — first-class · dashed — recomputed by the loop from the objects above it
The ontology

The objects

seven first-class things, one graph between them, and everything else derived

The record is a small number of first-class objects with a typed graph between them. Sources are documents — a web page, a PDF, a scan, a book, a file — stored as immutable, content-addressed snapshots. A source is cut into chunks, ~1,200 characters on paragraph boundaries, each remembering its byte offsets in the snapshot. Claims are atomic statements extracted from chunks. Evidence ties a claim to an exact quote in a chunk, with the quote's offsets and whether the bytes were found. Entities are the people, organisations, places, events and things claims mention, resolved from the names sources use. Questions are the frontier — what the loop works. Patterns are what the slow loop finds in the whole record.

Everything else is derived from those and recomputed as they change: pages (spans of an uploaded document's snapshot with a digest each), surfaces (the record along one axis), trajectories (momentum and entropy of a thread), standing answers, the narrative, breaks, the ledger of what each stage cost. Notes — your margin — are the one table deliberately outside all of it: never evidence, never cited, never graded.

The graph is one table, links, where every edge has a from-object, a to-object and a kind. Which kinds exist, and what each means, is in the last section of this page.

objectwhat it carries
sourceurl, kind (web · pdf · file · manual · fixture), title, author, published_at, status (candidate › fetched › extracted › rejected), content_hash (sha256 of the snapshot), rights
chunka char span of one source's snapshot: ord, text, char_start, char_end
claimtext, epistemic (fact · inference · hypothesis · speculation), status (provisional · corroborated · contested · retracted), created_by (the role that emitted it), meta (the entity slugs it mentions, any typed relations)
evidenceclaim → chunk, stance (supports · refutes · mentions), the exact quote, its char_start/char_end, verified (the quote was found in the bytes)
entitycanonical name, kind (person · org · place · event · thing · unknown), aliases (the mention slugs it absorbed), meta (a geocode, a Wikidata id)
questiontext, parent, origin (seed · secondary · decompose · pattern:<id> · gap), status (open · active · closed · dead), priority, closed_reason
patternkind (ten of them — below), scale (1 within a source · 2 across the corpus · 3 the investigation), description, support (the objects it rests on), strength, status (candidate · confirmed · dismissed)
linkfrom_type/from_id → to_type/to_id, kind, weight, meta — the whole graph
The ontology

What happens when a document is digested

from a dropped file to claims, entities, patterns and questions — every stage, in order

An upload goes through the same ontology as a page the loop fetched; the only extra table is pages. The document's text becomes one snapshot, so evidence offsets are ordinary, and each page is a char span over it. Every stage is metered in the ledger (ocr · extract per chunk · summarize per page · document · advance), and the wall-clock of each is kept on the source.

  1. 1
    Read

    A PDF is read page by page. An image — the JPEG a newspaper archive gives you — is wrapped as a one-page PDF sized from its own pixels, so OCR renders it at native resolution. Plain text and Markdown are one page. The same bytes dropped twice (file hash) or the same extracted text (content hash) is caught before anything is spent.

  2. 2
    OCR, only where there is nothing to read

    Pages with no text layer, or a garbled one, go through OCR: a fast pass, then a stronger one where the fast one struggles. Pages the OCR flags as handwritten are counted; if half or more of the text-bearing pages are handwritten, the whole document is extracted in notes mode — one claim per meaningful line, verbatim quotes, names kept exactly as written.

  3. 3
    The text-health gate

    Character-level damage is measured before ingest — mid-word punctuation from a broken PDF font map, replacement characters, mojibake — per thousand words. A corrupt text is refused rather than allowed to mint garbage claims, unquotable evidence and misspelled entities across the whole record; a suspect one is stamped with its measurement.

  4. 4
    Snapshot, source row, chunks

    The bytes are stored under their sha256; the source row records kind, title, rights (default-closed: copyrighted unless the host is .gov or .mil — a person downgrades deliberately) and status. The text is split into chunks of about 1,200 characters on paragraph boundaries, each with its offsets.

  5. 5
    Extraction, every chunk

    The extractor reads each chunk and returns claims: one proposition each, no specifics beyond the text, an exact contiguous quote, an epistemic grade that is what the source licenses (a reporter's guess is at best inference), the entities mentioned as kebab-case slugs, and — when the text states one — a typed relation. Chunks run in parallel; a chunk that fails yields nothing rather than a stub. Library and archive furniture (“a file named … is available for download”, “added to the collection”) is filtered before it becomes a claim.

  6. 6
    Verification, deterministic

    The quote must appear in the chunk — or, failing that, anywhere in the snapshot. If it does, the evidence row gets the exact offsets and verified = 1; if it does not, the row is kept and marked unverified. Nothing is asked whether a quote is real; the bytes decide.

  7. 7
    Dedup and mentions

    A claim whose normalised text already exists gains another piece of evidence instead of a twin row — that is what makes corroboration countable. Every entity slug on the claim becomes a mentions link from the chunk and from the claim.

  8. 8
    Page digests, then the document digest

    The summariser reads each page: a 2–4 sentence summary, 3–8 lowercase tags, the dates it mentions, the proper nouns. Then it reads the page summaries and writes the document digest: one paragraph, tags, a document type (report · letter · memo · article · book · transcript · form · legal · government · newspaper · other), the best title, the author or issuing body, the document date, and the period of events it covers — which can set the source's title, author and publication date. Each claim is placed on the page holding its first verified quote.

  9. 9
    Advance: the same chain a cycle runs

    Index (embeddings for the new chunks, claims and questions) → resolve entities (and geocode places, look up Wikidata) → provenance survey (kinship, citations, derivations) → the skeptic over the new claims → corroborate → survey for patterns → surfaces → dynamics → interrogate the patterns for new questions. The new material is in the graph, the timeline and the frontier before the document-ready notice is sent.

what to look for

Digests are for finding and reading; claims are the record. A page's summary is never evidence.

An upload extracts every chunk, because you asked for this document. A page the loop fetched is extracted up to a per-cycle budget, ranked by similarity to the question that found it; the rest wait for a later cycle.

Removing a source deletes its pages, chunks and evidence, and any claim left with no evidence anywhere — claims other sources also evidence stay. Snapshots are content-addressed and kept.

The ontology

How a claim is graded

two axes that are never collapsed: what kind of statement it is, and how well the record holds it

Epistemic grade is about the statement: fact — the source states it as so; inference — the source reasons to it; hypothesis — the source proposes it; speculation — the source guesses. It is set at extraction from what the source licenses and enforced as a database constraint. It never climbs: the skeptic can downgrade, nothing upgrades.

Status is about the record: every claim starts provisional. It becomes corroborated when it is a fact, its quote verified, and its verified supporting sources span two or more independent lineages — copies of one account collapse into one (see Lineage). It becomes contested when evidence refutes it, or when none of its evidence verifies. Retracted is a person's ruling, and the loop never touches a retracted claim again. You can set any status yourself; your ruling outlives the worker's later pushes.

Evidence stance is how a quote bears on a claim: supports, refutes, or merely mentions. Corroboration counts only verified supporting evidence; one refuting quote is enough to contest.

the evidence ladderwhat a rung means — the standing answers and breaks grade by it
mentionedthe claim exists; no quote at all
quotedit has evidence, but the quote could not be found in the bytes
verifiedat least one supporting quote was found in the snapshot at its recorded offsets
corroboratedstatus corroborated — verified evidence from more than one source
independentcorroborated, and its verified sources span two or more independent lineages
what to look for

A fact can be contested and a speculation can be corroborated: the grade says what was claimed, the status says how the record holds it.

‘Re-verify’ re-runs the quote matching over every unverified evidence row with the current snapping logic, computed, not written — a later improvement can heal evidence recorded earlier, and a contested-for-no-evidence claim can clear.

Near-duplicate claims that disagree on a number, or claims about one entity that disagree on a date, a count or an amount, become contradiction patterns; the judge confirms the real ones.

The ontology

Entities: from a mention to a name

slugs from the extractor, grouped deterministically, constrained by your rulings

The extractor does not name entities; it emits mention slugs — kebab-case strings like gabe-valdez or officer-gabe-valdez — and each becomes a mentions link from the chunk and the claim. The resolver then groups slugs into entity rows, computed, not written, by rules applied in order: one slug's tokens contained in another's (a bare single token must sit in the surname position — valdez joins officer-gabe-valdez, michael does not join every Michael in the corpus); an exact match once titles are stripped (dr, sheriff, senator…); a shared surname-like last token with compatible given names, nicknames included.

Your identity decisions constrain it: a pair ruled same is joined first; a pair ruled different may never share a group, whatever a rule would do. The canonical name is the fullest slug in the group; the kind is inferred from its last token — organisation suffixes, place suffixes, a person if it carried a title or is two or three plain tokens, else unknown. Entity ids follow the canonical name, so re-resolution every cycle reproduces the same ids and links: no growth, no reshuffling. Junk slugs — archive and library chrome, domain fragments — never become entities.

Places are geocoded; entities with a match are given a Wikidata id, which later serves as an identifier for identity questions. Two entities that might be one person are not merged by the system: they become an identity pattern with the evidence for and against, and nothing merges until you rule.

identity evidencestrong = alias or identifier, or name evidence plus overlap with no co-mention · possible = two lines for · weak = one
aliasthe text says one name is another's — known as, a.k.a., went by, signed as (weight 0.9)
identifierthe same Wikidata id on both (0.9); a different id on each is evidence against
nicknamesurname shared, given names a known nickname pair (0.5)
spellingsurnames one edit apart (0.4)
tokena shared distinctive token, for non-persons (0.35)
overlapshared neighbours in the corroborated graph, weighted so hubs count less, tested against a shuffle of the sources (0.3; 0.5 when beyond chance)
timetheir claims' years overlap or abut within two years (0.15)
againstidentifier mismatch · a lifespan the two cannot share · kind mismatch · co-mention (named together in one passage — usually two people)
what to look for

‘Conflate’, ‘hapax’ and ‘twins’ are suspect patterns about entities: one entity pooling several surnames, a person seen in exactly one passage, two entities a letter apart.

Map-out shows one entity completely — every claim, source, relation and question that touches it, and where the coverage runs out.

The ontology

Relations: typed claims

money and identity, stated by the text, arrow-checked, and carried on the claim

A relation is never written straight into the graph. It is structure on a claim — so its quote is verified like any other, corroboration counts independent lineages, a contradiction can fire on two disagreeing amounts, and a person can retract it. The extractor adds one only when the text states it (an attempt, an offer, a denied ownership is not a relation), with the text's own amount and currency, never converted, and the year the text gives.

Direction is where an extractor goes wrong, and a confident wrong arrow is worse than no edge: the skeptic reads the quote and confirms, corrects or drops the arrow — bounded per source. Passive spellings are folded so a fact has exactly one directed edge: B was paid by A is pays(A, B).

After resolution, relations are materialised as entity → entity links, one per (from, to, kind), whose weight is the number of independent lineages behind it — not the number of claims. Following the money from one entity walks those edges and names the leads: a sink (money in, none out), a spring (money out, none in), an edge resting on a single lineage, an edge backed by a contested claim.

kind, read left to rightgloss
paysmoney from A to B for something (event)
invests_inA puts capital into B (event)
contractsA engages B under contract (event)
transfers_tomoney or assets move from A to B (event)
ownsA holds B, wholly or in part (state)
representsA acts for B — counsel, agent, lobbyist (state)
insuresA carries B's risk (state)
banks_withA holds accounts at B (state)
alias_ofthe text says one name is another's — symmetric; feeds identity
The ontology

Lineage: who copied whom

dates, rights, kinship, citations — and the one number corroboration actually uses

Publication dates are inferred strongest-first: the page's own metadata, a date in the URL path, a date in the title, then text anchored to publication language — a byline, a dateline, a copyright line. A bare year in the body is never accepted. A date you set by hand outlives the worker's pushes. Rights default closed: every source is copyrighted unless it comes from a .gov or .mil host (government) or is a fixture; a copyrighted source's text never leaves the investigation in a package — the claims travel with the content hash.

Kinship is textual: two sources sharing enough distinctive six-word shingles (three, or twelve on the same site, where template chrome is shared) get an undirected kinship link weighted by how much they share. Kin at or above 0.5 fold into one lineage everywhere the system counts independent sources — the skeptic, corroboration, relation weights, breaks, the answers' ladder. Five publications repeating one wire story are one lineage.

Direction is earned, not guessed. A citation — source A naming source B's title verbatim, or its author with its year — is a directed cites link. A derivation is only written when strong kinship, date order and a judge's confirmation agree; similarity plus chronology alone never earns an arrow. When the corpus has many tellings of one account, drift measures whether the wording strengthened over the years while no new lineage arrived beneath it.

what to look for

Lineage is the view of this: citations as arrows, kinship as kinship, and the independent lineages corroboration counts.

A ‘break’ of kind echo is exactly this failure caught: a claim that looks corroborated across several sources that are one lineage.

The ontology

Surfaces: the record along one axis

the terrain read by time, place, actor and document — density, coverage, gaps

The store is the terrain — claims, evidence, entities, dated facts, documents. A surface is a reading of that terrain along one axis, made of cells, each carrying its claim mass (density = its claims over all claims), its verified share (coverage), its sources, its status mix, and the claims that sit there. Four axes are built in, and each also has a summary cell — placement rate, cell count, top cells, gap count.

How things land on a surface: the time axis places a claim in the year its text carries; the actor axis has one cell per person or organisation entity, and a claim lands there through its resolved mentions; the place axis has one cell per place entity, geocoded or not; the document axis has one cell per source. Every entity of the right kind gets a cell even with zero claims, because an empty cell is a gap too. Surfaces are recomputed every cycle and after every upload, never edited.

Gaps are read off the surfaces by fixed rules and emitted as gap patterns, so the slow loop can turn them into frontier questions. A surface gap that later closes is a break.

gap rulewhat it flags (at most six per axis)
time · holea year with nothing between two years that each hold three or more claims
time · thin coveragefive or more claims in a year, under 30% of them verified
time · single sourcesix or more claims in a year, all from one source
actor · uncorroboratedan actor with four or more claims from one source
place · no geocodea place with three or more claims that could not be located
document · silenta source with no claims at all
document · weakly verifiedfive or more claims, under half of them verified
The ontology

Trajectories: momentum and entropy

a thread through the store with a clock

A trajectory is a thread with a clock. Two kinds: a question thread — a frontier question with its lineage of child questions, the sources found through them, and the claims those sources evidence — and an entity thread — an actor, place or thing and every claim naming it. Each cycle a thread gains mass: 1.0 per verified claim, 0.4 per unverified, ×1.5 when corroborated, ×0.7 when contested. Velocity is a moving average of mass gained, acceleration its change, and momentum = velocity · √mass · (1 + ¼ ln sources). Coherence is the cosine between successive cycles' centroids in embedding space.

Two entropies say what a thread is made of: over its sources (low = a monoculture) and over the entities it names (low = focused). Per claim, surprise is how improbable its entity combination is under the corpus's co-occurrence; novelty is one minus its nearest neighbour; a bridge is a claim joining two well-established entities that almost never co-occur.

flagrule
accelerating · deceleratingmomentum rising or falling
focused · diffuseentity entropy under 2.0 bits · over 3.5
coherentsuccessive centroids at cosine 0.8 or above
dormant · woke_upflat mass for two cycles · a gain after three flat cycles
monocultureone source, five or more claims
bridgingthe thread holds a bridge claim
what to look for

The quadrants — accelerating-focused, accelerating-diffuse, dormant-focused, dormant-diffuse, steady — are the Dynamics view's map. Converged and stalled breaks come from the same numbers.

The ontology

Patterns: the slow loop's vocabulary

every third cycle the whole record is read for structure — ten kinds, one detector each

The survey is mechanical — computed, not written — over the whole corpus, and every pattern has a stable id, so re-surveying updates strength and support rather than minting twins, and a pattern you dismissed is never resurrected. A pattern a live question was born of is kept even when it drops out of the cut. Then interrogation hands the strongest forty fresh patterns — not raw claims — to the director, one scale up, and the questions it proposes land on the frontier at priority 0.85, linked born_of the pattern.

Corroboration is the survey's mirror image and writes links, not patterns: claims from different lineages sharing an entity and a compatible attribute value, passing the same-fact judge, get a corroborates link, and a provisional claim so confirmed is promoted.

kindwhat detects it
cooccurtwo entities recurring together across two or more chunks and two or more lineages, strength calibrated against a shuffle of the sources — chance-expected pairs fall toward zero
clusterthree or more claims from two or more sources within cosine 0.6 of each other
bridgean entity connecting two or more communities of the corroborated graph, ranked by betweenness
contradictiona refuting quote; one entity with conflicting dates, counts or amounts across sources (judge-confirmed, or lexical only); near-duplicate claims disagreeing on a number
temporala year three or more claims mention — and drift, when an account strengthened without new evidence
gapan entity mentioned three or more times that no question addresses; and every surface gap above
unhelda document the record cites but does not hold — a deposition, a letter, a case number; the most actionable kind, since its question is ‘go get this’
suspectconflate (one entity pooling several surnames) · hapax (a person seen once) · twins (two entities a letter apart)
missingtwo entities that never meet in a corroborated passage yet keep sharing associates, organisations, places and years — a structural inference, labelled as one, never a new node
identitytwo entities that may be one, with the evidence for and against; nothing merges until you rule
The ontology

Questions and standing answers

where a question comes from decides its priority; what the record says about it is written down and graded

A question's origin sets its priority: the primary you wrote, 1.0; a secondary, 0.95; one born of a pattern, 0.85; a routine decomposition of a bigger question, 0.7; a gap question about an entity nobody has asked about, 0.6. Each cycle takes the highest open question, older first on a tie — unless the last one worked was pattern-born, in which case the next pick is forced to be something else, so emergence and coverage interleave. Statuses: open, active (being worked), closed (the director closed it, with a reason), dead (you killed it).

The loop stops when it has spent its cycles, when the frontier is empty, or when — for a window of consecutive cycles — new claims per source and novelty of new text both stay below their floors. Measured, not felt.

A standing answer is what the record says about each question, in writing, composed strictly from retrieved claims with inline citations, and graded by rule: its best cited rung on the evidence ladder, and a confidence — contested if a cited claim is contested and fewer than two corroborated ones outweigh it; answered if the best rung is corroborated or better; partial if verified; lead otherwise; none if nothing is cited. The cited basis is hashed: an unchanged basis means an untouched answer and no cost; a changed one means a new version with its delta in words.

The ontology

Breaks, narrative, notes, ledger

the derived layers, briefly

A break is a moment the investigation's knowledge actually changed, detected deterministically after every cycle, survey and upload from the tables above. Kinds: independent (a claim now spans two lineages), echo (looks corroborated, is one lineage), contradiction, resolved, question_closed, load_bearing (an entity the graph's shape depends on), structure (a cluster more connected than chance, checked two ways), filled (a surface gap closed), converged and stalled, and the answer kinds first_answer, answered, answer_contested. At most three per pass are promoted to the tier that interrupts — notified, first on the Overview — and only at importance five or more; the rest go to the case log.

The narrative is an outline the director plans (re-planned when enough new material has arrived) and sections the writer composes strictly from in-scope claims, every factual sentence carrying its claim marker; three sections — open questions, contested, sources — are built from the store, computed, not written. A section's basis is hashed like an answer's: unchanged, untouched; changed, rewritten as a new version with the old one kept.

Notes are your margin — on any object, with an anchor to a char range, a time range or a point — and the one table with no epistemic ladder. The ledger records what every document and process cost: tokens and calls per stage, with units, so the cost of an OCR page or an extracted chunk falls out.

The ontology

The graph, in one table

every edge kind in links, and what it joins

Everything relational is one table. Reading it is reading the investigation's structure.

kindjoins
mentionschunk → entity slug · claim → entity slug — the extractor's raw mentions
resolved_toentity slug → entity — the resolver's grouping; every join from a mention to a name goes through it
kinshipsource ↔ source — shared distinctive text, weighted; ≥ 0.5 collapses into one lineage
citessource → source — a named citation; the only signal that earns a direction on its own
derives_fromsource → source — kinship plus chronology plus a judge's confirmation
corroboratesclaim ↔ claim — the same fact from different lineages, judge-confirmed
pays · invests_in · contracts · transfers_to · owns · represents · insures · banks_with · alias_ofentity → entity — materialised typed relations, weight = independent lineages
born_ofquestion → pattern — where a pattern-born lead came from
known_tosource → investigator overlay — a source marked as known to a person, for the horizon