Truth and provenance

A claim only lands in the graph if its evidence is a literal substring of the source, disagreeing claims surface through a query instead of a scroll-back, and a correction you make outranks the model structurally, not by convention.

Verified 2026-07-08 @ 3ee3d0fe

What this is

Truth on Ouroboros is not a confidence score an agent reports about itself. It is two mechanical checks that run whether or not anyone asks for them: a mined claim only lands in the knowledge graph if its evidence quote is a literal substring of the source document, and once you’ve corrected a claim, that correction structurally outranks the model’s version on every later read — not as a ranking preference an agent could ignore, but as a check that runs before a conflicting write is even allowed to land.

Why it exists

An early pass at relationship extraction in this codebase once produced Example Person employee_of Example Labs, role: CEO from a document that actually said Benjamin Smith, CEO; reports_to: Director (Example Brian Person) — the model connected the wrong two names with total confidence and no visible seam. Verbatim-quote grounding exists specifically to catch that failure: if the claim can’t point at a real sentence, it doesn’t get written. Contradiction surfacing exists for the sibling failure — two mining passes asserting different things about the same fact with neither one ever noticing, because a chat transcript has no WHERE clause to catch it.

And a correction has to actually stick. Telling an agent “no, that’s wrong” inside one conversation doesn’t help if next week’s mining pass or a different agent’s session quietly reasserts the old value. The user-outranks- LLM covenant is the guarantee that a correction is durable state, not a one-turn courtesy.

How it works

Grounding happens at write time, in proxy/src/extraction/claimGraph.ts. Every claim the extraction model emits carries an evidence string capped at 300 characters; verifyOne() normalizes both the evidence and the source text (smart quotes, dashes, markdown bold, unicode arrows all collapse to a plain form) and runs a literal .includes() check. No match, no write — the module’s own header comment calls it “a for-loop, not a model, not negotiable.” The same primitive is exposed as three separate tools for different moments: sophia.verify_evidence batch-checks a list of quotes against source text before a mining sub-agent submits anything, sophia.preflight_not_in_source checks one quote against one document by id, and sophia.verify_citation re-runs the check against a wiki page’s current source — catching drift when a document gets edited or re-mined after the citation was written.

The correction side runs at read time. Every query against the knowledge table orders corrected_by_user='1' rows first, and the write path checks for an existing correction before it dedupes or inserts — so a later extraction pass that would touch the same content is skipped outright, not merely outranked. Knowledge Graph covers that row shape in full; this page is about what an agent does once the row is there.

Two more primitives close the loop. sophia.find_contradictions groups active claims by (subject, predicate) and flags the ones with disagreeing objects within the same date bucket, with severity high whenever one side is a user correction. sophia.evidence_for walks the other direction from a single fact — its source document, its supersession chain, and any correction that touched it — in one call instead of three round trips.

grounding at write time, the covenant at read time
flowchart TB
  subgraph write["Write-time grounding"]
    M["Model emits claim + evidence"] --> V{"normalize + .includes()<br/>against source text"}
    V -- match --> ROW[("claim row<br/>in the graph")]
    V -- no match --> DROP["dropped — never written"]
  end
  subgraph read["Read-time covenant"]
    ROW --> Q["sophia.query_knowledge"]
    U["You correct a claim"] -->|"corrected_by_user='1'<br/>skips future overwrite"| ROW
    ROW -->|"ORDER BY corrected_by_user DESC"| Q
  end
  style U fill:#1a4d2e,stroke:#22c55e,color:#fff
  style DROP fill:#3a1a1a,stroke:#ef4444,color:#fff

What your agent does with it

// Real responses from this daemon, captured 2026-07-08:
const check = await sophia.verify_evidence({
source_text: 'The daemon journals every write with full before/after JSON...',
quotes: [
  { id: 'real', text: 'journals every write with full before/after JSON' },
  { id: 'fabricated', text: 'encrypts every write with a per-user key' },
],
});
// → { ok: false, found_count: 1, results: [
//     { id: 'real', found: true, match_span: [11, 59] },
//     { id: 'fabricated', found: false } ] }

const conflicts = await sophia.find_contradictions({ entity_id: 'bb3ad5a2-...' });
// → { contradiction_count: 3, by_severity: { high: 0, medium: 2, low: 1 },
//   contradictions: [ { subject: 'ouroboros repo', predicate: 'has_tooling_recommendation',
//     objects: [ /* two near-duplicate claims from the same document */ ], severity: 'medium' } ] }

const trail = await sophia.evidence_for({ fact_id: '7bb78a47-...' });
// → { fact: { predicate: 'has_tooling_recommendation', is_active: true },
//   bitemporal: { chain_parents: [], chain_children: [] },
//   correction: { corrected_by_user: false } }

The first call is the grounding primitive itself — one real quote, one fabricated one, run through the same check the extraction pipeline runs on every claim before it writes anything. The second is contradiction surfacing on a live entity: two near-identical tooling recommendations mined from the same document, medium severity because neither side is a user correction. The third is per-fact provenance — on an uncorrected, un- superseded fact the chain comes back empty, which is itself the honest answer: nothing has challenged this claim yet.

Boundaries

Grounding applies to claim-typed rows with a populated evidence field — the majority of what mining produces day to day (entity_mention and summary rows carry no evidence to check, and free-text notes written via sophia.remember_fact never had a source quote in the first place; those get a force-capped confidence instead, covered on Knowledge Graph). It categorically does not cover what an agent says out loud in a response — an agent can still summarize, extrapolate, or reason past what’s grounded. The check runs once, at the moment a claim is written to the graph; a fluent paragraph built on top of three grounded facts and one inference is not itself re-verified word by word.

What every past version of a corrected or superseded row looked like, and how to revert one, is Time Machine, not this page. The daemon and its storage layers are The State Layer; the full tool catalog these calls are drawn from is MCP Surface; the five enforced rules this page’s mechanics support are laid out end to end at Trust Covenant.